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 
  40 #    include "wx/gtk/win_gtk.h" 
  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 // wxGTK Debugging and idle stuff 
 133 //----------------------------------------------------------------------------- 
 138 #           define DEBUG_MAIN_THREAD \ 
 139                 if (wxThread::IsMain() && g_mainThreadLocked) \ 
 140                     wxPrintf(wxT("gui reentrance")); 
 142 #           define DEBUG_MAIN_THREAD 
 145 #      define DEBUG_MAIN_THREAD 
 148 extern void wxapp_install_idle_handler(); 
 149 extern bool g_isIdle
; 
 150 extern bool g_mainThreadLocked
; 
 153 //----------------------------------------------------------------------------- 
 154 //  wxLogTrace mask string 
 155 //----------------------------------------------------------------------------- 
 156 #define wxTRACE_GStreamer wxT("GStreamer") 
 158 //----------------------------------------------------------------------------- 
 160 //  wxGStreamerMediaBackend 
 162 //----------------------------------------------------------------------------- 
 163 class WXDLLIMPEXP_MEDIA
 
 164     wxGStreamerMediaBackend 
: public wxMediaBackendCommonBase
 
 168     wxGStreamerMediaBackend(); 
 169     ~wxGStreamerMediaBackend(); 
 171     virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 176                                      const wxValidator
& validator
, 
 177                                      const wxString
& name
); 
 180     virtual bool Pause(); 
 183     virtual bool Load(const wxString
& fileName
); 
 184     virtual bool Load(const wxURI
& location
); 
 186     virtual wxMediaState 
GetState(); 
 188     virtual bool SetPosition(wxLongLong where
); 
 189     virtual wxLongLong 
GetPosition(); 
 190     virtual wxLongLong 
GetDuration(); 
 192     virtual void Move(int x
, int y
, int w
, int h
); 
 193     wxSize 
GetVideoSize() const; 
 195     virtual double GetPlaybackRate(); 
 196     virtual bool SetPlaybackRate(double dRate
); 
 198     virtual wxLongLong 
GetDownloadProgress(); 
 199     virtual wxLongLong 
GetDownloadTotal(); 
 201     virtual bool SetVolume(double dVolume
); 
 202     virtual double GetVolume(); 
 204     //------------implementation from now on----------------------------------- 
 205     bool DoLoad(const wxString
& locstring
); 
 206     wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks 
 207     void HandleStateChange(GstElementState oldstate
, GstElementState newstate
); 
 208     bool QueryVideoSizeFromElement(GstElement
* element
); 
 209     bool QueryVideoSizeFromPad(GstPad
* caps
); 
 210     void SetupXOverlay(); 
 211     bool SyncStateChange(GstElement
* element
, GstElementState state
, 
 212                          gint64 llTimeout 
= wxGSTREAMER_TIMEOUT
); 
 213     bool TryAudioSink(GstElement
* audiosink
); 
 214     bool TryVideoSink(GstElement
* videosink
); 
 216     GstElement
*     m_playbin
;      // GStreamer media element 
 217     wxSize          m_videoSize
;    // Cached actual video size 
 218     double          m_dRate
;        // Current playback rate - 
 219                                     // see GetPlaybackRate for notes 
 220     wxLongLong      m_llPausedPos
;  // Paused position - see Pause() 
 221     GstXOverlay
*    m_xoverlay
;     // X Overlay that contains the GST video 
 222     wxMutex         m_asynclock
;    // See "discussion of internals" 
 223     class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below 
 225     friend class wxGStreamerMediaEventHandler
; 
 226     friend class wxGStreamerLoadWaitTimer
; 
 227     DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
); 
 230 //----------------------------------------------------------------------------- 
 231 // wxGStreamerMediaEventHandler 
 233 // OK, this will take an explanation - basically gstreamer callbacks 
 234 // are issued in a seperate thread, and in this thread we may not set 
 235 // the state of the playbin, so we need to send a wx event in that 
 236 // callback so that we set the state of the media and other stuff 
 238 //----------------------------------------------------------------------------- 
 239 class wxGStreamerMediaEventHandler 
: public wxEvtHandler
 
 242     wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
) 
 244         this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
, 
 245            wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
)); 
 248     void OnMediaFinish(wxMediaEvent
& event
); 
 250     wxGStreamerMediaBackend
* m_be
; 
 253 //============================================================================= 
 255 //============================================================================= 
 257 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
) 
 259 //----------------------------------------------------------------------------- 
 263 //----------------------------------------------------------------------------- 
 265 //----------------------------------------------------------------------------- 
 266 // "expose_event" from m_ctrl->m_wxwindow 
 268 // Handle GTK expose event from our window - here we hopefully 
 269 // redraw the video in the case of pausing and other instances... 
 270 // (Returns TRUE to pass to other handlers, FALSE if not) 
 272 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here? 
 273 //----------------------------------------------------------------------------- 
 276 static gboolean 
gtk_window_expose_callback(GtkWidget 
*widget
, 
 277                                            GdkEventExpose 
*event
, 
 278                                            wxGStreamerMediaBackend 
*be
) 
 283     GdkWindow 
*window 
= GTK_PIZZA(be
->GetControl()->m_wxwindow
)->bin_window
; 
 285     // I've seen this reccommended somewhere... 
 286     // TODO: Is this needed? Maybe it is just cruft... 
 287     // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay), 
 288     //                              GDK_WINDOW_XWINDOW( window ) ); 
 290     // If we have actual video..... 
 291     if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) && 
 292        GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
) 
 294         // GST Doesn't redraw automatically while paused 
 295         // Plus, the video sometimes doesn't redraw when it looses focus 
 296         // or is painted over so we just tell it to redraw... 
 297         gst_x_overlay_expose(be
->m_xoverlay
); 
 301         // draw a black background like some other backends do.... 
 302         gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0, 
 303                             widget
->allocation
.width
, 
 304                             widget
->allocation
.height
); 
 312 //----------------------------------------------------------------------------- 
 313 // "realize" from m_ctrl->m_wxwindow 
 315 // If the window wasn't realized when Load was called, this is the 
 316 // callback for when it is - the purpose of which is to tell 
 317 // GStreamer to play the video in our control 
 318 //----------------------------------------------------------------------------- 
 321 static gint 
gtk_window_realize_callback(GtkWidget
* theWidget
, 
 322                                         wxGStreamerMediaBackend
* be
) 
 324     DEBUG_MAIN_THREAD 
// TODO: Is this neccessary? 
 326     if (g_isIdle
)   // FIXME: Why is needed? For wxYield? ?? 
 327         wxapp_install_idle_handler(); 
 329     wxYield();    // FIXME: RN: X Server gets an error/crash if I don't do 
 330                   //       this or a messagebox beforehand?!?!?? 
 332     GdkWindow 
*window 
= GTK_PIZZA(theWidget
)->bin_window
; 
 335     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
), 
 336                                 GDK_WINDOW_XWINDOW( window 
) 
 338     g_signal_connect (be
->GetControl()->m_wxwindow
, 
 340                       G_CALLBACK(gtk_window_expose_callback
), be
); 
 346 //----------------------------------------------------------------------------- 
 347 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE 
 349 // Called by gstreamer when the state changes - here we 
 350 // send the appropriate corresponding wx event. 
 352 // 0.8 only as HandleStateChange does this in both versions 
 353 //----------------------------------------------------------------------------- 
 354 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 356 static void gst_state_change_callback(GstElement 
*play
, 
 357                                       GstElementState oldstate
, 
 358                                       GstElementState newstate
, 
 359                                       wxGStreamerMediaBackend
* be
) 
 361     if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
) 
 363         be
->HandleStateChange(oldstate
, newstate
); 
 364         be
->m_asynclock
.Unlock(); 
 370 //----------------------------------------------------------------------------- 
 371 // "eos" from m_playbin/GST_MESSAGE_EOS 
 373 // Called by gstreamer when the media is done playing ("end of stream") 
 374 //----------------------------------------------------------------------------- 
 376 static void gst_finish_callback(GstElement 
*play
, 
 377                                 wxGStreamerMediaBackend
* be
) 
 379     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback")); 
 380     wxMediaEvent 
event(wxEVT_MEDIA_FINISHED
); 
 381     be
->m_eventHandler
->AddPendingEvent(event
); 
 385 //----------------------------------------------------------------------------- 
 386 // "error" from m_playbin/GST_MESSAGE_ERROR 
 388 // Called by gstreamer when an error is encountered playing the media - 
 389 // We call wxLogTrace in addition wxLogSysError so that we can get it 
 390 // on the command line as well for those who want extra traces. 
 391 //----------------------------------------------------------------------------- 
 393 static void gst_error_callback(GstElement 
*play
, 
 397                                wxGStreamerMediaBackend
* be
) 
 400     sError
.Printf(wxT("gst_error_callback\n") 
 401                   wxT("Error Message:%s\nDebug:%s\n"), 
 402                   (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
), 
 403                   (const wxChar
*)wxConvUTF8
.cMB2WX(debug
)); 
 404     wxLogTrace(wxTRACE_GStreamer
, sError
); 
 405     wxLogSysError(sError
); 
 409 //----------------------------------------------------------------------------- 
 410 // "notify::caps" from the videopad inside "stream-info" of m_playbin 
 412 // Called by gstreamer when the video caps for the media is ready - currently 
 413 // we use the caps to get the natural size of the video 
 416 //----------------------------------------------------------------------------- 
 418 static void gst_notify_caps_callback(GstPad
* pad
, 
 420                                      wxGStreamerMediaBackend
* be
) 
 422     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback")); 
 423     be
->QueryVideoSizeFromPad(pad
); 
 427 //----------------------------------------------------------------------------- 
 428 // "notify::stream-info" from m_playbin 
 430 // Run through the stuff in "stream-info" of m_playbin for a valid 
 431 // video pad, and then attempt to query the video size from it - if not 
 432 // set up an event to do so when ready. 
 434 // Currently unused - now we just query it directly using 
 435 // QueryVideoSizeFromElement. 
 438 //----------------------------------------------------------------------------- 
 439 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 441 static void gst_notify_stream_info_callback(GstElement
* element
, 
 443                                             wxGStreamerMediaBackend
* be
) 
 445     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback")); 
 446     be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 451 //----------------------------------------------------------------------------- 
 452 // "desired-size-changed" from m_xoverlay 
 454 // 0.8-specific this provides us with the video size when it changes - 
 455 // even though we get the caps as well this seems to come before the 
 456 // caps notification does... 
 458 // Note it will return 16,16 for an early-bird value or for audio 
 459 //----------------------------------------------------------------------------- 
 460 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 462 static void gst_desired_size_changed_callback(GstElement 
* play
, 
 463                                               guint width
, guint height
, 
 464                                               wxGStreamerMediaBackend
* be
) 
 466     if(!(width 
== 16 && height 
== 16)) 
 468         be
->m_videoSize
.x 
= width
; 
 469         be
->m_videoSize
.y 
= height
; 
 472         be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 477 //----------------------------------------------------------------------------- 
 478 // gst_bus_async_callback [static] 
 479 // gst_bus_sync_callback [static] 
 481 // Called by m_playbin for notifications such as end-of-stream in 0.10 - 
 482 // in previous versions g_signal notifications were used. Because everything 
 483 // in centered in one switch statement though it reminds one of old WinAPI 
 486 // gst_bus_sync_callback is that sync version that is called on the main GUI 
 487 // thread before the async version that we use to set the xwindow id of the 
 488 // XOverlay (NB: This isn't currently used - see CreateControl()). 
 489 //----------------------------------------------------------------------------- 
 490 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 492 static gboolean 
gst_bus_async_callback(GstBus
* bus
, 
 494                                        wxGStreamerMediaBackend
* be
) 
 496     if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
) 
 498     if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
) 
 501     switch(GST_MESSAGE_TYPE(message
)) 
 503         case GST_MESSAGE_STATE_CHANGED
: 
 505             GstState oldstate
, newstate
, pendingstate
; 
 506             gst_message_parse_state_changed(message
, &oldstate
, 
 507                                             &newstate
, &pendingstate
); 
 508             be
->HandleStateChange(oldstate
, newstate
); 
 511         case GST_MESSAGE_EOS
: 
 513             gst_finish_callback(NULL
, be
); 
 516         case GST_MESSAGE_ERROR
: 
 520             gst_message_parse_error(message
, &error
, &debug
); 
 521             gst_error_callback(NULL
, NULL
, error
, debug
, be
); 
 528     be
->m_asynclock
.Unlock(); 
 529     return FALSE
; // remove the message from Z queue 
 532 static GstBusSyncReply 
gst_bus_sync_callback(GstBus
* bus
, 
 534                                              wxGStreamerMediaBackend
* be
) 
 536     // Pass a non-xwindowid-setting event on to the async handler where it 
 538     if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT 
|| 
 539         !gst_structure_has_name (message
->structure
, "prepare-xwindow-id")) 
 542         // NB: Unfortunately, the async callback can be quite 
 543         // buggy at times and often doesn't get called at all, 
 544         // so here we are processing it right here in the calling 
 545         // thread instead of the GUI one... 
 547         if(gst_bus_async_callback(bus
, message
, be
)) 
 553     wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id")); 
 555     return GST_BUS_DROP
; // We handled this message - drop from the queue 
 560 //----------------------------------------------------------------------------- 
 562 // Private (although not in the C++ sense)  methods 
 564 //----------------------------------------------------------------------------- 
 566 //----------------------------------------------------------------------------- 
 567 // wxGStreamerMediaBackend::HandleStateChange 
 569 // Handles a state change event from our C Callback for "state-change" or 
 570 // the async queue in 0.10. (Mostly this is here to avoid locking the 
 571 // the mutex twice...) 
 572 //----------------------------------------------------------------------------- 
 573 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
, 
 574                                                 GstElementState newstate
) 
 578         case GST_STATE_PLAYING
: 
 579             wxLogTrace(wxTRACE_GStreamer
, wxT("Play event")); 
 582         case GST_STATE_PAUSED
: 
 583             // For some reason .10 sends a lot of oldstate == newstate 
 584             // messages - most likely for pending ones - also 
 585             // !<GST_STATE_PAUSED as we are only concerned 
 586             if(oldstate 
< GST_STATE_PAUSED 
|| oldstate 
== newstate
) 
 588             if(wxGStreamerMediaBackend::GetPosition() != 0) 
 590                 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event")); 
 595                 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event")); 
 599        default: // GST_STATE_NULL etc. 
 604 //----------------------------------------------------------------------------- 
 605 // wxGStreamerMediaBackend::QueryVideoSizeFromElement 
 607 // Run through the stuff in "stream-info" of element for a valid 
 608 // video pad, and then attempt to query the video size from it - if not 
 609 // set up an event to do so when ready. Return true 
 610 // if we got a valid video pad. 
 611 //----------------------------------------------------------------------------- 
 612 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
) 
 614     const GList 
*list 
= NULL
; 
 615     g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
); 
 617     for ( ; list 
!= NULL
; list 
= list
->next
) 
 619         GObject 
*info 
= (GObject 
*) list
->data
; 
 625         g_object_get (info
, "type", &type
, NULL
); 
 626         pspec 
= g_object_class_find_property ( 
 627                         G_OBJECT_GET_CLASS (info
), "type"); 
 628         val 
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
); 
 630         if (!strncasecmp(val
->value_name
, "video", 5) || 
 631             !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21)) 
 633             // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"... 
 634             // but a lot of old plugins still use "pad" :) 
 635             pspec 
= g_object_class_find_property ( 
 636                         G_OBJECT_GET_CLASS (info
), "object"); 
 639                 g_object_get (info
, "pad", &pad
, NULL
); 
 641                 g_object_get (info
, "object", &pad
, NULL
); 
 643 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8 
 644             // Killed in 0.9, presumely because events and such 
 645             // should be pushed on pads regardless of whether they 
 646             // are currently linked 
 647             pad 
= (GstPad 
*) GST_PAD_REALIZE (pad
); 
 651             if(!QueryVideoSizeFromPad(pad
)) 
 653                 // wait for those caps to get ready 
 657                 G_CALLBACK(gst_notify_caps_callback
), 
 662     }// end searching through info list 
 664     // no video (or extremely delayed stream-info) 
 667         m_videoSize 
= wxSize(0,0); 
 674 //----------------------------------------------------------------------------- 
 675 // wxGStreamerMediaBackend::QueryVideoSizeFromPad 
 677 // Gets the size of our video (in wxSize) from a GstPad 
 678 //----------------------------------------------------------------------------- 
 679 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
) 
 681     const GstCaps
* caps 
= GST_PAD_CAPS(pad
); 
 684         const GstStructure 
*s 
= gst_caps_get_structure (caps
, 0); 
 687         gst_structure_get_int (s
, "width", &m_videoSize
.x
); 
 688         gst_structure_get_int (s
, "height", &m_videoSize
.y
); 
 691         par 
= gst_structure_get_value (s
, "pixel-aspect-ratio"); 
 695             wxLogTrace(wxTRACE_GStreamer
, 
 696                        wxT("pixel-aspect-ratio found in pad")); 
 697             int num 
= par
->data
[0].v_int
, 
 698                 den 
= par
->data
[1].v_int
; 
 700             // TODO: maybe better fraction normalization... 
 702                 m_videoSize
.x 
= (int) ((float) num 
* m_videoSize
.x 
/ den
); 
 704                 m_videoSize
.y 
= (int) ((float) den 
* m_videoSize
.y 
/ num
); 
 707          wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"), 
 708                      m_videoSize
.x
, m_videoSize
.y
); 
 712     return false; // not ready/massive failure 
 715 //----------------------------------------------------------------------------- 
 716 // wxGStreamerMediaBackend::SetupXOverlay 
 718 // Attempts to set the XWindow id of our GstXOverlay to tell it which 
 719 // window to play video in. 
 720 //----------------------------------------------------------------------------- 
 721 void wxGStreamerMediaBackend::SetupXOverlay() 
 723     // Use the xoverlay extension to tell gstreamer to play in our window 
 725     if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
)) 
 727         // Not realized yet - set to connect at realization time 
 728         g_signal_connect (m_ctrl
->m_wxwindow
, 
 730                           G_CALLBACK (gtk_window_realize_callback
), 
 735         wxYield(); // see realize callback... 
 736         GdkWindow 
*window 
= GTK_PIZZA(m_ctrl
->m_wxwindow
)->bin_window
; 
 740     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay
), 
 742                         GDK_WINDOW_XWINDOW( window 
) 
 749     g_signal_connect (m_ctrl
->m_wxwindow
, 
 750                         // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/, 
 752                       G_CALLBACK(gtk_window_expose_callback
), this); 
 753     } // end if GtkPizza realized 
 757 //----------------------------------------------------------------------------- 
 758 // wxGStreamerMediaBackend::SyncStateChange 
 760 // This function is rather complex - basically the idea is that we 
 761 // poll the GstBus of m_playbin until it has reached desiredstate, an error 
 762 // is reached, or there are no more messages left in the GstBus queue. 
 764 // Returns true if there are no messages left in the queue or 
 765 // the current state reaches the disired state. 
 767 // PRECONDITION: Assumes m_asynclock is Lock()ed 
 768 //----------------------------------------------------------------------------- 
 769 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 770 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 771                                               GstElementState desiredstate
, 
 774     GstBus
* bus 
= gst_element_get_bus(element
); 
 778     gint64 llTimeWaited 
= 0; 
 783         // NB: The GStreamer gst_bus_poll is unfortunately broken and 
 784         // throws silly critical internal errors (for instance 
 785         // "message != NULL" when the whole point of it is to 
 786         // poll for the message in the first place!) so we implement 
 787         // our own "waiting mechinism" 
 788         if(gst_bus_have_pending(bus
) == FALSE
) 
 790             if(llTimeWaited 
>= llTimeout
) 
 791                 return true; // Reached timeout... assume success 
 792             llTimeWaited 
+= 10*GST_MSECOND
; 
 797         message 
= gst_bus_pop(bus
); 
 799         message 
= gst_bus_poll(bus
, (GstMessageType
) 
 800                            (GST_MESSAGE_STATE_CHANGED 
| 
 802                             GST_MESSAGE_EOS
), llTimeout
); 
 806         if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
) 
 808             switch(GST_MESSAGE_TYPE(message
)) 
 810                 case GST_MESSAGE_STATE_CHANGED
: 
 812                     GstState oldstate
, newstate
, pendingstate
; 
 813                     gst_message_parse_state_changed(message
, &oldstate
, 
 814                                                     &newstate
, &pendingstate
); 
 815                     if(newstate 
== desiredstate
) 
 817                         bSuccess 
= bBreak 
= true; 
 821                 case GST_MESSAGE_ERROR
: 
 825                     gst_message_parse_error(message
, &error
, &debug
); 
 826                     gst_error_callback(NULL
, NULL
, error
, debug
, this); 
 830                 case GST_MESSAGE_EOS
: 
 831                     wxLogSysError(wxT("Reached end of stream prematurely")); 
 835                     break; // not handled 
 839         gst_message_unref(message
); 
 844 #else // 0.8 implementation 
 845 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 846                                               GstElementState desiredstate
, 
 849     gint64 llTimeWaited 
= 0; 
 850     while(GST_STATE(element
) != desiredstate
) 
 852         if(llTimeWaited 
>= llTimeout
) 
 854         llTimeWaited 
+= 10*GST_MSECOND
; 
 858     return llTimeWaited 
!= llTimeout
; 
 862 //----------------------------------------------------------------------------- 
 863 // wxGStreamerMediaBackend::TryAudioSink 
 864 // wxGStreamerMediaBackend::TryVideoSink 
 866 // Uses various means to determine whether a passed in video/audio sink 
 867 // if suitable for us - if it is not we return false and unref the 
 868 // inappropriate sink. 
 869 //----------------------------------------------------------------------------- 
 870 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
) 
 872     if( !GST_IS_ELEMENT(audiosink
) ) 
 874         if(G_IS_OBJECT(audiosink
)) 
 875             g_object_unref(audiosink
); 
 882 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
) 
 884     // Check if the video sink either is an xoverlay or might contain one... 
 885     if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) ) 
 887         if(G_IS_OBJECT(videosink
)) 
 888             g_object_unref(videosink
); 
 892     // Make our video sink and make sure it supports the x overlay interface 
 893     // the x overlay enables us to put the video in our control window 
 894     // (i.e. we NEED it!) - also connect to the natural video size change event 
 895     if( GST_IS_BIN(videosink
) ) 
 896         m_xoverlay 
= (GstXOverlay
*) 
 897                         gst_bin_get_by_interface (GST_BIN (videosink
), 
 900         m_xoverlay 
= (GstXOverlay
*) videosink
; 
 902     if ( !GST_IS_X_OVERLAY(m_xoverlay
) ) 
 904         g_object_unref(videosink
); 
 911 //----------------------------------------------------------------------------- 
 912 // wxGStreamerMediaEventHandler::OnMediaFinish 
 914 // Called when the media is about to stop 
 915 //----------------------------------------------------------------------------- 
 916 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
)) 
 918     // (RN - I have no idea why I thought this was good behaviour.... 
 919     // maybe it made sense for streaming/nonseeking data but 
 920     // generally it seems like a really bad idea) - 
 921     if(m_be
->SendStopEvent()) 
 923         // Stop the media (we need to set it back to paused 
 924         // so that people can get the duration et al. 
 925         // and send the finish event (luckily we can "Sync" it out... LOL!) 
 926         // (We don't check return values here because we can't really do 
 928         wxMutexLocker 
lock(m_be
->m_asynclock
); 
 930         // Set element to ready+sync it 
 931         gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
); 
 932         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
); 
 934         // Now set it to paused + update pause pos to 0 and 
 935         // Sync that as well (note that we don't call Stop() here 
 936         // due to mutex issues) 
 937         gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
); 
 938         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
); 
 939         m_be
->m_llPausedPos 
= 0; 
 941         // Finally, queue the finish event 
 942         m_be
->QueueFinishEvent(); 
 946 //----------------------------------------------------------------------------- 
 950 //----------------------------------------------------------------------------- 
 952 //----------------------------------------------------------------------------- 
 953 // wxGStreamerMediaBackend Constructor 
 955 // Sets m_playbin to NULL signifying we havn't loaded anything yet 
 956 //----------------------------------------------------------------------------- 
 957 wxGStreamerMediaBackend::wxGStreamerMediaBackend() 
 962 //----------------------------------------------------------------------------- 
 963 // wxGStreamerMediaBackend Destructor 
 965 // Stops/cleans up memory 
 967 // NB: This could trigger a critical warning but doing a SyncStateChange 
 968 //     here is just going to slow down quitting of the app, which is bad. 
 969 //----------------------------------------------------------------------------- 
 970 wxGStreamerMediaBackend::~wxGStreamerMediaBackend() 
 972     // Dispose of the main player and related objects 
 975         wxASSERT( GST_IS_OBJECT(m_playbin
) ); 
 976         gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
 977         gst_object_unref (GST_OBJECT (m_playbin
)); 
 978         delete m_eventHandler
; 
 982 //----------------------------------------------------------------------------- 
 983 // wxGStreamerMediaBackend::CreateControl 
 985 // Initializes GStreamer and creates the wx side of our media control 
 986 //----------------------------------------------------------------------------- 
 987 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 992                                 const wxValidator
& validator
, 
 993                                 const wxString
& name
) 
 999     //Convert arguments to unicode if enabled 
1002     char **argvGST 
= new char*[wxTheApp
->argc 
+ 1]; 
1003     for ( i 
= 0; i 
< wxTheApp
->argc
; i
++ ) 
1005         argvGST
[i
] = wxStrdupA(wxConvUTF8
.cWX2MB(wxTheApp
->argv
[i
])); 
1008     argvGST
[wxTheApp
->argc
] = NULL
; 
1010     int argcGST 
= wxTheApp
->argc
; 
1012 #define argcGST wxTheApp->argc 
1013 #define argvGST wxTheApp->argv 
1016     //Really init gstreamer 
1018     GError
* error 
= NULL
; 
1019 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1020     bInited 
= gst_init_check(&argcGST
, &argvGST
, &error
); 
1022     bInited 
= gst_init_check(&argcGST
, &argvGST
); 
1025     // Cleanup arguments for unicode case 
1027     for ( i 
= 0; i 
< argcGST
; i
++ ) 
1035     if(!bInited
)    //gst_init_check fail? 
1039             wxLogSysError(wxT("Could not initialize GStreamer\n") 
1040                           wxT("Error Message:%s"), 
1041                           (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
) 
1043             g_error_free(error
); 
1046             wxLogSysError(wxT("Could not initialize GStreamer")); 
1052     // wxControl creation 
1054     m_ctrl 
= wxStaticCast(ctrl
, wxMediaCtrl
); 
1057     // We handle our own GTK expose events 
1058     m_ctrl
->m_noExpose 
= true; 
1061     if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
, 
1062                             style
,  // TODO: remove borders??? 
1065         wxFAIL_MSG(wxT("Could not create wxControl!!!")); 
1070     // Turn off double-buffering so that 
1071     // so it doesn't draw over the video and cause sporadic 
1072     // disappearances of the video 
1073     gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
); 
1076     // don't erase the background of our control window 
1077     // so that resizing is a bit smoother 
1078     m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
1080     // Create our playbin object 
1081     m_playbin 
= gst_element_factory_make ("playbin", "play"); 
1082     if (!GST_IS_ELEMENT(m_playbin
)) 
1084         if(G_IS_OBJECT(m_playbin
)) 
1085             g_object_unref(m_playbin
); 
1086         wxLogSysError(wxT("Got an invalid playbin")); 
1090 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1091     // Connect the glib events/callbacks we want to our playbin 
1092     g_signal_connect(m_playbin
, "eos", 
1093                      G_CALLBACK(gst_finish_callback
), this); 
1094     g_signal_connect(m_playbin
, "error", 
1095                      G_CALLBACK(gst_error_callback
), this); 
1096     g_signal_connect(m_playbin
, "state-change", 
1097                      G_CALLBACK(gst_state_change_callback
), this); 
1099     // GStreamer 0.10+ uses GstBus for this now, connect to the sync 
1100     // handler as well so we can set the X window id of our xoverlay 
1101     gst_bus_add_watch (gst_element_get_bus(m_playbin
), 
1102                        (GstBusFunc
) gst_bus_async_callback
, this); 
1103     gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
), 
1104                              (GstBusSyncHandler
) gst_bus_sync_callback
, this); 
1105     g_signal_connect(m_playbin
, "notify::stream-info", 
1106                      G_CALLBACK(gst_notify_stream_info_callback
), this); 
1109     // Get the audio sink 
1110     GstElement
* audiosink 
= gst_gconf_get_default_audio_sink(); 
1111     if( !TryAudioSink(audiosink
) ) 
1113         // fallback to autodetection, then alsa, then oss as a stopgap 
1114         audiosink 
= gst_element_factory_make ("autoaudiosink", "audio-sink"); 
1115         if( !TryAudioSink(audiosink
) ) 
1117             audiosink 
= gst_element_factory_make ("alsasink", "alsa-output"); 
1118             if( !TryAudioSink(audiosink
) ) 
1120                 audiosink 
= gst_element_factory_make ("osssink", "play_audio"); 
1121                 if( !TryAudioSink(audiosink
) ) 
1123                     wxLogSysError(wxT("Could not find a valid audiosink")); 
1130     // Setup video sink - first try gconf, then auto, then xvimage and 
1131     // then finally plain ximage 
1132     GstElement
* videosink 
= gst_gconf_get_default_video_sink(); 
1133     if( !TryVideoSink(videosink
) ) 
1135         videosink 
= gst_element_factory_make ("autovideosink", "video-sink"); 
1136         if( !TryVideoSink(videosink
) ) 
1138             videosink 
= gst_element_factory_make ("xvimagesink", "video-sink"); 
1139             if( !TryVideoSink(videosink
) ) 
1141                 // finally, do a final fallback to ximagesink 
1143                     gst_element_factory_make ("ximagesink", "video-sink"); 
1144                 if( !TryVideoSink(videosink
) ) 
1146                     g_object_unref(audiosink
); 
1147                     wxLogSysError(wxT("Could not find a suitable video sink")); 
1154 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1155     // Not on 0.10... called when video size changes 
1156     g_signal_connect(m_xoverlay
, "desired-size-changed", 
1157                      G_CALLBACK(gst_desired_size_changed_callback
), this); 
1159     // Tell GStreamer which window to draw to in 0.8 - 0.10 
1160     // sometimes needs this too... 
1163     // Now that we know (or, rather think) our video and audio sink 
1164     // are valid set our playbin to use them 
1165     g_object_set (G_OBJECT (m_playbin
), 
1166                   "video-sink", videosink
, 
1167                   "audio-sink", audiosink
, 
1170     m_eventHandler 
= new wxGStreamerMediaEventHandler(this); 
1174 //----------------------------------------------------------------------------- 
1175 // wxGStreamerMediaBackend::Load (File version) 
1177 // Just calls DoLoad() with a prepended file scheme 
1178 //----------------------------------------------------------------------------- 
1179 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
) 
1181     return DoLoad(wxString( wxT("file://") ) + fileName
); 
1184 //----------------------------------------------------------------------------- 
1185 // wxGStreamerMediaBackend::Load (URI version) 
1187 // In the case of a file URI passes it unencoded - 
1188 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer) 
1189 // is sort of broken and only accepts uris with at least two slashes 
1190 // after the scheme (i.e. file: == not ok, file:// == ok) 
1191 //----------------------------------------------------------------------------- 
1192 bool wxGStreamerMediaBackend::Load(const wxURI
& location
) 
1194     if(location
.GetScheme().CmpNoCase(wxT("file")) == 0) 
1196         wxString uristring 
= location
.BuildUnescapedURI(); 
1198         //Workaround GstURI leading "//" problem and make sure it leads 
1200         return DoLoad(wxString(wxT("file://")) + 
1201                       uristring
.Right(uristring
.length() - 5) 
1205         return DoLoad(location
.BuildURI()); 
1208 //----------------------------------------------------------------------------- 
1209 // wxGStreamerMediaBackend::DoLoad 
1212 // 1) Reset member variables and set playbin back to ready state 
1213 // 2) Check URI for validity and then tell the playbin to load it 
1214 // 3) Set the playbin to the pause state 
1216 // NB: Even after this function is over with we probably don't have the 
1217 // video size or duration - no amount of clever hacking is going to get 
1218 // around that, unfortunately. 
1219 //----------------------------------------------------------------------------- 
1220 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
) 
1222     wxMutexLocker 
lock(m_asynclock
); // lock state events and async callbacks 
1224     // Reset positions & rate 
1227     m_videoSize 
= wxSize(0,0); 
1229     // Set playbin to ready to stop the current media... 
1230     if( gst_element_set_state (m_playbin
, 
1231                                GST_STATE_READY
) == GST_STATE_FAILURE 
|| 
1232         !SyncStateChange(m_playbin
, GST_STATE_READY
)) 
1234         wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ") 
1235                       wxT("Could not set initial state to ready")); 
1239     // Make sure the passed URI is valid and tell playbin to load it 
1240     // non-file uris are encoded 
1241     wxASSERT(gst_uri_protocol_is_valid("file")); 
1242     wxASSERT(gst_uri_is_valid(locstring
.mb_str())); 
1244     g_object_set (G_OBJECT (m_playbin
), "uri", 
1245                   (const char*)locstring
.mb_str(), NULL
); 
1247     // Try to pause media as gstreamer won't let us query attributes 
1248     // such as video size unless it is paused or playing 
1249     if( gst_element_set_state (m_playbin
, 
1250                                GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1251         !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1253         return false; // no real error message needed here as this is 
1254                       // generic failure 99% of the time (i.e. no 
1255                       // source etc.) and has an error message 
1259     NotifyMovieLoaded(); // Notify the user - all we can do for now 
1264 //----------------------------------------------------------------------------- 
1265 // wxGStreamerMediaBackend::Play 
1267 // Sets the stream to a playing state 
1269 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1270 //----------------------------------------------------------------------------- 
1271 bool wxGStreamerMediaBackend::Play() 
1273     if (gst_element_set_state (m_playbin
, 
1274                                GST_STATE_PLAYING
) == GST_STATE_FAILURE
) 
1279 //----------------------------------------------------------------------------- 
1280 // wxGStreamerMediaBackend::Pause 
1282 // Marks where we paused and pauses the stream 
1284 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1285 //----------------------------------------------------------------------------- 
1286 bool wxGStreamerMediaBackend::Pause() 
1288     m_llPausedPos 
= wxGStreamerMediaBackend::GetPosition(); 
1289     if (gst_element_set_state (m_playbin
, 
1290                                GST_STATE_PAUSED
) == GST_STATE_FAILURE
) 
1295 //----------------------------------------------------------------------------- 
1296 // wxGStreamerMediaBackend::Stop 
1298 // Pauses the stream and sets the position to 0. Note that this is 
1299 // synchronous (!) pausing. 
1301 // Due to the mutex locking this is probably thread-safe actually. 
1302 //----------------------------------------------------------------------------- 
1303 bool wxGStreamerMediaBackend::Stop() 
1305     {   // begin state lock 
1306         wxMutexLocker 
lock(m_asynclock
); 
1307         if(gst_element_set_state (m_playbin
, 
1308                                   GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1309           !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1311             wxLogSysError(wxT("Could not set state to paused for Stop()")); 
1316     bool bSeekedOK 
= wxGStreamerMediaBackend::SetPosition(0); 
1320         wxLogSysError(wxT("Could not seek to initial position in Stop()")); 
1324     QueueStopEvent(); // Success 
1328 //----------------------------------------------------------------------------- 
1329 // wxGStreamerMediaBackend::GetState 
1331 // Gets the state of the media 
1332 //----------------------------------------------------------------------------- 
1333 wxMediaState 
wxGStreamerMediaBackend::GetState() 
1335     switch(GST_STATE(m_playbin
)) 
1337         case GST_STATE_PLAYING
: 
1338             return wxMEDIASTATE_PLAYING
; 
1339         case GST_STATE_PAUSED
: 
1340             if (m_llPausedPos 
== 0) 
1341                 return wxMEDIASTATE_STOPPED
; 
1343                 return wxMEDIASTATE_PAUSED
; 
1344         default://case GST_STATE_READY: 
1345             return wxMEDIASTATE_STOPPED
; 
1349 //----------------------------------------------------------------------------- 
1350 // wxGStreamerMediaBackend::GetPosition 
1352 // If paused, returns our marked position - otherwise it queries the 
1353 // GStreamer playbin for the position and returns that 
1356 // NB: At least in 0.8, when you pause and seek gstreamer 
1357 // NB: doesn't update the position sometimes, so we need to keep track of 
1358 // NB: whether we have paused or not and keep track of the time after the 
1359 // NB: pause and whenever the user seeks while paused 
1362 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused. 
1363 //----------------------------------------------------------------------------- 
1364 wxLongLong 
wxGStreamerMediaBackend::GetPosition() 
1366     if(GetState() != wxMEDIASTATE_PLAYING
) 
1367         return m_llPausedPos
; 
1371         GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1373         if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) || 
1374             fmtTime 
!= GST_FORMAT_TIME 
|| pos 
== -1) 
1376         return pos 
/ GST_MSECOND 
; 
1380 //----------------------------------------------------------------------------- 
1381 // wxGStreamerMediaBackend::SetPosition 
1383 // Sets the position of the stream 
1384 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so 
1385 // there is 1000000 nanoseconds in a millisecond) 
1387 // If we are paused we update the cached pause position. 
1389 // This is also an exceedingly ugly function due to the three implementations 
1390 // (or, rather two plus one implementation without a seek function). 
1392 // This is asynchronous and thread-safe on both 0.8 and 0.10. 
1394 // NB: This fires both a stop and play event if the media was previously 
1395 // playing... which in some ways makes sense. And yes, this makes the video 
1396 // go all haywire at times - a gstreamer bug... 
1397 //----------------------------------------------------------------------------- 
1398 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
) 
1400 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \ 
1401                            && GST_VERSION_MICRO == 0 
1402     // 0.8.0 has no gst_element_seek according to official docs!!! 
1403     wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek") 
1404                   wxT(" according to official docs")); 
1408 #   if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1409         gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
, 
1410            (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1411                           GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
, 
1412                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
); 
1414         // NB: Some gstreamer versions return false basically all the time 
1415         // here - even totem doesn't bother to check the return value here 
1416         // so I guess we'll just assume it worked - 
1417         // TODO: maybe check the gst error callback??? 
1418         gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET 
| 
1419             GST_FORMAT_TIME 
| GST_SEEK_FLAG_FLUSH
), 
1420             where
.GetValue() * GST_MSECOND 
); 
1422 #   endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1425         m_llPausedPos 
= where
; 
1432 //----------------------------------------------------------------------------- 
1433 // wxGStreamerMediaBackend::GetDuration 
1435 // Obtains the total time of our stream 
1436 // THREAD-UNSAFE, requires media to be paused or playing 
1437 //----------------------------------------------------------------------------- 
1438 wxLongLong 
wxGStreamerMediaBackend::GetDuration() 
1441     GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1443     if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) || 
1444        fmtTime 
!= GST_FORMAT_TIME 
|| length 
== -1) 
1446     return length 
/ GST_MSECOND 
; 
1449 //----------------------------------------------------------------------------- 
1450 // wxGStreamerMediaBackend::Move 
1452 // Called when the window is moved - GStreamer takes care of this 
1453 // for us so nothing is needed 
1454 //----------------------------------------------------------------------------- 
1455 void wxGStreamerMediaBackend::Move(int x
, int y
, int w
, int h
) 
1459 //----------------------------------------------------------------------------- 
1460 // wxGStreamerMediaBackend::GetVideoSize 
1462 // Returns our cached video size from Load/gst_notify_caps_callback 
1463 // gst_x_overlay_get_desired_size also does this in 0.8... 
1464 //----------------------------------------------------------------------------- 
1465 wxSize 
wxGStreamerMediaBackend::GetVideoSize() const 
1470 //----------------------------------------------------------------------------- 
1471 // wxGStreamerMediaBackend::GetPlaybackRate 
1472 // wxGStreamerMediaBackend::SetPlaybackRate 
1474 // Obtains/Sets the playback rate of the stream 
1476 //TODO: PlaybackRate not currently supported via playbin directly - 
1477 //TODO: Ronald S. Bultje noted on gstreamer-devel: 
1479 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As 
1480 //TODO: for the first, yes, we have elements for that, btu they"re not part of 
1481 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual 
1482 //TODO: video/audiosink and the speed-changing element for this, and set that 
1483 //TODO: element as video-sink or audio-sink property in playbin. The 
1484 //TODO: audio-element is called "speed", the video-element is called "videodrop" 
1485 //TODO: (although that appears to be deprecated in favour of "videorate", which 
1486 //TODO: again cannot do this, so this may not work at all in the end). For 
1487 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is 
1488 //TODO: part of playbin. 
1490 // In 0.10 GStreamer has new gst_element_seek API that might 
1491 // support this - and I've got an attempt to do so but it is untested 
1492 // but it would appear to work... 
1493 //----------------------------------------------------------------------------- 
1494 double wxGStreamerMediaBackend::GetPlaybackRate() 
1496     return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem 
1497                     // final on that yet and there may not be any actual 
1498                     // plugins that support it... 
1501 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
) 
1503 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1504 #if 0 // not tested enough 
1505     if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
, 
1506                  (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1507                           GST_SEEK_TYPE_CUR
, 0, 
1508                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
) ) 
1520 //----------------------------------------------------------------------------- 
1521 // wxGStreamerMediaBackend::GetDownloadProgress 
1523 // Not really outwardly possible - have been suggested that one could 
1524 // get the information from the component that "downloads" 
1525 //----------------------------------------------------------------------------- 
1526 wxLongLong 
wxGStreamerMediaBackend::GetDownloadProgress() 
1531 //----------------------------------------------------------------------------- 
1532 // wxGStreamerMediaBackend::GetDownloadTotal 
1534 // TODO: Cache this? 
1535 // NB: The length changes every call for some reason due to 
1536 //     GStreamer implementation issues 
1537 // THREAD-UNSAFE, requires media to be paused or playing 
1538 //----------------------------------------------------------------------------- 
1539 wxLongLong 
wxGStreamerMediaBackend::GetDownloadTotal() 
1542     GstFormat fmtBytes 
= GST_FORMAT_BYTES
; 
1544     if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) || 
1545           fmtBytes 
!= GST_FORMAT_BYTES 
|| length 
== -1) 
1550 //----------------------------------------------------------------------------- 
1551 // wxGStreamerMediaBackend::SetVolume 
1552 // wxGStreamerMediaBackend::GetVolume 
1554 // Sets/Gets the volume through the playbin object. 
1555 // Note that this requires a relatively recent gst-plugins so we 
1556 // check at runtime to see whether it is available or not otherwise 
1557 // GST spits out an error on the command line 
1558 //----------------------------------------------------------------------------- 
1559 bool wxGStreamerMediaBackend::SetVolume(double dVolume
) 
1561     if(g_object_class_find_property( 
1562             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1565         g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
); 
1570         wxLogTrace(wxTRACE_GStreamer
, 
1571             wxT("SetVolume: volume prop not found - 0.8.5 of ") 
1572             wxT("gst-plugins probably needed")); 
1577 double wxGStreamerMediaBackend::GetVolume() 
1579     double dVolume 
= 1.0; 
1581     if(g_object_class_find_property( 
1582             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1585         g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
); 
1589         wxLogTrace(wxTRACE_GStreamer
, 
1590             wxT("GetVolume: volume prop not found - 0.8.5 of ") 
1591             wxT("gst-plugins probably needed")); 
1597 #endif //wxUSE_GSTREAMER 
1599 // Force link into main library so this backend can be loaded 
1600 #include "wx/html/forcelnk.h" 
1601 FORCE_LINK_ME(basewxmediabackends
) 
1603 #endif //wxUSE_MEDIACTRL