1 /////////////////////////////////////////////////////////////////////////////
2 // Name: unix/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Unix
4 // Author: Ryan Norton <wxprojects@comcast.net>
8 // Copyright: (c) 2004-2005 Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 //===========================================================================
14 //===========================================================================
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "mediactrl.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
31 //---------------------------------------------------------------------------
33 //---------------------------------------------------------------------------
34 #include "wx/mediactrl.h"
36 //---------------------------------------------------------------------------
38 //---------------------------------------------------------------------------
41 //===========================================================================
42 // BACKEND DECLARATIONS
43 //===========================================================================
45 //---------------------------------------------------------------------------
47 // wxGStreamerMediaBackend
50 //TODO: This is really not the best way to play-stop -
51 //TODO: it should just have one playbin and stick with it the whole
52 //TODO: instance of wxGStreamerMediaBackend - but stopping appears
53 //TODO: to invalidate the playbin object...
56 //---------------------------------------------------------------------------
59 //---------------------------------------------------------------------------
61 //---------------------------------------------------------------------------
63 #include <gst/xoverlay/xoverlay.h>
65 #include <string.h> //strstr
68 #include "wx/msgdlg.h"
71 //for <gdk/gdkx.h>/related for GDK_WINDOW_XWINDOW
72 # include "wx/gtk/win_gtk.h"
73 # include <gtk/gtksignal.h>
77 class WXDLLIMPEXP_MEDIA wxGStreamerMediaBackend
: public wxMediaBackend
81 wxGStreamerMediaBackend();
82 ~wxGStreamerMediaBackend();
84 virtual bool CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
89 const wxValidator
& validator
,
90 const wxString
& name
);
96 virtual bool Load(const wxString
& fileName
);
97 virtual bool Load(const wxURI
& location
);
99 virtual wxMediaState
GetState();
101 virtual bool SetPosition(wxLongLong where
);
102 virtual wxLongLong
GetPosition();
103 virtual wxLongLong
GetDuration();
105 virtual void Move(int x
, int y
, int w
, int h
);
106 wxSize
GetVideoSize() const;
108 virtual double GetPlaybackRate();
109 virtual bool SetPlaybackRate(double dRate
);
113 static void OnFinish(GstElement
*play
, gpointer data
);
114 static void OnError (GstElement
*play
, GstElement
*src
,
115 GError
*err
, gchar
*debug
,
117 static void OnVideoCapsReady(GstPad
* pad
, GParamSpec
* pspec
, gpointer data
);
119 static bool TransCapsToVideoSize(wxGStreamerMediaBackend
* be
, GstPad
* caps
);
120 void PostRecalcSize();
123 static gint
OnGTKRealize(GtkWidget
* theWidget
, wxGStreamerMediaBackend
* be
);
126 GstElement
* m_player
; //GStreamer media element
127 // GstElement* m_audiosink;
128 GstElement
* m_videosink
;
133 wxLongLong m_nPausedPos
;
135 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend
);
139 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
141 // wxGStreamerMediaBackend
143 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
145 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend
, wxMediaBackend
);
147 //---------------------------------------------------------------------------
148 // wxGStreamerMediaBackend Constructor
150 // Sets m_player to NULL signifying we havn't loaded anything yet
151 //---------------------------------------------------------------------------
152 wxGStreamerMediaBackend::wxGStreamerMediaBackend() : m_player(NULL
), m_videoSize(0,0)
156 //---------------------------------------------------------------------------
157 // wxGStreamerMediaBackend Destructor
159 // Stops/cleans up memory
160 //---------------------------------------------------------------------------
161 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
166 //---------------------------------------------------------------------------
167 // wxGStreamerMediaBackend::OnGTKRealize
169 // If the window wasn't realized when Load was called, this is the
170 // callback for when it is.
172 // 1) Installs GTK idle handler if it doesn't exist
173 // 2) Yeilds to avoid an X11 bug (?)
174 // 3) Tells GStreamer to play the video in our control
175 //---------------------------------------------------------------------------
181 # define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
183 # define DEBUG_MAIN_THREAD
186 #define DEBUG_MAIN_THREAD
189 extern void wxapp_install_idle_handler();
190 extern bool g_isIdle
;
191 extern bool g_mainThreadLocked
;
193 gint
wxGStreamerMediaBackend::OnGTKRealize(GtkWidget
* theWidget
,
194 wxGStreamerMediaBackend
* be
)
199 wxapp_install_idle_handler();
201 wxYield(); //FIXME: X Server gets an error if I don't do this or a messagebox beforehand?!?!??
203 GdkWindow
*window
= GTK_PIZZA(theWidget
)->bin_window
;
206 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be
->m_videosink
),
207 GDK_WINDOW_XWINDOW( window
)
216 //---------------------------------------------------------------------------
217 // wxGStreamerMediaBackend::Cleanup
219 // Frees the gstreamer interfaces if there were any created
220 //TODO: Do we need to free the video interface? I'm getting segfaults
222 //---------------------------------------------------------------------------
223 void wxGStreamerMediaBackend::Cleanup()
225 if(m_player
&& GST_IS_OBJECT(m_player
))
227 gst_element_set_state (m_player
, GST_STATE_NULL
);
228 gst_object_unref (GST_OBJECT (m_player
));
230 //if(GST_IS_OBJECT(m_videosink))
231 // gst_object_unref (GST_OBJECT (m_videosink));
232 //if(GST_IS_OBJECT(m_audiosink))
233 // gst_object_unref (GST_OBJECT (m_audiosink));
237 //---------------------------------------------------------------------------
238 // wxGStreamerMediaBackend::CreateControl
240 // Initializes GStreamer and creates the wx side of our media control
241 //---------------------------------------------------------------------------
242 bool wxGStreamerMediaBackend::CreateControl(wxControl
* ctrl
, wxWindow
* parent
,
247 const wxValidator
& validator
,
248 const wxString
& name
)
251 gst_init(NULL
, NULL
);
255 return m_ctrl
->wxControl::Create(parent
, id
, pos
, size
,
256 style
, //remove borders???
260 //---------------------------------------------------------------------------
261 // wxGStreamerMediaBackend::TransCapsToVideoSize
263 // Gets the size of our video (in wxSize) from a GstPad
264 //---------------------------------------------------------------------------
265 bool wxGStreamerMediaBackend::TransCapsToVideoSize(wxGStreamerMediaBackend
* be
, GstPad
* pad
)
267 const GstCaps
* caps
= GST_PAD_CAPS (pad
);
271 const GstStructure
*s
;
272 s
= gst_caps_get_structure (caps
, 0);
275 gst_structure_get_int (s
, "width", &be
->m_videoSize
.x
);
276 gst_structure_get_int (s
, "height", &be
->m_videoSize
.y
);
279 par
= gst_structure_get_value (s
, "pixel-aspect-ratio");
283 int num
= gst_value_get_fraction_numerator (par
),
284 den
= gst_value_get_fraction_denominator (par
);
286 //TODO: maybe better fraction normalization...
288 be
->m_videoSize
.x
= (int) ((float) num
* be
->m_videoSize
.x
/ den
);
290 be
->m_videoSize
.y
= (int) ((float) den
* be
->m_videoSize
.y
/ num
);
293 be
->PostRecalcSize();
300 //---------------------------------------------------------------------------
301 // wxGStreamerMediaBackend::PostRecalcSize
303 // Forces parent to recalc its layout if it has sizers to update
304 // to the new video size
305 //---------------------------------------------------------------------------
306 void wxGStreamerMediaBackend::PostRecalcSize()
308 m_ctrl
->InvalidateBestSize();
309 m_ctrl
->GetParent()->Layout();
310 m_ctrl
->GetParent()->Refresh();
311 m_ctrl
->GetParent()->Update();
314 //---------------------------------------------------------------------------
315 // wxGStreamerMediaBackend::OnFinish
317 // Called by gstreamer when the media is done playing
319 // 1) Send a wxEVT_MEDIA_STOP to the control
320 // 2) If veteod, break out
321 // 3) really stop the media
322 // 4) Send a wxEVT_MEDIA_FINISHED to the control
323 //---------------------------------------------------------------------------
324 void wxGStreamerMediaBackend::OnFinish(GstElement
*play
, gpointer data
)
326 wxGStreamerMediaBackend
* m_parent
= (wxGStreamerMediaBackend
*) data
;
328 wxMediaEvent
theEvent(wxEVT_MEDIA_STOP
,
329 m_parent
->m_ctrl
->GetId());
330 m_parent
->m_ctrl
->ProcessEvent(theEvent
);
332 if(theEvent
.IsAllowed())
334 bool bOk
= m_parent
->Stop();
337 //send the event to our child
338 wxMediaEvent
theEvent(wxEVT_MEDIA_FINISHED
,
339 m_parent
->m_ctrl
->GetId());
340 m_parent
->m_ctrl
->ProcessEvent(theEvent
);
344 //---------------------------------------------------------------------------
345 // wxGStreamerMediaBackend::OnError
347 // Called by gstreamer when an error is encountered playing the media
349 // TODO: Make this better - maybe some more intelligent wxLog stuff
350 //---------------------------------------------------------------------------
351 void wxGStreamerMediaBackend::OnError(GstElement
*play
,
357 wxMessageBox(wxString::Format(wxT("Error in wxMediaCtrl!\nError Message:%s"), wxString(err
->message
, wxConvLocal
).c_str()));
361 //---------------------------------------------------------------------------
362 // wxGStreamerMediaBackend::Load (File version)
364 // Just calls the URI version
365 //---------------------------------------------------------------------------
366 bool wxGStreamerMediaBackend::Load(const wxString
& fileName
)
370 wxString( wxT("file://") ) + fileName
375 //---------------------------------------------------------------------------
376 // wxGStreamerMediaBackend::OnVideoCapsReady
378 // Called by gstreamer when the video caps for the media is ready
379 //---------------------------------------------------------------------------
380 void wxGStreamerMediaBackend::OnVideoCapsReady(GstPad
* pad
, GParamSpec
* pspec
, gpointer data
)
382 wxGStreamerMediaBackend::TransCapsToVideoSize((wxGStreamerMediaBackend
*) data
, pad
);
385 //---------------------------------------------------------------------------
386 // wxGStreamerMediaBackend::Load (URI version)
388 // 1) Stops/Cleanups the previous instance if there is any
389 // 2) Creates the gstreamer interfaces - playbin and xvimagesink for video
390 // 3) If there is no playbin or video sink it bails out
391 // 4) Sets the playbin to have our video sink so we can set the window later
392 // 5) Set up the error and end-of-stream callbacks for our player
393 // 6) Make sure our video sink can support the x overlay interface
394 // 7) Make sure the passed URI is valid and tell playbin to load it
395 // 8) Use the xoverlay extension to tell gstreamer to play in our window
396 // 9) Get the video size - pause required to set the stream in action
397 //---------------------------------------------------------------------------
398 bool wxGStreamerMediaBackend::Load(const wxURI
& location
)
404 m_player
= gst_element_factory_make ("playbin", "play");
405 // m_audiosink = gst_element_factory_make ("alsasink", "audiosink");
406 m_videosink
= gst_element_factory_make ("xvimagesink", "videosink");
409 //no playbin -- outta here :)
410 if (!m_player
|| !GST_IS_OBJECT(m_videosink
))
414 // if (GST_IS_OBJECT(m_audiosink) == false)
417 // m_audiosink = gst_element_factory_make ("osssink", "audiosink");
418 // wxASSERT_MSG(GST_IS_OBJECT(m_audiosink), wxT("WARNING: Alsa and OSS drivers for gstreamer not found - audio will be unavailable for wxMediaCtrl"));
423 g_object_set (G_OBJECT (m_player
),
424 "video-sink", m_videosink
,
425 // "audio-sink", m_audiosink,
429 g_signal_connect (m_player
, "eos", G_CALLBACK (OnError
), this);
430 g_signal_connect (m_player
, "error", G_CALLBACK (OnFinish
), this);
433 wxASSERT( GST_IS_X_OVERLAY(m_videosink
) );
434 if ( ! GST_IS_X_OVERLAY(m_videosink
) )
438 wxString locstring
= location
.BuildUnescapedURI();
439 wxASSERT(gst_uri_protocol_is_valid("file"));
440 wxASSERT(gst_uri_is_valid(locstring
.mb_str()));
442 g_object_set (G_OBJECT (m_player
), "uri", (const char*)locstring
.mb_str(), NULL
);
446 if(!GTK_WIDGET_REALIZED(m_ctrl
->m_wxwindow
))
448 //Not realized yet - set to connect at realization time
449 gtk_signal_connect( GTK_OBJECT(m_ctrl
->m_wxwindow
),
451 GTK_SIGNAL_FUNC(wxGStreamerMediaBackend::OnGTKRealize
),
456 wxYield(); //see realize callback...
457 GdkWindow
*window
= GTK_PIZZA(m_ctrl
->m_wxwindow
)->bin_window
;
462 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_videosink
),
464 GDK_WINDOW_XWINDOW( window
)
475 wxASSERT(gst_element_set_state (m_player
,
476 GST_STATE_PAUSED
) == GST_STATE_SUCCESS
);
478 const GList
*list
= NULL
;
479 g_object_get (G_OBJECT (m_player
), "stream-info", &list
, NULL
);
481 for ( ; list
!= NULL
; list
= list
->next
)
483 GObject
*info
= (GObject
*) list
->data
;
489 g_object_get (info
, "type", &type
, NULL
);
490 pspec
= g_object_class_find_property (
491 G_OBJECT_GET_CLASS (info
), "type");
492 val
= g_enum_get_value (G_PARAM_SPEC_ENUM (pspec
)->enum_class
, type
);
494 if (strstr (val
->value_name
, "VIDEO"))
496 //Newer gstreamer 0.8+ is SUPPOSED to have "object"...
497 //but a lot of old plugins still use "pad" :)
498 pspec
= g_object_class_find_property (
499 G_OBJECT_GET_CLASS (info
), "object");
502 g_object_get (info
, "pad", &pad
, NULL
);
504 g_object_get (info
, "object", &pad
, NULL
);
506 pad
= (GstPad
*) GST_PAD_REALIZE (pad
);
509 if(!wxGStreamerMediaBackend::TransCapsToVideoSize(this, pad
));
511 //wait for those caps to get ready
515 G_CALLBACK(wxGStreamerMediaBackend::OnVideoCapsReady
),
521 m_videoSize
= wxSize(0,0);
524 }//end searching through info list
530 //---------------------------------------------------------------------------
531 // wxGStreamerMediaBackend::Play
533 // Sets the stream to a playing state
534 //---------------------------------------------------------------------------
535 bool wxGStreamerMediaBackend::Play()
537 if (gst_element_set_state (m_player
, GST_STATE_PLAYING
)
538 != GST_STATE_SUCCESS
)
543 //---------------------------------------------------------------------------
544 // wxGStreamerMediaBackend::Pause
546 // Marks where we paused and pauses the stream
547 //---------------------------------------------------------------------------
548 bool wxGStreamerMediaBackend::Pause()
550 m_nPausedPos
= GetPosition();
551 if (gst_element_set_state (m_player
, GST_STATE_PAUSED
)
552 != GST_STATE_SUCCESS
)
557 //---------------------------------------------------------------------------
558 // wxGStreamerMediaBackend::Stop
560 // Pauses the stream and sets the position to 0
561 //---------------------------------------------------------------------------
562 bool wxGStreamerMediaBackend::Stop()
564 if (gst_element_set_state (m_player
,
565 GST_STATE_PAUSED
) != GST_STATE_SUCCESS
)
567 return wxGStreamerMediaBackend::SetPosition(0);
570 //---------------------------------------------------------------------------
571 // wxGStreamerMediaBackend::GetState
573 // Gets the state of the stream
574 //---------------------------------------------------------------------------
575 wxMediaState
wxGStreamerMediaBackend::GetState()
577 switch(GST_STATE(m_player
))
579 case GST_STATE_PLAYING
:
580 return wxMEDIASTATE_PLAYING
;
581 case GST_STATE_PAUSED
:
582 if (m_nPausedPos
== 0)
583 return wxMEDIASTATE_STOPPED
;
585 return wxMEDIASTATE_PAUSED
;
586 default://case GST_STATE_READY:
587 return wxMEDIASTATE_STOPPED
;
591 //---------------------------------------------------------------------------
592 // wxGStreamerMediaBackend::GetPosition
594 // If paused, returns our marked position - otherwise it queries the
595 // GStreamer playbin for the position and returns that
598 //TODO: In lue of the last big TODO, when you pause and seek gstreamer
599 //TODO: doesn't update the position sometimes, so we need to keep track of whether
600 //TODO: we have paused or not and keep track of the time after the pause
601 //TODO: and whenever the user seeks while paused
603 //---------------------------------------------------------------------------
604 wxLongLong
wxGStreamerMediaBackend::GetPosition()
606 if(GetState() != wxMEDIASTATE_PLAYING
)
611 GstFormat fmtTime
= GST_FORMAT_TIME
;
613 if (!gst_element_query (m_player
, GST_QUERY_POSITION
, &fmtTime
, &pos
))
615 return pos
/ GST_MSECOND
;
619 //---------------------------------------------------------------------------
620 // wxGStreamerMediaBackend::SetPosition
622 // Sets the position of the stream
623 // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so
624 // there is 1000000 nanoseconds in a millisecond)
626 // If paused marks where we seeked to
627 //---------------------------------------------------------------------------
628 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where
)
630 if( gst_element_seek (m_player
, (GstSeekType
) (GST_SEEK_METHOD_SET
|
631 GST_FORMAT_TIME
| GST_SEEK_FLAG_FLUSH
),
632 where
.GetValue() * GST_MSECOND
) )
634 if (GetState() != wxMEDIASTATE_PLAYING
)
635 m_nPausedPos
= where
;
643 //---------------------------------------------------------------------------
644 // wxGStreamerMediaBackend::GetDuration
646 // Obtains the total time of our stream
647 //---------------------------------------------------------------------------
648 wxLongLong
wxGStreamerMediaBackend::GetDuration()
651 GstFormat fmtTime
= GST_FORMAT_TIME
;
653 if(!gst_element_query(m_player
, GST_QUERY_TOTAL
, &fmtTime
, &length
))
655 return length
/ GST_MSECOND
;
658 //---------------------------------------------------------------------------
659 // wxGStreamerMediaBackend::Move
661 // Called when the window is moved - GStreamer takes care of this
662 // for us so nothing is needed
663 //---------------------------------------------------------------------------
664 void wxGStreamerMediaBackend::Move(int x
, int y
, int w
, int h
)
668 //---------------------------------------------------------------------------
669 // wxGStreamerMediaBackend::GetVideoSize
671 // Returns our cached video size from Load/OnVideoCapsReady
672 //---------------------------------------------------------------------------
673 wxSize
wxGStreamerMediaBackend::GetVideoSize() const
678 //---------------------------------------------------------------------------
679 // wxGStreamerMediaBackend::GetPlaybackRate
680 // wxGStreamerMediaBackend::SetPlaybackRate
682 // Obtains/Sets the playback rate of the stream
684 //TODO: PlaybackRate not currently supported via playbin directly -
685 //TODO: Ronald S. Bultje noted on gstreamer-devel:
687 //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As
688 //TODO: for the first, yes, we have elements for that, btu they"re not part of
689 //TODO: playbin. You can create a bin (with a ghost pad) containing the actual
690 //TODO: video/audiosink and the speed-changing element for this, and set that
691 //TODO: element as video-sink or audio-sink property in playbin. The
692 //TODO: audio-element is called "speed", the video-element is called "videodrop"
693 //TODO: (although that appears to be deprecated in favour of "videorate", which
694 //TODO: again cannot do this, so this may not work at all in the end). For
695 //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is
696 //TODO: part of playbin.
697 //---------------------------------------------------------------------------
698 double wxGStreamerMediaBackend::GetPlaybackRate()
700 //not currently supported via playbin
704 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate
)
706 //not currently supported via playbin
710 #endif //wxUSE_GSTREAMER
712 //in source file that contains stuff you don't directly use
713 #include <wx/html/forcelnk.h>
714 FORCE_LINK_ME(basewxmediabackends
);
716 #endif //wxUSE_MEDIACTRL