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 
 149 extern void wxapp_install_idle_handler(); 
 150 extern bool g_isIdle
; 
 152 extern bool g_mainThreadLocked
; 
 155 //----------------------------------------------------------------------------- 
 156 //  wxLogTrace mask string 
 157 //----------------------------------------------------------------------------- 
 158 #define wxTRACE_GStreamer wxT("GStreamer") 
 160 //----------------------------------------------------------------------------- 
 162 //  wxGStreamerMediaBackend 
 164 //----------------------------------------------------------------------------- 
 165 class WXDLLIMPEXP_MEDIA
 
 166     wxGStreamerMediaBackend 
: public wxMediaBackendCommonBase
 
 170     wxGStreamerMediaBackend(); 
 171     virtual ~wxGStreamerMediaBackend(); 
 173     virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 178                                      const wxValidator
& validator
, 
 179                                      const wxString
& name
); 
 182     virtual bool Pause(); 
 185     virtual bool Load(const wxString
& fileName
); 
 186     virtual bool Load(const wxURI
& location
); 
 188     virtual wxMediaState 
GetState(); 
 190     virtual bool SetPosition(wxLongLong where
); 
 191     virtual wxLongLong 
GetPosition(); 
 192     virtual wxLongLong 
GetDuration(); 
 194     virtual void Move(int x
, int y
, int w
, int h
); 
 195     wxSize 
GetVideoSize() const; 
 197     virtual double GetPlaybackRate(); 
 198     virtual bool SetPlaybackRate(double dRate
); 
 200     virtual wxLongLong 
GetDownloadProgress(); 
 201     virtual wxLongLong 
GetDownloadTotal(); 
 203     virtual bool SetVolume(double dVolume
); 
 204     virtual double GetVolume(); 
 206     //------------implementation from now on----------------------------------- 
 207     bool DoLoad(const wxString
& locstring
); 
 208     wxMediaCtrl
* GetControl() { return m_ctrl
; } // for C Callbacks 
 209     void HandleStateChange(GstElementState oldstate
, GstElementState newstate
); 
 210     bool QueryVideoSizeFromElement(GstElement
* element
); 
 211     bool QueryVideoSizeFromPad(GstPad
* caps
); 
 212     void SetupXOverlay(); 
 213     bool SyncStateChange(GstElement
* element
, GstElementState state
, 
 214                          gint64 llTimeout 
= wxGSTREAMER_TIMEOUT
); 
 215     bool TryAudioSink(GstElement
* audiosink
); 
 216     bool TryVideoSink(GstElement
* videosink
); 
 218     GstElement
*     m_playbin
;      // GStreamer media element 
 219     wxSize          m_videoSize
;    // Cached actual video size 
 220     double          m_dRate
;        // Current playback rate - 
 221                                     // see GetPlaybackRate for notes 
 222     wxLongLong      m_llPausedPos
;  // Paused position - see Pause() 
 223     GstXOverlay
*    m_xoverlay
;     // X Overlay that contains the GST video 
 224     wxMutex         m_asynclock
;    // See "discussion of internals" 
 225     class wxGStreamerMediaEventHandler
* m_eventHandler
; // see below 
 227     friend class wxGStreamerMediaEventHandler
; 
 228     friend class wxGStreamerLoadWaitTimer
; 
 229     DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
); 
 232 //----------------------------------------------------------------------------- 
 233 // wxGStreamerMediaEventHandler 
 235 // OK, this will take an explanation - basically gstreamer callbacks 
 236 // are issued in a seperate thread, and in this thread we may not set 
 237 // the state of the playbin, so we need to send a wx event in that 
 238 // callback so that we set the state of the media and other stuff 
 240 //----------------------------------------------------------------------------- 
 241 class wxGStreamerMediaEventHandler 
: public wxEvtHandler
 
 244     wxGStreamerMediaEventHandler(wxGStreamerMediaBackend
* be
) : m_be(be
) 
 246         this->Connect(wxID_ANY
, wxEVT_MEDIA_FINISHED
, 
 247            wxMediaEventHandler(wxGStreamerMediaEventHandler::OnMediaFinish
)); 
 250     void OnMediaFinish(wxMediaEvent
& event
); 
 252     wxGStreamerMediaBackend
* m_be
; 
 255 //============================================================================= 
 257 //============================================================================= 
 259 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
) 
 261 //----------------------------------------------------------------------------- 
 265 //----------------------------------------------------------------------------- 
 267 //----------------------------------------------------------------------------- 
 268 // "expose_event" from m_ctrl->m_wxwindow 
 270 // Handle GTK expose event from our window - here we hopefully 
 271 // redraw the video in the case of pausing and other instances... 
 272 // (Returns TRUE to pass to other handlers, FALSE if not) 
 274 // TODO: Do a DEBUG_MAIN_THREAD/install_idle_handler here? 
 275 //----------------------------------------------------------------------------- 
 278 static gboolean 
gtk_window_expose_callback(GtkWidget 
*widget
, 
 279                                            GdkEventExpose 
*event
, 
 280                                            wxGStreamerMediaBackend 
*be
) 
 285     GdkWindow 
*window 
= GTK_PIZZA(be
->GetControl()->m_wxwindow
)->bin_window
; 
 287     // I've seen this reccommended somewhere... 
 288     // TODO: Is this needed? Maybe it is just cruft... 
 289     // gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_xoverlay), 
 290     //                              GDK_WINDOW_XWINDOW( window ) ); 
 292     // If we have actual video..... 
 293     if(!(be
->m_videoSize
.x
==0&&be
->m_videoSize
.y
==0) && 
 294        GST_STATE(be
->m_playbin
) >= GST_STATE_PAUSED
) 
 296         // GST Doesn't redraw automatically while paused 
 297         // Plus, the video sometimes doesn't redraw when it looses focus 
 298         // or is painted over so we just tell it to redraw... 
 299         gst_x_overlay_expose(be
->m_xoverlay
); 
 303         // draw a black background like some other backends do.... 
 304         gdk_draw_rectangle (window
, widget
->style
->black_gc
, TRUE
, 0, 0, 
 305                             widget
->allocation
.width
, 
 306                             widget
->allocation
.height
); 
 314 //----------------------------------------------------------------------------- 
 315 // "realize" from m_ctrl->m_wxwindow 
 317 // If the window wasn't realized when Load was called, this is the 
 318 // callback for when it is - the purpose of which is to tell 
 319 // GStreamer to play the video in our control 
 320 //----------------------------------------------------------------------------- 
 323 static gint 
gtk_window_realize_callback(GtkWidget
* theWidget
, 
 324                                         wxGStreamerMediaBackend
* be
) 
 326     DEBUG_MAIN_THREAD 
// TODO: Is this neccessary? 
 329     if (g_isIdle
)   // FIXME: Why is needed? For wxYield? ?? 
 330         wxapp_install_idle_handler(); 
 333     wxYield();    // FIXME: RN: X Server gets an error/crash if I don't do 
 334                   //       this or a messagebox beforehand?!?!?? 
 336     GdkWindow 
*window 
= GTK_PIZZA(theWidget
)->bin_window
; 
 339     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_xoverlay
), 
 340                                 GDK_WINDOW_XWINDOW( window 
) 
 342     g_signal_connect (be
->GetControl()->m_wxwindow
, 
 344                       G_CALLBACK(gtk_window_expose_callback
), be
); 
 350 //----------------------------------------------------------------------------- 
 351 // "state-change" from m_playbin/GST_MESSAGE_STATE_CHANGE 
 353 // Called by gstreamer when the state changes - here we 
 354 // send the appropriate corresponding wx event. 
 356 // 0.8 only as HandleStateChange does this in both versions 
 357 //----------------------------------------------------------------------------- 
 358 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 360 static void gst_state_change_callback(GstElement 
*play
, 
 361                                       GstElementState oldstate
, 
 362                                       GstElementState newstate
, 
 363                                       wxGStreamerMediaBackend
* be
) 
 365     if(be
->m_asynclock
.TryLock() == wxMUTEX_NO_ERROR
) 
 367         be
->HandleStateChange(oldstate
, newstate
); 
 368         be
->m_asynclock
.Unlock(); 
 374 //----------------------------------------------------------------------------- 
 375 // "eos" from m_playbin/GST_MESSAGE_EOS 
 377 // Called by gstreamer when the media is done playing ("end of stream") 
 378 //----------------------------------------------------------------------------- 
 380 static void gst_finish_callback(GstElement 
*play
, 
 381                                 wxGStreamerMediaBackend
* be
) 
 383     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_finish_callback")); 
 384     wxMediaEvent 
event(wxEVT_MEDIA_FINISHED
); 
 385     be
->m_eventHandler
->AddPendingEvent(event
); 
 389 //----------------------------------------------------------------------------- 
 390 // "error" from m_playbin/GST_MESSAGE_ERROR 
 392 // Called by gstreamer when an error is encountered playing the media - 
 393 // We call wxLogTrace in addition wxLogSysError so that we can get it 
 394 // on the command line as well for those who want extra traces. 
 395 //----------------------------------------------------------------------------- 
 397 static void gst_error_callback(GstElement 
*play
, 
 401                                wxGStreamerMediaBackend
* be
) 
 404     sError
.Printf(wxT("gst_error_callback\n") 
 405                   wxT("Error Message:%s\nDebug:%s\n"), 
 406                   (const wxChar
*)wxConvUTF8
.cMB2WX(err
->message
), 
 407                   (const wxChar
*)wxConvUTF8
.cMB2WX(debug
)); 
 408     wxLogTrace(wxTRACE_GStreamer
, sError
); 
 409     wxLogSysError(sError
); 
 413 //----------------------------------------------------------------------------- 
 414 // "notify::caps" from the videopad inside "stream-info" of m_playbin 
 416 // Called by gstreamer when the video caps for the media is ready - currently 
 417 // we use the caps to get the natural size of the video 
 420 //----------------------------------------------------------------------------- 
 422 static void gst_notify_caps_callback(GstPad
* pad
, 
 424                                      wxGStreamerMediaBackend
* be
) 
 426     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_caps_callback")); 
 427     be
->QueryVideoSizeFromPad(pad
); 
 431 //----------------------------------------------------------------------------- 
 432 // "notify::stream-info" from m_playbin 
 434 // Run through the stuff in "stream-info" of m_playbin for a valid 
 435 // video pad, and then attempt to query the video size from it - if not 
 436 // set up an event to do so when ready. 
 438 // Currently unused - now we just query it directly using 
 439 // QueryVideoSizeFromElement. 
 442 //----------------------------------------------------------------------------- 
 443 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 445 static void gst_notify_stream_info_callback(GstElement
* element
, 
 447                                             wxGStreamerMediaBackend
* be
) 
 449     wxLogTrace(wxTRACE_GStreamer
, wxT("gst_notify_stream_info_callback")); 
 450     be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 455 //----------------------------------------------------------------------------- 
 456 // "desired-size-changed" from m_xoverlay 
 458 // 0.8-specific this provides us with the video size when it changes - 
 459 // even though we get the caps as well this seems to come before the 
 460 // caps notification does... 
 462 // Note it will return 16,16 for an early-bird value or for audio 
 463 //----------------------------------------------------------------------------- 
 464 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
 466 static void gst_desired_size_changed_callback(GstElement 
* play
, 
 467                                               guint width
, guint height
, 
 468                                               wxGStreamerMediaBackend
* be
) 
 470     if(!(width 
== 16 && height 
== 16)) 
 472         be
->m_videoSize
.x 
= width
; 
 473         be
->m_videoSize
.y 
= height
; 
 476         be
->QueryVideoSizeFromElement(be
->m_playbin
); 
 481 //----------------------------------------------------------------------------- 
 482 // gst_bus_async_callback [static] 
 483 // gst_bus_sync_callback [static] 
 485 // Called by m_playbin for notifications such as end-of-stream in 0.10 - 
 486 // in previous versions g_signal notifications were used. Because everything 
 487 // in centered in one switch statement though it reminds one of old WinAPI 
 490 // gst_bus_sync_callback is that sync version that is called on the main GUI 
 491 // thread before the async version that we use to set the xwindow id of the 
 492 // XOverlay (NB: This isn't currently used - see CreateControl()). 
 493 //----------------------------------------------------------------------------- 
 494 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 496 static gboolean 
gst_bus_async_callback(GstBus
* bus
, 
 498                                        wxGStreamerMediaBackend
* be
) 
 500     if(((GstElement
*)GST_MESSAGE_SRC(message
)) != be
->m_playbin
) 
 502     if(be
->m_asynclock
.TryLock() != wxMUTEX_NO_ERROR
) 
 505     switch(GST_MESSAGE_TYPE(message
)) 
 507         case GST_MESSAGE_STATE_CHANGED
: 
 509             GstState oldstate
, newstate
, pendingstate
; 
 510             gst_message_parse_state_changed(message
, &oldstate
, 
 511                                             &newstate
, &pendingstate
); 
 512             be
->HandleStateChange(oldstate
, newstate
); 
 515         case GST_MESSAGE_EOS
: 
 517             gst_finish_callback(NULL
, be
); 
 520         case GST_MESSAGE_ERROR
: 
 524             gst_message_parse_error(message
, &error
, &debug
); 
 525             gst_error_callback(NULL
, NULL
, error
, debug
, be
); 
 532     be
->m_asynclock
.Unlock(); 
 533     return FALSE
; // remove the message from Z queue 
 536 static GstBusSyncReply 
gst_bus_sync_callback(GstBus
* bus
, 
 538                                              wxGStreamerMediaBackend
* be
) 
 540     // Pass a non-xwindowid-setting event on to the async handler where it 
 542     if (GST_MESSAGE_TYPE (message
) != GST_MESSAGE_ELEMENT 
|| 
 543         !gst_structure_has_name (message
->structure
, "prepare-xwindow-id")) 
 546         // NB: Unfortunately, the async callback can be quite 
 547         // buggy at times and often doesn't get called at all, 
 548         // so here we are processing it right here in the calling 
 549         // thread instead of the GUI one... 
 551         if(gst_bus_async_callback(bus
, message
, be
)) 
 557     wxLogTrace(wxTRACE_GStreamer
, wxT("Got prepare-xwindow-id")); 
 559     return GST_BUS_DROP
; // We handled this message - drop from the queue 
 564 //----------------------------------------------------------------------------- 
 566 // Private (although not in the C++ sense)  methods 
 568 //----------------------------------------------------------------------------- 
 570 //----------------------------------------------------------------------------- 
 571 // wxGStreamerMediaBackend::HandleStateChange 
 573 // Handles a state change event from our C Callback for "state-change" or 
 574 // the async queue in 0.10. (Mostly this is here to avoid locking the 
 575 // the mutex twice...) 
 576 //----------------------------------------------------------------------------- 
 577 void wxGStreamerMediaBackend::HandleStateChange(GstElementState oldstate
, 
 578                                                 GstElementState newstate
) 
 582         case GST_STATE_PLAYING
: 
 583             wxLogTrace(wxTRACE_GStreamer
, wxT("Play event")); 
 586         case GST_STATE_PAUSED
: 
 587             // For some reason .10 sends a lot of oldstate == newstate 
 588             // messages - most likely for pending ones - also 
 589             // !<GST_STATE_PAUSED as we are only concerned 
 590             if(oldstate 
< GST_STATE_PAUSED 
|| oldstate 
== newstate
) 
 592             if(wxGStreamerMediaBackend::GetPosition() != 0) 
 594                 wxLogTrace(wxTRACE_GStreamer
, wxT("Pause event")); 
 599                 wxLogTrace(wxTRACE_GStreamer
, wxT("Stop event")); 
 603        default: // GST_STATE_NULL etc. 
 608 //----------------------------------------------------------------------------- 
 609 // wxGStreamerMediaBackend::QueryVideoSizeFromElement 
 611 // Run through the stuff in "stream-info" of element for a valid 
 612 // video pad, and then attempt to query the video size from it - if not 
 613 // set up an event to do so when ready. Return true 
 614 // if we got a valid video pad. 
 615 //----------------------------------------------------------------------------- 
 616 bool wxGStreamerMediaBackend::QueryVideoSizeFromElement(GstElement
* element
) 
 618     const GList 
*list 
= NULL
; 
 619     g_object_get (G_OBJECT (element
), "stream-info", &list
, NULL
); 
 621     for ( ; list 
!= NULL
; list 
= list
->next
) 
 623         GObject 
*info 
= (GObject 
*) list
->data
; 
 629         g_object_get (info
, "type", &type
, NULL
); 
 630         pspec 
= g_object_class_find_property ( 
 631                         G_OBJECT_GET_CLASS (info
), "type"); 
 632         val 
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
); 
 634         if (!strncasecmp(val
->value_name
, "video", 5) || 
 635             !strncmp(val
->value_name
, "GST_STREAM_TYPE_VIDEO", 21)) 
 637             // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"... 
 638             // but a lot of old plugins still use "pad" :) 
 639             pspec 
= g_object_class_find_property ( 
 640                         G_OBJECT_GET_CLASS (info
), "object"); 
 643                 g_object_get (info
, "pad", &pad
, NULL
); 
 645                 g_object_get (info
, "object", &pad
, NULL
); 
 647 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8 
 648             // Killed in 0.9, presumely because events and such 
 649             // should be pushed on pads regardless of whether they 
 650             // are currently linked 
 651             pad 
= (GstPad 
*) GST_PAD_REALIZE (pad
); 
 655             if(!QueryVideoSizeFromPad(pad
)) 
 657                 // wait for those caps to get ready 
 661                 G_CALLBACK(gst_notify_caps_callback
), 
 666     }// end searching through info list 
 668     // no video (or extremely delayed stream-info) 
 671         m_videoSize 
= wxSize(0,0); 
 678 //----------------------------------------------------------------------------- 
 679 // wxGStreamerMediaBackend::QueryVideoSizeFromPad 
 681 // Gets the size of our video (in wxSize) from a GstPad 
 682 //----------------------------------------------------------------------------- 
 683 bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad
* pad
) 
 685     const GstCaps
* caps 
= GST_PAD_CAPS(pad
); 
 688         const GstStructure 
*s 
= gst_caps_get_structure (caps
, 0); 
 691         gst_structure_get_int (s
, "width", &m_videoSize
.x
); 
 692         gst_structure_get_int (s
, "height", &m_videoSize
.y
); 
 695         par 
= gst_structure_get_value (s
, "pixel-aspect-ratio"); 
 699             wxLogTrace(wxTRACE_GStreamer
, 
 700                        wxT("pixel-aspect-ratio found in pad")); 
 701             int num 
= par
->data
[0].v_int
, 
 702                 den 
= par
->data
[1].v_int
; 
 704             // TODO: maybe better fraction normalization... 
 706                 m_videoSize
.x 
= (int) ((float) num 
* m_videoSize
.x 
/ den
); 
 708                 m_videoSize
.y 
= (int) ((float) den 
* m_videoSize
.y 
/ num
); 
 711          wxLogTrace(wxTRACE_GStreamer
, wxT("Adjusted video size: [%i,%i]"), 
 712                      m_videoSize
.x
, m_videoSize
.y
); 
 716     return false; // not ready/massive failure 
 719 //----------------------------------------------------------------------------- 
 720 // wxGStreamerMediaBackend::SetupXOverlay 
 722 // Attempts to set the XWindow id of our GstXOverlay to tell it which 
 723 // window to play video in. 
 724 //----------------------------------------------------------------------------- 
 725 void wxGStreamerMediaBackend::SetupXOverlay() 
 727     // Use the xoverlay extension to tell gstreamer to play in our window 
 729     if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
)) 
 731         // Not realized yet - set to connect at realization time 
 732         g_signal_connect (m_ctrl
->m_wxwindow
, 
 734                           G_CALLBACK (gtk_window_realize_callback
), 
 739         wxYield(); // see realize callback... 
 740         GdkWindow 
*window 
= GTK_PIZZA(m_ctrl
->m_wxwindow
)->bin_window
; 
 744     gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay
), 
 746                         GDK_WINDOW_XWINDOW( window 
) 
 753     g_signal_connect (m_ctrl
->m_wxwindow
, 
 754                         // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/, 
 756                       G_CALLBACK(gtk_window_expose_callback
), this); 
 757     } // end if GtkPizza realized 
 761 //----------------------------------------------------------------------------- 
 762 // wxGStreamerMediaBackend::SyncStateChange 
 764 // This function is rather complex - basically the idea is that we 
 765 // poll the GstBus of m_playbin until it has reached desiredstate, an error 
 766 // is reached, or there are no more messages left in the GstBus queue. 
 768 // Returns true if there are no messages left in the queue or 
 769 // the current state reaches the disired state. 
 771 // PRECONDITION: Assumes m_asynclock is Lock()ed 
 772 //----------------------------------------------------------------------------- 
 773 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
 774 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 775                                               GstElementState desiredstate
, 
 778     GstBus
* bus 
= gst_element_get_bus(element
); 
 782     gint64 llTimeWaited 
= 0; 
 787         // NB: The GStreamer gst_bus_poll is unfortunately broken and 
 788         // throws silly critical internal errors (for instance 
 789         // "message != NULL" when the whole point of it is to 
 790         // poll for the message in the first place!) so we implement 
 791         // our own "waiting mechinism" 
 792         if(gst_bus_have_pending(bus
) == FALSE
) 
 794             if(llTimeWaited 
>= llTimeout
) 
 795                 return true; // Reached timeout... assume success 
 796             llTimeWaited 
+= 10*GST_MSECOND
; 
 801         message 
= gst_bus_pop(bus
); 
 803         message 
= gst_bus_poll(bus
, (GstMessageType
) 
 804                            (GST_MESSAGE_STATE_CHANGED 
| 
 806                             GST_MESSAGE_EOS
), llTimeout
); 
 810         if(((GstElement
*)GST_MESSAGE_SRC(message
)) == element
) 
 812             switch(GST_MESSAGE_TYPE(message
)) 
 814                 case GST_MESSAGE_STATE_CHANGED
: 
 816                     GstState oldstate
, newstate
, pendingstate
; 
 817                     gst_message_parse_state_changed(message
, &oldstate
, 
 818                                                     &newstate
, &pendingstate
); 
 819                     if(newstate 
== desiredstate
) 
 821                         bSuccess 
= bBreak 
= true; 
 825                 case GST_MESSAGE_ERROR
: 
 829                     gst_message_parse_error(message
, &error
, &debug
); 
 830                     gst_error_callback(NULL
, NULL
, error
, debug
, this); 
 834                 case GST_MESSAGE_EOS
: 
 835                     wxLogSysError(wxT("Reached end of stream prematurely")); 
 839                     break; // not handled 
 843         gst_message_unref(message
); 
 848 #else // 0.8 implementation 
 849 bool wxGStreamerMediaBackend::SyncStateChange(GstElement
* element
, 
 850                                               GstElementState desiredstate
, 
 853     gint64 llTimeWaited 
= 0; 
 854     while(GST_STATE(element
) != desiredstate
) 
 856         if(llTimeWaited 
>= llTimeout
) 
 858         llTimeWaited 
+= 10*GST_MSECOND
; 
 862     return llTimeWaited 
!= llTimeout
; 
 866 //----------------------------------------------------------------------------- 
 867 // wxGStreamerMediaBackend::TryAudioSink 
 868 // wxGStreamerMediaBackend::TryVideoSink 
 870 // Uses various means to determine whether a passed in video/audio sink 
 871 // if suitable for us - if it is not we return false and unref the 
 872 // inappropriate sink. 
 873 //----------------------------------------------------------------------------- 
 874 bool wxGStreamerMediaBackend::TryAudioSink(GstElement
* audiosink
) 
 876     if( !GST_IS_ELEMENT(audiosink
) ) 
 878         if(G_IS_OBJECT(audiosink
)) 
 879             g_object_unref(audiosink
); 
 886 bool wxGStreamerMediaBackend::TryVideoSink(GstElement
* videosink
) 
 888     // Check if the video sink either is an xoverlay or might contain one... 
 889     if( !GST_IS_BIN(videosink
) && !GST_IS_X_OVERLAY(videosink
) ) 
 891         if(G_IS_OBJECT(videosink
)) 
 892             g_object_unref(videosink
); 
 896     // Make our video sink and make sure it supports the x overlay interface 
 897     // the x overlay enables us to put the video in our control window 
 898     // (i.e. we NEED it!) - also connect to the natural video size change event 
 899     if( GST_IS_BIN(videosink
) ) 
 900         m_xoverlay 
= (GstXOverlay
*) 
 901                         gst_bin_get_by_interface (GST_BIN (videosink
), 
 904         m_xoverlay 
= (GstXOverlay
*) videosink
; 
 906     if ( !GST_IS_X_OVERLAY(m_xoverlay
) ) 
 908         g_object_unref(videosink
); 
 915 //----------------------------------------------------------------------------- 
 916 // wxGStreamerMediaEventHandler::OnMediaFinish 
 918 // Called when the media is about to stop 
 919 //----------------------------------------------------------------------------- 
 920 void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent
& WXUNUSED(event
)) 
 922     // (RN - I have no idea why I thought this was good behaviour.... 
 923     // maybe it made sense for streaming/nonseeking data but 
 924     // generally it seems like a really bad idea) - 
 925     if(m_be
->SendStopEvent()) 
 927         // Stop the media (we need to set it back to paused 
 928         // so that people can get the duration et al. 
 929         // and send the finish event (luckily we can "Sync" it out... LOL!) 
 930         // (We don't check return values here because we can't really do 
 932         wxMutexLocker 
lock(m_be
->m_asynclock
); 
 934         // Set element to ready+sync it 
 935         gst_element_set_state (m_be
->m_playbin
, GST_STATE_READY
); 
 936         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_READY
); 
 938         // Now set it to paused + update pause pos to 0 and 
 939         // Sync that as well (note that we don't call Stop() here 
 940         // due to mutex issues) 
 941         gst_element_set_state (m_be
->m_playbin
, GST_STATE_PAUSED
); 
 942         m_be
->SyncStateChange(m_be
->m_playbin
, GST_STATE_PAUSED
); 
 943         m_be
->m_llPausedPos 
= 0; 
 945         // Finally, queue the finish event 
 946         m_be
->QueueFinishEvent(); 
 950 //----------------------------------------------------------------------------- 
 954 //----------------------------------------------------------------------------- 
 956 //----------------------------------------------------------------------------- 
 957 // wxGStreamerMediaBackend Constructor 
 959 // Sets m_playbin to NULL signifying we havn't loaded anything yet 
 960 //----------------------------------------------------------------------------- 
 961 wxGStreamerMediaBackend::wxGStreamerMediaBackend() 
 966 //----------------------------------------------------------------------------- 
 967 // wxGStreamerMediaBackend Destructor 
 969 // Stops/cleans up memory 
 971 // NB: This could trigger a critical warning but doing a SyncStateChange 
 972 //     here is just going to slow down quitting of the app, which is bad. 
 973 //----------------------------------------------------------------------------- 
 974 wxGStreamerMediaBackend::~wxGStreamerMediaBackend() 
 976     // Dispose of the main player and related objects 
 979         wxASSERT( GST_IS_OBJECT(m_playbin
) ); 
 980         gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
 981         gst_object_unref (GST_OBJECT (m_playbin
)); 
 982         delete m_eventHandler
; 
 986 //----------------------------------------------------------------------------- 
 987 // wxGStreamerMediaBackend::CreateControl 
 989 // Initializes GStreamer and creates the wx side of our media control 
 990 //----------------------------------------------------------------------------- 
 991 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
, 
 996                                 const wxValidator
& validator
, 
 997                                 const wxString
& name
) 
1003     //Convert arguments to unicode if enabled 
1006     char **argvGST 
= new char*[wxTheApp
->argc 
+ 1]; 
1007     for ( i 
= 0; i 
< wxTheApp
->argc
; i
++ ) 
1009         argvGST
[i
] = wxStrdupA(wxConvUTF8
.cWX2MB(wxTheApp
->argv
[i
])); 
1012     argvGST
[wxTheApp
->argc
] = NULL
; 
1014     int argcGST 
= wxTheApp
->argc
; 
1016 #define argcGST wxTheApp->argc 
1017 #define argvGST wxTheApp->argv 
1020     //Really init gstreamer 
1022     GError
* error 
= NULL
; 
1023 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1024     bInited 
= gst_init_check(&argcGST
, &argvGST
, &error
); 
1026     bInited 
= gst_init_check(&argcGST
, &argvGST
); 
1029     // Cleanup arguments for unicode case 
1031     for ( i 
= 0; i 
< argcGST
; i
++ ) 
1039     if(!bInited
)    //gst_init_check fail? 
1043             wxLogSysError(wxT("Could not initialize GStreamer\n") 
1044                           wxT("Error Message:%s"), 
1045                           (const wxChar
*) wxConvUTF8
.cMB2WX(error
->message
) 
1047             g_error_free(error
); 
1050             wxLogSysError(wxT("Could not initialize GStreamer")); 
1056     // wxControl creation 
1058     m_ctrl 
= wxStaticCast(ctrl
, wxMediaCtrl
); 
1061     // We handle our own GTK expose events 
1062     m_ctrl
->m_noExpose 
= true; 
1065     if( !m_ctrl
->wxControl::Create(parent
, id
, pos
, size
, 
1066                             style
,  // TODO: remove borders??? 
1069         wxFAIL_MSG(wxT("Could not create wxControl!!!")); 
1074     // Turn off double-buffering so that 
1075     // so it doesn't draw over the video and cause sporadic 
1076     // disappearances of the video 
1077     gtk_widget_set_double_buffered(m_ctrl
->m_wxwindow
, FALSE
); 
1080     // don't erase the background of our control window 
1081     // so that resizing is a bit smoother 
1082     m_ctrl
->SetBackgroundStyle(wxBG_STYLE_CUSTOM
); 
1084     // Create our playbin object 
1085     m_playbin 
= gst_element_factory_make ("playbin", "play"); 
1086     if (!GST_IS_ELEMENT(m_playbin
)) 
1088         if(G_IS_OBJECT(m_playbin
)) 
1089             g_object_unref(m_playbin
); 
1090         wxLogSysError(wxT("Got an invalid playbin")); 
1094 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1095     // Connect the glib events/callbacks we want to our playbin 
1096     g_signal_connect(m_playbin
, "eos", 
1097                      G_CALLBACK(gst_finish_callback
), this); 
1098     g_signal_connect(m_playbin
, "error", 
1099                      G_CALLBACK(gst_error_callback
), this); 
1100     g_signal_connect(m_playbin
, "state-change", 
1101                      G_CALLBACK(gst_state_change_callback
), this); 
1103     // GStreamer 0.10+ uses GstBus for this now, connect to the sync 
1104     // handler as well so we can set the X window id of our xoverlay 
1105     gst_bus_add_watch (gst_element_get_bus(m_playbin
), 
1106                        (GstBusFunc
) gst_bus_async_callback
, this); 
1107     gst_bus_set_sync_handler(gst_element_get_bus(m_playbin
), 
1108                              (GstBusSyncHandler
) gst_bus_sync_callback
, this); 
1109     g_signal_connect(m_playbin
, "notify::stream-info", 
1110                      G_CALLBACK(gst_notify_stream_info_callback
), this); 
1113     // Get the audio sink 
1114     GstElement
* audiosink 
= gst_gconf_get_default_audio_sink(); 
1115     if( !TryAudioSink(audiosink
) ) 
1117         // fallback to autodetection, then alsa, then oss as a stopgap 
1118         audiosink 
= gst_element_factory_make ("autoaudiosink", "audio-sink"); 
1119         if( !TryAudioSink(audiosink
) ) 
1121             audiosink 
= gst_element_factory_make ("alsasink", "alsa-output"); 
1122             if( !TryAudioSink(audiosink
) ) 
1124                 audiosink 
= gst_element_factory_make ("osssink", "play_audio"); 
1125                 if( !TryAudioSink(audiosink
) ) 
1127                     wxLogSysError(wxT("Could not find a valid audiosink")); 
1134     // Setup video sink - first try gconf, then auto, then xvimage and 
1135     // then finally plain ximage 
1136     GstElement
* videosink 
= gst_gconf_get_default_video_sink(); 
1137     if( !TryVideoSink(videosink
) ) 
1139         videosink 
= gst_element_factory_make ("autovideosink", "video-sink"); 
1140         if( !TryVideoSink(videosink
) ) 
1142             videosink 
= gst_element_factory_make ("xvimagesink", "video-sink"); 
1143             if( !TryVideoSink(videosink
) ) 
1145                 // finally, do a final fallback to ximagesink 
1147                     gst_element_factory_make ("ximagesink", "video-sink"); 
1148                 if( !TryVideoSink(videosink
) ) 
1150                     g_object_unref(audiosink
); 
1151                     wxLogSysError(wxT("Could not find a suitable video sink")); 
1158 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR < 10 
1159     // Not on 0.10... called when video size changes 
1160     g_signal_connect(m_xoverlay
, "desired-size-changed", 
1161                      G_CALLBACK(gst_desired_size_changed_callback
), this); 
1163     // Tell GStreamer which window to draw to in 0.8 - 0.10 
1164     // sometimes needs this too... 
1167     // Now that we know (or, rather think) our video and audio sink 
1168     // are valid set our playbin to use them 
1169     g_object_set (G_OBJECT (m_playbin
), 
1170                   "video-sink", videosink
, 
1171                   "audio-sink", audiosink
, 
1174     m_eventHandler 
= new wxGStreamerMediaEventHandler(this); 
1178 //----------------------------------------------------------------------------- 
1179 // wxGStreamerMediaBackend::Load (File version) 
1181 // Just calls DoLoad() with a prepended file scheme 
1182 //----------------------------------------------------------------------------- 
1183 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
) 
1185     return DoLoad(wxString( wxT("file://") ) + fileName
); 
1188 //----------------------------------------------------------------------------- 
1189 // wxGStreamerMediaBackend::Load (URI version) 
1191 // In the case of a file URI passes it unencoded - 
1192 // also, as of 0.10.3 and earlier GstURI (the uri parser for gstreamer) 
1193 // is sort of broken and only accepts uris with at least two slashes 
1194 // after the scheme (i.e. file: == not ok, file:// == ok) 
1195 //----------------------------------------------------------------------------- 
1196 bool wxGStreamerMediaBackend::Load(const wxURI
& location
) 
1198     if(location
.GetScheme().CmpNoCase(wxT("file")) == 0) 
1200         wxString uristring 
= location
.BuildUnescapedURI(); 
1202         //Workaround GstURI leading "//" problem and make sure it leads 
1204         return DoLoad(wxString(wxT("file://")) + 
1205                       uristring
.Right(uristring
.length() - 5) 
1209         return DoLoad(location
.BuildURI()); 
1212 //----------------------------------------------------------------------------- 
1213 // wxGStreamerMediaBackend::DoLoad 
1216 // 1) Reset member variables and set playbin back to ready state 
1217 // 2) Check URI for validity and then tell the playbin to load it 
1218 // 3) Set the playbin to the pause state 
1220 // NB: Even after this function is over with we probably don't have the 
1221 // video size or duration - no amount of clever hacking is going to get 
1222 // around that, unfortunately. 
1223 //----------------------------------------------------------------------------- 
1224 bool wxGStreamerMediaBackend::DoLoad(const wxString
& locstring
) 
1226     wxMutexLocker 
lock(m_asynclock
); // lock state events and async callbacks 
1228     // Reset positions & rate 
1231     m_videoSize 
= wxSize(0,0); 
1233     // Set playbin to ready to stop the current media... 
1234     if( gst_element_set_state (m_playbin
, 
1235                                GST_STATE_READY
) == GST_STATE_FAILURE 
|| 
1236         !SyncStateChange(m_playbin
, GST_STATE_READY
)) 
1238         wxLogSysError(wxT("wxGStreamerMediaBackend::Load - ") 
1239                       wxT("Could not set initial state to ready")); 
1243     // free current media resources 
1244     gst_element_set_state (m_playbin
, GST_STATE_NULL
); 
1246     // Make sure the passed URI is valid and tell playbin to load it 
1247     // non-file uris are encoded 
1248     wxASSERT(gst_uri_protocol_is_valid("file")); 
1249     wxASSERT(gst_uri_is_valid(locstring
.mb_str())); 
1251     g_object_set (G_OBJECT (m_playbin
), "uri", 
1252                   (const char*)locstring
.mb_str(), NULL
); 
1254     // Try to pause media as gstreamer won't let us query attributes 
1255     // such as video size unless it is paused or playing 
1256     if( gst_element_set_state (m_playbin
, 
1257                                GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1258         !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1260         return false; // no real error message needed here as this is 
1261                       // generic failure 99% of the time (i.e. no 
1262                       // source etc.) and has an error message 
1266     NotifyMovieLoaded(); // Notify the user - all we can do for now 
1271 //----------------------------------------------------------------------------- 
1272 // wxGStreamerMediaBackend::Play 
1274 // Sets the stream to a playing state 
1276 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1277 //----------------------------------------------------------------------------- 
1278 bool wxGStreamerMediaBackend::Play() 
1280     if (gst_element_set_state (m_playbin
, 
1281                                GST_STATE_PLAYING
) == GST_STATE_FAILURE
) 
1286 //----------------------------------------------------------------------------- 
1287 // wxGStreamerMediaBackend::Pause 
1289 // Marks where we paused and pauses the stream 
1291 // THREAD-UNSAFE in 0.8, maybe in 0.10 as well 
1292 //----------------------------------------------------------------------------- 
1293 bool wxGStreamerMediaBackend::Pause() 
1295     m_llPausedPos 
= wxGStreamerMediaBackend::GetPosition(); 
1296     if (gst_element_set_state (m_playbin
, 
1297                                GST_STATE_PAUSED
) == GST_STATE_FAILURE
) 
1302 //----------------------------------------------------------------------------- 
1303 // wxGStreamerMediaBackend::Stop 
1305 // Pauses the stream and sets the position to 0. Note that this is 
1306 // synchronous (!) pausing. 
1308 // Due to the mutex locking this is probably thread-safe actually. 
1309 //----------------------------------------------------------------------------- 
1310 bool wxGStreamerMediaBackend::Stop() 
1312     {   // begin state lock 
1313         wxMutexLocker 
lock(m_asynclock
); 
1314         if(gst_element_set_state (m_playbin
, 
1315                                   GST_STATE_PAUSED
) == GST_STATE_FAILURE 
|| 
1316           !SyncStateChange(m_playbin
, GST_STATE_PAUSED
)) 
1318             wxLogSysError(wxT("Could not set state to paused for Stop()")); 
1323     bool bSeekedOK 
= wxGStreamerMediaBackend::SetPosition(0); 
1327         wxLogSysError(wxT("Could not seek to initial position in Stop()")); 
1331     QueueStopEvent(); // Success 
1335 //----------------------------------------------------------------------------- 
1336 // wxGStreamerMediaBackend::GetState 
1338 // Gets the state of the media 
1339 //----------------------------------------------------------------------------- 
1340 wxMediaState 
wxGStreamerMediaBackend::GetState() 
1342     switch(GST_STATE(m_playbin
)) 
1344         case GST_STATE_PLAYING
: 
1345             return wxMEDIASTATE_PLAYING
; 
1346         case GST_STATE_PAUSED
: 
1347             if (m_llPausedPos 
== 0) 
1348                 return wxMEDIASTATE_STOPPED
; 
1350                 return wxMEDIASTATE_PAUSED
; 
1351         default://case GST_STATE_READY: 
1352             return wxMEDIASTATE_STOPPED
; 
1356 //----------------------------------------------------------------------------- 
1357 // wxGStreamerMediaBackend::GetPosition 
1359 // If paused, returns our marked position - otherwise it queries the 
1360 // GStreamer playbin for the position and returns that 
1363 // NB: At least in 0.8, when you pause and seek gstreamer 
1364 // NB: doesn't update the position sometimes, so we need to keep track of 
1365 // NB: whether we have paused or not and keep track of the time after the 
1366 // NB: pause and whenever the user seeks while paused 
1369 // THREAD-UNSAFE, at least if not paused. Requires media to be at least paused. 
1370 //----------------------------------------------------------------------------- 
1371 wxLongLong 
wxGStreamerMediaBackend::GetPosition() 
1373     if(GetState() != wxMEDIASTATE_PLAYING
) 
1374         return m_llPausedPos
; 
1378         GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1380         if (!wxGst_element_query_position(m_playbin
, &fmtTime
, &pos
) || 
1381             fmtTime 
!= GST_FORMAT_TIME 
|| pos 
== -1) 
1383         return pos 
/ GST_MSECOND 
; 
1387 //----------------------------------------------------------------------------- 
1388 // wxGStreamerMediaBackend::SetPosition 
1390 // Sets the position of the stream 
1391 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so 
1392 // there is 1000000 nanoseconds in a millisecond) 
1394 // If we are paused we update the cached pause position. 
1396 // This is also an exceedingly ugly function due to the three implementations 
1397 // (or, rather two plus one implementation without a seek function). 
1399 // This is asynchronous and thread-safe on both 0.8 and 0.10. 
1401 // NB: This fires both a stop and play event if the media was previously 
1402 // playing... which in some ways makes sense. And yes, this makes the video 
1403 // go all haywire at times - a gstreamer bug... 
1404 //----------------------------------------------------------------------------- 
1405 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
) 
1407 #if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR == 8 \ 
1408                            && GST_VERSION_MICRO == 0 
1409     // 0.8.0 has no gst_element_seek according to official docs!!! 
1410     wxLogSysError(wxT("GStreamer 0.8.0 does not have gst_element_seek") 
1411                   wxT(" according to official docs")); 
1415 #   if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1416         gst_element_seek (m_playbin
, m_dRate
, GST_FORMAT_TIME
, 
1417            (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1418                           GST_SEEK_TYPE_SET
, where
.GetValue() * GST_MSECOND
, 
1419                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
); 
1421         // NB: Some gstreamer versions return false basically all the time 
1422         // here - even totem doesn't bother to check the return value here 
1423         // so I guess we'll just assume it worked - 
1424         // TODO: maybe check the gst error callback??? 
1425         gst_element_seek (m_playbin
, (GstSeekType
) (GST_SEEK_METHOD_SET 
| 
1426             GST_FORMAT_TIME 
| GST_SEEK_FLAG_FLUSH
), 
1427             where
.GetValue() * GST_MSECOND 
); 
1429 #   endif // GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1432         m_llPausedPos 
= where
; 
1439 //----------------------------------------------------------------------------- 
1440 // wxGStreamerMediaBackend::GetDuration 
1442 // Obtains the total time of our stream 
1443 // THREAD-UNSAFE, requires media to be paused or playing 
1444 //----------------------------------------------------------------------------- 
1445 wxLongLong 
wxGStreamerMediaBackend::GetDuration() 
1448     GstFormat fmtTime 
= GST_FORMAT_TIME
; 
1450     if(!wxGst_element_query_duration(m_playbin
, &fmtTime
, &length
) || 
1451        fmtTime 
!= GST_FORMAT_TIME 
|| length 
== -1) 
1453     return length 
/ GST_MSECOND 
; 
1456 //----------------------------------------------------------------------------- 
1457 // wxGStreamerMediaBackend::Move 
1459 // Called when the window is moved - GStreamer takes care of this 
1460 // for us so nothing is needed 
1461 //----------------------------------------------------------------------------- 
1462 void wxGStreamerMediaBackend::Move(int x
, int y
, int w
, int h
) 
1466 //----------------------------------------------------------------------------- 
1467 // wxGStreamerMediaBackend::GetVideoSize 
1469 // Returns our cached video size from Load/gst_notify_caps_callback 
1470 // gst_x_overlay_get_desired_size also does this in 0.8... 
1471 //----------------------------------------------------------------------------- 
1472 wxSize 
wxGStreamerMediaBackend::GetVideoSize() const 
1477 //----------------------------------------------------------------------------- 
1478 // wxGStreamerMediaBackend::GetPlaybackRate 
1479 // wxGStreamerMediaBackend::SetPlaybackRate 
1481 // Obtains/Sets the playback rate of the stream 
1483 //TODO: PlaybackRate not currently supported via playbin directly - 
1484 //TODO: Ronald S. Bultje noted on gstreamer-devel: 
1486 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As 
1487 //TODO: for the first, yes, we have elements for that, btu they"re not part of 
1488 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual 
1489 //TODO: video/audiosink and the speed-changing element for this, and set that 
1490 //TODO: element as video-sink or audio-sink property in playbin. The 
1491 //TODO: audio-element is called "speed", the video-element is called "videodrop" 
1492 //TODO: (although that appears to be deprecated in favour of "videorate", which 
1493 //TODO: again cannot do this, so this may not work at all in the end). For 
1494 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is 
1495 //TODO: part of playbin. 
1497 // In 0.10 GStreamer has new gst_element_seek API that might 
1498 // support this - and I've got an attempt to do so but it is untested 
1499 // but it would appear to work... 
1500 //----------------------------------------------------------------------------- 
1501 double wxGStreamerMediaBackend::GetPlaybackRate() 
1503     return m_dRate
; // Could use GST_QUERY_RATE but the API doesn't seem 
1504                     // final on that yet and there may not be any actual 
1505                     // plugins that support it... 
1508 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
) 
1510 #if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10 
1511 #if 0 // not tested enough 
1512     if( gst_element_seek (m_playbin
, dRate
, GST_FORMAT_TIME
, 
1513                  (GstSeekFlags
)(GST_SEEK_FLAG_FLUSH 
| GST_SEEK_FLAG_KEY_UNIT
), 
1514                           GST_SEEK_TYPE_CUR
, 0, 
1515                           GST_SEEK_TYPE_NONE
, GST_CLOCK_TIME_NONE 
) ) 
1527 //----------------------------------------------------------------------------- 
1528 // wxGStreamerMediaBackend::GetDownloadProgress 
1530 // Not really outwardly possible - have been suggested that one could 
1531 // get the information from the component that "downloads" 
1532 //----------------------------------------------------------------------------- 
1533 wxLongLong 
wxGStreamerMediaBackend::GetDownloadProgress() 
1538 //----------------------------------------------------------------------------- 
1539 // wxGStreamerMediaBackend::GetDownloadTotal 
1541 // TODO: Cache this? 
1542 // NB: The length changes every call for some reason due to 
1543 //     GStreamer implementation issues 
1544 // THREAD-UNSAFE, requires media to be paused or playing 
1545 //----------------------------------------------------------------------------- 
1546 wxLongLong 
wxGStreamerMediaBackend::GetDownloadTotal() 
1549     GstFormat fmtBytes 
= GST_FORMAT_BYTES
; 
1551     if (!wxGst_element_query_duration(m_playbin
, &fmtBytes
, &length
) || 
1552           fmtBytes 
!= GST_FORMAT_BYTES 
|| length 
== -1) 
1557 //----------------------------------------------------------------------------- 
1558 // wxGStreamerMediaBackend::SetVolume 
1559 // wxGStreamerMediaBackend::GetVolume 
1561 // Sets/Gets the volume through the playbin object. 
1562 // Note that this requires a relatively recent gst-plugins so we 
1563 // check at runtime to see whether it is available or not otherwise 
1564 // GST spits out an error on the command line 
1565 //----------------------------------------------------------------------------- 
1566 bool wxGStreamerMediaBackend::SetVolume(double dVolume
) 
1568     if(g_object_class_find_property( 
1569             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1572         g_object_set(G_OBJECT(m_playbin
), "volume", dVolume
, NULL
); 
1577         wxLogTrace(wxTRACE_GStreamer
, 
1578             wxT("SetVolume: volume prop not found - 0.8.5 of ") 
1579             wxT("gst-plugins probably needed")); 
1584 double wxGStreamerMediaBackend::GetVolume() 
1586     double dVolume 
= 1.0; 
1588     if(g_object_class_find_property( 
1589             G_OBJECT_GET_CLASS(G_OBJECT(m_playbin
)), 
1592         g_object_get(G_OBJECT(m_playbin
), "volume", &dVolume
, NULL
); 
1596         wxLogTrace(wxTRACE_GStreamer
, 
1597             wxT("GetVolume: volume prop not found - 0.8.5 of ") 
1598             wxT("gst-plugins probably needed")); 
1604 #endif //wxUSE_GSTREAMER 
1606 // Force link into main library so this backend can be loaded 
1607 #include "wx/html/forcelnk.h" 
1608 FORCE_LINK_ME(basewxmediabackends
) 
1610 #endif //wxUSE_MEDIACTRL