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 
  42     #include "wx/gtk/private/gtk2-compat.h" 
  45 //----------------------------------------------------------------------------- 
  46 // Discussion of internals 
  47 //----------------------------------------------------------------------------- 
  50    This is the GStreamer backend for unix. Currently we require 0.8 or 
  51    0.10. Here we use the "playbin" GstElement for ease of use. 
  53    Note that now we compare state change functions to GST_STATE_FAILURE 
  54    now rather than GST_STATE_SUCCESS as newer gstreamer versions return 
  55    non-success values for returns that are otherwise successful but not 
  58    Also this probably doesn't work with anything other than wxGTK at the 
  59    moment but with a tad bit of work it could theorectically work in 
  62    One last note is that resuming from pausing/seeking can result 
  63    in erratic video playback (GStreamer-based bug, happens in totem as well) 
  64    - this is better in 0.10, however. One thing that might make it worse 
  65    here is that we don't preserve the aspect ratio of the video and stretch 
  66    it to the whole window. 
  68    Note that there are some things used here that could be undocumented - 
  69    for reference see the media player Kiss and Totem as well as some 
  70    other sources. There was a backend for a kde media player as well 
  71    that attempted thread-safety... 
  73    Then there is the issue of m_asynclock. This serves several purposes: 
  74    1) It prevents the C callbacks from sending wx state change events 
  75       so that we don't get duplicate ones in 0.8 
  76    2) It makes the sync and async handlers in 0.10 not drop any 
  77       messages so that while we are polling it we get the messages in 
  78       SyncStateChange instead of the queue. 
  79    3) Keeps the pausing in Stop() synchronous 
  81    RN: Note that I've tried to follow the wxGTK conventions here as close 
  82    as possible. In the implementation the C Callbacks come first, then 
  83    the internal functions, then the public ones. Set your vi to 80 
  87 //============================================================================= 
  89 //============================================================================= 
  91 //----------------------------------------------------------------------------- 
  92 //  GStreamer (most version compatability) macros 
  93 //----------------------------------------------------------------------------- 
  95 // In 0.9 there was a HUGE change to GstQuery and the 
  96 // gst_element_query function changed dramatically and split off 
  97 // into two seperate ones 
  98 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8 
  99 #    define wxGst_element_query_duration(e, f, p) \ 
 100                 gst_element_query(e, GST_QUERY_TOTAL, f, p) 
 101 #    define wxGst_element_query_position(e, f, p) \ 
 102                 gst_element_query(e, GST_QUERY_POSITION, f, p) 
 103 #elif GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 9 
 104 // However, the actual 0.9 version has a slightly different definition 
 105 // and instead of gst_element_query_duration it has two parameters to 
 106 // gst_element_query_position instead 
 107 #    define wxGst_element_query_duration(e, f, p) \ 
 108                 gst_element_query_position(e, f, 0, p) 
 109 #    define wxGst_element_query_position(e, f, p) \ 
 110                 gst_element_query_position(e, f, p, 0) 
 112 #    define wxGst_element_query_duration \ 
 113                 gst_element_query_duration 
 114 #    define wxGst_element_query_position \ 
 115                 gst_element_query_position 
 119 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 120 #   define GST_STATE_FAILURE GST_STATE_CHANGE_FAILURE 
 121 #   define GST_STATE_SUCCESS GST_STATE_CHANGE_SUCCESS 
 122 #   define GstElementState GstState 
 123 #   define gst_gconf_get_default_video_sink() \ 
 124         gst_element_factory_make ("gconfvideosink", "video-sink"); 
 125 #   define gst_gconf_get_default_audio_sink() \ 
 126         gst_element_factory_make ("gconfaudiosink", "audio-sink"); 
 129 // Max wait time for element state waiting - GST_CLOCK_TIME_NONE for inf 
 130 #define wxGSTREAMER_TIMEOUT (100 * GST_MSECOND) // Max 100 milliseconds 
 132 //----------------------------------------------------------------------------- 
 133 //  wxLogTrace mask string 
 134 //----------------------------------------------------------------------------- 
 135 #define wxTRACE_GStreamer wxT("GStreamer") 
 137 //----------------------------------------------------------------------------- 
 139 //  wxGStreamerMediaBackend 
 141 //----------------------------------------------------------------------------- 
 142 class WXDLLIMPEXP_MEDIA
 
 143     wxGStreamerMediaBackend 
: public wxMediaBackendCommonBase
 
 147     wxGStreamerMediaBackend(); 
 148     virtual ~wxGStreamerMediaBackend(); 
 150     virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 155                                      const wxValidator
& validator
, 
 156                                      const wxString
& name
); 
 159     virtual bool Pause(); 
 162     virtual bool Load(const wxString
& fileName
); 
 163     virtual bool Load(const wxURI
& location
); 
 164     virtual bool Load(const wxURI
& location
, 
 166         { return wxMediaBackendCommonBase::Load(location
, proxy
); } 
 169     virtual wxMediaState 
GetState(); 
 171     virtual bool SetPosition(wxLongLong where
); 
 172     virtual wxLongLong 
GetPosition(); 
 173     virtual wxLongLong 
GetDuration(); 
 175     virtual void Move(int x
, int y
, int w
, int h
); 
 176     wxSize 
GetVideoSize() const; 
 178     virtual double GetPlaybackRate(); 
 179     virtual bool SetPlaybackRate(double dRate
); 
 181     virtual wxLongLong 
GetDownloadProgress(); 
 182     virtual wxLongLong 
GetDownloadTotal(); 
 184     virtual bool SetVolume(double dVolume
); 
 185     virtual double GetVolume(); 
 187     //------------implementation from now on----------------------------------- 
 188     bool DoLoad(const wxString
& locstring
); 
 189     wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks 
 190     void HandleStateChange(GstElementState oldstate
, GstElementState newstate
); 
 191     bool QueryVideoSizeFromElement(GstElement
* element
); 
 192     bool QueryVideoSizeFromPad(GstPad
* caps
); 
 193     void SetupXOverlay(); 
 194     bool SyncStateChange(GstElement
* element
, GstElementState state
, 
 195                          gint64 llTimeout 
= wxGSTREAMER_TIMEOUT
); 
 196     bool TryAudioSink(GstElement
* audiosink
); 
 197     bool TryVideoSink(GstElement
* videosink
); 
 199     GstElement
*     m_playbin
;      // GStreamer media element 
 200     wxSize          m_videoSize
;    // Cached actual video size 
 201     double          m_dRate
;        // Current playback rate - 
 202                                     // see GetPlaybackRate for notes 
 203     wxLongLong      m_llPausedPos
;  // Paused position - see Pause() 
 204     GstXOverlay
*    m_xoverlay
;     // X Overlay that contains the GST video 
 205     wxMutex         m_asynclock
;    // See "discussion of internals" 
 206     class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below 
 208     friend class wxGStreamerMediaEventHandler
; 
 209     friend class wxGStreamerLoadWaitTimer
; 
 210     DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
); 
 213 //----------------------------------------------------------------------------- 
 214 // wxGStreamerMediaEventHandler 
 216 // OK, this will take an explanation - basically gstreamer callbacks 
 217 // are issued in a seperate thread, and in this thread we may not set 
 218 // the state of the playbin, so we need to send a wx event in that 
 219 // callback so that we set the state of the media and other stuff 
 221 //----------------------------------------------------------------------------- 
 222 class wxGStreamerMediaEventHandler 
: public wxEvtHandler
 
 225     wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
) 
 227         this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
, 
 228            wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
)); 
 231     void OnMediaFinish(wxMediaEvent
& event
); 
 233     wxGStreamerMediaBackend
* m_be
; 
 236 //============================================================================= 
 238 //============================================================================= 
 240 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
) 
 242 //----------------------------------------------------------------------------- 
 246 //----------------------------------------------------------------------------- 
 248 //----------------------------------------------------------------------------- 
 249 // "expose_event" from m_ctrl->m_wxwindow 
 251 // Handle GTK expose event from our window - here we hopefully 
 252 // redraw the video in the case of pausing and other instances... 
 253 // (Returns TRUE to pass to other handlers, FALSE if not) 
 255 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here? 
 256 //----------------------------------------------------------------------------- 
 259 static gboolean 
gtk_window_expose_callback(GtkWidget 
*widget
, 
 260                                            GdkEventExpose 
*event
, 
 261                                            wxGStreamerMediaBackend 
*be
) 
 266     GdkWindow
* window 
= gtk_widget_get_window(widget
); 
 268     // I've seen this reccommended somewhere... 
 269     // TODO: Is this needed? Maybe it is just cruft... 
 270     // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay), 
 271     //                              GDK_WINDOW_XWINDOW( window ) ); 
 273     // If we have actual video..... 
 274     if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) && 
 275        GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
) 
 277         // GST Doesn't redraw automatically while paused 
 278         // Plus, the video sometimes doesn't redraw when it looses focus 
 279         // or is painted over so we just tell it to redraw... 
 280         gst_x_overlay_expose(be
->m_xoverlay
); 
 284         // draw a black background like some other backends do.... 
 285         gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0, 
 286                             widget
->allocation
.width
, 
 287                             widget
->allocation
.height
); 
 295 //----------------------------------------------------------------------------- 
 296 // "realize" from m_ctrl->m_wxwindow 
 298 // If the window wasn't realized when Load was called, this is the 
 299 // callback for when it is - the purpose of which is to tell 
 300 // GStreamer to play the video in our control 
 301 //----------------------------------------------------------------------------- 
 304 static gint 
gtk_window_realize_callback(GtkWidget
* widget
, 
 305                                         wxGStreamerMediaBackend
* be
) 
 309     GdkWindow
* window 
= gtk_widget_get_window(widget
); 
 312     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
), 
 313                                 GDK_WINDOW_XWINDOW( window 
) 
 315     g_signal_connect (be
->GetControl()->m_wxwindow
, 
 317                       G_CALLBACK(gtk_window_expose_callback
), be
); 
 323 //----------------------------------------------------------------------------- 
 324 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE 
 326 // Called by gstreamer when the state changes - here we 
 327 // send the appropriate corresponding wx event. 
 329 // 0.8 only as HandleStateChange does this in both versions 
 330 //----------------------------------------------------------------------------- 
 331 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 333 static void gst_state_change_callback(GstElement 
*play
, 
 334                                       GstElementState oldstate
, 
 335                                       GstElementState newstate
, 
 336                                       wxGStreamerMediaBackend
* be
) 
 338     if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
) 
 340         be
->HandleStateChange(oldstate
, newstate
); 
 341         be
->m_asynclock
.Unlock(); 
 347 //----------------------------------------------------------------------------- 
 348 // "eos" from m_playbin/GST_MESSAGE_EOS 
 350 // Called by gstreamer when the media is done playing ("end of stream") 
 351 //----------------------------------------------------------------------------- 
 353 static void gst_finish_callback(GstElement 
*WXUNUSED(play
), 
 354                                 wxGStreamerMediaBackend
* be
) 
 356     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback")); 
 357     wxMediaEvent 
event(wxEVT_MEDIA_FINISHED
); 
 358     be
->m_eventHandler
->AddPendingEvent(event
); 
 362 //----------------------------------------------------------------------------- 
 363 // "error" from m_playbin/GST_MESSAGE_ERROR 
 365 // Called by gstreamer when an error is encountered playing the media - 
 366 // We call wxLogTrace in addition wxLogSysError so that we can get it 
 367 // on the command line as well for those who want extra traces. 
 368 //----------------------------------------------------------------------------- 
 370 static void gst_error_callback(GstElement 
*WXUNUSED(play
), 
 371                                GstElement 
*WXUNUSED(src
), 
 374                                wxGStreamerMediaBackend
* WXUNUSED(be
)) 
 377     sError
.Printf(wxT("gst_error_callback\n") 
 378                   wxT("Error Message:%s\nDebug:%s\n"), 
 379                   (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
), 
 380                   (const wxChar
*)wxConvUTF8
.cMB2WX(debug
)); 
 381     wxLogTrace(wxTRACE_GStreamer
, sError
); 
 382     wxLogSysError(sError
); 
 386 //----------------------------------------------------------------------------- 
 387 // "notify::caps" from the videopad inside "stream-info" of m_playbin 
 389 // Called by gstreamer when the video caps for the media is ready - currently 
 390 // we use the caps to get the natural size of the video 
 393 //----------------------------------------------------------------------------- 
 395 static void gst_notify_caps_callback(GstPad
* pad
, 
 396                                      GParamSpec
* WXUNUSED(pspec
), 
 397                                      wxGStreamerMediaBackend
* be
) 
 399     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback")); 
 400     be
->QueryVideoSizeFromPad(pad
); 
 404 //----------------------------------------------------------------------------- 
 405 // "notify::stream-info" from m_playbin 
 407 // Run through the stuff in "stream-info" of m_playbin for a valid 
 408 // video pad, and then attempt to query the video size from it - if not 
 409 // set up an event to do so when ready. 
 411 // Currently unused - now we just query it directly using 
 412 // QueryVideoSizeFromElement. 
 415 //----------------------------------------------------------------------------- 
 416 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 418 static void gst_notify_stream_info_callback(GstElement
* WXUNUSED(element
), 
 419                                             GParamSpec
* WXUNUSED(pspec
), 
 420                                             wxGStreamerMediaBackend
* be
) 
 422     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback")); 
 423     be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 428 //----------------------------------------------------------------------------- 
 429 // "desired-size-changed" from m_xoverlay 
 431 // 0.8-specific this provides us with the video size when it changes - 
 432 // even though we get the caps as well this seems to come before the 
 433 // caps notification does... 
 435 // Note it will return 16,16 for an early-bird value or for audio 
 436 //----------------------------------------------------------------------------- 
 437 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 439 static void gst_desired_size_changed_callback(GstElement 
* play
, 
 440                                               guint width
, guint height
, 
 441                                               wxGStreamerMediaBackend
* be
) 
 443     if(!(width 
== 16 && height 
== 16)) 
 445         be
->m_videoSize
.x 
= width
; 
 446         be
->m_videoSize
.y 
= height
; 
 449         be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 454 //----------------------------------------------------------------------------- 
 455 // gst_bus_async_callback [static] 
 456 // gst_bus_sync_callback [static] 
 458 // Called by m_playbin for notifications such as end-of-stream in 0.10 - 
 459 // in previous versions g_signal notifications were used. Because everything 
 460 // in centered in one switch statement though it reminds one of old WinAPI 
 463 // gst_bus_sync_callback is that sync version that is called on the main GUI 
 464 // thread before the async version that we use to set the xwindow id of the 
 465 // XOverlay (NB: This isn't currently used - see CreateControl()). 
 466 //----------------------------------------------------------------------------- 
 467 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 469 static gboolean 
gst_bus_async_callback(GstBus
* WXUNUSED(bus
), 
 471                                        wxGStreamerMediaBackend
* be
) 
 473     if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
) 
 475     if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
) 
 478     switch(GST_MESSAGE_TYPE(message
)) 
 480         case GST_MESSAGE_STATE_CHANGED
: 
 482             GstState oldstate
, newstate
, pendingstate
; 
 483             gst_message_parse_state_changed(message
, &oldstate
, 
 484                                             &newstate
, &pendingstate
); 
 485             be
->HandleStateChange(oldstate
, newstate
); 
 488         case GST_MESSAGE_EOS
: 
 490             gst_finish_callback(NULL
, be
); 
 493         case GST_MESSAGE_ERROR
: 
 497             gst_message_parse_error(message
, &error
, &debug
); 
 498             gst_error_callback(NULL
, NULL
, error
, debug
, be
); 
 505     be
->m_asynclock
.Unlock(); 
 506     return FALSE
; // remove the message from Z queue 
 509 static GstBusSyncReply 
gst_bus_sync_callback(GstBus
* bus
, 
 511                                              wxGStreamerMediaBackend
* be
) 
 513     // Pass a non-xwindowid-setting event on to the async handler where it 
 515     if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT 
|| 
 516         !gst_structure_has_name (message
->structure
, "prepare-xwindow-id")) 
 519         // NB: Unfortunately, the async callback can be quite 
 520         // buggy at times and often doesn't get called at all, 
 521         // so here we are processing it right here in the calling 
 522         // thread instead of the GUI one... 
 524         if(gst_bus_async_callback(bus
, message
, be
)) 
 530     wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id")); 
 532     return GST_BUS_DROP
; // We handled this message - drop from the queue 
 537 //----------------------------------------------------------------------------- 
 539 // Private (although not in the C++ sense)  methods 
 541 //----------------------------------------------------------------------------- 
 543 //----------------------------------------------------------------------------- 
 544 // wxGStreamerMediaBackend::HandleStateChange 
 546 // Handles a state change event from our C Callback for "state-change" or 
 547 // the async queue in 0.10. (Mostly this is here to avoid locking the 
 548 // the mutex twice...) 
 549 //----------------------------------------------------------------------------- 
 550 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
, 
 551                                                 GstElementState newstate
) 
 555         case GST_STATE_PLAYING
: 
 556             wxLogTrace(wxTRACE_GStreamer
, wxT("Play event")); 
 559         case GST_STATE_PAUSED
: 
 560             // For some reason .10 sends a lot of oldstate == newstate 
 561             // messages - most likely for pending ones - also 
 562             // !<GST_STATE_PAUSED as we are only concerned 
 563             if(oldstate 
< GST_STATE_PAUSED 
|| oldstate 
== newstate
) 
 565             if(wxGStreamerMediaBackend::GetPosition() != 0) 
 567                 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event")); 
 572                 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event")); 
 576        default: // GST_STATE_NULL etc. 
 581 //----------------------------------------------------------------------------- 
 582 // wxGStreamerMediaBackend::QueryVideoSizeFromElement 
 584 // Run through the stuff in "stream-info" of element for a valid 
 585 // video pad, and then attempt to query the video size from it - if not 
 586 // set up an event to do so when ready. Return true 
 587 // if we got a valid video pad. 
 588 //----------------------------------------------------------------------------- 
 589 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
) 
 591     const GList 
*list 
= NULL
; 
 592     g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
); 
 594     for ( ; list 
!= NULL
; list 
= list
->next
) 
 596         GObject 
*info 
= (GObject 
*) list
->data
; 
 602         g_object_get (info
, "type", &type
, NULL
); 
 603         pspec 
= g_object_class_find_property ( 
 604                         G_OBJECT_GET_CLASS (info
), "type"); 
 605         val 
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
); 
 607         if (!strncasecmp(val
->value_name
, "video", 5) || 
 608             !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21)) 
 610             // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"... 
 611             // but a lot of old plugins still use "pad" :) 
 612             pspec 
= g_object_class_find_property ( 
 613                         G_OBJECT_GET_CLASS (info
), "object"); 
 616                 g_object_get (info
, "pad", &pad
, NULL
); 
 618                 g_object_get (info
, "object", &pad
, NULL
); 
 620 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8 
 621             // Killed in 0.9, presumely because events and such 
 622             // should be pushed on pads regardless of whether they 
 623             // are currently linked 
 624             pad 
= (GstPad 
*) GST_PAD_REALIZE (pad
); 
 628             if(!QueryVideoSizeFromPad(pad
)) 
 630                 // wait for those caps to get ready 
 634                 G_CALLBACK(gst_notify_caps_callback
), 
 639     }// end searching through info list 
 641     // no video (or extremely delayed stream-info) 
 644         m_videoSize 
= wxSize(0,0); 
 651 //----------------------------------------------------------------------------- 
 652 // wxGStreamerMediaBackend::QueryVideoSizeFromPad 
 654 // Gets the size of our video (in wxSize) from a GstPad 
 655 //----------------------------------------------------------------------------- 
 656 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
) 
 658     const GstCaps
* caps 
= GST_PAD_CAPS(pad
); 
 661         const GstStructure 
*s 
= gst_caps_get_structure (caps
, 0); 
 664         gst_structure_get_int (s
, "width", &m_videoSize
.x
); 
 665         gst_structure_get_int (s
, "height", &m_videoSize
.y
); 
 668         par 
= gst_structure_get_value (s
, "pixel-aspect-ratio"); 
 672             wxLogTrace(wxTRACE_GStreamer
, 
 673                        wxT("pixel-aspect-ratio found in pad")); 
 674             int num 
= par
->data
[0].v_int
, 
 675                 den 
= par
->data
[1].v_int
; 
 677             // TODO: maybe better fraction normalization... 
 679                 m_videoSize
.x 
= (int) ((float) num 
* m_videoSize
.x 
/ den
); 
 681                 m_videoSize
.y 
= (int) ((float) den 
* m_videoSize
.y 
/ num
); 
 684          wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"), 
 685                      m_videoSize
.x
, m_videoSize
.y
); 
 689     return false; // not ready/massive failure 
 692 //----------------------------------------------------------------------------- 
 693 // wxGStreamerMediaBackend::SetupXOverlay 
 695 // Attempts to set the XWindow id of our GstXOverlay to tell it which 
 696 // window to play video in. 
 697 //----------------------------------------------------------------------------- 
 698 void wxGStreamerMediaBackend::SetupXOverlay() 
 700     // Use the xoverlay extension to tell gstreamer to play in our window 
 702     if (!gtk_widget_get_realized(m_ctrl
->m_wxwindow
)) 
 704         // Not realized yet - set to connect at realization time 
 705         g_signal_connect (m_ctrl
->m_wxwindow
, 
 707                           G_CALLBACK (gtk_window_realize_callback
), 
 714         GdkWindow
* window 
= gtk_widget_get_window(m_ctrl
->m_wxwindow
); 
 717         gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_xoverlay
), 
 719                         GDK_WINDOW_XWINDOW( window 
) 
 725         g_signal_connect(m_ctrl
->m_wxwindow
, 
 726                         // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/, 
 728                       G_CALLBACK(gtk_window_expose_callback
), this); 
 729     } // end if GtkPizza realized 
 733 //----------------------------------------------------------------------------- 
 734 // wxGStreamerMediaBackend::SyncStateChange 
 736 // This function is rather complex - basically the idea is that we 
 737 // poll the GstBus of m_playbin until it has reached desiredstate, an error 
 738 // is reached, or there are no more messages left in the GstBus queue. 
 740 // Returns true if there are no messages left in the queue or 
 741 // the current state reaches the disired state. 
 743 // PRECONDITION: Assumes m_asynclock is Lock()ed 
 744 //----------------------------------------------------------------------------- 
 745 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 746 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 747                                               GstElementState desiredstate
, 
 750     GstBus
* bus 
= gst_element_get_bus(element
); 
 754     gint64 llTimeWaited 
= 0; 
 759         // NB: The GStreamer gst_bus_poll is unfortunately broken and 
 760         // throws silly critical internal errors (for instance 
 761         // "message != NULL" when the whole point of it is to 
 762         // poll for the message in the first place!) so we implement 
 763         // our own "waiting mechinism" 
 764         if(gst_bus_have_pending(bus
) == FALSE
) 
 766             if(llTimeWaited 
>= llTimeout
) 
 767                 return true; // Reached timeout... assume success 
 768             llTimeWaited 
+= 10*GST_MSECOND
; 
 773         message 
= gst_bus_pop(bus
); 
 775         message 
= gst_bus_poll(bus
, (GstMessageType
) 
 776                            (GST_MESSAGE_STATE_CHANGED 
| 
 778                             GST_MESSAGE_EOS
), llTimeout
); 
 782         if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
) 
 784             switch(GST_MESSAGE_TYPE(message
)) 
 786                 case GST_MESSAGE_STATE_CHANGED
: 
 788                     GstState oldstate
, newstate
, pendingstate
; 
 789                     gst_message_parse_state_changed(message
, &oldstate
, 
 790                                                     &newstate
, &pendingstate
); 
 791                     if(newstate 
== desiredstate
) 
 793                         bSuccess 
= bBreak 
= true; 
 797                 case GST_MESSAGE_ERROR
: 
 801                     gst_message_parse_error(message
, &error
, &debug
); 
 802                     gst_error_callback(NULL
, NULL
, error
, debug
, this); 
 806                 case GST_MESSAGE_EOS
: 
 807                     wxLogSysError(wxT("Reached end of stream prematurely")); 
 811                     break; // not handled 
 815         gst_message_unref(message
); 
 820 #else // 0.8 implementation 
 821 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 822                                               GstElementState desiredstate
, 
 825     gint64 llTimeWaited 
= 0; 
 826     while(GST_STATE(element
) != desiredstate
) 
 828         if(llTimeWaited 
>= llTimeout
) 
 830         llTimeWaited 
+= 10*GST_MSECOND
; 
 834     return llTimeWaited 
!= llTimeout
; 
 838 //----------------------------------------------------------------------------- 
 839 // wxGStreamerMediaBackend::TryAudioSink 
 840 // wxGStreamerMediaBackend::TryVideoSink 
 842 // Uses various means to determine whether a passed in video/audio sink 
 843 // if suitable for us - if it is not we return false and unref the 
 844 // inappropriate sink. 
 845 //----------------------------------------------------------------------------- 
 846 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
) 
 848     if( !GST_IS_ELEMENT(audiosink
) ) 
 850         if(G_IS_OBJECT(audiosink
)) 
 851             g_object_unref(audiosink
); 
 858 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
) 
 860     // Check if the video sink either is an xoverlay or might contain one... 
 861     if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) ) 
 863         if(G_IS_OBJECT(videosink
)) 
 864             g_object_unref(videosink
); 
 868     // Make our video sink and make sure it supports the x overlay interface 
 869     // the x overlay enables us to put the video in our control window 
 870     // (i.e. we NEED it!) - also connect to the natural video size change event 
 871     if( GST_IS_BIN(videosink
) ) 
 872         m_xoverlay 
= (GstXOverlay
*) 
 873                         gst_bin_get_by_interface (GST_BIN (videosink
), 
 876         m_xoverlay 
= (GstXOverlay
*) videosink
; 
 878     if ( !GST_IS_X_OVERLAY(m_xoverlay
) ) 
 880         g_object_unref(videosink
); 
 887 //----------------------------------------------------------------------------- 
 888 // wxGStreamerMediaEventHandler::OnMediaFinish 
 890 // Called when the media is about to stop 
 891 //----------------------------------------------------------------------------- 
 892 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
)) 
 894     // (RN - I have no idea why I thought this was good behaviour.... 
 895     // maybe it made sense for streaming/nonseeking data but 
 896     // generally it seems like a really bad idea) - 
 897     if(m_be
->SendStopEvent()) 
 899         // Stop the media (we need to set it back to paused 
 900         // so that people can get the duration et al. 
 901         // and send the finish event (luckily we can "Sync" it out... LOL!) 
 902         // (We don't check return values here because we can't really do 
 904         wxMutexLocker 
lock(m_be
->m_asynclock
); 
 906         // Set element to ready+sync it 
 907         gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
); 
 908         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
); 
 910         // Now set it to paused + update pause pos to 0 and 
 911         // Sync that as well (note that we don't call Stop() here 
 912         // due to mutex issues) 
 913         gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
); 
 914         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
); 
 915         m_be
->m_llPausedPos 
= 0; 
 917         // Finally, queue the finish event 
 918         m_be
->QueueFinishEvent(); 
 922 //----------------------------------------------------------------------------- 
 926 //----------------------------------------------------------------------------- 
 928 //----------------------------------------------------------------------------- 
 929 // wxGStreamerMediaBackend Constructor 
 931 // Sets m_playbin to NULL signifying we havn't loaded anything yet 
 932 //----------------------------------------------------------------------------- 
 933 wxGStreamerMediaBackend::wxGStreamerMediaBackend() 
 939 //----------------------------------------------------------------------------- 
 940 // wxGStreamerMediaBackend Destructor 
 942 // Stops/cleans up memory 
 944 // NB: This could trigger a critical warning but doing a SyncStateChange 
 945 //     here is just going to slow down quitting of the app, which is bad. 
 946 //----------------------------------------------------------------------------- 
 947 wxGStreamerMediaBackend::~wxGStreamerMediaBackend() 
 949     // Dispose of the main player and related objects 
 952         wxASSERT( GST_IS_OBJECT(m_playbin
) ); 
 953         gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
 954         gst_object_unref (GST_OBJECT (m_playbin
)); 
 955         delete m_eventHandler
; 
 959 //----------------------------------------------------------------------------- 
 960 // wxGStreamerMediaBackend::CreateControl 
 962 // Initializes GStreamer and creates the wx side of our media control 
 963 //----------------------------------------------------------------------------- 
 964 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 969                                 const wxValidator
& validator
, 
 970                                 const wxString
& name
) 
 976     //Convert arguments to unicode if enabled 
 979     char **argvGST 
= new char*[wxTheApp
->argc 
+ 1]; 
 980     for ( i 
= 0; i 
< wxTheApp
->argc
; i
++ ) 
 982 #if wxUSE_UNICODE_WCHAR 
 983         argvGST
[i
] = wxStrdupA(wxConvUTF8
.cWX2MB(wxTheApp
->argv
[i
])); 
 985         argvGST
[i
] = wxStrdupA(wxTheApp
->argv
[i
].utf8_str()); 
 989     argvGST
[wxTheApp
->argc
] = NULL
; 
 991     int argcGST 
= wxTheApp
->argc
; 
 993 #define argcGST wxTheApp->argc 
 994 #define argvGST wxTheApp->argv 
 997     //Really init gstreamer 
 999     GError
* error 
= NULL
; 
1000 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1001     bInited 
= gst_init_check(&argcGST
, &argvGST
, &error
); 
1003     bInited 
= gst_init_check(&argcGST
, &argvGST
); 
1006     // Cleanup arguments for unicode case 
1008     for ( i 
= 0; i 
< argcGST
; i
++ ) 
1016     if(!bInited
)    //gst_init_check fail? 
1020             wxLogSysError(wxT("Could not initialize GStreamer\n") 
1021                           wxT("Error Message:%s"), 
1022                           (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
) 
1024             g_error_free(error
); 
1027             wxLogSysError(wxT("Could not initialize GStreamer")); 
1033     // wxControl creation 
1035     m_ctrl 
= wxStaticCast(ctrl
, wxMediaCtrl
); 
1038     // We handle our own GTK expose events 
1039     m_ctrl
->m_noExpose 
= true; 
1042     if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
, 
1043                             style
,  // TODO: remove borders??? 
1046         wxFAIL_MSG(wxT("Could not create wxControl!!!")); 
1051     // Turn off double-buffering so that 
1052     // so it doesn't draw over the video and cause sporadic 
1053     // disappearances of the video 
1054     gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
); 
1057     // don't erase the background of our control window 
1058     // so that resizing is a bit smoother 
1059     m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
1061     // Create our playbin object 
1062     m_playbin 
= gst_element_factory_make ("playbin", "play"); 
1063     if (!GST_IS_ELEMENT(m_playbin
)) 
1065         if(G_IS_OBJECT(m_playbin
)) 
1066             g_object_unref(m_playbin
); 
1067         wxLogSysError(wxT("Got an invalid playbin")); 
1071 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1072     // Connect the glib events/callbacks we want to our playbin 
1073     g_signal_connect(m_playbin
, "eos", 
1074                      G_CALLBACK(gst_finish_callback
), this); 
1075     g_signal_connect(m_playbin
, "error", 
1076                      G_CALLBACK(gst_error_callback
), this); 
1077     g_signal_connect(m_playbin
, "state-change", 
1078                      G_CALLBACK(gst_state_change_callback
), this); 
1080     // GStreamer 0.10+ uses GstBus for this now, connect to the sync 
1081     // handler as well so we can set the X window id of our xoverlay 
1082     gst_bus_add_watch (gst_element_get_bus(m_playbin
), 
1083                        (GstBusFunc
) gst_bus_async_callback
, this); 
1084     gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
), 
1085                              (GstBusSyncHandler
) gst_bus_sync_callback
, this); 
1086     g_signal_connect(m_playbin
, "notify::stream-info", 
1087                      G_CALLBACK(gst_notify_stream_info_callback
), this); 
1090     // Get the audio sink 
1091     GstElement
* audiosink 
= gst_gconf_get_default_audio_sink(); 
1092     if( !TryAudioSink(audiosink
) ) 
1094         // fallback to autodetection, then alsa, then oss as a stopgap 
1095         audiosink 
= gst_element_factory_make ("autoaudiosink", "audio-sink"); 
1096         if( !TryAudioSink(audiosink
) ) 
1098             audiosink 
= gst_element_factory_make ("alsasink", "alsa-output"); 
1099             if( !TryAudioSink(audiosink
) ) 
1101                 audiosink 
= gst_element_factory_make ("osssink", "play_audio"); 
1102                 if( !TryAudioSink(audiosink
) ) 
1104                     wxLogSysError(wxT("Could not find a valid audiosink")); 
1111     // Setup video sink - first try gconf, then auto, then xvimage and 
1112     // then finally plain ximage 
1113     GstElement
* videosink 
= gst_gconf_get_default_video_sink(); 
1114     if( !TryVideoSink(videosink
) ) 
1116         videosink 
= gst_element_factory_make ("autovideosink", "video-sink"); 
1117         if( !TryVideoSink(videosink
) ) 
1119             videosink 
= gst_element_factory_make ("xvimagesink", "video-sink"); 
1120             if( !TryVideoSink(videosink
) ) 
1122                 // finally, do a final fallback to ximagesink 
1124                     gst_element_factory_make ("ximagesink", "video-sink"); 
1125                 if( !TryVideoSink(videosink
) ) 
1127                     g_object_unref(audiosink
); 
1128                     wxLogSysError(wxT("Could not find a suitable video sink")); 
1135 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1136     // Not on 0.10... called when video size changes 
1137     g_signal_connect(m_xoverlay
, "desired-size-changed", 
1138                      G_CALLBACK(gst_desired_size_changed_callback
), this); 
1140     // Tell GStreamer which window to draw to in 0.8 - 0.10 
1141     // sometimes needs this too... 
1144     // Now that we know (or, rather think) our video and audio sink 
1145     // are valid set our playbin to use them 
1146     g_object_set (G_OBJECT (m_playbin
), 
1147                   "video-sink", videosink
, 
1148                   "audio-sink", audiosink
, 
1151     m_eventHandler 
= new wxGStreamerMediaEventHandler(this); 
1155 //----------------------------------------------------------------------------- 
1156 // wxGStreamerMediaBackend::Load (File version) 
1158 // Just calls DoLoad() with a prepended file scheme 
1159 //----------------------------------------------------------------------------- 
1160 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
) 
1162     return DoLoad(wxString( wxT("file://") ) + fileName
); 
1165 //----------------------------------------------------------------------------- 
1166 // wxGStreamerMediaBackend::Load (URI version) 
1168 // In the case of a file URI passes it unencoded - 
1169 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer) 
1170 // is sort of broken and only accepts uris with at least two slashes 
1171 // after the scheme (i.e. file: == not ok, file:// == ok) 
1172 //----------------------------------------------------------------------------- 
1173 bool wxGStreamerMediaBackend::Load(const wxURI
& location
) 
1175     if(location
.GetScheme().CmpNoCase(wxT("file")) == 0) 
1177         wxString uristring 
= location
.BuildUnescapedURI(); 
1179         //Workaround GstURI leading "//" problem and make sure it leads 
1181         return DoLoad(wxString(wxT("file://")) + 
1182                       uristring
.Right(uristring
.length() - 5) 
1186         return DoLoad(location
.BuildURI()); 
1189 //----------------------------------------------------------------------------- 
1190 // wxGStreamerMediaBackend::DoLoad 
1193 // 1) Reset member variables and set playbin back to ready state 
1194 // 2) Check URI for validity and then tell the playbin to load it 
1195 // 3) Set the playbin to the pause state 
1197 // NB: Even after this function is over with we probably don't have the 
1198 // video size or duration - no amount of clever hacking is going to get 
1199 // around that, unfortunately. 
1200 //----------------------------------------------------------------------------- 
1201 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
) 
1203     wxMutexLocker 
lock(m_asynclock
); // lock state events and async callbacks 
1205     // Reset positions & rate 
1208     m_videoSize 
= wxSize(0,0); 
1210     // Set playbin to ready to stop the current media... 
1211     if( gst_element_set_state (m_playbin
, 
1212                                GST_STATE_READY
) == GST_STATE_FAILURE 
|| 
1213         !SyncStateChange(m_playbin
, GST_STATE_READY
)) 
1215         wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ") 
1216                       wxT("Could not set initial state to ready")); 
1220     // free current media resources 
1221     gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
1223     // Make sure the passed URI is valid and tell playbin to load it 
1224     // non-file uris are encoded 
1225     wxASSERT(gst_uri_protocol_is_valid("file")); 
1226     wxASSERT(gst_uri_is_valid(locstring
.mb_str())); 
1228     g_object_set (G_OBJECT (m_playbin
), "uri", 
1229                   (const char*)locstring
.mb_str(), NULL
); 
1231     // Try to pause media as gstreamer won't let us query attributes 
1232     // such as video size unless it is paused or playing 
1233     if( gst_element_set_state (m_playbin
, 
1234                                GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1235         !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1237         return false; // no real error message needed here as this is 
1238                       // generic failure 99% of the time (i.e. no 
1239                       // source etc.) and has an error message 
1243     NotifyMovieLoaded(); // Notify the user - all we can do for now 
1248 //----------------------------------------------------------------------------- 
1249 // wxGStreamerMediaBackend::Play 
1251 // Sets the stream to a playing state 
1253 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1254 //----------------------------------------------------------------------------- 
1255 bool wxGStreamerMediaBackend::Play() 
1257     if (gst_element_set_state (m_playbin
, 
1258                                GST_STATE_PLAYING
) == GST_STATE_FAILURE
) 
1263 //----------------------------------------------------------------------------- 
1264 // wxGStreamerMediaBackend::Pause 
1266 // Marks where we paused and pauses the stream 
1268 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1269 //----------------------------------------------------------------------------- 
1270 bool wxGStreamerMediaBackend::Pause() 
1272     m_llPausedPos 
= wxGStreamerMediaBackend::GetPosition(); 
1273     if (gst_element_set_state (m_playbin
, 
1274                                GST_STATE_PAUSED
) == GST_STATE_FAILURE
) 
1279 //----------------------------------------------------------------------------- 
1280 // wxGStreamerMediaBackend::Stop 
1282 // Pauses the stream and sets the position to 0. Note that this is 
1283 // synchronous (!) pausing. 
1285 // Due to the mutex locking this is probably thread-safe actually. 
1286 //----------------------------------------------------------------------------- 
1287 bool wxGStreamerMediaBackend::Stop() 
1289     {   // begin state lock 
1290         wxMutexLocker 
lock(m_asynclock
); 
1291         if(gst_element_set_state (m_playbin
, 
1292                                   GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1293           !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1295             wxLogSysError(wxT("Could not set state to paused for Stop()")); 
1300     bool bSeekedOK 
= wxGStreamerMediaBackend::SetPosition(0); 
1304         wxLogSysError(wxT("Could not seek to initial position in Stop()")); 
1308     QueueStopEvent(); // Success 
1312 //----------------------------------------------------------------------------- 
1313 // wxGStreamerMediaBackend::GetState 
1315 // Gets the state of the media 
1316 //----------------------------------------------------------------------------- 
1317 wxMediaState 
wxGStreamerMediaBackend::GetState() 
1319     switch(GST_STATE(m_playbin
)) 
1321         case GST_STATE_PLAYING
: 
1322             return wxMEDIASTATE_PLAYING
; 
1323         case GST_STATE_PAUSED
: 
1324             if (m_llPausedPos 
== 0) 
1325                 return wxMEDIASTATE_STOPPED
; 
1327                 return wxMEDIASTATE_PAUSED
; 
1328         default://case GST_STATE_READY: 
1329             return wxMEDIASTATE_STOPPED
; 
1333 //----------------------------------------------------------------------------- 
1334 // wxGStreamerMediaBackend::GetPosition 
1336 // If paused, returns our marked position - otherwise it queries the 
1337 // GStreamer playbin for the position and returns that 
1340 // NB: At least in 0.8, when you pause and seek gstreamer 
1341 // NB: doesn't update the position sometimes, so we need to keep track of 
1342 // NB: whether we have paused or not and keep track of the time after the 
1343 // NB: pause and whenever the user seeks while paused 
1346 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused. 
1347 //----------------------------------------------------------------------------- 
1348 wxLongLong 
wxGStreamerMediaBackend::GetPosition() 
1350     if(GetState() != wxMEDIASTATE_PLAYING
) 
1351         return m_llPausedPos
; 
1355         GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1357         if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) || 
1358             fmtTime 
!= GST_FORMAT_TIME 
|| pos 
== -1) 
1360         return pos 
/ GST_MSECOND 
; 
1364 //----------------------------------------------------------------------------- 
1365 // wxGStreamerMediaBackend::SetPosition 
1367 // Sets the position of the stream 
1368 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so 
1369 // there is 1000000 nanoseconds in a millisecond) 
1371 // If we are paused we update the cached pause position. 
1373 // This is also an exceedingly ugly function due to the three implementations 
1374 // (or, rather two plus one implementation without a seek function). 
1376 // This is asynchronous and thread-safe on both 0.8 and 0.10. 
1378 // NB: This fires both a stop and play event if the media was previously 
1379 // playing... which in some ways makes sense. And yes, this makes the video 
1380 // go all haywire at times - a gstreamer bug... 
1381 //----------------------------------------------------------------------------- 
1382 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
) 
1384 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \ 
1385                            && GST_VERSION_MICRO == 0 
1386     // 0.8.0 has no gst_element_seek according to official docs!!! 
1387     wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek") 
1388                   wxT(" according to official docs")); 
1392 #   if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1393         gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
, 
1394            (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1395                           GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
, 
1396                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
); 
1398         // NB: Some gstreamer versions return false basically all the time 
1399         // here - even totem doesn't bother to check the return value here 
1400         // so I guess we'll just assume it worked - 
1401         // TODO: maybe check the gst error callback??? 
1402         gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET 
| 
1403             GST_FORMAT_TIME 
| GST_SEEK_FLAG_FLUSH
), 
1404             where
.GetValue() * GST_MSECOND 
); 
1406 #   endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1409         m_llPausedPos 
= where
; 
1416 //----------------------------------------------------------------------------- 
1417 // wxGStreamerMediaBackend::GetDuration 
1419 // Obtains the total time of our stream 
1420 // THREAD-UNSAFE, requires media to be paused or playing 
1421 //----------------------------------------------------------------------------- 
1422 wxLongLong 
wxGStreamerMediaBackend::GetDuration() 
1425     GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1427     if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) || 
1428        fmtTime 
!= GST_FORMAT_TIME 
|| length 
== -1) 
1430     return length 
/ GST_MSECOND 
; 
1433 //----------------------------------------------------------------------------- 
1434 // wxGStreamerMediaBackend::Move 
1436 // Called when the window is moved - GStreamer takes care of this 
1437 // for us so nothing is needed 
1438 //----------------------------------------------------------------------------- 
1439 void wxGStreamerMediaBackend::Move(int WXUNUSED(x
), 
1446 //----------------------------------------------------------------------------- 
1447 // wxGStreamerMediaBackend::GetVideoSize 
1449 // Returns our cached video size from Load/gst_notify_caps_callback 
1450 // gst_x_overlay_get_desired_size also does this in 0.8... 
1451 //----------------------------------------------------------------------------- 
1452 wxSize 
wxGStreamerMediaBackend::GetVideoSize() const 
1457 //----------------------------------------------------------------------------- 
1458 // wxGStreamerMediaBackend::GetPlaybackRate 
1459 // wxGStreamerMediaBackend::SetPlaybackRate 
1461 // Obtains/Sets the playback rate of the stream 
1463 //TODO: PlaybackRate not currently supported via playbin directly - 
1464 //TODO: Ronald S. Bultje noted on gstreamer-devel: 
1466 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As 
1467 //TODO: for the first, yes, we have elements for that, btu they"re not part of 
1468 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual 
1469 //TODO: video/audiosink and the speed-changing element for this, and set that 
1470 //TODO: element as video-sink or audio-sink property in playbin. The 
1471 //TODO: audio-element is called "speed", the video-element is called "videodrop" 
1472 //TODO: (although that appears to be deprecated in favour of "videorate", which 
1473 //TODO: again cannot do this, so this may not work at all in the end). For 
1474 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is 
1475 //TODO: part of playbin. 
1477 // In 0.10 GStreamer has new gst_element_seek API that might 
1478 // support this - and I've got an attempt to do so but it is untested 
1479 // but it would appear to work... 
1480 //----------------------------------------------------------------------------- 
1481 double wxGStreamerMediaBackend::GetPlaybackRate() 
1483     return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem 
1484                     // final on that yet and there may not be any actual 
1485                     // plugins that support it... 
1488 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
) 
1490 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1491 #if 0 // not tested enough 
1492     if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
, 
1493                  (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1494                           GST_SEEK_TYPE_CUR
, 0, 
1495                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
) ) 
1509 //----------------------------------------------------------------------------- 
1510 // wxGStreamerMediaBackend::GetDownloadProgress 
1512 // Not really outwardly possible - have been suggested that one could 
1513 // get the information from the component that "downloads" 
1514 //----------------------------------------------------------------------------- 
1515 wxLongLong 
wxGStreamerMediaBackend::GetDownloadProgress() 
1520 //----------------------------------------------------------------------------- 
1521 // wxGStreamerMediaBackend::GetDownloadTotal 
1523 // TODO: Cache this? 
1524 // NB: The length changes every call for some reason due to 
1525 //     GStreamer implementation issues 
1526 // THREAD-UNSAFE, requires media to be paused or playing 
1527 //----------------------------------------------------------------------------- 
1528 wxLongLong 
wxGStreamerMediaBackend::GetDownloadTotal() 
1531     GstFormat fmtBytes 
= GST_FORMAT_BYTES
; 
1533     if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) || 
1534           fmtBytes 
!= GST_FORMAT_BYTES 
|| length 
== -1) 
1539 //----------------------------------------------------------------------------- 
1540 // wxGStreamerMediaBackend::SetVolume 
1541 // wxGStreamerMediaBackend::GetVolume 
1543 // Sets/Gets the volume through the playbin object. 
1544 // Note that this requires a relatively recent gst-plugins so we 
1545 // check at runtime to see whether it is available or not otherwise 
1546 // GST spits out an error on the command line 
1547 //----------------------------------------------------------------------------- 
1548 bool wxGStreamerMediaBackend::SetVolume(double dVolume
) 
1550     if(g_object_class_find_property( 
1551             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1554         g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
); 
1559         wxLogTrace(wxTRACE_GStreamer
, 
1560             wxT("SetVolume: volume prop not found - 0.8.5 of ") 
1561             wxT("gst-plugins probably needed")); 
1566 double wxGStreamerMediaBackend::GetVolume() 
1568     double dVolume 
= 1.0; 
1570     if(g_object_class_find_property( 
1571             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1574         g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
); 
1578         wxLogTrace(wxTRACE_GStreamer
, 
1579             wxT("GetVolume: volume prop not found - 0.8.5 of ") 
1580             wxT("gst-plugins probably needed")); 
1586 #endif //wxUSE_GSTREAMER 
1588 // Force link into main library so this backend can be loaded 
1589 #include "wx/html/forcelnk.h" 
1590 FORCE_LINK_ME(basewxmediabackends
) 
1592 #endif //wxUSE_MEDIACTRL