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