]> git.saurik.com Git - wxWidgets.git/blob - src/unix/mediactrl.cpp
change configure to better detect gstreamer. Check create is valid in mediaplayer...
[wxWidgets.git] / src / unix / mediactrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: unix/mediactrl.cpp
3 // Purpose: Built-in Media Backends for Unix
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
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "mediactrl.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 //---------------------------------------------------------------------------
32 // Includes
33 //---------------------------------------------------------------------------
34 #include "wx/mediactrl.h"
35
36 //---------------------------------------------------------------------------
37 // Compilation guard
38 //---------------------------------------------------------------------------
39 #if wxUSE_MEDIACTRL
40
41 //===========================================================================
42 // BACKEND DECLARATIONS
43 //===========================================================================
44
45 //---------------------------------------------------------------------------
46 //
47 // wxGStreamerMediaBackend
48 //
49 //TODO:
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...
54 //TODO:
55 //
56 //---------------------------------------------------------------------------
57 #if wxUSE_GSTREAMER
58
59 //---------------------------------------------------------------------------
60 // GStreamer Includes
61 //---------------------------------------------------------------------------
62 #include <gst/gst.h>
63 #include <gst/xoverlay/xoverlay.h>
64
65 #include <string.h> //strstr
66
67 #include "wx/log.h"
68 #include "wx/msgdlg.h"
69
70 #ifdef __WXGTK__
71 //for <gdk/gdkx.h>/related for GDK_WINDOW_XWINDOW
72 # include "wx/gtk/win_gtk.h"
73 # include <gtk/gtksignal.h>
74 #endif
75
76
77 class WXDLLIMPEXP_MEDIA wxGStreamerMediaBackend : public wxMediaBackend
78 {
79 public:
80
81 wxGStreamerMediaBackend();
82 ~wxGStreamerMediaBackend();
83
84 virtual bool CreateControl(wxControl* ctrl, wxWindow* parent,
85 wxWindowID id,
86 const wxPoint& pos,
87 const wxSize& size,
88 long style,
89 const wxValidator& validator,
90 const wxString& name);
91
92 virtual bool Play();
93 virtual bool Pause();
94 virtual bool Stop();
95
96 virtual bool Load(const wxString& fileName);
97 virtual bool Load(const wxURI& location);
98
99 virtual wxMediaState GetState();
100
101 virtual bool SetPosition(wxLongLong where);
102 virtual wxLongLong GetPosition();
103 virtual wxLongLong GetDuration();
104
105 virtual void Move(int x, int y, int w, int h);
106 wxSize GetVideoSize() const;
107
108 virtual double GetPlaybackRate();
109 virtual bool SetPlaybackRate(double dRate);
110
111 void Cleanup();
112
113 static void OnFinish(GstElement *play, gpointer data);
114 static void OnError (GstElement *play, GstElement *src,
115 GError *err, gchar *debug,
116 gpointer data);
117 static void OnVideoCapsReady(GstPad* pad, GParamSpec* pspec, gpointer data);
118
119 static bool TransCapsToVideoSize(wxGStreamerMediaBackend* be, GstPad* caps);
120 void PostRecalcSize();
121
122 #ifdef __WXGTK__
123 static gint OnGTKRealize(GtkWidget* theWidget, wxGStreamerMediaBackend* be);
124 #endif
125
126 GstElement* m_player; //GStreamer media element
127 // GstElement* m_audiosink;
128 GstElement* m_videosink;
129
130 wxSize m_videoSize;
131 wxControl* m_ctrl;
132
133 wxLongLong m_nPausedPos;
134
135 DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend);
136 };
137
138
139 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
140 //
141 // wxGStreamerMediaBackend
142 //
143 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
144
145 IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend, wxMediaBackend);
146
147 //---------------------------------------------------------------------------
148 // wxGStreamerMediaBackend Constructor
149 //
150 // Sets m_player to NULL signifying we havn't loaded anything yet
151 //---------------------------------------------------------------------------
152 wxGStreamerMediaBackend::wxGStreamerMediaBackend() : m_player(NULL), m_videoSize(0,0)
153 {
154 }
155
156 //---------------------------------------------------------------------------
157 // wxGStreamerMediaBackend Destructor
158 //
159 // Stops/cleans up memory
160 //---------------------------------------------------------------------------
161 wxGStreamerMediaBackend::~wxGStreamerMediaBackend()
162 {
163 Cleanup();
164 }
165
166 //---------------------------------------------------------------------------
167 // wxGStreamerMediaBackend::OnGTKRealize
168 //
169 // If the window wasn't realized when Load was called, this is the
170 // callback for when it is.
171 //
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 //---------------------------------------------------------------------------
176 #ifdef __WXGTK__
177
178 #ifdef __WXDEBUG__
179
180 #if wxUSE_THREADS
181 # define DEBUG_MAIN_THREAD if (wxThread::IsMain() && g_mainThreadLocked) printf("gui reentrance");
182 #else
183 # define DEBUG_MAIN_THREAD
184 #endif
185 #else
186 #define DEBUG_MAIN_THREAD
187 #endif // Debug
188
189 extern void wxapp_install_idle_handler();
190 extern bool g_isIdle;
191 extern bool g_mainThreadLocked;
192
193 gint wxGStreamerMediaBackend::OnGTKRealize(GtkWidget* theWidget,
194 wxGStreamerMediaBackend* be)
195 {
196 DEBUG_MAIN_THREAD
197
198 if (g_isIdle)
199 wxapp_install_idle_handler();
200
201 wxYield(); //FIXME: X Server gets an error if I don't do this or a messagebox beforehand?!?!??
202
203 GdkWindow *window = GTK_PIZZA(theWidget)->bin_window;
204 wxASSERT(window);
205
206 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(be->m_videosink),
207 GDK_WINDOW_XWINDOW( window )
208 );
209
210 return 0;
211 }
212
213
214 #endif
215
216 //---------------------------------------------------------------------------
217 // wxGStreamerMediaBackend::Cleanup
218 //
219 // Frees the gstreamer interfaces if there were any created
220 //TODO: Do we need to free the video interface? I'm getting segfaults
221 //if I do...
222 //---------------------------------------------------------------------------
223 void wxGStreamerMediaBackend::Cleanup()
224 {
225 if(m_player && GST_IS_OBJECT(m_player))
226 {
227 gst_element_set_state (m_player, GST_STATE_NULL);
228 gst_object_unref (GST_OBJECT (m_player));
229
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));
234 }
235 }
236
237 //---------------------------------------------------------------------------
238 // wxGStreamerMediaBackend::CreateControl
239 //
240 // Initializes GStreamer and creates the wx side of our media control
241 //---------------------------------------------------------------------------
242 bool wxGStreamerMediaBackend::CreateControl(wxControl* ctrl, wxWindow* parent,
243 wxWindowID id,
244 const wxPoint& pos,
245 const wxSize& size,
246 long style,
247 const wxValidator& validator,
248 const wxString& name)
249 {
250 //init gstreamer
251 gst_init(NULL, NULL);
252
253 m_ctrl = ctrl;
254
255 return m_ctrl->wxControl::Create(parent, id, pos, size,
256 style, //remove borders???
257 validator, name);
258 }
259
260 //---------------------------------------------------------------------------
261 // wxGStreamerMediaBackend::TransCapsToVideoSize
262 //
263 // Gets the size of our video (in wxSize) from a GstPad
264 //---------------------------------------------------------------------------
265 bool wxGStreamerMediaBackend::TransCapsToVideoSize(wxGStreamerMediaBackend* be, GstPad* pad)
266 {
267 const GstCaps* caps = GST_PAD_CAPS (pad);
268 if(caps)
269 {
270
271 const GstStructure *s;
272 s = gst_caps_get_structure (caps, 0);
273 wxASSERT(s);
274
275 gst_structure_get_int (s, "width", &be->m_videoSize.x);
276 gst_structure_get_int (s, "height", &be->m_videoSize.y);
277
278 const GValue *par;
279 par = gst_structure_get_value (s, "pixel-aspect-ratio");
280
281 if (par)
282 {
283 int num = gst_value_get_fraction_numerator (par),
284 den = gst_value_get_fraction_denominator (par);
285
286 //TODO: maybe better fraction normalization...
287 if (num > den)
288 be->m_videoSize.x = (int) ((float) num * be->m_videoSize.x / den);
289 else
290 be->m_videoSize.y = (int) ((float) den * be->m_videoSize.y / num);
291 }
292
293 be->PostRecalcSize();
294 return true;
295 }//end if caps
296
297 return false;
298 }
299
300 //---------------------------------------------------------------------------
301 // wxGStreamerMediaBackend::PostRecalcSize
302 //
303 // Forces parent to recalc its layout if it has sizers to update
304 // to the new video size
305 //---------------------------------------------------------------------------
306 void wxGStreamerMediaBackend::PostRecalcSize()
307 {
308 m_ctrl->InvalidateBestSize();
309 m_ctrl->GetParent()->Layout();
310 m_ctrl->GetParent()->Refresh();
311 m_ctrl->GetParent()->Update();
312 }
313
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 //---------------------------------------------------------------------------
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
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 //---------------------------------------------------------------------------
351 void wxGStreamerMediaBackend::OnError(GstElement *play,
352 GstElement *src,
353 GError *err,
354 gchar *debug,
355 gpointer data)
356 {
357 wxMessageBox(wxString::Format(wxT("Error in wxMediaCtrl!\nError Message:%s"), wxString(err->message, wxConvLocal).c_str()));
358 }
359
360
361 //---------------------------------------------------------------------------
362 // wxGStreamerMediaBackend::Load (File version)
363 //
364 // Just calls the URI version
365 //---------------------------------------------------------------------------
366 bool wxGStreamerMediaBackend::Load(const wxString& fileName)
367 {
368 return Load(
369 wxURI(
370 wxString( wxT("file://") ) + fileName
371 )
372 );
373 }
374
375 //---------------------------------------------------------------------------
376 // wxGStreamerMediaBackend::OnVideoCapsReady
377 //
378 // Called by gstreamer when the video caps for the media is ready
379 //---------------------------------------------------------------------------
380 void wxGStreamerMediaBackend::OnVideoCapsReady(GstPad* pad, GParamSpec* pspec, gpointer data)
381 {
382 wxGStreamerMediaBackend::TransCapsToVideoSize((wxGStreamerMediaBackend*) data, pad);
383 }
384
385 //---------------------------------------------------------------------------
386 // wxGStreamerMediaBackend::Load (URI version)
387 //
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)
399 {
400 //1
401 Cleanup();
402
403 //2
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");
407
408 //3
409 //no playbin -- outta here :)
410 if (!m_player || !GST_IS_OBJECT(m_videosink))
411 return false;
412
413 // //have alsa?
414 // if (GST_IS_OBJECT(m_audiosink) == false)
415 // {
416 // //nope, try OSS
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"));
419 // }
420
421
422 //4
423 g_object_set (G_OBJECT (m_player),
424 "video-sink", m_videosink,
425 // "audio-sink", m_audiosink,
426 NULL);
427
428 //5
429 g_signal_connect (m_player, "eos", G_CALLBACK (OnError), this);
430 g_signal_connect (m_player, "error", G_CALLBACK (OnFinish), this);
431
432 //6
433 wxASSERT( GST_IS_X_OVERLAY(m_videosink) );
434 if ( ! GST_IS_X_OVERLAY(m_videosink) )
435 return false;
436
437 //7
438 wxString locstring = location.BuildUnescapedURI();
439 wxASSERT(gst_uri_protocol_is_valid("file"));
440 wxASSERT(gst_uri_is_valid(locstring.mb_str()));
441
442 g_object_set (G_OBJECT (m_player), "uri", (const char*)locstring.mb_str(), NULL);
443
444 //8
445 #ifdef __WXGTK__
446 if(!GTK_WIDGET_REALIZED(m_ctrl->m_wxwindow))
447 {
448 //Not realized yet - set to connect at realization time
449 gtk_signal_connect( GTK_OBJECT(m_ctrl->m_wxwindow),
450 "realize",
451 GTK_SIGNAL_FUNC(wxGStreamerMediaBackend::OnGTKRealize),
452 (gpointer) this );
453 }
454 else
455 {
456 wxYield(); //see realize callback...
457 GdkWindow *window = GTK_PIZZA(m_ctrl->m_wxwindow)->bin_window;
458 wxASSERT(window);
459 #endif
460
461
462 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_videosink),
463 #ifdef __WXGTK__
464 GDK_WINDOW_XWINDOW( window )
465 #else
466 ctrl->GetHandle()
467 #endif
468 );
469
470 #ifdef __WXGTK__
471 } //end else block
472 #endif
473
474 //9
475 wxASSERT(gst_element_set_state (m_player,
476 GST_STATE_PAUSED) == GST_STATE_SUCCESS);
477
478 const GList *list = NULL;
479 g_object_get (G_OBJECT (m_player), "stream-info", &list, NULL);
480
481 for ( ; list != NULL; list = list->next)
482 {
483 GObject *info = (GObject *) list->data;
484 gint type;
485 GParamSpec *pspec;
486 GEnumValue *val;
487 GstPad *pad = NULL;
488
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);
493
494 if (strstr (val->value_name, "VIDEO"))
495 {
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");
500
501 if (!pspec)
502 g_object_get (info, "pad", &pad, NULL);
503 else
504 g_object_get (info, "object", &pad, NULL);
505
506 pad = (GstPad *) GST_PAD_REALIZE (pad);
507 wxASSERT(pad);
508
509 if(!wxGStreamerMediaBackend::TransCapsToVideoSize(this, pad));
510 {
511 //wait for those caps to get ready
512 g_signal_connect(
513 pad,
514 "notify::caps",
515 G_CALLBACK(wxGStreamerMediaBackend::OnVideoCapsReady),
516 this);
517 }
518 }//end if video
519 else
520 {
521 m_videoSize = wxSize(0,0);
522 PostRecalcSize();
523 }
524 }//end searching through info list
525
526 m_nPausedPos = 0;
527 return true;
528 }
529
530 //---------------------------------------------------------------------------
531 // wxGStreamerMediaBackend::Play
532 //
533 // Sets the stream to a playing state
534 //---------------------------------------------------------------------------
535 bool wxGStreamerMediaBackend::Play()
536 {
537 if (gst_element_set_state (m_player, GST_STATE_PLAYING)
538 != GST_STATE_SUCCESS)
539 return false;
540 return true;
541 }
542
543 //---------------------------------------------------------------------------
544 // wxGStreamerMediaBackend::Pause
545 //
546 // Marks where we paused and pauses the stream
547 //---------------------------------------------------------------------------
548 bool wxGStreamerMediaBackend::Pause()
549 {
550 m_nPausedPos = GetPosition();
551 if (gst_element_set_state (m_player, GST_STATE_PAUSED)
552 != GST_STATE_SUCCESS)
553 return false;
554 return true;
555 }
556
557 //---------------------------------------------------------------------------
558 // wxGStreamerMediaBackend::Stop
559 //
560 // Pauses the stream and sets the position to 0
561 //---------------------------------------------------------------------------
562 bool wxGStreamerMediaBackend::Stop()
563 {
564 if (gst_element_set_state (m_player,
565 GST_STATE_PAUSED) != GST_STATE_SUCCESS)
566 return false;
567 return wxGStreamerMediaBackend::SetPosition(0);
568 }
569
570 //---------------------------------------------------------------------------
571 // wxGStreamerMediaBackend::GetState
572 //
573 // Gets the state of the stream
574 //---------------------------------------------------------------------------
575 wxMediaState wxGStreamerMediaBackend::GetState()
576 {
577 switch(GST_STATE(m_player))
578 {
579 case GST_STATE_PLAYING:
580 return wxMEDIASTATE_PLAYING;
581 case GST_STATE_PAUSED:
582 if (m_nPausedPos == 0)
583 return wxMEDIASTATE_STOPPED;
584 else
585 return wxMEDIASTATE_PAUSED;
586 default://case GST_STATE_READY:
587 return wxMEDIASTATE_STOPPED;
588 }
589 }
590
591 //---------------------------------------------------------------------------
592 // wxGStreamerMediaBackend::GetPosition
593 //
594 // If paused, returns our marked position - otherwise it queries the
595 // GStreamer playbin for the position and returns that
596 //
597 //TODO:
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
602 //TODO:
603 //---------------------------------------------------------------------------
604 wxLongLong wxGStreamerMediaBackend::GetPosition()
605 {
606 if(GetState() != wxMEDIASTATE_PLAYING)
607 return m_nPausedPos;
608 else
609 {
610 gint64 pos;
611 GstFormat fmtTime = GST_FORMAT_TIME;
612
613 if (!gst_element_query (m_player, GST_QUERY_POSITION, &fmtTime, &pos))
614 return 0;
615 return pos / GST_MSECOND ;
616 }
617 }
618
619 //---------------------------------------------------------------------------
620 // wxGStreamerMediaBackend::SetPosition
621 //
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)
625 //
626 // If paused marks where we seeked to
627 //---------------------------------------------------------------------------
628 bool wxGStreamerMediaBackend::SetPosition(wxLongLong where)
629 {
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 ) )
633 {
634 if (GetState() != wxMEDIASTATE_PLAYING)
635 m_nPausedPos = where;
636
637 return true;
638 }
639
640 return false;
641 }
642
643 //---------------------------------------------------------------------------
644 // wxGStreamerMediaBackend::GetDuration
645 //
646 // Obtains the total time of our stream
647 //---------------------------------------------------------------------------
648 wxLongLong wxGStreamerMediaBackend::GetDuration()
649 {
650 gint64 length;
651 GstFormat fmtTime = GST_FORMAT_TIME;
652
653 if(!gst_element_query(m_player, GST_QUERY_TOTAL, &fmtTime, &length))
654 return 0;
655 return length / GST_MSECOND ;
656 }
657
658 //---------------------------------------------------------------------------
659 // wxGStreamerMediaBackend::Move
660 //
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)
665 {
666 }
667
668 //---------------------------------------------------------------------------
669 // wxGStreamerMediaBackend::GetVideoSize
670 //
671 // Returns our cached video size from Load/OnVideoCapsReady
672 //---------------------------------------------------------------------------
673 wxSize wxGStreamerMediaBackend::GetVideoSize() const
674 {
675 return m_videoSize;
676 }
677
678 //---------------------------------------------------------------------------
679 // wxGStreamerMediaBackend::GetPlaybackRate
680 // wxGStreamerMediaBackend::SetPlaybackRate
681 //
682 // Obtains/Sets the playback rate of the stream
683 //
684 //TODO: PlaybackRate not currently supported via playbin directly -
685 //TODO: Ronald S. Bultje noted on gstreamer-devel:
686 //TODO:
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()
699 {
700 //not currently supported via playbin
701 return 1.0;
702 }
703
704 bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate)
705 {
706 //not currently supported via playbin
707 return false;
708 }
709
710 #endif //wxUSE_GSTREAMER
711
712 //in source file that contains stuff you don't directly use
713 #include <wx/html/forcelnk.h>
714 FORCE_LINK_ME(basewxmediabackends);
715
716 #endif //wxUSE_MEDIACTRL
717
718
719
720
721