1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/unix/mediactrl.cpp
3 // Purpose: GStreamer backend for Unix
4 // Author: Ryan Norton <wxprojects@comcast.net>
8 // Copyright: (c) 2004-2005 Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
17 #include "wx/mediactrl.h"
21 #include <gst/gst.h> // main gstreamer header
23 // xoverlay/video stuff, gst-gconf for 0.8
24 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
25 # include <gst/interfaces/xoverlay.h>
27 # include <gst/xoverlay/xoverlay.h>
28 # include <gst/gconf/gconf.h> // gstreamer glib configuration
32 #include "wx/log.h" // wxLogDebug/wxLogSysError/wxLogTrace
33 #include "wx/app.h" // wxTheApp->argc, wxTheApp->argv
34 #include "wx/timer.h" // wxTimer
37 #include "wx/filesys.h" // FileNameToURL()
38 #include "wx/thread.h" // wxMutex/wxMutexLocker
39 #include "wx/vector.h" // wxVector<wxString>
44 #include "wx/gtk/private/gtk2-compat.h"
47 //-----------------------------------------------------------------------------
48 // Discussion of internals
49 //-----------------------------------------------------------------------------
52 This is the GStreamer backend for unix. Currently we require 0.8 or
53 0.10. Here we use the "playbin" GstElement for ease of use.
55 Note that now we compare state change functions to GST_STATE_FAILURE
56 now rather than GST_STATE_SUCCESS as newer gstreamer versions return
57 non-success values for returns that are otherwise successful but not
60 Also this probably doesn't work with anything other than wxGTK at the
61 moment but with a tad bit of work it could theorectically work in
64 One last note is that resuming from pausing/seeking can result
65 in erratic video playback (GStreamer-based bug, happens in totem as well)
66 - this is better in 0.10, however. One thing that might make it worse
67 here is that we don't preserve the aspect ratio of the video and stretch
68 it to the whole window.
70 Note that there are some things used here that could be undocumented -
71 for reference see the media player Kiss and Totem as well as some
72 other sources. There was a backend for a kde media player as well
73 that attempted thread-safety...
75 Then there is the issue of m_asynclock. This serves several purposes:
76 1) It prevents the C callbacks from sending wx state change events
77 so that we don't get duplicate ones in 0.8
78 2) It makes the sync and async handlers in 0.10 not drop any
79 messages so that while we are polling it we get the messages in
80 SyncStateChange instead of the queue.
81 3) Keeps the pausing in Stop() synchronous
83 RN: Note that I've tried to follow the wxGTK conventions here as close
84 as possible. In the implementation the C Callbacks come first, then
85 the internal functions, then the public ones. Set your vi to 80
89 //=============================================================================
91 //=============================================================================
93 //-----------------------------------------------------------------------------
94 // GStreamer (most version compatibility) macros
95 //-----------------------------------------------------------------------------
97 // In 0.9 there was a HUGE change to GstQuery and the
98 // gst_element_query function changed dramatically and split off
99 // into two separate ones
100 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
101 # define wxGst_element_query_duration(e, f, p) \
102 gst_element_query(e, GST_QUERY_TOTAL, f, p)
103 # define wxGst_element_query_position(e, f, p) \
104 gst_element_query(e, GST_QUERY_POSITION, f, p)
105 #elif GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 9
106 // However, the actual 0.9 version has a slightly different definition
107 // and instead of gst_element_query_duration it has two parameters to
108 // gst_element_query_position instead
109 # define wxGst_element_query_duration(e, f, p) \
110 gst_element_query_position(e, f, 0, p)
111 # define wxGst_element_query_position(e, f, p) \
112 gst_element_query_position(e, f, p, 0)
114 # define wxGst_element_query_duration \
115 gst_element_query_duration
116 # define wxGst_element_query_position \
117 gst_element_query_position
121 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
122 # define GST_STATE_FAILURE GST_STATE_CHANGE_FAILURE
123 # define GST_STATE_SUCCESS GST_STATE_CHANGE_SUCCESS
124 # define GstElementState GstState
125 # define gst_gconf_get_default_video_sink() \
126 gst_element_factory_make ("gconfvideosink", "video-sink");
127 # define gst_gconf_get_default_audio_sink() \
128 gst_element_factory_make ("gconfaudiosink", "audio-sink");
131 // Max wait time for element state waiting - GST_CLOCK_TIME_NONE for inf
132 #define wxGSTREAMER_TIMEOUT (100 * GST_MSECOND) // Max 100 milliseconds
134 //-----------------------------------------------------------------------------
135 // wxLogTrace mask string
136 //-----------------------------------------------------------------------------
137 #define wxTRACE_GStreamer wxT("GStreamer")
139 //-----------------------------------------------------------------------------
141 // wxGStreamerMediaBackend
143 //-----------------------------------------------------------------------------
144 class WXDLLIMPEXP_MEDIA
145 wxGStreamerMediaBackend
: public wxMediaBackendCommonBase
149 wxGStreamerMediaBackend();
150 virtual ~wxGStreamerMediaBackend();
152 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
157 const wxValidator
& validator
,
158 const wxString
& name
);
161 virtual bool Pause();
164 virtual bool Load(const wxString
& fileName
);
165 virtual bool Load(const wxURI
& location
);
166 virtual bool Load(const wxURI
& location
,
168 { return wxMediaBackendCommonBase::Load(location
, proxy
); }
171 virtual wxMediaState
GetState();
173 virtual bool SetPosition(wxLongLong where
);
174 virtual wxLongLong
GetPosition();
175 virtual wxLongLong
GetDuration();
177 virtual void Move(int x
, int y
, int w
, int h
);
178 wxSize
GetVideoSize() const;
180 virtual double GetPlaybackRate();
181 virtual bool SetPlaybackRate(double dRate
);
183 virtual wxLongLong
GetDownloadProgress();
184 virtual wxLongLong
GetDownloadTotal();
186 virtual bool SetVolume(double dVolume
);
187 virtual double GetVolume();
189 //------------implementation from now on-----------------------------------
190 bool CheckForErrors();
191 bool DoLoad(const wxString
& locstring
);
192 wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks
193 void HandleStateChange(GstElementState oldstate
, GstElementState newstate
);
194 bool QueryVideoSizeFromElement(GstElement
* element
);
195 bool QueryVideoSizeFromPad(GstPad
* caps
);
196 void SetupXOverlay();
197 bool SyncStateChange(GstElement
* element
, GstElementState state
,
198 gint64 llTimeout
= wxGSTREAMER_TIMEOUT
);
199 bool TryAudioSink(GstElement
* audiosink
);
200 bool TryVideoSink(GstElement
* videosink
);
202 GstElement
* m_playbin
; // GStreamer media element
203 wxSize m_videoSize
; // Cached actual video size
204 double m_dRate
; // Current playback rate -
205 // see GetPlaybackRate for notes
206 wxLongLong m_llPausedPos
; // Paused position - see Pause()
207 GstXOverlay
* m_xoverlay
; // X Overlay that contains the GST video
208 wxMutex m_asynclock
; // See "discussion of internals"
209 class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below
211 // Mutex protecting just the variables below which are set from
212 // gst_error_callback() called from a different thread.
216 Error(const gchar
* message
, const gchar
* debug
)
217 : m_message(message
, wxConvUTF8
),
218 m_debug(debug
, wxConvUTF8
)
226 wxVector
<Error
> m_errors
;
228 friend class wxGStreamerMediaEventHandler
;
229 friend class wxGStreamerLoadWaitTimer
;
230 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
)
233 //-----------------------------------------------------------------------------
234 // wxGStreamerMediaEventHandler
236 // OK, this will take an explanation - basically gstreamer callbacks
237 // are issued in a separate thread, and in this thread we may not set
238 // the state of the playbin, so we need to send a wx event in that
239 // callback so that we set the state of the media and other stuff
241 //-----------------------------------------------------------------------------
242 class wxGStreamerMediaEventHandler
: public wxEvtHandler
245 wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
)
247 this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
,
248 wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
));
251 void OnMediaFinish(wxMediaEvent
& event
);
253 wxGStreamerMediaBackend
* m_be
;
256 //=============================================================================
258 //=============================================================================
260 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
)
262 //-----------------------------------------------------------------------------
266 //-----------------------------------------------------------------------------
268 //-----------------------------------------------------------------------------
269 // "expose_event" from m_ctrl->m_wxwindow
271 // Handle GTK expose event from our window - here we hopefully
272 // redraw the video in the case of pausing and other instances...
273 // (Returns TRUE to pass to other handlers, FALSE if not)
275 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here?
276 //-----------------------------------------------------------------------------
281 draw(GtkWidget
* widget
, cairo_t
* cr
, wxGStreamerMediaBackend
* be
)
283 expose_event(GtkWidget
* widget
, GdkEventExpose
* event
, wxGStreamerMediaBackend
* be
)
286 // I've seen this recommended somewhere...
287 // TODO: Is this needed? Maybe it is just cruft...
288 // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay),
289 // GDK_WINDOW_XWINDOW( window ) );
291 // If we have actual video.....
292 if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) &&
293 GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
)
295 // GST Doesn't redraw automatically while paused
296 // Plus, the video sometimes doesn't redraw when it looses focus
297 // or is painted over so we just tell it to redraw...
298 gst_x_overlay_expose(be
->m_xoverlay
);
302 // draw a black background like some other backends do....
305 gtk_widget_get_allocation(widget
, &a
);
306 cairo_rectangle(cr
, 0, 0, a
.width
, a
.height
);
307 cairo_set_source_rgb(cr
, 0, 0, 0);
310 gdk_draw_rectangle (event
->window
, widget
->style
->black_gc
, TRUE
, 0, 0,
311 widget
->allocation
.width
,
312 widget
->allocation
.height
);
321 //-----------------------------------------------------------------------------
322 // "realize" from m_ctrl->m_wxwindow
324 // If the window wasn't realized when Load was called, this is the
325 // callback for when it is - the purpose of which is to tell
326 // GStreamer to play the video in our control
327 //-----------------------------------------------------------------------------
330 static gint
gtk_window_realize_callback(GtkWidget
* widget
,
331 wxGStreamerMediaBackend
* be
)
335 GdkWindow
* window
= gtk_widget_get_window(widget
);
338 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
),
339 GDK_WINDOW_XID(window
)
341 g_signal_connect (be
->GetControl()->m_wxwindow
,
343 "draw", G_CALLBACK(draw
),
345 "expose_event", G_CALLBACK(expose_event
),
353 //-----------------------------------------------------------------------------
354 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE
356 // Called by gstreamer when the state changes - here we
357 // send the appropriate corresponding wx event.
359 // 0.8 only as HandleStateChange does this in both versions
360 //-----------------------------------------------------------------------------
361 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
363 static void gst_state_change_callback(GstElement
*play
,
364 GstElementState oldstate
,
365 GstElementState newstate
,
366 wxGStreamerMediaBackend
* be
)
368 if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
)
370 be
->HandleStateChange(oldstate
, newstate
);
371 be
->m_asynclock
.Unlock();
377 //-----------------------------------------------------------------------------
378 // "eos" from m_playbin/GST_MESSAGE_EOS
380 // Called by gstreamer when the media is done playing ("end of stream")
381 //-----------------------------------------------------------------------------
383 static void gst_finish_callback(GstElement
*WXUNUSED(play
),
384 wxGStreamerMediaBackend
* be
)
386 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback"));
387 wxMediaEvent
event(wxEVT_MEDIA_FINISHED
);
388 be
->m_eventHandler
->AddPendingEvent(event
);
392 //-----------------------------------------------------------------------------
393 // "error" from m_playbin/GST_MESSAGE_ERROR
395 // Called by gstreamer when an error is encountered playing the media -
396 // We call wxLogTrace in addition wxLogSysError so that we can get it
397 // on the command line as well for those who want extra traces.
398 //-----------------------------------------------------------------------------
400 static void gst_error_callback(GstElement
*WXUNUSED(play
),
401 GstElement
*WXUNUSED(src
),
404 wxGStreamerMediaBackend
* be
)
406 wxMutexLocker
lock(be
->m_mutexErr
);
407 be
->m_errors
.push_back(wxGStreamerMediaBackend::Error(err
->message
, debug
));
411 //-----------------------------------------------------------------------------
412 // "notify::caps" from the videopad inside "stream-info" of m_playbin
414 // Called by gstreamer when the video caps for the media is ready - currently
415 // we use the caps to get the natural size of the video
418 //-----------------------------------------------------------------------------
420 static void gst_notify_caps_callback(GstPad
* pad
,
421 GParamSpec
* WXUNUSED(pspec
),
422 wxGStreamerMediaBackend
* be
)
424 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback"));
425 be
->QueryVideoSizeFromPad(pad
);
429 //-----------------------------------------------------------------------------
430 // "notify::stream-info" from m_playbin
432 // Run through the stuff in "stream-info" of m_playbin for a valid
433 // video pad, and then attempt to query the video size from it - if not
434 // set up an event to do so when ready.
436 // Currently unused - now we just query it directly using
437 // QueryVideoSizeFromElement.
440 //-----------------------------------------------------------------------------
441 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
443 static void gst_notify_stream_info_callback(GstElement
* WXUNUSED(element
),
444 GParamSpec
* WXUNUSED(pspec
),
445 wxGStreamerMediaBackend
* be
)
447 wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback"));
448 be
->QueryVideoSizeFromElement(be
->m_playbin
);
453 //-----------------------------------------------------------------------------
454 // "desired-size-changed" from m_xoverlay
456 // 0.8-specific this provides us with the video size when it changes -
457 // even though we get the caps as well this seems to come before the
458 // caps notification does...
460 // Note it will return 16,16 for an early-bird value or for audio
461 //-----------------------------------------------------------------------------
462 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
464 static void gst_desired_size_changed_callback(GstElement
* play
,
465 guint width
, guint height
,
466 wxGStreamerMediaBackend
* be
)
468 if(!(width
== 16 && height
== 16))
470 be
->m_videoSize
.x
= width
;
471 be
->m_videoSize
.y
= height
;
474 be
->QueryVideoSizeFromElement(be
->m_playbin
);
479 //-----------------------------------------------------------------------------
480 // gst_bus_async_callback [static]
481 // gst_bus_sync_callback [static]
483 // Called by m_playbin for notifications such as end-of-stream in 0.10 -
484 // in previous versions g_signal notifications were used. Because everything
485 // in centered in one switch statement though it reminds one of old WinAPI
488 // gst_bus_sync_callback is that sync version that is called on the main GUI
489 // thread before the async version that we use to set the xwindow id of the
490 // XOverlay (NB: This isn't currently used - see CreateControl()).
491 //-----------------------------------------------------------------------------
492 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
494 static gboolean
gst_bus_async_callback(GstBus
* WXUNUSED(bus
),
496 wxGStreamerMediaBackend
* be
)
498 if ( GST_MESSAGE_TYPE(message
) == GST_MESSAGE_ERROR
)
502 gst_message_parse_error(message
, &error
, &debug
);
503 gst_error_callback(NULL
, NULL
, error
, debug
, be
);
507 if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
)
509 if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
)
512 switch(GST_MESSAGE_TYPE(message
))
514 case GST_MESSAGE_STATE_CHANGED
:
516 GstState oldstate
, newstate
, pendingstate
;
517 gst_message_parse_state_changed(message
, &oldstate
,
518 &newstate
, &pendingstate
);
519 be
->HandleStateChange(oldstate
, newstate
);
522 case GST_MESSAGE_EOS
:
524 gst_finish_callback(NULL
, be
);
532 be
->m_asynclock
.Unlock();
533 return FALSE
; // remove the message from Z queue
536 static GstBusSyncReply
gst_bus_sync_callback(GstBus
* bus
,
538 wxGStreamerMediaBackend
* be
)
540 // Pass a non-xwindowid-setting event on to the async handler where it
542 if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT
||
543 !gst_structure_has_name (message
->structure
, "prepare-xwindow-id"))
546 // NB: Unfortunately, the async callback can be quite
547 // buggy at times and often doesn't get called at all,
548 // so here we are processing it right here in the calling
549 // thread instead of the GUI one...
551 if(gst_bus_async_callback(bus
, message
, be
))
557 wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id"));
559 return GST_BUS_DROP
; // We handled this message - drop from the queue
564 //-----------------------------------------------------------------------------
566 // Private (although not in the C++ sense) methods
568 //-----------------------------------------------------------------------------
570 //-----------------------------------------------------------------------------
571 // wxGStreamerMediaBackend::HandleStateChange
573 // Handles a state change event from our C Callback for "state-change" or
574 // the async queue in 0.10. (Mostly this is here to avoid locking the
575 // the mutex twice...)
576 //-----------------------------------------------------------------------------
577 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
,
578 GstElementState newstate
)
582 case GST_STATE_PLAYING
:
583 wxLogTrace(wxTRACE_GStreamer
, wxT("Play event"));
586 case GST_STATE_PAUSED
:
587 // For some reason .10 sends a lot of oldstate == newstate
588 // messages - most likely for pending ones - also
589 // !<GST_STATE_PAUSED as we are only concerned
590 if(oldstate
< GST_STATE_PAUSED
|| oldstate
== newstate
)
592 if(wxGStreamerMediaBackend::GetPosition() != 0)
594 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event"));
599 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event"));
603 default: // GST_STATE_NULL etc.
608 //-----------------------------------------------------------------------------
609 // wxGStreamerMediaBackend::QueryVideoSizeFromElement
611 // Run through the stuff in "stream-info" of element for a valid
612 // video pad, and then attempt to query the video size from it - if not
613 // set up an event to do so when ready. Return true
614 // if we got a valid video pad.
615 //-----------------------------------------------------------------------------
616 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
)
618 const GList
*list
= NULL
;
619 g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
);
621 for ( ; list
!= NULL
; list
= list
->next
)
623 GObject
*info
= (GObject
*) list
->data
;
629 g_object_get (info
, "type", &type
, NULL
);
630 pspec
= g_object_class_find_property (
631 G_OBJECT_GET_CLASS (info
), "type");
632 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
634 if (!strncasecmp(val
->value_name
, "video", 5) ||
635 !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21))
637 // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
638 // but a lot of old plugins still use "pad" :)
639 pspec
= g_object_class_find_property (
640 G_OBJECT_GET_CLASS (info
), "object");
643 g_object_get (info
, "pad", &pad
, NULL
);
645 g_object_get (info
, "object", &pad
, NULL
);
647 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
648 // Killed in 0.9, presumely because events and such
649 // should be pushed on pads regardless of whether they
650 // are currently linked
651 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
655 if(!QueryVideoSizeFromPad(pad
))
657 // wait for those caps to get ready
661 G_CALLBACK(gst_notify_caps_callback
),
666 }// end searching through info list
668 // no video (or extremely delayed stream-info)
671 m_videoSize
= wxSize(0,0);
678 //-----------------------------------------------------------------------------
679 // wxGStreamerMediaBackend::QueryVideoSizeFromPad
681 // Gets the size of our video (in wxSize) from a GstPad
682 //-----------------------------------------------------------------------------
683 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
)
685 const GstCaps
* caps
= GST_PAD_CAPS(pad
);
688 const GstStructure
*s
= gst_caps_get_structure (caps
, 0);
691 gst_structure_get_int (s
, "width", &m_videoSize
.x
);
692 gst_structure_get_int (s
, "height", &m_videoSize
.y
);
695 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
699 wxLogTrace(wxTRACE_GStreamer
,
700 wxT("pixel-aspect-ratio found in pad"));
701 int num
= par
->data
[0].v_int
,
702 den
= par
->data
[1].v_int
;
704 // TODO: maybe better fraction normalization...
706 m_videoSize
.x
= (int) ((float) num
* m_videoSize
.x
/ den
);
708 m_videoSize
.y
= (int) ((float) den
* m_videoSize
.y
/ num
);
711 wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"),
712 m_videoSize
.x
, m_videoSize
.y
);
716 return false; // not ready/massive failure
719 //-----------------------------------------------------------------------------
720 // wxGStreamerMediaBackend::SetupXOverlay
722 // Attempts to set the XWindow id of our GstXOverlay to tell it which
723 // window to play video in.
724 //-----------------------------------------------------------------------------
725 void wxGStreamerMediaBackend::SetupXOverlay()
727 // Use the xoverlay extension to tell gstreamer to play in our window
729 if (!gtk_widget_get_realized(m_ctrl
->m_wxwindow
))
731 // Not realized yet - set to connect at realization time
732 g_signal_connect (m_ctrl
->m_wxwindow
,
734 G_CALLBACK (gtk_window_realize_callback
),
741 GdkWindow
* window
= gtk_widget_get_window(m_ctrl
->m_wxwindow
);
744 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_xoverlay
),
746 GDK_WINDOW_XID(window
)
752 g_signal_connect(m_ctrl
->m_wxwindow
,
754 "draw", G_CALLBACK(draw
),
756 "expose_event", G_CALLBACK(expose_event
),
759 } // end if GtkPizza realized
763 //-----------------------------------------------------------------------------
764 // wxGStreamerMediaBackend::SyncStateChange
766 // This function is rather complex - basically the idea is that we
767 // poll the GstBus of m_playbin until it has reached desiredstate, an error
768 // is reached, or there are no more messages left in the GstBus queue.
770 // Returns true if there are no messages left in the queue or
771 // the current state reaches the disired state.
773 // PRECONDITION: Assumes m_asynclock is Lock()ed
774 //-----------------------------------------------------------------------------
775 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
776 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
777 GstElementState desiredstate
,
780 GstBus
* bus
= gst_element_get_bus(element
);
784 gint64 llTimeWaited
= 0;
789 // NB: The GStreamer gst_bus_poll is unfortunately broken and
790 // throws silly critical internal errors (for instance
791 // "message != NULL" when the whole point of it is to
792 // poll for the message in the first place!) so we implement
793 // our own "waiting mechinism"
794 if(gst_bus_have_pending(bus
) == FALSE
)
796 if(llTimeWaited
>= llTimeout
)
797 return true; // Reached timeout... assume success
798 llTimeWaited
+= 10*GST_MSECOND
;
803 message
= gst_bus_pop(bus
);
805 message
= gst_bus_poll(bus
, (GstMessageType
)
806 (GST_MESSAGE_STATE_CHANGED
|
808 GST_MESSAGE_EOS
), llTimeout
);
812 if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
)
814 switch(GST_MESSAGE_TYPE(message
))
816 case GST_MESSAGE_STATE_CHANGED
:
818 GstState oldstate
, newstate
, pendingstate
;
819 gst_message_parse_state_changed(message
, &oldstate
,
820 &newstate
, &pendingstate
);
821 if(newstate
== desiredstate
)
823 bSuccess
= bBreak
= true;
827 case GST_MESSAGE_ERROR
:
831 gst_message_parse_error(message
, &error
, &debug
);
832 gst_error_callback(NULL
, NULL
, error
, debug
, this);
836 case GST_MESSAGE_EOS
:
837 wxLogSysError(wxT("Reached end of stream prematurely"));
841 break; // not handled
845 gst_message_unref(message
);
850 #else // 0.8 implementation
851 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
,
852 GstElementState desiredstate
,
855 gint64 llTimeWaited
= 0;
856 while(GST_STATE(element
) != desiredstate
)
858 if(llTimeWaited
>= llTimeout
)
860 llTimeWaited
+= 10*GST_MSECOND
;
864 return llTimeWaited
!= llTimeout
;
868 //-----------------------------------------------------------------------------
869 // wxGStreamerMediaBackend::TryAudioSink
870 // wxGStreamerMediaBackend::TryVideoSink
872 // Uses various means to determine whether a passed in video/audio sink
873 // if suitable for us - if it is not we return false and unref the
874 // inappropriate sink.
875 //-----------------------------------------------------------------------------
876 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
)
878 if( !GST_IS_ELEMENT(audiosink
) )
880 if(G_IS_OBJECT(audiosink
))
881 g_object_unref(audiosink
);
888 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
)
890 // Check if the video sink either is an xoverlay or might contain one...
891 if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) )
893 if(G_IS_OBJECT(videosink
))
894 g_object_unref(videosink
);
898 // Make our video sink and make sure it supports the x overlay interface
899 // the x overlay enables us to put the video in our control window
900 // (i.e. we NEED it!) - also connect to the natural video size change event
901 if( GST_IS_BIN(videosink
) )
902 m_xoverlay
= (GstXOverlay
*)
903 gst_bin_get_by_interface (GST_BIN (videosink
),
906 m_xoverlay
= (GstXOverlay
*) videosink
;
908 if ( !GST_IS_X_OVERLAY(m_xoverlay
) )
910 g_object_unref(videosink
);
917 //-----------------------------------------------------------------------------
918 // wxGStreamerMediaEventHandler::OnMediaFinish
920 // Called when the media is about to stop
921 //-----------------------------------------------------------------------------
922 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
))
924 // (RN - I have no idea why I thought this was good behaviour....
925 // maybe it made sense for streaming/nonseeking data but
926 // generally it seems like a really bad idea) -
927 if(m_be
->SendStopEvent())
929 // Stop the media (we need to set it back to paused
930 // so that people can get the duration et al.
931 // and send the finish event (luckily we can "Sync" it out... LOL!)
932 // (We don't check return values here because we can't really do
934 wxMutexLocker
lock(m_be
->m_asynclock
);
936 // Set element to ready+sync it
937 gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
);
938 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
);
940 // Now set it to paused + update pause pos to 0 and
941 // Sync that as well (note that we don't call Stop() here
942 // due to mutex issues)
943 gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
);
944 m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
);
945 m_be
->m_llPausedPos
= 0;
947 // Finally, queue the finish event
948 m_be
->QueueFinishEvent();
952 //-----------------------------------------------------------------------------
956 //-----------------------------------------------------------------------------
958 //-----------------------------------------------------------------------------
959 // wxGStreamerMediaBackend Constructor
961 // Sets m_playbin to NULL signifying we havn't loaded anything yet
962 //-----------------------------------------------------------------------------
963 wxGStreamerMediaBackend::wxGStreamerMediaBackend()
969 //-----------------------------------------------------------------------------
970 // wxGStreamerMediaBackend Destructor
972 // Stops/cleans up memory
974 // NB: This could trigger a critical warning but doing a SyncStateChange
975 // here is just going to slow down quitting of the app, which is bad.
976 //-----------------------------------------------------------------------------
977 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
979 // Dispose of the main player and related objects
982 wxASSERT( GST_IS_OBJECT(m_playbin
) );
983 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
984 gst_object_unref (GST_OBJECT (m_playbin
));
985 delete m_eventHandler
;
989 //-----------------------------------------------------------------------------
990 // wxGStreamerMediaBackend::CheckForErrors
992 // Reports any errors received from gstreamer. Should be called after any
994 //-----------------------------------------------------------------------------
995 bool wxGStreamerMediaBackend::CheckForErrors()
997 wxMutexLocker
lock(m_mutexErr
);
998 if ( m_errors
.empty() )
1001 for ( unsigned n
= 0; n
< m_errors
.size(); n
++ )
1003 const Error
& err
= m_errors
[n
];
1005 wxLogTrace(wxTRACE_GStreamer
,
1006 "gst_error_callback: %s", err
.m_debug
);
1007 wxLogError(_("Media playback error: %s"), err
.m_message
);
1015 //-----------------------------------------------------------------------------
1016 // wxGStreamerMediaBackend::CreateControl
1018 // Initializes GStreamer and creates the wx side of our media control
1019 //-----------------------------------------------------------------------------
1020 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
1025 const wxValidator
& validator
,
1026 const wxString
& name
)
1032 //Convert arguments to unicode if enabled
1035 char **argvGST
= new char*[wxTheApp
->argc
+ 1];
1036 for ( i
= 0; i
< wxTheApp
->argc
; i
++ )
1038 argvGST
[i
] = wxStrdupA(wxTheApp
->argv
[i
].utf8_str());
1041 argvGST
[wxTheApp
->argc
] = NULL
;
1043 int argcGST
= wxTheApp
->argc
;
1045 #define argcGST wxTheApp->argc
1046 #define argvGST wxTheApp->argv
1049 //Really init gstreamer
1051 GError
* error
= NULL
;
1052 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1053 bInited
= gst_init_check(&argcGST
, &argvGST
, &error
);
1055 bInited
= gst_init_check(&argcGST
, &argvGST
);
1058 // Cleanup arguments for unicode case
1060 for ( i
= 0; i
< argcGST
; i
++ )
1068 if(!bInited
) //gst_init_check fail?
1072 wxLogSysError(wxT("Could not initialize GStreamer\n")
1073 wxT("Error Message:%s"),
1074 (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
)
1076 g_error_free(error
);
1079 wxLogSysError(wxT("Could not initialize GStreamer"));
1085 // wxControl creation
1087 m_ctrl
= wxStaticCast(ctrl
, wxMediaCtrl
);
1090 // We handle our own GTK expose events
1091 m_ctrl
->m_noExpose
= true;
1094 if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
1095 style
, // TODO: remove borders???
1098 wxFAIL_MSG(wxT("Could not create wxControl!!!"));
1103 // Turn off double-buffering so that
1104 // so it doesn't draw over the video and cause sporadic
1105 // disappearances of the video
1106 gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
);
1109 // don't erase the background of our control window
1110 // so that resizing is a bit smoother
1111 m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
);
1113 // Create our playbin object
1114 m_playbin
= gst_element_factory_make ("playbin", "play");
1115 if (!GST_IS_ELEMENT(m_playbin
))
1117 if(G_IS_OBJECT(m_playbin
))
1118 g_object_unref(m_playbin
);
1119 wxLogSysError(wxT("Got an invalid playbin"));
1123 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1124 // Connect the glib events/callbacks we want to our playbin
1125 g_signal_connect(m_playbin
, "eos",
1126 G_CALLBACK(gst_finish_callback
), this);
1127 g_signal_connect(m_playbin
, "error",
1128 G_CALLBACK(gst_error_callback
), this);
1129 g_signal_connect(m_playbin
, "state-change",
1130 G_CALLBACK(gst_state_change_callback
), this);
1132 // GStreamer 0.10+ uses GstBus for this now, connect to the sync
1133 // handler as well so we can set the X window id of our xoverlay
1134 gst_bus_add_watch (gst_element_get_bus(m_playbin
),
1135 (GstBusFunc
) gst_bus_async_callback
, this);
1136 gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
),
1137 (GstBusSyncHandler
) gst_bus_sync_callback
, this);
1138 g_signal_connect(m_playbin
, "notify::stream-info",
1139 G_CALLBACK(gst_notify_stream_info_callback
), this);
1142 // Get the audio sink
1143 GstElement
* audiosink
= gst_gconf_get_default_audio_sink();
1144 if( !TryAudioSink(audiosink
) )
1146 // fallback to autodetection, then alsa, then oss as a stopgap
1147 audiosink
= gst_element_factory_make ("autoaudiosink", "audio-sink");
1148 if( !TryAudioSink(audiosink
) )
1150 audiosink
= gst_element_factory_make ("alsasink", "alsa-output");
1151 if( !TryAudioSink(audiosink
) )
1153 audiosink
= gst_element_factory_make ("osssink", "play_audio");
1154 if( !TryAudioSink(audiosink
) )
1156 wxLogSysError(wxT("Could not find a valid audiosink"));
1163 // Setup video sink - first try gconf, then auto, then xvimage and
1164 // then finally plain ximage
1165 GstElement
* videosink
= gst_gconf_get_default_video_sink();
1166 if( !TryVideoSink(videosink
) )
1168 videosink
= gst_element_factory_make ("autovideosink", "video-sink");
1169 if( !TryVideoSink(videosink
) )
1171 videosink
= gst_element_factory_make ("xvimagesink", "video-sink");
1172 if( !TryVideoSink(videosink
) )
1174 // finally, do a final fallback to ximagesink
1176 gst_element_factory_make ("ximagesink", "video-sink");
1177 if( !TryVideoSink(videosink
) )
1179 g_object_unref(audiosink
);
1180 wxLogSysError(wxT("Could not find a suitable video sink"));
1187 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10
1188 // Not on 0.10... called when video size changes
1189 g_signal_connect(m_xoverlay
, "desired-size-changed",
1190 G_CALLBACK(gst_desired_size_changed_callback
), this);
1192 // Tell GStreamer which window to draw to in 0.8 - 0.10
1193 // sometimes needs this too...
1196 // Now that we know (or, rather think) our video and audio sink
1197 // are valid set our playbin to use them
1198 g_object_set (G_OBJECT (m_playbin
),
1199 "video-sink", videosink
,
1200 "audio-sink", audiosink
,
1203 m_eventHandler
= new wxGStreamerMediaEventHandler(this);
1207 //-----------------------------------------------------------------------------
1208 // wxGStreamerMediaBackend::Load (File version)
1210 // Just calls DoLoad() with a prepended file scheme
1211 //-----------------------------------------------------------------------------
1212 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
1214 return DoLoad(wxFileSystem::FileNameToURL(fileName
));
1217 //-----------------------------------------------------------------------------
1218 // wxGStreamerMediaBackend::Load (URI version)
1220 // In the case of a file URI passes it unencoded -
1221 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer)
1222 // is sort of broken and only accepts uris with at least two slashes
1223 // after the scheme (i.e. file: == not ok, file:// == ok)
1224 //-----------------------------------------------------------------------------
1225 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
1227 if(location
.GetScheme().CmpNoCase(wxT("file")) == 0)
1229 wxString uristring
= location
.BuildUnescapedURI();
1231 //Workaround GstURI leading "//" problem and make sure it leads
1233 return DoLoad(wxString(wxT("file://")) +
1234 uristring
.Right(uristring
.length() - 5)
1238 return DoLoad(location
.BuildURI());
1241 //-----------------------------------------------------------------------------
1242 // wxGStreamerMediaBackend::DoLoad
1245 // 1) Reset member variables and set playbin back to ready state
1246 // 2) Check URI for validity and then tell the playbin to load it
1247 // 3) Set the playbin to the pause state
1249 // NB: Even after this function is over with we probably don't have the
1250 // video size or duration - no amount of clever hacking is going to get
1251 // around that, unfortunately.
1252 //-----------------------------------------------------------------------------
1253 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
)
1255 wxMutexLocker
lock(m_asynclock
); // lock state events and async callbacks
1257 // Reset positions & rate
1260 m_videoSize
= wxSize(0,0);
1262 // Set playbin to ready to stop the current media...
1263 if( gst_element_set_state (m_playbin
,
1264 GST_STATE_READY
) == GST_STATE_FAILURE
||
1265 !SyncStateChange(m_playbin
, GST_STATE_READY
))
1269 wxLogError(_("Failed to prepare playing \"%s\"."), locstring
);
1273 // free current media resources
1274 gst_element_set_state (m_playbin
, GST_STATE_NULL
);
1276 // Make sure the passed URI is valid and tell playbin to load it
1277 // non-file uris are encoded
1278 wxASSERT(gst_uri_protocol_is_valid("file"));
1279 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
1281 g_object_set (G_OBJECT (m_playbin
), "uri",
1282 (const char*)locstring
.mb_str(), NULL
);
1284 // Try to pause media as gstreamer won't let us query attributes
1285 // such as video size unless it is paused or playing
1286 if( gst_element_set_state (m_playbin
,
1287 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1288 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1291 return false; // no real error message needed here as this is
1292 // generic failure 99% of the time (i.e. no
1293 // source etc.) and has an error message
1296 // It may happen that both calls above succeed but we actually had some
1297 // errors during the pipeline setup and it doesn't play. E.g. this happens
1298 // if XVideo extension is unavailable but xvimagesink is still used.
1299 if ( CheckForErrors() )
1303 NotifyMovieLoaded(); // Notify the user - all we can do for now
1308 //-----------------------------------------------------------------------------
1309 // wxGStreamerMediaBackend::Play
1311 // Sets the stream to a playing state
1313 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1314 //-----------------------------------------------------------------------------
1315 bool wxGStreamerMediaBackend::Play()
1317 if (gst_element_set_state (m_playbin
,
1318 GST_STATE_PLAYING
) == GST_STATE_FAILURE
)
1327 //-----------------------------------------------------------------------------
1328 // wxGStreamerMediaBackend::Pause
1330 // Marks where we paused and pauses the stream
1332 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well
1333 //-----------------------------------------------------------------------------
1334 bool wxGStreamerMediaBackend::Pause()
1336 m_llPausedPos
= wxGStreamerMediaBackend::GetPosition();
1337 if (gst_element_set_state (m_playbin
,
1338 GST_STATE_PAUSED
) == GST_STATE_FAILURE
)
1346 //-----------------------------------------------------------------------------
1347 // wxGStreamerMediaBackend::Stop
1349 // Pauses the stream and sets the position to 0. Note that this is
1350 // synchronous (!) pausing.
1352 // Due to the mutex locking this is probably thread-safe actually.
1353 //-----------------------------------------------------------------------------
1354 bool wxGStreamerMediaBackend::Stop()
1356 { // begin state lock
1357 wxMutexLocker
lock(m_asynclock
);
1358 if(gst_element_set_state (m_playbin
,
1359 GST_STATE_PAUSED
) == GST_STATE_FAILURE
||
1360 !SyncStateChange(m_playbin
, GST_STATE_PAUSED
))
1363 wxLogSysError(wxT("Could not set state to paused for Stop()"));
1368 bool bSeekedOK
= wxGStreamerMediaBackend::SetPosition(0);
1372 wxLogSysError(wxT("Could not seek to initial position in Stop()"));
1376 QueueStopEvent(); // Success
1380 //-----------------------------------------------------------------------------
1381 // wxGStreamerMediaBackend::GetState
1383 // Gets the state of the media
1384 //-----------------------------------------------------------------------------
1385 wxMediaState
wxGStreamerMediaBackend::GetState()
1387 switch(GST_STATE(m_playbin
))
1389 case GST_STATE_PLAYING
:
1390 return wxMEDIASTATE_PLAYING
;
1391 case GST_STATE_PAUSED
:
1392 if (m_llPausedPos
== 0)
1393 return wxMEDIASTATE_STOPPED
;
1395 return wxMEDIASTATE_PAUSED
;
1396 default://case GST_STATE_READY:
1397 return wxMEDIASTATE_STOPPED
;
1401 //-----------------------------------------------------------------------------
1402 // wxGStreamerMediaBackend::GetPosition
1404 // If paused, returns our marked position - otherwise it queries the
1405 // GStreamer playbin for the position and returns that
1408 // NB: At least in 0.8, when you pause and seek gstreamer
1409 // NB: doesn't update the position sometimes, so we need to keep track of
1410 // NB: whether we have paused or not and keep track of the time after the
1411 // NB: pause and whenever the user seeks while paused
1414 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused.
1415 //-----------------------------------------------------------------------------
1416 wxLongLong
wxGStreamerMediaBackend::GetPosition()
1418 if(GetState() != wxMEDIASTATE_PLAYING
)
1419 return m_llPausedPos
;
1423 GstFormat fmtTime
= GST_FORMAT_TIME
;
1425 if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) ||
1426 fmtTime
!= GST_FORMAT_TIME
|| pos
== -1)
1428 return pos
/ GST_MSECOND
;
1432 //-----------------------------------------------------------------------------
1433 // wxGStreamerMediaBackend::SetPosition
1435 // Sets the position of the stream
1436 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
1437 // there is 1000000 nanoseconds in a millisecond)
1439 // If we are paused we update the cached pause position.
1441 // This is also an exceedingly ugly function due to the three implementations
1442 // (or, rather two plus one implementation without a seek function).
1444 // This is asynchronous and thread-safe on both 0.8 and 0.10.
1446 // NB: This fires both a stop and play event if the media was previously
1447 // playing... which in some ways makes sense. And yes, this makes the video
1448 // go all haywire at times - a gstreamer bug...
1449 //-----------------------------------------------------------------------------
1450 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
1452 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \
1453 && GST_VERSION_MICRO == 0
1454 // 0.8.0 has no gst_element_seek according to official docs!!!
1455 wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek")
1456 wxT(" according to official docs"));
1460 # if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1461 gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
,
1462 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1463 GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
,
1464 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
);
1466 // NB: Some gstreamer versions return false basically all the time
1467 // here - even totem doesn't bother to check the return value here
1468 // so I guess we'll just assume it worked -
1469 // TODO: maybe check the gst error callback???
1470 gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
1471 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
1472 where
.GetValue() * GST_MSECOND
);
1474 # endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1477 m_llPausedPos
= where
;
1484 //-----------------------------------------------------------------------------
1485 // wxGStreamerMediaBackend::GetDuration
1487 // Obtains the total time of our stream
1488 // THREAD-UNSAFE, requires media to be paused or playing
1489 //-----------------------------------------------------------------------------
1490 wxLongLong
wxGStreamerMediaBackend::GetDuration()
1493 GstFormat fmtTime
= GST_FORMAT_TIME
;
1495 if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) ||
1496 fmtTime
!= GST_FORMAT_TIME
|| length
== -1)
1498 return length
/ GST_MSECOND
;
1501 //-----------------------------------------------------------------------------
1502 // wxGStreamerMediaBackend::Move
1504 // Called when the window is moved - GStreamer takes care of this
1505 // for us so nothing is needed
1506 //-----------------------------------------------------------------------------
1507 void wxGStreamerMediaBackend::Move(int WXUNUSED(x
),
1514 //-----------------------------------------------------------------------------
1515 // wxGStreamerMediaBackend::GetVideoSize
1517 // Returns our cached video size from Load/gst_notify_caps_callback
1518 // gst_x_overlay_get_desired_size also does this in 0.8...
1519 //-----------------------------------------------------------------------------
1520 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
1525 //-----------------------------------------------------------------------------
1526 // wxGStreamerMediaBackend::GetPlaybackRate
1527 // wxGStreamerMediaBackend::SetPlaybackRate
1529 // Obtains/Sets the playback rate of the stream
1531 //TODO: PlaybackRate not currently supported via playbin directly -
1532 //TODO: Ronald S. Bultje noted on gstreamer-devel:
1534 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
1535 //TODO: for the first, yes, we have elements for that, btu they"re not part of
1536 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
1537 //TODO: video/audiosink and the speed-changing element for this, and set that
1538 //TODO: element as video-sink or audio-sink property in playbin. The
1539 //TODO: audio-element is called "speed", the video-element is called "videodrop"
1540 //TODO: (although that appears to be deprecated in favour of "videorate", which
1541 //TODO: again cannot do this, so this may not work at all in the end). For
1542 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
1543 //TODO: part of playbin.
1545 // In 0.10 GStreamer has new gst_element_seek API that might
1546 // support this - and I've got an attempt to do so but it is untested
1547 // but it would appear to work...
1548 //-----------------------------------------------------------------------------
1549 double wxGStreamerMediaBackend::GetPlaybackRate()
1551 return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem
1552 // final on that yet and there may not be any actual
1553 // plugins that support it...
1556 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
1558 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
1559 #if 0 // not tested enough
1560 if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
,
1561 (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH
| GST_SEEK_FLAG_KEY_UNIT
),
1562 GST_SEEK_TYPE_CUR
, 0,
1563 GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE
) )
1577 //-----------------------------------------------------------------------------
1578 // wxGStreamerMediaBackend::GetDownloadProgress
1580 // Not really outwardly possible - have been suggested that one could
1581 // get the information from the component that "downloads"
1582 //-----------------------------------------------------------------------------
1583 wxLongLong
wxGStreamerMediaBackend::GetDownloadProgress()
1588 //-----------------------------------------------------------------------------
1589 // wxGStreamerMediaBackend::GetDownloadTotal
1591 // TODO: Cache this?
1592 // NB: The length changes every call for some reason due to
1593 // GStreamer implementation issues
1594 // THREAD-UNSAFE, requires media to be paused or playing
1595 //-----------------------------------------------------------------------------
1596 wxLongLong
wxGStreamerMediaBackend::GetDownloadTotal()
1599 GstFormat fmtBytes
= GST_FORMAT_BYTES
;
1601 if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) ||
1602 fmtBytes
!= GST_FORMAT_BYTES
|| length
== -1)
1607 //-----------------------------------------------------------------------------
1608 // wxGStreamerMediaBackend::SetVolume
1609 // wxGStreamerMediaBackend::GetVolume
1611 // Sets/Gets the volume through the playbin object.
1612 // Note that this requires a relatively recent gst-plugins so we
1613 // check at runtime to see whether it is available or not otherwise
1614 // GST spits out an error on the command line
1615 //-----------------------------------------------------------------------------
1616 bool wxGStreamerMediaBackend::SetVolume(double dVolume
)
1618 if(g_object_class_find_property(
1619 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1622 g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
);
1627 wxLogTrace(wxTRACE_GStreamer
,
1628 wxT("SetVolume: volume prop not found - 0.8.5 of ")
1629 wxT("gst-plugins probably needed"));
1634 double wxGStreamerMediaBackend::GetVolume()
1636 double dVolume
= 1.0;
1638 if(g_object_class_find_property(
1639 G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)),
1642 g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
);
1646 wxLogTrace(wxTRACE_GStreamer
,
1647 wxT("GetVolume: volume prop not found - 0.8.5 of ")
1648 wxT("gst-plugins probably needed"));
1654 #endif //wxUSE_GSTREAMER
1656 // Force link into main library so this backend can be loaded
1657 #include "wx/html/forcelnk.h"
1658 FORCE_LINK_ME(basewxmediabackends
)
1660 #endif //wxUSE_MEDIACTRL