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
); 
 163     virtual bool Load(const wxURI
& location
, 
 165         { return wxMediaBackendCommonBase::Load(location
, proxy
); } 
 168     virtual wxMediaState 
GetState(); 
 170     virtual bool SetPosition(wxLongLong where
); 
 171     virtual wxLongLong 
GetPosition(); 
 172     virtual wxLongLong 
GetDuration(); 
 174     virtual void Move(int x
, int y
, int w
, int h
); 
 175     wxSize 
GetVideoSize() const; 
 177     virtual double GetPlaybackRate(); 
 178     virtual bool SetPlaybackRate(double dRate
); 
 180     virtual wxLongLong 
GetDownloadProgress(); 
 181     virtual wxLongLong 
GetDownloadTotal(); 
 183     virtual bool SetVolume(double dVolume
); 
 184     virtual double GetVolume(); 
 186     //------------implementation from now on----------------------------------- 
 187     bool DoLoad(const wxString
& locstring
); 
 188     wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks 
 189     void HandleStateChange(GstElementState oldstate
, GstElementState newstate
); 
 190     bool QueryVideoSizeFromElement(GstElement
* element
); 
 191     bool QueryVideoSizeFromPad(GstPad
* caps
); 
 192     void SetupXOverlay(); 
 193     bool SyncStateChange(GstElement
* element
, GstElementState state
, 
 194                          gint64 llTimeout 
= wxGSTREAMER_TIMEOUT
); 
 195     bool TryAudioSink(GstElement
* audiosink
); 
 196     bool TryVideoSink(GstElement
* videosink
); 
 198     GstElement
*     m_playbin
;      // GStreamer media element 
 199     wxSize          m_videoSize
;    // Cached actual video size 
 200     double          m_dRate
;        // Current playback rate - 
 201                                     // see GetPlaybackRate for notes 
 202     wxLongLong      m_llPausedPos
;  // Paused position - see Pause() 
 203     GstXOverlay
*    m_xoverlay
;     // X Overlay that contains the GST video 
 204     wxMutex         m_asynclock
;    // See "discussion of internals" 
 205     class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below 
 207     friend class wxGStreamerMediaEventHandler
; 
 208     friend class wxGStreamerLoadWaitTimer
; 
 209     DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
); 
 212 //----------------------------------------------------------------------------- 
 213 // wxGStreamerMediaEventHandler 
 215 // OK, this will take an explanation - basically gstreamer callbacks 
 216 // are issued in a seperate thread, and in this thread we may not set 
 217 // the state of the playbin, so we need to send a wx event in that 
 218 // callback so that we set the state of the media and other stuff 
 220 //----------------------------------------------------------------------------- 
 221 class wxGStreamerMediaEventHandler 
: public wxEvtHandler
 
 224     wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
) 
 226         this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
, 
 227            wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
)); 
 230     void OnMediaFinish(wxMediaEvent
& event
); 
 232     wxGStreamerMediaBackend
* m_be
; 
 235 //============================================================================= 
 237 //============================================================================= 
 239 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
) 
 241 //----------------------------------------------------------------------------- 
 245 //----------------------------------------------------------------------------- 
 247 //----------------------------------------------------------------------------- 
 248 // "expose_event" from m_ctrl->m_wxwindow 
 250 // Handle GTK expose event from our window - here we hopefully 
 251 // redraw the video in the case of pausing and other instances... 
 252 // (Returns TRUE to pass to other handlers, FALSE if not) 
 254 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here? 
 255 //----------------------------------------------------------------------------- 
 258 static gboolean 
gtk_window_expose_callback(GtkWidget 
*widget
, 
 259                                            GdkEventExpose 
*event
, 
 260                                            wxGStreamerMediaBackend 
*be
) 
 265     GdkWindow 
*window 
= widget
->window
; 
 267     // I've seen this reccommended somewhere... 
 268     // TODO: Is this needed? Maybe it is just cruft... 
 269     // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay), 
 270     //                              GDK_WINDOW_XWINDOW( window ) ); 
 272     // If we have actual video..... 
 273     if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) && 
 274        GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
) 
 276         // GST Doesn't redraw automatically while paused 
 277         // Plus, the video sometimes doesn't redraw when it looses focus 
 278         // or is painted over so we just tell it to redraw... 
 279         gst_x_overlay_expose(be
->m_xoverlay
); 
 283         // draw a black background like some other backends do.... 
 284         gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0, 
 285                             widget
->allocation
.width
, 
 286                             widget
->allocation
.height
); 
 294 //----------------------------------------------------------------------------- 
 295 // "realize" from m_ctrl->m_wxwindow 
 297 // If the window wasn't realized when Load was called, this is the 
 298 // callback for when it is - the purpose of which is to tell 
 299 // GStreamer to play the video in our control 
 300 //----------------------------------------------------------------------------- 
 303 static gint 
gtk_window_realize_callback(GtkWidget
* widget
, 
 304                                         wxGStreamerMediaBackend
* be
) 
 308     GdkWindow 
*window 
= widget
->window
; 
 311     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
), 
 312                                 GDK_WINDOW_XWINDOW( window 
) 
 314     g_signal_connect (be
->GetControl()->m_wxwindow
, 
 316                       G_CALLBACK(gtk_window_expose_callback
), be
); 
 322 //----------------------------------------------------------------------------- 
 323 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE 
 325 // Called by gstreamer when the state changes - here we 
 326 // send the appropriate corresponding wx event. 
 328 // 0.8 only as HandleStateChange does this in both versions 
 329 //----------------------------------------------------------------------------- 
 330 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 332 static void gst_state_change_callback(GstElement 
*play
, 
 333                                       GstElementState oldstate
, 
 334                                       GstElementState newstate
, 
 335                                       wxGStreamerMediaBackend
* be
) 
 337     if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
) 
 339         be
->HandleStateChange(oldstate
, newstate
); 
 340         be
->m_asynclock
.Unlock(); 
 346 //----------------------------------------------------------------------------- 
 347 // "eos" from m_playbin/GST_MESSAGE_EOS 
 349 // Called by gstreamer when the media is done playing ("end of stream") 
 350 //----------------------------------------------------------------------------- 
 352 static void gst_finish_callback(GstElement 
*WXUNUSED(play
), 
 353                                 wxGStreamerMediaBackend
* be
) 
 355     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback")); 
 356     wxMediaEvent 
event(wxEVT_MEDIA_FINISHED
); 
 357     be
->m_eventHandler
->AddPendingEvent(event
); 
 361 //----------------------------------------------------------------------------- 
 362 // "error" from m_playbin/GST_MESSAGE_ERROR 
 364 // Called by gstreamer when an error is encountered playing the media - 
 365 // We call wxLogTrace in addition wxLogSysError so that we can get it 
 366 // on the command line as well for those who want extra traces. 
 367 //----------------------------------------------------------------------------- 
 369 static void gst_error_callback(GstElement 
*WXUNUSED(play
), 
 370                                GstElement 
*WXUNUSED(src
), 
 373                                wxGStreamerMediaBackend
* WXUNUSED(be
)) 
 376     sError
.Printf(wxT("gst_error_callback\n") 
 377                   wxT("Error Message:%s\nDebug:%s\n"), 
 378                   (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
), 
 379                   (const wxChar
*)wxConvUTF8
.cMB2WX(debug
)); 
 380     wxLogTrace(wxTRACE_GStreamer
, sError
); 
 381     wxLogSysError(sError
); 
 385 //----------------------------------------------------------------------------- 
 386 // "notify::caps" from the videopad inside "stream-info" of m_playbin 
 388 // Called by gstreamer when the video caps for the media is ready - currently 
 389 // we use the caps to get the natural size of the video 
 392 //----------------------------------------------------------------------------- 
 394 static void gst_notify_caps_callback(GstPad
* pad
, 
 395                                      GParamSpec
* WXUNUSED(pspec
), 
 396                                      wxGStreamerMediaBackend
* be
) 
 398     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback")); 
 399     be
->QueryVideoSizeFromPad(pad
); 
 403 //----------------------------------------------------------------------------- 
 404 // "notify::stream-info" from m_playbin 
 406 // Run through the stuff in "stream-info" of m_playbin for a valid 
 407 // video pad, and then attempt to query the video size from it - if not 
 408 // set up an event to do so when ready. 
 410 // Currently unused - now we just query it directly using 
 411 // QueryVideoSizeFromElement. 
 414 //----------------------------------------------------------------------------- 
 415 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 417 static void gst_notify_stream_info_callback(GstElement
* WXUNUSED(element
), 
 418                                             GParamSpec
* WXUNUSED(pspec
), 
 419                                             wxGStreamerMediaBackend
* be
) 
 421     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback")); 
 422     be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 427 //----------------------------------------------------------------------------- 
 428 // "desired-size-changed" from m_xoverlay 
 430 // 0.8-specific this provides us with the video size when it changes - 
 431 // even though we get the caps as well this seems to come before the 
 432 // caps notification does... 
 434 // Note it will return 16,16 for an early-bird value or for audio 
 435 //----------------------------------------------------------------------------- 
 436 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 438 static void gst_desired_size_changed_callback(GstElement 
* play
, 
 439                                               guint width
, guint height
, 
 440                                               wxGStreamerMediaBackend
* be
) 
 442     if(!(width 
== 16 && height 
== 16)) 
 444         be
->m_videoSize
.x 
= width
; 
 445         be
->m_videoSize
.y 
= height
; 
 448         be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 453 //----------------------------------------------------------------------------- 
 454 // gst_bus_async_callback [static] 
 455 // gst_bus_sync_callback [static] 
 457 // Called by m_playbin for notifications such as end-of-stream in 0.10 - 
 458 // in previous versions g_signal notifications were used. Because everything 
 459 // in centered in one switch statement though it reminds one of old WinAPI 
 462 // gst_bus_sync_callback is that sync version that is called on the main GUI 
 463 // thread before the async version that we use to set the xwindow id of the 
 464 // XOverlay (NB: This isn't currently used - see CreateControl()). 
 465 //----------------------------------------------------------------------------- 
 466 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 468 static gboolean 
gst_bus_async_callback(GstBus
* WXUNUSED(bus
), 
 470                                        wxGStreamerMediaBackend
* be
) 
 472     if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
) 
 474     if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
) 
 477     switch(GST_MESSAGE_TYPE(message
)) 
 479         case GST_MESSAGE_STATE_CHANGED
: 
 481             GstState oldstate
, newstate
, pendingstate
; 
 482             gst_message_parse_state_changed(message
, &oldstate
, 
 483                                             &newstate
, &pendingstate
); 
 484             be
->HandleStateChange(oldstate
, newstate
); 
 487         case GST_MESSAGE_EOS
: 
 489             gst_finish_callback(NULL
, be
); 
 492         case GST_MESSAGE_ERROR
: 
 496             gst_message_parse_error(message
, &error
, &debug
); 
 497             gst_error_callback(NULL
, NULL
, error
, debug
, be
); 
 504     be
->m_asynclock
.Unlock(); 
 505     return FALSE
; // remove the message from Z queue 
 508 static GstBusSyncReply 
gst_bus_sync_callback(GstBus
* bus
, 
 510                                              wxGStreamerMediaBackend
* be
) 
 512     // Pass a non-xwindowid-setting event on to the async handler where it 
 514     if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT 
|| 
 515         !gst_structure_has_name (message
->structure
, "prepare-xwindow-id")) 
 518         // NB: Unfortunately, the async callback can be quite 
 519         // buggy at times and often doesn't get called at all, 
 520         // so here we are processing it right here in the calling 
 521         // thread instead of the GUI one... 
 523         if(gst_bus_async_callback(bus
, message
, be
)) 
 529     wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id")); 
 531     return GST_BUS_DROP
; // We handled this message - drop from the queue 
 536 //----------------------------------------------------------------------------- 
 538 // Private (although not in the C++ sense)  methods 
 540 //----------------------------------------------------------------------------- 
 542 //----------------------------------------------------------------------------- 
 543 // wxGStreamerMediaBackend::HandleStateChange 
 545 // Handles a state change event from our C Callback for "state-change" or 
 546 // the async queue in 0.10. (Mostly this is here to avoid locking the 
 547 // the mutex twice...) 
 548 //----------------------------------------------------------------------------- 
 549 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
, 
 550                                                 GstElementState newstate
) 
 554         case GST_STATE_PLAYING
: 
 555             wxLogTrace(wxTRACE_GStreamer
, wxT("Play event")); 
 558         case GST_STATE_PAUSED
: 
 559             // For some reason .10 sends a lot of oldstate == newstate 
 560             // messages - most likely for pending ones - also 
 561             // !<GST_STATE_PAUSED as we are only concerned 
 562             if(oldstate 
< GST_STATE_PAUSED 
|| oldstate 
== newstate
) 
 564             if(wxGStreamerMediaBackend::GetPosition() != 0) 
 566                 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event")); 
 571                 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event")); 
 575        default: // GST_STATE_NULL etc. 
 580 //----------------------------------------------------------------------------- 
 581 // wxGStreamerMediaBackend::QueryVideoSizeFromElement 
 583 // Run through the stuff in "stream-info" of element for a valid 
 584 // video pad, and then attempt to query the video size from it - if not 
 585 // set up an event to do so when ready. Return true 
 586 // if we got a valid video pad. 
 587 //----------------------------------------------------------------------------- 
 588 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
) 
 590     const GList 
*list 
= NULL
; 
 591     g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
); 
 593     for ( ; list 
!= NULL
; list 
= list
->next
) 
 595         GObject 
*info 
= (GObject 
*) list
->data
; 
 601         g_object_get (info
, "type", &type
, NULL
); 
 602         pspec 
= g_object_class_find_property ( 
 603                         G_OBJECT_GET_CLASS (info
), "type"); 
 604         val 
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
); 
 606         if (!strncasecmp(val
->value_name
, "video", 5) || 
 607             !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21)) 
 609             // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"... 
 610             // but a lot of old plugins still use "pad" :) 
 611             pspec 
= g_object_class_find_property ( 
 612                         G_OBJECT_GET_CLASS (info
), "object"); 
 615                 g_object_get (info
, "pad", &pad
, NULL
); 
 617                 g_object_get (info
, "object", &pad
, NULL
); 
 619 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8 
 620             // Killed in 0.9, presumely because events and such 
 621             // should be pushed on pads regardless of whether they 
 622             // are currently linked 
 623             pad 
= (GstPad 
*) GST_PAD_REALIZE (pad
); 
 627             if(!QueryVideoSizeFromPad(pad
)) 
 629                 // wait for those caps to get ready 
 633                 G_CALLBACK(gst_notify_caps_callback
), 
 638     }// end searching through info list 
 640     // no video (or extremely delayed stream-info) 
 643         m_videoSize 
= wxSize(0,0); 
 650 //----------------------------------------------------------------------------- 
 651 // wxGStreamerMediaBackend::QueryVideoSizeFromPad 
 653 // Gets the size of our video (in wxSize) from a GstPad 
 654 //----------------------------------------------------------------------------- 
 655 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
) 
 657     const GstCaps
* caps 
= GST_PAD_CAPS(pad
); 
 660         const GstStructure 
*s 
= gst_caps_get_structure (caps
, 0); 
 663         gst_structure_get_int (s
, "width", &m_videoSize
.x
); 
 664         gst_structure_get_int (s
, "height", &m_videoSize
.y
); 
 667         par 
= gst_structure_get_value (s
, "pixel-aspect-ratio"); 
 671             wxLogTrace(wxTRACE_GStreamer
, 
 672                        wxT("pixel-aspect-ratio found in pad")); 
 673             int num 
= par
->data
[0].v_int
, 
 674                 den 
= par
->data
[1].v_int
; 
 676             // TODO: maybe better fraction normalization... 
 678                 m_videoSize
.x 
= (int) ((float) num 
* m_videoSize
.x 
/ den
); 
 680                 m_videoSize
.y 
= (int) ((float) den 
* m_videoSize
.y 
/ num
); 
 683          wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"), 
 684                      m_videoSize
.x
, m_videoSize
.y
); 
 688     return false; // not ready/massive failure 
 691 //----------------------------------------------------------------------------- 
 692 // wxGStreamerMediaBackend::SetupXOverlay 
 694 // Attempts to set the XWindow id of our GstXOverlay to tell it which 
 695 // window to play video in. 
 696 //----------------------------------------------------------------------------- 
 697 void wxGStreamerMediaBackend::SetupXOverlay() 
 699     // Use the xoverlay extension to tell gstreamer to play in our window 
 701     if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
)) 
 703         // Not realized yet - set to connect at realization time 
 704         g_signal_connect (m_ctrl
->m_wxwindow
, 
 706                           G_CALLBACK (gtk_window_realize_callback
), 
 713         GdkWindow 
*window 
= m_ctrl
->m_wxwindow
->window
; 
 717     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay
), 
 719                         GDK_WINDOW_XWINDOW( window 
) 
 726     g_signal_connect (m_ctrl
->m_wxwindow
, 
 727                         // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/, 
 729                       G_CALLBACK(gtk_window_expose_callback
), this); 
 730     } // end if GtkPizza realized 
 734 //----------------------------------------------------------------------------- 
 735 // wxGStreamerMediaBackend::SyncStateChange 
 737 // This function is rather complex - basically the idea is that we 
 738 // poll the GstBus of m_playbin until it has reached desiredstate, an error 
 739 // is reached, or there are no more messages left in the GstBus queue. 
 741 // Returns true if there are no messages left in the queue or 
 742 // the current state reaches the disired state. 
 744 // PRECONDITION: Assumes m_asynclock is Lock()ed 
 745 //----------------------------------------------------------------------------- 
 746 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 747 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 748                                               GstElementState desiredstate
, 
 751     GstBus
* bus 
= gst_element_get_bus(element
); 
 755     gint64 llTimeWaited 
= 0; 
 760         // NB: The GStreamer gst_bus_poll is unfortunately broken and 
 761         // throws silly critical internal errors (for instance 
 762         // "message != NULL" when the whole point of it is to 
 763         // poll for the message in the first place!) so we implement 
 764         // our own "waiting mechinism" 
 765         if(gst_bus_have_pending(bus
) == FALSE
) 
 767             if(llTimeWaited 
>= llTimeout
) 
 768                 return true; // Reached timeout... assume success 
 769             llTimeWaited 
+= 10*GST_MSECOND
; 
 774         message 
= gst_bus_pop(bus
); 
 776         message 
= gst_bus_poll(bus
, (GstMessageType
) 
 777                            (GST_MESSAGE_STATE_CHANGED 
| 
 779                             GST_MESSAGE_EOS
), llTimeout
); 
 783         if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
) 
 785             switch(GST_MESSAGE_TYPE(message
)) 
 787                 case GST_MESSAGE_STATE_CHANGED
: 
 789                     GstState oldstate
, newstate
, pendingstate
; 
 790                     gst_message_parse_state_changed(message
, &oldstate
, 
 791                                                     &newstate
, &pendingstate
); 
 792                     if(newstate 
== desiredstate
) 
 794                         bSuccess 
= bBreak 
= true; 
 798                 case GST_MESSAGE_ERROR
: 
 802                     gst_message_parse_error(message
, &error
, &debug
); 
 803                     gst_error_callback(NULL
, NULL
, error
, debug
, this); 
 807                 case GST_MESSAGE_EOS
: 
 808                     wxLogSysError(wxT("Reached end of stream prematurely")); 
 812                     break; // not handled 
 816         gst_message_unref(message
); 
 821 #else // 0.8 implementation 
 822 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 823                                               GstElementState desiredstate
, 
 826     gint64 llTimeWaited 
= 0; 
 827     while(GST_STATE(element
) != desiredstate
) 
 829         if(llTimeWaited 
>= llTimeout
) 
 831         llTimeWaited 
+= 10*GST_MSECOND
; 
 835     return llTimeWaited 
!= llTimeout
; 
 839 //----------------------------------------------------------------------------- 
 840 // wxGStreamerMediaBackend::TryAudioSink 
 841 // wxGStreamerMediaBackend::TryVideoSink 
 843 // Uses various means to determine whether a passed in video/audio sink 
 844 // if suitable for us - if it is not we return false and unref the 
 845 // inappropriate sink. 
 846 //----------------------------------------------------------------------------- 
 847 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
) 
 849     if( !GST_IS_ELEMENT(audiosink
) ) 
 851         if(G_IS_OBJECT(audiosink
)) 
 852             g_object_unref(audiosink
); 
 859 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
) 
 861     // Check if the video sink either is an xoverlay or might contain one... 
 862     if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) ) 
 864         if(G_IS_OBJECT(videosink
)) 
 865             g_object_unref(videosink
); 
 869     // Make our video sink and make sure it supports the x overlay interface 
 870     // the x overlay enables us to put the video in our control window 
 871     // (i.e. we NEED it!) - also connect to the natural video size change event 
 872     if( GST_IS_BIN(videosink
) ) 
 873         m_xoverlay 
= (GstXOverlay
*) 
 874                         gst_bin_get_by_interface (GST_BIN (videosink
), 
 877         m_xoverlay 
= (GstXOverlay
*) videosink
; 
 879     if ( !GST_IS_X_OVERLAY(m_xoverlay
) ) 
 881         g_object_unref(videosink
); 
 888 //----------------------------------------------------------------------------- 
 889 // wxGStreamerMediaEventHandler::OnMediaFinish 
 891 // Called when the media is about to stop 
 892 //----------------------------------------------------------------------------- 
 893 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
)) 
 895     // (RN - I have no idea why I thought this was good behaviour.... 
 896     // maybe it made sense for streaming/nonseeking data but 
 897     // generally it seems like a really bad idea) - 
 898     if(m_be
->SendStopEvent()) 
 900         // Stop the media (we need to set it back to paused 
 901         // so that people can get the duration et al. 
 902         // and send the finish event (luckily we can "Sync" it out... LOL!) 
 903         // (We don't check return values here because we can't really do 
 905         wxMutexLocker 
lock(m_be
->m_asynclock
); 
 907         // Set element to ready+sync it 
 908         gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
); 
 909         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
); 
 911         // Now set it to paused + update pause pos to 0 and 
 912         // Sync that as well (note that we don't call Stop() here 
 913         // due to mutex issues) 
 914         gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
); 
 915         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
); 
 916         m_be
->m_llPausedPos 
= 0; 
 918         // Finally, queue the finish event 
 919         m_be
->QueueFinishEvent(); 
 923 //----------------------------------------------------------------------------- 
 927 //----------------------------------------------------------------------------- 
 929 //----------------------------------------------------------------------------- 
 930 // wxGStreamerMediaBackend Constructor 
 932 // Sets m_playbin to NULL signifying we havn't loaded anything yet 
 933 //----------------------------------------------------------------------------- 
 934 wxGStreamerMediaBackend::wxGStreamerMediaBackend() 
 940 //----------------------------------------------------------------------------- 
 941 // wxGStreamerMediaBackend Destructor 
 943 // Stops/cleans up memory 
 945 // NB: This could trigger a critical warning but doing a SyncStateChange 
 946 //     here is just going to slow down quitting of the app, which is bad. 
 947 //----------------------------------------------------------------------------- 
 948 wxGStreamerMediaBackend::~wxGStreamerMediaBackend() 
 950     // Dispose of the main player and related objects 
 953         wxASSERT( GST_IS_OBJECT(m_playbin
) ); 
 954         gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
 955         gst_object_unref (GST_OBJECT (m_playbin
)); 
 956         delete m_eventHandler
; 
 960 //----------------------------------------------------------------------------- 
 961 // wxGStreamerMediaBackend::CreateControl 
 963 // Initializes GStreamer and creates the wx side of our media control 
 964 //----------------------------------------------------------------------------- 
 965 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 970                                 const wxValidator
& validator
, 
 971                                 const wxString
& name
) 
 977     //Convert arguments to unicode if enabled 
 980     char **argvGST 
= new char*[wxTheApp
->argc 
+ 1]; 
 981     for ( i 
= 0; i 
< wxTheApp
->argc
; i
++ ) 
 983 #if wxUSE_UNICODE_WCHAR 
 984         argvGST
[i
] = wxStrdupA(wxConvUTF8
.cWX2MB(wxTheApp
->argv
[i
])); 
 986         argvGST
[i
] = wxStrdupA(wxTheApp
->argv
[i
].utf8_str()); 
 990     argvGST
[wxTheApp
->argc
] = NULL
; 
 992     int argcGST 
= wxTheApp
->argc
; 
 994 #define argcGST wxTheApp->argc 
 995 #define argvGST wxTheApp->argv 
 998     //Really init gstreamer 
1000     GError
* error 
= NULL
; 
1001 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1002     bInited 
= gst_init_check(&argcGST
, &argvGST
, &error
); 
1004     bInited 
= gst_init_check(&argcGST
, &argvGST
); 
1007     // Cleanup arguments for unicode case 
1009     for ( i 
= 0; i 
< argcGST
; i
++ ) 
1017     if(!bInited
)    //gst_init_check fail? 
1021             wxLogSysError(wxT("Could not initialize GStreamer\n") 
1022                           wxT("Error Message:%s"), 
1023                           (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
) 
1025             g_error_free(error
); 
1028             wxLogSysError(wxT("Could not initialize GStreamer")); 
1034     // wxControl creation 
1036     m_ctrl 
= wxStaticCast(ctrl
, wxMediaCtrl
); 
1039     // We handle our own GTK expose events 
1040     m_ctrl
->m_noExpose 
= true; 
1043     if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
, 
1044                             style
,  // TODO: remove borders??? 
1047         wxFAIL_MSG(wxT("Could not create wxControl!!!")); 
1052     // Turn off double-buffering so that 
1053     // so it doesn't draw over the video and cause sporadic 
1054     // disappearances of the video 
1055     gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
); 
1058     // don't erase the background of our control window 
1059     // so that resizing is a bit smoother 
1060     m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
1062     // Create our playbin object 
1063     m_playbin 
= gst_element_factory_make ("playbin", "play"); 
1064     if (!GST_IS_ELEMENT(m_playbin
)) 
1066         if(G_IS_OBJECT(m_playbin
)) 
1067             g_object_unref(m_playbin
); 
1068         wxLogSysError(wxT("Got an invalid playbin")); 
1072 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1073     // Connect the glib events/callbacks we want to our playbin 
1074     g_signal_connect(m_playbin
, "eos", 
1075                      G_CALLBACK(gst_finish_callback
), this); 
1076     g_signal_connect(m_playbin
, "error", 
1077                      G_CALLBACK(gst_error_callback
), this); 
1078     g_signal_connect(m_playbin
, "state-change", 
1079                      G_CALLBACK(gst_state_change_callback
), this); 
1081     // GStreamer 0.10+ uses GstBus for this now, connect to the sync 
1082     // handler as well so we can set the X window id of our xoverlay 
1083     gst_bus_add_watch (gst_element_get_bus(m_playbin
), 
1084                        (GstBusFunc
) gst_bus_async_callback
, this); 
1085     gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
), 
1086                              (GstBusSyncHandler
) gst_bus_sync_callback
, this); 
1087     g_signal_connect(m_playbin
, "notify::stream-info", 
1088                      G_CALLBACK(gst_notify_stream_info_callback
), this); 
1091     // Get the audio sink 
1092     GstElement
* audiosink 
= gst_gconf_get_default_audio_sink(); 
1093     if( !TryAudioSink(audiosink
) ) 
1095         // fallback to autodetection, then alsa, then oss as a stopgap 
1096         audiosink 
= gst_element_factory_make ("autoaudiosink", "audio-sink"); 
1097         if( !TryAudioSink(audiosink
) ) 
1099             audiosink 
= gst_element_factory_make ("alsasink", "alsa-output"); 
1100             if( !TryAudioSink(audiosink
) ) 
1102                 audiosink 
= gst_element_factory_make ("osssink", "play_audio"); 
1103                 if( !TryAudioSink(audiosink
) ) 
1105                     wxLogSysError(wxT("Could not find a valid audiosink")); 
1112     // Setup video sink - first try gconf, then auto, then xvimage and 
1113     // then finally plain ximage 
1114     GstElement
* videosink 
= gst_gconf_get_default_video_sink(); 
1115     if( !TryVideoSink(videosink
) ) 
1117         videosink 
= gst_element_factory_make ("autovideosink", "video-sink"); 
1118         if( !TryVideoSink(videosink
) ) 
1120             videosink 
= gst_element_factory_make ("xvimagesink", "video-sink"); 
1121             if( !TryVideoSink(videosink
) ) 
1123                 // finally, do a final fallback to ximagesink 
1125                     gst_element_factory_make ("ximagesink", "video-sink"); 
1126                 if( !TryVideoSink(videosink
) ) 
1128                     g_object_unref(audiosink
); 
1129                     wxLogSysError(wxT("Could not find a suitable video sink")); 
1136 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1137     // Not on 0.10... called when video size changes 
1138     g_signal_connect(m_xoverlay
, "desired-size-changed", 
1139                      G_CALLBACK(gst_desired_size_changed_callback
), this); 
1141     // Tell GStreamer which window to draw to in 0.8 - 0.10 
1142     // sometimes needs this too... 
1145     // Now that we know (or, rather think) our video and audio sink 
1146     // are valid set our playbin to use them 
1147     g_object_set (G_OBJECT (m_playbin
), 
1148                   "video-sink", videosink
, 
1149                   "audio-sink", audiosink
, 
1152     m_eventHandler 
= new wxGStreamerMediaEventHandler(this); 
1156 //----------------------------------------------------------------------------- 
1157 // wxGStreamerMediaBackend::Load (File version) 
1159 // Just calls DoLoad() with a prepended file scheme 
1160 //----------------------------------------------------------------------------- 
1161 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
) 
1163     return DoLoad(wxString( wxT("file://") ) + fileName
); 
1166 //----------------------------------------------------------------------------- 
1167 // wxGStreamerMediaBackend::Load (URI version) 
1169 // In the case of a file URI passes it unencoded - 
1170 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer) 
1171 // is sort of broken and only accepts uris with at least two slashes 
1172 // after the scheme (i.e. file: == not ok, file:// == ok) 
1173 //----------------------------------------------------------------------------- 
1174 bool wxGStreamerMediaBackend::Load(const wxURI
& location
) 
1176     if(location
.GetScheme().CmpNoCase(wxT("file")) == 0) 
1178         wxString uristring 
= location
.BuildUnescapedURI(); 
1180         //Workaround GstURI leading "//" problem and make sure it leads 
1182         return DoLoad(wxString(wxT("file://")) + 
1183                       uristring
.Right(uristring
.length() - 5) 
1187         return DoLoad(location
.BuildURI()); 
1190 //----------------------------------------------------------------------------- 
1191 // wxGStreamerMediaBackend::DoLoad 
1194 // 1) Reset member variables and set playbin back to ready state 
1195 // 2) Check URI for validity and then tell the playbin to load it 
1196 // 3) Set the playbin to the pause state 
1198 // NB: Even after this function is over with we probably don't have the 
1199 // video size or duration - no amount of clever hacking is going to get 
1200 // around that, unfortunately. 
1201 //----------------------------------------------------------------------------- 
1202 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
) 
1204     wxMutexLocker 
lock(m_asynclock
); // lock state events and async callbacks 
1206     // Reset positions & rate 
1209     m_videoSize 
= wxSize(0,0); 
1211     // Set playbin to ready to stop the current media... 
1212     if( gst_element_set_state (m_playbin
, 
1213                                GST_STATE_READY
) == GST_STATE_FAILURE 
|| 
1214         !SyncStateChange(m_playbin
, GST_STATE_READY
)) 
1216         wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ") 
1217                       wxT("Could not set initial state to ready")); 
1221     // free current media resources 
1222     gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
1224     // Make sure the passed URI is valid and tell playbin to load it 
1225     // non-file uris are encoded 
1226     wxASSERT(gst_uri_protocol_is_valid("file")); 
1227     wxASSERT(gst_uri_is_valid(locstring
.mb_str())); 
1229     g_object_set (G_OBJECT (m_playbin
), "uri", 
1230                   (const char*)locstring
.mb_str(), NULL
); 
1232     // Try to pause media as gstreamer won't let us query attributes 
1233     // such as video size unless it is paused or playing 
1234     if( gst_element_set_state (m_playbin
, 
1235                                GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1236         !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1238         return false; // no real error message needed here as this is 
1239                       // generic failure 99% of the time (i.e. no 
1240                       // source etc.) and has an error message 
1244     NotifyMovieLoaded(); // Notify the user - all we can do for now 
1249 //----------------------------------------------------------------------------- 
1250 // wxGStreamerMediaBackend::Play 
1252 // Sets the stream to a playing state 
1254 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1255 //----------------------------------------------------------------------------- 
1256 bool wxGStreamerMediaBackend::Play() 
1258     if (gst_element_set_state (m_playbin
, 
1259                                GST_STATE_PLAYING
) == GST_STATE_FAILURE
) 
1264 //----------------------------------------------------------------------------- 
1265 // wxGStreamerMediaBackend::Pause 
1267 // Marks where we paused and pauses the stream 
1269 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1270 //----------------------------------------------------------------------------- 
1271 bool wxGStreamerMediaBackend::Pause() 
1273     m_llPausedPos 
= wxGStreamerMediaBackend::GetPosition(); 
1274     if (gst_element_set_state (m_playbin
, 
1275                                GST_STATE_PAUSED
) == GST_STATE_FAILURE
) 
1280 //----------------------------------------------------------------------------- 
1281 // wxGStreamerMediaBackend::Stop 
1283 // Pauses the stream and sets the position to 0. Note that this is 
1284 // synchronous (!) pausing. 
1286 // Due to the mutex locking this is probably thread-safe actually. 
1287 //----------------------------------------------------------------------------- 
1288 bool wxGStreamerMediaBackend::Stop() 
1290     {   // begin state lock 
1291         wxMutexLocker 
lock(m_asynclock
); 
1292         if(gst_element_set_state (m_playbin
, 
1293                                   GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1294           !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1296             wxLogSysError(wxT("Could not set state to paused for Stop()")); 
1301     bool bSeekedOK 
= wxGStreamerMediaBackend::SetPosition(0); 
1305         wxLogSysError(wxT("Could not seek to initial position in Stop()")); 
1309     QueueStopEvent(); // Success 
1313 //----------------------------------------------------------------------------- 
1314 // wxGStreamerMediaBackend::GetState 
1316 // Gets the state of the media 
1317 //----------------------------------------------------------------------------- 
1318 wxMediaState 
wxGStreamerMediaBackend::GetState() 
1320     switch(GST_STATE(m_playbin
)) 
1322         case GST_STATE_PLAYING
: 
1323             return wxMEDIASTATE_PLAYING
; 
1324         case GST_STATE_PAUSED
: 
1325             if (m_llPausedPos 
== 0) 
1326                 return wxMEDIASTATE_STOPPED
; 
1328                 return wxMEDIASTATE_PAUSED
; 
1329         default://case GST_STATE_READY: 
1330             return wxMEDIASTATE_STOPPED
; 
1334 //----------------------------------------------------------------------------- 
1335 // wxGStreamerMediaBackend::GetPosition 
1337 // If paused, returns our marked position - otherwise it queries the 
1338 // GStreamer playbin for the position and returns that 
1341 // NB: At least in 0.8, when you pause and seek gstreamer 
1342 // NB: doesn't update the position sometimes, so we need to keep track of 
1343 // NB: whether we have paused or not and keep track of the time after the 
1344 // NB: pause and whenever the user seeks while paused 
1347 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused. 
1348 //----------------------------------------------------------------------------- 
1349 wxLongLong 
wxGStreamerMediaBackend::GetPosition() 
1351     if(GetState() != wxMEDIASTATE_PLAYING
) 
1352         return m_llPausedPos
; 
1356         GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1358         if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) || 
1359             fmtTime 
!= GST_FORMAT_TIME 
|| pos 
== -1) 
1361         return pos 
/ GST_MSECOND 
; 
1365 //----------------------------------------------------------------------------- 
1366 // wxGStreamerMediaBackend::SetPosition 
1368 // Sets the position of the stream 
1369 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so 
1370 // there is 1000000 nanoseconds in a millisecond) 
1372 // If we are paused we update the cached pause position. 
1374 // This is also an exceedingly ugly function due to the three implementations 
1375 // (or, rather two plus one implementation without a seek function). 
1377 // This is asynchronous and thread-safe on both 0.8 and 0.10. 
1379 // NB: This fires both a stop and play event if the media was previously 
1380 // playing... which in some ways makes sense. And yes, this makes the video 
1381 // go all haywire at times - a gstreamer bug... 
1382 //----------------------------------------------------------------------------- 
1383 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
) 
1385 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \ 
1386                            && GST_VERSION_MICRO == 0 
1387     // 0.8.0 has no gst_element_seek according to official docs!!! 
1388     wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek") 
1389                   wxT(" according to official docs")); 
1393 #   if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1394         gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
, 
1395            (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1396                           GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
, 
1397                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
); 
1399         // NB: Some gstreamer versions return false basically all the time 
1400         // here - even totem doesn't bother to check the return value here 
1401         // so I guess we'll just assume it worked - 
1402         // TODO: maybe check the gst error callback??? 
1403         gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET 
| 
1404             GST_FORMAT_TIME 
| GST_SEEK_FLAG_FLUSH
), 
1405             where
.GetValue() * GST_MSECOND 
); 
1407 #   endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1410         m_llPausedPos 
= where
; 
1417 //----------------------------------------------------------------------------- 
1418 // wxGStreamerMediaBackend::GetDuration 
1420 // Obtains the total time of our stream 
1421 // THREAD-UNSAFE, requires media to be paused or playing 
1422 //----------------------------------------------------------------------------- 
1423 wxLongLong 
wxGStreamerMediaBackend::GetDuration() 
1426     GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1428     if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) || 
1429        fmtTime 
!= GST_FORMAT_TIME 
|| length 
== -1) 
1431     return length 
/ GST_MSECOND 
; 
1434 //----------------------------------------------------------------------------- 
1435 // wxGStreamerMediaBackend::Move 
1437 // Called when the window is moved - GStreamer takes care of this 
1438 // for us so nothing is needed 
1439 //----------------------------------------------------------------------------- 
1440 void wxGStreamerMediaBackend::Move(int WXUNUSED(x
), 
1447 //----------------------------------------------------------------------------- 
1448 // wxGStreamerMediaBackend::GetVideoSize 
1450 // Returns our cached video size from Load/gst_notify_caps_callback 
1451 // gst_x_overlay_get_desired_size also does this in 0.8... 
1452 //----------------------------------------------------------------------------- 
1453 wxSize 
wxGStreamerMediaBackend::GetVideoSize() const 
1458 //----------------------------------------------------------------------------- 
1459 // wxGStreamerMediaBackend::GetPlaybackRate 
1460 // wxGStreamerMediaBackend::SetPlaybackRate 
1462 // Obtains/Sets the playback rate of the stream 
1464 //TODO: PlaybackRate not currently supported via playbin directly - 
1465 //TODO: Ronald S. Bultje noted on gstreamer-devel: 
1467 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As 
1468 //TODO: for the first, yes, we have elements for that, btu they"re not part of 
1469 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual 
1470 //TODO: video/audiosink and the speed-changing element for this, and set that 
1471 //TODO: element as video-sink or audio-sink property in playbin. The 
1472 //TODO: audio-element is called "speed", the video-element is called "videodrop" 
1473 //TODO: (although that appears to be deprecated in favour of "videorate", which 
1474 //TODO: again cannot do this, so this may not work at all in the end). For 
1475 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is 
1476 //TODO: part of playbin. 
1478 // In 0.10 GStreamer has new gst_element_seek API that might 
1479 // support this - and I've got an attempt to do so but it is untested 
1480 // but it would appear to work... 
1481 //----------------------------------------------------------------------------- 
1482 double wxGStreamerMediaBackend::GetPlaybackRate() 
1484     return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem 
1485                     // final on that yet and there may not be any actual 
1486                     // plugins that support it... 
1489 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
) 
1491 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1492 #if 0 // not tested enough 
1493     if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
, 
1494                  (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1495                           GST_SEEK_TYPE_CUR
, 0, 
1496                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
) ) 
1510 //----------------------------------------------------------------------------- 
1511 // wxGStreamerMediaBackend::GetDownloadProgress 
1513 // Not really outwardly possible - have been suggested that one could 
1514 // get the information from the component that "downloads" 
1515 //----------------------------------------------------------------------------- 
1516 wxLongLong 
wxGStreamerMediaBackend::GetDownloadProgress() 
1521 //----------------------------------------------------------------------------- 
1522 // wxGStreamerMediaBackend::GetDownloadTotal 
1524 // TODO: Cache this? 
1525 // NB: The length changes every call for some reason due to 
1526 //     GStreamer implementation issues 
1527 // THREAD-UNSAFE, requires media to be paused or playing 
1528 //----------------------------------------------------------------------------- 
1529 wxLongLong 
wxGStreamerMediaBackend::GetDownloadTotal() 
1532     GstFormat fmtBytes 
= GST_FORMAT_BYTES
; 
1534     if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) || 
1535           fmtBytes 
!= GST_FORMAT_BYTES 
|| length 
== -1) 
1540 //----------------------------------------------------------------------------- 
1541 // wxGStreamerMediaBackend::SetVolume 
1542 // wxGStreamerMediaBackend::GetVolume 
1544 // Sets/Gets the volume through the playbin object. 
1545 // Note that this requires a relatively recent gst-plugins so we 
1546 // check at runtime to see whether it is available or not otherwise 
1547 // GST spits out an error on the command line 
1548 //----------------------------------------------------------------------------- 
1549 bool wxGStreamerMediaBackend::SetVolume(double dVolume
) 
1551     if(g_object_class_find_property( 
1552             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1555         g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
); 
1560         wxLogTrace(wxTRACE_GStreamer
, 
1561             wxT("SetVolume: volume prop not found - 0.8.5 of ") 
1562             wxT("gst-plugins probably needed")); 
1567 double wxGStreamerMediaBackend::GetVolume() 
1569     double dVolume 
= 1.0; 
1571     if(g_object_class_find_property( 
1572             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1575         g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
); 
1579         wxLogTrace(wxTRACE_GStreamer
, 
1580             wxT("GetVolume: volume prop not found - 0.8.5 of ") 
1581             wxT("gst-plugins probably needed")); 
1587 #endif //wxUSE_GSTREAMER 
1589 // Force link into main library so this backend can be loaded 
1590 #include "wx/html/forcelnk.h" 
1591 FORCE_LINK_ME(basewxmediabackends
) 
1593 #endif //wxUSE_MEDIACTRL