]>
Commit | Line | Data |
---|---|---|
ddc90a8d | 1 | ///////////////////////////////////////////////////////////////////////////// |
7ec69821 | 2 | // Name: src/unix/mediactrl.cpp |
0c5c0375 | 3 | // Purpose: Built-in Media Backends for Unix |
ddc90a8d RN |
4 | // Author: Ryan Norton <wxprojects@comcast.net> |
5 | // Modified by: | |
6 | // Created: 02/04/05 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) 2004-2005 Ryan Norton | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | //=========================================================================== | |
13 | // DECLARATIONS | |
14 | //=========================================================================== | |
15 | ||
16 | //--------------------------------------------------------------------------- | |
17 | // Pre-compiled header stuff | |
18 | //--------------------------------------------------------------------------- | |
19 | ||
ddc90a8d RN |
20 | // For compilers that support precompilation, includes "wx.h". |
21 | #include "wx/wxprec.h" | |
22 | ||
23 | #ifdef __BORLANDC__ | |
24 | #pragma hdrstop | |
25 | #endif | |
26 | ||
27 | //--------------------------------------------------------------------------- | |
28 | // Includes | |
29 | //--------------------------------------------------------------------------- | |
30 | #include "wx/mediactrl.h" | |
31 | ||
32 | //--------------------------------------------------------------------------- | |
33 | // Compilation guard | |
34 | //--------------------------------------------------------------------------- | |
35 | #if wxUSE_MEDIACTRL | |
36 | ||
0c5c0375 RN |
37 | //=========================================================================== |
38 | // BACKEND DECLARATIONS | |
39 | //=========================================================================== | |
40 | ||
41 | //--------------------------------------------------------------------------- | |
42 | // | |
43 | // wxGStreamerMediaBackend | |
44 | // | |
dae87f93 RN |
45 | //TODO: |
46 | //TODO: This is really not the best way to play-stop - | |
47 | //TODO: it should just have one playbin and stick with it the whole | |
48 | //TODO: instance of wxGStreamerMediaBackend - but stopping appears | |
49 | //TODO: to invalidate the playbin object... | |
50 | //TODO: | |
51 | // | |
0c5c0375 RN |
52 | //--------------------------------------------------------------------------- |
53 | #if wxUSE_GSTREAMER | |
54 | ||
55 | //--------------------------------------------------------------------------- | |
56 | // GStreamer Includes | |
57 | //--------------------------------------------------------------------------- | |
58 | #include <gst/gst.h> | |
59 | #include <gst/xoverlay/xoverlay.h> | |
60 | ||
61 | #include <string.h> //strstr | |
62 | ||
63 | #include "wx/log.h" | |
64 | ||
65 | #ifdef __WXGTK__ | |
66 | //for <gdk/gdkx.h>/related for GDK_WINDOW_XWINDOW | |
1e22656e RN |
67 | # include "wx/gtk/win_gtk.h" |
68 | # include <gtk/gtksignal.h> | |
a4945572 RN |
69 | # if wxUSE_DYNLIB_CLASS |
70 | # include "wx/dynlib.h" | |
71 | # endif | |
72 | //# include <gst/gconf/gconf.h> //gstreamer gnome interface - needs deps | |
0c5c0375 RN |
73 | #endif |
74 | ||
1e22656e | 75 | |
0c5c0375 RN |
76 | class WXDLLIMPEXP_MEDIA wxGStreamerMediaBackend : public wxMediaBackend |
77 | { | |
78 | public: | |
79 | ||
80 | wxGStreamerMediaBackend(); | |
81 | ~wxGStreamerMediaBackend(); | |
82 | ||
83 | virtual bool CreateControl(wxControl* ctrl, wxWindow* parent, | |
84 | wxWindowID id, | |
85 | const wxPoint& pos, | |
86 | const wxSize& size, | |
87 | long style, | |
88 | const wxValidator& validator, | |
89 | const wxString& name); | |
90 | ||
91 | virtual bool Play(); | |
92 | virtual bool Pause(); | |
93 | virtual bool Stop(); | |
94 | ||
95 | virtual bool Load(const wxString& fileName); | |
96 | virtual bool Load(const wxURI& location); | |
97 | ||
98 | virtual wxMediaState GetState(); | |
99 | ||
100 | virtual bool SetPosition(wxLongLong where); | |
101 | virtual wxLongLong GetPosition(); | |
102 | virtual wxLongLong GetDuration(); | |
103 | ||
104 | virtual void Move(int x, int y, int w, int h); | |
105 | wxSize GetVideoSize() const; | |
106 | ||
107 | virtual double GetPlaybackRate(); | |
108 | virtual bool SetPlaybackRate(double dRate); | |
109 | ||
110 | void Cleanup(); | |
111 | ||
112 | static void OnFinish(GstElement *play, gpointer data); | |
113 | static void OnError (GstElement *play, GstElement *src, | |
114 | GError *err, gchar *debug, | |
115 | gpointer data); | |
1e22656e | 116 | static void OnVideoCapsReady(GstPad* pad, GParamSpec* pspec, gpointer data); |
7ec69821 | 117 | |
1e22656e RN |
118 | static bool TransCapsToVideoSize(wxGStreamerMediaBackend* be, GstPad* caps); |
119 | void PostRecalcSize(); | |
7ec69821 | 120 | |
1e22656e RN |
121 | #ifdef __WXGTK__ |
122 | static gint OnGTKRealize(GtkWidget* theWidget, wxGStreamerMediaBackend* be); | |
123 | #endif | |
0c5c0375 RN |
124 | |
125 | GstElement* m_player; //GStreamer media element | |
7ec69821 | 126 | |
1e22656e RN |
127 | wxSize m_videoSize; |
128 | wxControl* m_ctrl; | |
7ec69821 | 129 | |
1e22656e | 130 | wxLongLong m_nPausedPos; |
0c5c0375 RN |
131 | |
132 | DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend); | |
133 | }; | |
134 | ||
135 | ||
136 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
137 | // | |
138 | // wxGStreamerMediaBackend | |
139 | // | |
140 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | |
141 | ||
412e0d47 | 142 | IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend, wxMediaBackend) |
0c5c0375 | 143 | |
dae87f93 RN |
144 | //--------------------------------------------------------------------------- |
145 | // wxGStreamerMediaBackend Constructor | |
146 | // | |
147 | // Sets m_player to NULL signifying we havn't loaded anything yet | |
148 | //--------------------------------------------------------------------------- | |
1e22656e | 149 | wxGStreamerMediaBackend::wxGStreamerMediaBackend() : m_player(NULL), m_videoSize(0,0) |
0c5c0375 RN |
150 | { |
151 | } | |
152 | ||
dae87f93 RN |
153 | //--------------------------------------------------------------------------- |
154 | // wxGStreamerMediaBackend Destructor | |
155 | // | |
7ec69821 | 156 | // Stops/cleans up memory |
dae87f93 | 157 | //--------------------------------------------------------------------------- |
0c5c0375 RN |
158 | wxGStreamerMediaBackend::~wxGStreamerMediaBackend() |
159 | { | |
1e22656e RN |
160 | Cleanup(); |
161 | } | |
162 | ||
dae87f93 RN |
163 | //--------------------------------------------------------------------------- |
164 | // wxGStreamerMediaBackend::OnGTKRealize | |
165 | // | |
166 | // If the window wasn't realized when Load was called, this is the | |
167 | // callback for when it is. | |
168 | // | |
169 | // 1) Installs GTK idle handler if it doesn't exist | |
170 | // 2) Yeilds to avoid an X11 bug (?) | |
171 | // 3) Tells GStreamer to play the video in our control | |
172 | //--------------------------------------------------------------------------- | |
1e22656e RN |
173 | #ifdef __WXGTK__ |
174 | ||
175 | #ifdef __WXDEBUG__ | |
176 | ||
177 | #if wxUSE_THREADS | |
178 | # define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance"); | |
179 | #else | |
180 | # define DEBUG_MAIN_THREAD | |
181 | #endif | |
182 | #else | |
183 | #define DEBUG_MAIN_THREAD | |
184 | #endif // Debug | |
185 | ||
186 | extern void wxapp_install_idle_handler(); | |
187 | extern bool g_isIdle; | |
188 | extern bool g_mainThreadLocked; | |
189 | ||
7ec69821 | 190 | gint wxGStreamerMediaBackend::OnGTKRealize(GtkWidget* theWidget, |
1e22656e RN |
191 | wxGStreamerMediaBackend* be) |
192 | { | |
193 | DEBUG_MAIN_THREAD | |
7ec69821 | 194 | |
1e22656e RN |
195 | if (g_isIdle) |
196 | wxapp_install_idle_handler(); | |
197 | ||
dae87f93 | 198 | wxYield(); //FIXME: X Server gets an error if I don't do this or a messagebox beforehand?!?!?? |
7ec69821 | 199 | |
1e22656e RN |
200 | GdkWindow *window = GTK_PIZZA(theWidget)->bin_window; |
201 | wxASSERT(window); | |
7ec69821 | 202 | |
b4a345a6 RN |
203 | GstElement* videosink; |
204 | g_object_get (G_OBJECT (be->m_player), "video-sink", &videosink, NULL); | |
7ec69821 | 205 | |
a4945572 RN |
206 | GstElement* overlay = gst_bin_get_by_interface (GST_BIN (videosink), |
207 | GST_TYPE_X_OVERLAY); | |
208 | gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(overlay), | |
209 | GDK_WINDOW_XWINDOW( window ) | |
210 | ); | |
7ec69821 | 211 | |
1e22656e RN |
212 | return 0; |
213 | } | |
214 | ||
215 | ||
216 | #endif | |
217 | ||
dae87f93 RN |
218 | //--------------------------------------------------------------------------- |
219 | // wxGStreamerMediaBackend::Cleanup | |
220 | // | |
221 | // Frees the gstreamer interfaces if there were any created | |
dae87f93 | 222 | //--------------------------------------------------------------------------- |
1e22656e RN |
223 | void wxGStreamerMediaBackend::Cleanup() |
224 | { | |
225 | if(m_player && GST_IS_OBJECT(m_player)) | |
226 | { | |
1e22656e RN |
227 | gst_element_set_state (m_player, GST_STATE_NULL); |
228 | gst_object_unref (GST_OBJECT (m_player)); | |
1e22656e | 229 | } |
0c5c0375 RN |
230 | } |
231 | ||
dae87f93 RN |
232 | //--------------------------------------------------------------------------- |
233 | // wxGStreamerMediaBackend::CreateControl | |
234 | // | |
235 | // Initializes GStreamer and creates the wx side of our media control | |
236 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
237 | bool wxGStreamerMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent, |
238 | wxWindowID id, | |
239 | const wxPoint& pos, | |
240 | const wxSize& size, | |
241 | long style, | |
242 | const wxValidator& validator, | |
243 | const wxString& name) | |
244 | { | |
245 | //init gstreamer | |
246 | gst_init(NULL, NULL); | |
7ec69821 | 247 | |
1e22656e | 248 | m_ctrl = ctrl; |
0c5c0375 | 249 | |
1e22656e | 250 | return m_ctrl->wxControl::Create(parent, id, pos, size, |
0c5c0375 | 251 | style, //remove borders??? |
1e22656e RN |
252 | validator, name); |
253 | } | |
0c5c0375 | 254 | |
dae87f93 RN |
255 | //--------------------------------------------------------------------------- |
256 | // wxGStreamerMediaBackend::TransCapsToVideoSize | |
257 | // | |
258 | // Gets the size of our video (in wxSize) from a GstPad | |
259 | //--------------------------------------------------------------------------- | |
1e22656e RN |
260 | bool wxGStreamerMediaBackend::TransCapsToVideoSize(wxGStreamerMediaBackend* be, GstPad* pad) |
261 | { | |
262 | const GstCaps* caps = GST_PAD_CAPS (pad); | |
263 | if(caps) | |
264 | { | |
0c5c0375 | 265 | |
1e22656e RN |
266 | const GstStructure *s; |
267 | s = gst_caps_get_structure (caps, 0); | |
268 | wxASSERT(s); | |
0c5c0375 | 269 | |
1e22656e RN |
270 | gst_structure_get_int (s, "width", &be->m_videoSize.x); |
271 | gst_structure_get_int (s, "height", &be->m_videoSize.y); | |
0c5c0375 | 272 | |
c5191fbd VZ |
273 | wxLogDebug(wxT("Native video size: [%i,%i]"), be->m_videoSize.x, be->m_videoSize.y); |
274 | ||
1e22656e RN |
275 | const GValue *par; |
276 | par = gst_structure_get_value (s, "pixel-aspect-ratio"); | |
0c5c0375 | 277 | |
1e22656e RN |
278 | if (par) |
279 | { | |
280 | int num = gst_value_get_fraction_numerator (par), | |
281 | den = gst_value_get_fraction_denominator (par); | |
282 | ||
283 | //TODO: maybe better fraction normalization... | |
284 | if (num > den) | |
285 | be->m_videoSize.x = (int) ((float) num * be->m_videoSize.x / den); | |
286 | else | |
287 | be->m_videoSize.y = (int) ((float) den * be->m_videoSize.y / num); | |
288 | } | |
289 | ||
c5191fbd VZ |
290 | wxLogDebug(wxT("Adjusted video size: [%i,%i]"), be->m_videoSize.x, be->m_videoSize.y); |
291 | ||
7ec69821 | 292 | be->PostRecalcSize(); |
1e22656e RN |
293 | return true; |
294 | }//end if caps | |
7ec69821 | 295 | |
1e22656e RN |
296 | return false; |
297 | } | |
298 | ||
dae87f93 RN |
299 | //--------------------------------------------------------------------------- |
300 | // wxGStreamerMediaBackend::PostRecalcSize | |
301 | // | |
302 | // Forces parent to recalc its layout if it has sizers to update | |
303 | // to the new video size | |
304 | //--------------------------------------------------------------------------- | |
1e22656e RN |
305 | void wxGStreamerMediaBackend::PostRecalcSize() |
306 | { | |
307 | m_ctrl->InvalidateBestSize(); | |
308 | m_ctrl->GetParent()->Layout(); | |
309 | m_ctrl->GetParent()->Refresh(); | |
310 | m_ctrl->GetParent()->Update(); | |
c5191fbd | 311 | m_ctrl->SetSize(m_ctrl->GetSize()); |
0c5c0375 RN |
312 | } |
313 | ||
dae87f93 RN |
314 | //--------------------------------------------------------------------------- |
315 | // wxGStreamerMediaBackend::OnFinish | |
316 | // | |
317 | // Called by gstreamer when the media is done playing | |
318 | // | |
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 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
324 | void wxGStreamerMediaBackend::OnFinish(GstElement *play, gpointer data) |
325 | { | |
326 | wxGStreamerMediaBackend* m_parent = (wxGStreamerMediaBackend*) data; | |
327 | ||
328 | wxMediaEvent theEvent(wxEVT_MEDIA_STOP, | |
329 | m_parent->m_ctrl->GetId()); | |
330 | m_parent->m_ctrl->ProcessEvent(theEvent); | |
331 | ||
332 | if(theEvent.IsAllowed()) | |
333 | { | |
334 | bool bOk = m_parent->Stop(); | |
335 | wxASSERT(bOk); | |
336 | ||
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); | |
341 | } | |
342 | } | |
343 | ||
dae87f93 RN |
344 | //--------------------------------------------------------------------------- |
345 | // wxGStreamerMediaBackend::OnError | |
346 | // | |
347 | // Called by gstreamer when an error is encountered playing the media | |
348 | // | |
349 | // TODO: Make this better - maybe some more intelligent wxLog stuff | |
350 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
351 | void wxGStreamerMediaBackend::OnError(GstElement *play, |
352 | GstElement *src, | |
353 | GError *err, | |
354 | gchar *debug, | |
355 | gpointer data) | |
356 | { | |
a4945572 RN |
357 | wxLogSysError( |
358 | wxString::Format( | |
7ec69821 | 359 | wxT("Error in wxMediaCtrl!\nError Message:%s\nDebug:%s\n"), |
a4945572 RN |
360 | (const wxChar*)wxConvUTF8.cMB2WX(err->message), |
361 | (const wxChar*)wxConvUTF8.cMB2WX(debug) | |
362 | ) | |
363 | ); | |
0c5c0375 RN |
364 | } |
365 | ||
366 | ||
dae87f93 RN |
367 | //--------------------------------------------------------------------------- |
368 | // wxGStreamerMediaBackend::Load (File version) | |
369 | // | |
370 | // Just calls the URI version | |
371 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
372 | bool wxGStreamerMediaBackend::Load(const wxString& fileName) |
373 | { | |
7ec69821 WS |
374 | return Load( |
375 | wxURI( | |
376 | wxString( wxT("file://") ) + fileName | |
377 | ) | |
0c5c0375 RN |
378 | ); |
379 | } | |
380 | ||
dae87f93 RN |
381 | //--------------------------------------------------------------------------- |
382 | // wxGStreamerMediaBackend::OnVideoCapsReady | |
383 | // | |
384 | // Called by gstreamer when the video caps for the media is ready | |
385 | //--------------------------------------------------------------------------- | |
1e22656e RN |
386 | void wxGStreamerMediaBackend::OnVideoCapsReady(GstPad* pad, GParamSpec* pspec, gpointer data) |
387 | { | |
7ec69821 | 388 | wxGStreamerMediaBackend::TransCapsToVideoSize((wxGStreamerMediaBackend*) data, pad); |
1e22656e RN |
389 | } |
390 | ||
dae87f93 RN |
391 | //--------------------------------------------------------------------------- |
392 | // wxGStreamerMediaBackend::Load (URI version) | |
393 | // | |
394 | // 1) Stops/Cleanups the previous instance if there is any | |
a4945572 | 395 | // 2) Creates the gstreamer playbin |
b4a345a6 RN |
396 | // 3) If there is no playbin bail out |
397 | // 4) Set up the error and end-of-stream callbacks for our player | |
a4945572 | 398 | // 5) Make our video sink and make sure it supports the x overlay interface |
b4a345a6 RN |
399 | // 6) Make sure the passed URI is valid and tell playbin to load it |
400 | // 7) Use the xoverlay extension to tell gstreamer to play in our window | |
401 | // 8) Get the video size - pause required to set the stream in action | |
dae87f93 | 402 | //--------------------------------------------------------------------------- |
0c5c0375 RN |
403 | bool wxGStreamerMediaBackend::Load(const wxURI& location) |
404 | { | |
dae87f93 | 405 | //1 |
0c5c0375 | 406 | Cleanup(); |
7ec69821 | 407 | |
dae87f93 | 408 | //2 |
1e22656e | 409 | m_player = gst_element_factory_make ("playbin", "play"); |
1e22656e | 410 | |
dae87f93 | 411 | //3 |
b4a345a6 | 412 | if (!m_player) |
1e22656e | 413 | return false; |
7ec69821 | 414 | |
b4a345a6 | 415 | //4 |
a4945572 RN |
416 | g_signal_connect (m_player, "eos", G_CALLBACK (OnFinish), this); |
417 | g_signal_connect (m_player, "error", G_CALLBACK (OnError), this); | |
0c5c0375 | 418 | |
b4a345a6 | 419 | //5 |
a4945572 RN |
420 | GstElement* overlay = NULL; |
421 | GstElement* videosink; | |
422 | ||
423 | #if defined(__WXGTK__) && wxUSE_DYNLIB_CLASS | |
424 | ||
b4a345a6 | 425 | //use gnome-specific gstreamer extensions |
7ec69821 | 426 | //if synthisis (?) file not found, it |
a4945572 RN |
427 | //spits out a warning and uses ximagesink |
428 | wxDynamicLibrary gstgconf; | |
429 | if(gstgconf.Load(gstgconf.CanonicalizeName(wxT("gstgconf-0.8")))) | |
430 | { | |
431 | typedef GstElement* (*LPgst_gconf_get_default_video_sink) (void); | |
7ec69821 | 432 | LPgst_gconf_get_default_video_sink pGst_gconf_get_default_video_sink = |
a4945572 RN |
433 | (LPgst_gconf_get_default_video_sink) |
434 | gstgconf.GetSymbol(wxT("gst_gconf_get_default_video_sink")); | |
435 | ||
7ec69821 | 436 | if (pGst_gconf_get_default_video_sink) |
a4945572 RN |
437 | { |
438 | videosink = (*pGst_gconf_get_default_video_sink) (); | |
439 | wxASSERT( GST_IS_BIN(videosink) ); | |
440 | overlay = gst_bin_get_by_interface (GST_BIN (videosink), | |
441 | GST_TYPE_X_OVERLAY); | |
442 | } | |
7ec69821 | 443 | |
a4945572 RN |
444 | gstgconf.Detach(); |
445 | } | |
7ec69821 WS |
446 | |
447 | if ( ! GST_IS_X_OVERLAY(overlay) ) | |
448 | { | |
a4945572 | 449 | #endif |
7ec69821 WS |
450 | wxLogDebug(wxT("Could not load Gnome preferences, reverting to xvimagesink for video for gstreamer")); |
451 | videosink = gst_element_factory_make ("xvimagesink", "videosink"); | |
452 | if ( !GST_IS_OBJECT(videosink) ) | |
453 | videosink = gst_element_factory_make ("ximagesink", "videosink"); | |
454 | ||
455 | overlay = videosink; | |
456 | ||
457 | wxASSERT( GST_IS_X_OVERLAY(overlay) ); | |
458 | if ( ! GST_IS_X_OVERLAY(overlay) ) | |
459 | return false; | |
a4945572 | 460 | #if defined(__WXGTK__) && wxUSE_DYNLIB_CLASS |
7ec69821 | 461 | } |
a4945572 | 462 | #endif |
b4a345a6 RN |
463 | |
464 | g_object_set (G_OBJECT (m_player), | |
465 | "video-sink", videosink, | |
466 | // "audio-sink", m_audiosink, | |
7ec69821 | 467 | NULL); |
a4945572 | 468 | |
b4a345a6 | 469 | //6 |
1e22656e RN |
470 | wxString locstring = location.BuildUnescapedURI(); |
471 | wxASSERT(gst_uri_protocol_is_valid("file")); | |
472 | wxASSERT(gst_uri_is_valid(locstring.mb_str())); | |
0c5c0375 | 473 | |
1e22656e | 474 | g_object_set (G_OBJECT (m_player), "uri", (const char*)locstring.mb_str(), NULL); |
7ec69821 WS |
475 | |
476 | //7 | |
1e22656e RN |
477 | #ifdef __WXGTK__ |
478 | if(!GTK_WIDGET_REALIZED(m_ctrl->m_wxwindow)) | |
479 | { | |
480 | //Not realized yet - set to connect at realization time | |
7ec69821 | 481 | gtk_signal_connect( GTK_OBJECT(m_ctrl->m_wxwindow), |
1e22656e RN |
482 | "realize", |
483 | GTK_SIGNAL_FUNC(wxGStreamerMediaBackend::OnGTKRealize), | |
484 | (gpointer) this ); | |
7ec69821 | 485 | } |
1e22656e RN |
486 | else |
487 | { | |
488 | wxYield(); //see realize callback... | |
489 | GdkWindow *window = GTK_PIZZA(m_ctrl->m_wxwindow)->bin_window; | |
490 | wxASSERT(window); | |
491 | #endif | |
0c5c0375 | 492 | |
7ec69821 | 493 | |
a4945572 | 494 | gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(overlay), |
1e22656e RN |
495 | #ifdef __WXGTK__ |
496 | GDK_WINDOW_XWINDOW( window ) | |
497 | #else | |
498 | ctrl->GetHandle() | |
499 | #endif | |
500 | ); | |
501 | ||
7ec69821 | 502 | #ifdef __WXGTK__ |
1e22656e | 503 | } //end else block |
7ec69821 WS |
504 | #endif |
505 | ||
b4a345a6 | 506 | //8 |
7ec69821 WS |
507 | int nResult = gst_element_set_state (m_player, GST_STATE_PAUSED); |
508 | if(nResult != GST_STATE_SUCCESS) | |
509 | { | |
510 | wxLogDebug(wxT("Could not set initial state to paused!")); | |
511 | return false; | |
512 | } | |
a4945572 | 513 | |
1e22656e RN |
514 | const GList *list = NULL; |
515 | g_object_get (G_OBJECT (m_player), "stream-info", &list, NULL); | |
516 | ||
c5191fbd VZ |
517 | bool bVideoFound = false; |
518 | ||
1e22656e RN |
519 | for ( ; list != NULL; list = list->next) |
520 | { | |
521 | GObject *info = (GObject *) list->data; | |
522 | gint type; | |
523 | GParamSpec *pspec; | |
524 | GEnumValue *val; | |
525 | GstPad *pad = NULL; | |
526 | ||
527 | g_object_get (info, "type", &type, NULL); | |
528 | pspec = g_object_class_find_property ( | |
529 | G_OBJECT_GET_CLASS (info), "type"); | |
530 | val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type); | |
531 | ||
532 | if (strstr (val->value_name, "VIDEO")) | |
533 | { | |
534 | //Newer gstreamer 0.8+ is SUPPOSED to have "object"... | |
535 | //but a lot of old plugins still use "pad" :) | |
536 | pspec = g_object_class_find_property ( | |
537 | G_OBJECT_GET_CLASS (info), "object"); | |
7ec69821 | 538 | |
1e22656e RN |
539 | if (!pspec) |
540 | g_object_get (info, "pad", &pad, NULL); | |
541 | else | |
542 | g_object_get (info, "object", &pad, NULL); | |
7ec69821 | 543 | |
1e22656e RN |
544 | pad = (GstPad *) GST_PAD_REALIZE (pad); |
545 | wxASSERT(pad); | |
546 | ||
547 | if(!wxGStreamerMediaBackend::TransCapsToVideoSize(this, pad)); | |
548 | { | |
549 | //wait for those caps to get ready | |
550 | g_signal_connect( | |
7ec69821 WS |
551 | pad, |
552 | "notify::caps", | |
1e22656e RN |
553 | G_CALLBACK(wxGStreamerMediaBackend::OnVideoCapsReady), |
554 | this); | |
555 | } | |
c5191fbd VZ |
556 | |
557 | bVideoFound = true; | |
558 | break; | |
1e22656e RN |
559 | }//end if video |
560 | else | |
561 | { | |
562 | m_videoSize = wxSize(0,0); | |
563 | PostRecalcSize(); | |
564 | } | |
7ec69821 | 565 | }//end searching through info list |
1e22656e | 566 | |
c5191fbd VZ |
567 | if(!bVideoFound) |
568 | { | |
569 | wxLogDebug(wxT("No video found for gstreamer stream")); | |
570 | } | |
1e22656e | 571 | m_nPausedPos = 0; |
c5191fbd VZ |
572 | |
573 | //send loaded event | |
574 | wxMediaEvent theEvent(wxEVT_MEDIA_LOADED, | |
575 | m_ctrl->GetId()); | |
576 | m_ctrl->AddPendingEvent(theEvent); | |
7ec69821 | 577 | |
1e22656e | 578 | return true; |
0c5c0375 RN |
579 | } |
580 | ||
dae87f93 RN |
581 | //--------------------------------------------------------------------------- |
582 | // wxGStreamerMediaBackend::Play | |
583 | // | |
584 | // Sets the stream to a playing state | |
585 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
586 | bool wxGStreamerMediaBackend::Play() |
587 | { | |
588 | if (gst_element_set_state (m_player, GST_STATE_PLAYING) | |
589 | != GST_STATE_SUCCESS) | |
590 | return false; | |
591 | return true; | |
592 | } | |
593 | ||
dae87f93 RN |
594 | //--------------------------------------------------------------------------- |
595 | // wxGStreamerMediaBackend::Pause | |
596 | // | |
597 | // Marks where we paused and pauses the stream | |
598 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
599 | bool wxGStreamerMediaBackend::Pause() |
600 | { | |
1e22656e | 601 | m_nPausedPos = GetPosition(); |
0c5c0375 RN |
602 | if (gst_element_set_state (m_player, GST_STATE_PAUSED) |
603 | != GST_STATE_SUCCESS) | |
604 | return false; | |
605 | return true; | |
606 | } | |
607 | ||
dae87f93 RN |
608 | //--------------------------------------------------------------------------- |
609 | // wxGStreamerMediaBackend::Stop | |
610 | // | |
611 | // Pauses the stream and sets the position to 0 | |
612 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
613 | bool wxGStreamerMediaBackend::Stop() |
614 | { | |
615 | if (gst_element_set_state (m_player, | |
1e22656e | 616 | GST_STATE_PAUSED) != GST_STATE_SUCCESS) |
0c5c0375 | 617 | return false; |
1e22656e | 618 | return wxGStreamerMediaBackend::SetPosition(0); |
0c5c0375 RN |
619 | } |
620 | ||
dae87f93 RN |
621 | //--------------------------------------------------------------------------- |
622 | // wxGStreamerMediaBackend::GetState | |
623 | // | |
624 | // Gets the state of the stream | |
625 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
626 | wxMediaState wxGStreamerMediaBackend::GetState() |
627 | { | |
628 | switch(GST_STATE(m_player)) | |
629 | { | |
630 | case GST_STATE_PLAYING: | |
631 | return wxMEDIASTATE_PLAYING; | |
632 | case GST_STATE_PAUSED: | |
1e22656e RN |
633 | if (m_nPausedPos == 0) |
634 | return wxMEDIASTATE_STOPPED; | |
635 | else | |
636 | return wxMEDIASTATE_PAUSED; | |
0c5c0375 RN |
637 | default://case GST_STATE_READY: |
638 | return wxMEDIASTATE_STOPPED; | |
639 | } | |
640 | } | |
641 | ||
dae87f93 RN |
642 | //--------------------------------------------------------------------------- |
643 | // wxGStreamerMediaBackend::GetPosition | |
644 | // | |
7ec69821 | 645 | // If paused, returns our marked position - otherwise it queries the |
dae87f93 RN |
646 | // GStreamer playbin for the position and returns that |
647 | // | |
648 | //TODO: | |
649 | //TODO: In lue of the last big TODO, when you pause and seek gstreamer | |
7ec69821 | 650 | //TODO: doesn't update the position sometimes, so we need to keep track of whether |
dae87f93 RN |
651 | //TODO: we have paused or not and keep track of the time after the pause |
652 | //TODO: and whenever the user seeks while paused | |
653 | //TODO: | |
654 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
655 | wxLongLong wxGStreamerMediaBackend::GetPosition() |
656 | { | |
1e22656e RN |
657 | if(GetState() != wxMEDIASTATE_PLAYING) |
658 | return m_nPausedPos; | |
659 | else | |
660 | { | |
661 | gint64 pos; | |
662 | GstFormat fmtTime = GST_FORMAT_TIME; | |
7ec69821 | 663 | |
1e22656e RN |
664 | if (!gst_element_query (m_player, GST_QUERY_POSITION, &fmtTime, &pos)) |
665 | return 0; | |
666 | return pos / GST_MSECOND ; | |
667 | } | |
0c5c0375 RN |
668 | } |
669 | ||
dae87f93 RN |
670 | //--------------------------------------------------------------------------- |
671 | // wxGStreamerMediaBackend::SetPosition | |
672 | // | |
673 | // Sets the position of the stream | |
674 | // Note that GST_MSECOND is 1000000 (GStreamer uses nanoseconds - so | |
675 | // there is 1000000 nanoseconds in a millisecond) | |
676 | // | |
7ec69821 | 677 | // If paused marks where we seeked to |
dae87f93 RN |
678 | //--------------------------------------------------------------------------- |
679 | bool wxGStreamerMediaBackend::SetPosition(wxLongLong where) | |
680 | { | |
681 | if( gst_element_seek (m_player, (GstSeekType) (GST_SEEK_METHOD_SET | | |
682 | GST_FORMAT_TIME | GST_SEEK_FLAG_FLUSH), | |
683 | where.GetValue() * GST_MSECOND ) ) | |
684 | { | |
685 | if (GetState() != wxMEDIASTATE_PLAYING) | |
686 | m_nPausedPos = where; | |
7ec69821 | 687 | |
dae87f93 | 688 | return true; |
7ec69821 WS |
689 | } |
690 | ||
dae87f93 RN |
691 | return false; |
692 | } | |
693 | ||
694 | //--------------------------------------------------------------------------- | |
695 | // wxGStreamerMediaBackend::GetDuration | |
696 | // | |
697 | // Obtains the total time of our stream | |
698 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
699 | wxLongLong wxGStreamerMediaBackend::GetDuration() |
700 | { | |
701 | gint64 length; | |
702 | GstFormat fmtTime = GST_FORMAT_TIME; | |
703 | ||
704 | if(!gst_element_query(m_player, GST_QUERY_TOTAL, &fmtTime, &length)) | |
705 | return 0; | |
706 | return length / GST_MSECOND ; | |
707 | } | |
708 | ||
dae87f93 RN |
709 | //--------------------------------------------------------------------------- |
710 | // wxGStreamerMediaBackend::Move | |
711 | // | |
712 | // Called when the window is moved - GStreamer takes care of this | |
713 | // for us so nothing is needed | |
714 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
715 | void wxGStreamerMediaBackend::Move(int x, int y, int w, int h) |
716 | { | |
717 | } | |
718 | ||
dae87f93 RN |
719 | //--------------------------------------------------------------------------- |
720 | // wxGStreamerMediaBackend::GetVideoSize | |
721 | // | |
722 | // Returns our cached video size from Load/OnVideoCapsReady | |
723 | //--------------------------------------------------------------------------- | |
0c5c0375 | 724 | wxSize wxGStreamerMediaBackend::GetVideoSize() const |
7ec69821 | 725 | { |
1e22656e | 726 | return m_videoSize; |
0c5c0375 RN |
727 | } |
728 | ||
dae87f93 RN |
729 | //--------------------------------------------------------------------------- |
730 | // wxGStreamerMediaBackend::GetPlaybackRate | |
731 | // wxGStreamerMediaBackend::SetPlaybackRate | |
1e22656e | 732 | // |
dae87f93 | 733 | // Obtains/Sets the playback rate of the stream |
1e22656e | 734 | // |
dae87f93 RN |
735 | //TODO: PlaybackRate not currently supported via playbin directly - |
736 | //TODO: Ronald S. Bultje noted on gstreamer-devel: | |
737 | //TODO: | |
738 | //TODO: Like "play at twice normal speed"? Or "play at 25 fps and 44,1 kHz"? As | |
739 | //TODO: for the first, yes, we have elements for that, btu they"re not part of | |
740 | //TODO: playbin. You can create a bin (with a ghost pad) containing the actual | |
741 | //TODO: video/audiosink and the speed-changing element for this, and set that | |
742 | //TODO: element as video-sink or audio-sink property in playbin. The | |
743 | //TODO: audio-element is called "speed", the video-element is called "videodrop" | |
744 | //TODO: (although that appears to be deprecated in favour of "videorate", which | |
745 | //TODO: again cannot do this, so this may not work at all in the end). For | |
746 | //TODO: forcing frame/samplerates, see audioscale and videorate. Audioscale is | |
747 | //TODO: part of playbin. | |
748 | //--------------------------------------------------------------------------- | |
0c5c0375 RN |
749 | double wxGStreamerMediaBackend::GetPlaybackRate() |
750 | { | |
1e22656e RN |
751 | //not currently supported via playbin |
752 | return 1.0; | |
0c5c0375 RN |
753 | } |
754 | ||
755 | bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate) | |
756 | { | |
1e22656e RN |
757 | //not currently supported via playbin |
758 | return false; | |
0c5c0375 RN |
759 | } |
760 | ||
761 | #endif //wxUSE_GSTREAMER | |
762 | ||
ddc90a8d | 763 | //in source file that contains stuff you don't directly use |
7ec69821 | 764 | #include "wx/html/forcelnk.h" |
412e0d47 | 765 | FORCE_LINK_ME(basewxmediabackends) |
ddc90a8d RN |
766 | |
767 | #endif //wxUSE_MEDIACTRL |