| 1 | ///////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: src/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 | // 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 | |
| 37 | //=========================================================================== |
| 38 | // BACKEND DECLARATIONS |
| 39 | //=========================================================================== |
| 40 | |
| 41 | //--------------------------------------------------------------------------- |
| 42 | // |
| 43 | // wxGStreamerMediaBackend |
| 44 | // |
| 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 | // |
| 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 |
| 67 | # include "wx/gtk/win_gtk.h" |
| 68 | # include <gtk/gtksignal.h> |
| 69 | # if wxUSE_DYNLIB_CLASS |
| 70 | # include "wx/dynlib.h" |
| 71 | # endif |
| 72 | //# include <gst/gconf/gconf.h> //gstreamer gnome interface - needs deps |
| 73 | #endif |
| 74 | |
| 75 | |
| 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); |
| 116 | static void OnVideoCapsReady(GstPad* pad, GParamSpec* pspec, gpointer data); |
| 117 | |
| 118 | static bool TransCapsToVideoSize(wxGStreamerMediaBackend* be, GstPad* caps); |
| 119 | void PostRecalcSize(); |
| 120 | |
| 121 | #ifdef __WXGTK__ |
| 122 | static gint OnGTKRealize(GtkWidget* theWidget, wxGStreamerMediaBackend* be); |
| 123 | #endif |
| 124 | |
| 125 | GstElement* m_player; //GStreamer media element |
| 126 | |
| 127 | wxSize m_videoSize; |
| 128 | wxControl* m_ctrl; |
| 129 | |
| 130 | wxLongLong m_nPausedPos; |
| 131 | |
| 132 | DECLARE_DYNAMIC_CLASS(wxGStreamerMediaBackend); |
| 133 | }; |
| 134 | |
| 135 | |
| 136 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 137 | // |
| 138 | // wxGStreamerMediaBackend |
| 139 | // |
| 140 | //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 141 | |
| 142 | IMPLEMENT_DYNAMIC_CLASS(wxGStreamerMediaBackend, wxMediaBackend) |
| 143 | |
| 144 | //--------------------------------------------------------------------------- |
| 145 | // wxGStreamerMediaBackend Constructor |
| 146 | // |
| 147 | // Sets m_player to NULL signifying we havn't loaded anything yet |
| 148 | //--------------------------------------------------------------------------- |
| 149 | wxGStreamerMediaBackend::wxGStreamerMediaBackend() : m_player(NULL), m_videoSize(0,0) |
| 150 | { |
| 151 | } |
| 152 | |
| 153 | //--------------------------------------------------------------------------- |
| 154 | // wxGStreamerMediaBackend Destructor |
| 155 | // |
| 156 | // Stops/cleans up memory |
| 157 | //--------------------------------------------------------------------------- |
| 158 | wxGStreamerMediaBackend::~wxGStreamerMediaBackend() |
| 159 | { |
| 160 | Cleanup(); |
| 161 | } |
| 162 | |
| 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 | //--------------------------------------------------------------------------- |
| 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 | |
| 190 | gint wxGStreamerMediaBackend::OnGTKRealize(GtkWidget* theWidget, |
| 191 | wxGStreamerMediaBackend* be) |
| 192 | { |
| 193 | DEBUG_MAIN_THREAD |
| 194 | |
| 195 | if (g_isIdle) |
| 196 | wxapp_install_idle_handler(); |
| 197 | |
| 198 | wxYield(); //FIXME: X Server gets an error if I don't do this or a messagebox beforehand?!?!?? |
| 199 | |
| 200 | GdkWindow *window = GTK_PIZZA(theWidget)->bin_window; |
| 201 | wxASSERT(window); |
| 202 | |
| 203 | GstElement* videosink; |
| 204 | g_object_get (G_OBJECT (be->m_player), "video-sink", &videosink, NULL); |
| 205 | |
| 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 | ); |
| 211 | |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | |
| 216 | #endif |
| 217 | |
| 218 | //--------------------------------------------------------------------------- |
| 219 | // wxGStreamerMediaBackend::Cleanup |
| 220 | // |
| 221 | // Frees the gstreamer interfaces if there were any created |
| 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 | } |
| 231 | |
| 232 | //--------------------------------------------------------------------------- |
| 233 | // wxGStreamerMediaBackend::CreateControl |
| 234 | // |
| 235 | // Initializes GStreamer and creates the wx side of our media control |
| 236 | //--------------------------------------------------------------------------- |
| 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); |
| 247 | |
| 248 | m_ctrl = ctrl; |
| 249 | |
| 250 | return m_ctrl->wxControl::Create(parent, id, pos, size, |
| 251 | style, //remove borders??? |
| 252 | validator, name); |
| 253 | } |
| 254 | |
| 255 | //--------------------------------------------------------------------------- |
| 256 | // wxGStreamerMediaBackend::TransCapsToVideoSize |
| 257 | // |
| 258 | // Gets the size of our video (in wxSize) from a GstPad |
| 259 | //--------------------------------------------------------------------------- |
| 260 | bool wxGStreamerMediaBackend::TransCapsToVideoSize(wxGStreamerMediaBackend* be, GstPad* pad) |
| 261 | { |
| 262 | const GstCaps* caps = GST_PAD_CAPS (pad); |
| 263 | if(caps) |
| 264 | { |
| 265 | |
| 266 | const GstStructure *s; |
| 267 | s = gst_caps_get_structure (caps, 0); |
| 268 | wxASSERT(s); |
| 269 | |
| 270 | gst_structure_get_int (s, "width", &be->m_videoSize.x); |
| 271 | gst_structure_get_int (s, "height", &be->m_videoSize.y); |
| 272 | |
| 273 | wxLogDebug(wxT("Native video size: [%i,%i]"), be->m_videoSize.x, be->m_videoSize.y); |
| 274 | |
| 275 | const GValue *par; |
| 276 | par = gst_structure_get_value (s, "pixel-aspect-ratio"); |
| 277 | |
| 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 | |
| 290 | wxLogDebug(wxT("Adjusted video size: [%i,%i]"), be->m_videoSize.x, be->m_videoSize.y); |
| 291 | |
| 292 | be->PostRecalcSize(); |
| 293 | return true; |
| 294 | }//end if caps |
| 295 | |
| 296 | return false; |
| 297 | } |
| 298 | |
| 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 | //--------------------------------------------------------------------------- |
| 305 | void wxGStreamerMediaBackend::PostRecalcSize() |
| 306 | { |
| 307 | m_ctrl->InvalidateBestSize(); |
| 308 | m_ctrl->GetParent()->Layout(); |
| 309 | m_ctrl->GetParent()->Refresh(); |
| 310 | m_ctrl->GetParent()->Update(); |
| 311 | m_ctrl->SetSize(m_ctrl->GetSize()); |
| 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 | wxLogSysError( |
| 358 | wxString::Format( |
| 359 | wxT("Error in wxMediaCtrl!\nError Message:%s\nDebug:%s\n"), |
| 360 | (const wxChar*)wxConvUTF8.cMB2WX(err->message), |
| 361 | (const wxChar*)wxConvUTF8.cMB2WX(debug) |
| 362 | ) |
| 363 | ); |
| 364 | } |
| 365 | |
| 366 | |
| 367 | //--------------------------------------------------------------------------- |
| 368 | // wxGStreamerMediaBackend::Load (File version) |
| 369 | // |
| 370 | // Just calls the URI version |
| 371 | //--------------------------------------------------------------------------- |
| 372 | bool wxGStreamerMediaBackend::Load(const wxString& fileName) |
| 373 | { |
| 374 | return Load( |
| 375 | wxURI( |
| 376 | wxString( wxT("file://") ) + fileName |
| 377 | ) |
| 378 | ); |
| 379 | } |
| 380 | |
| 381 | //--------------------------------------------------------------------------- |
| 382 | // wxGStreamerMediaBackend::OnVideoCapsReady |
| 383 | // |
| 384 | // Called by gstreamer when the video caps for the media is ready |
| 385 | //--------------------------------------------------------------------------- |
| 386 | void wxGStreamerMediaBackend::OnVideoCapsReady(GstPad* pad, GParamSpec* pspec, gpointer data) |
| 387 | { |
| 388 | wxGStreamerMediaBackend::TransCapsToVideoSize((wxGStreamerMediaBackend*) data, pad); |
| 389 | } |
| 390 | |
| 391 | //--------------------------------------------------------------------------- |
| 392 | // wxGStreamerMediaBackend::Load (URI version) |
| 393 | // |
| 394 | // 1) Stops/Cleanups the previous instance if there is any |
| 395 | // 2) Creates the gstreamer playbin |
| 396 | // 3) If there is no playbin bail out |
| 397 | // 4) Set up the error and end-of-stream callbacks for our player |
| 398 | // 5) Make our video sink and make sure it supports the x overlay interface |
| 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 |
| 402 | //--------------------------------------------------------------------------- |
| 403 | bool wxGStreamerMediaBackend::Load(const wxURI& location) |
| 404 | { |
| 405 | //1 |
| 406 | Cleanup(); |
| 407 | |
| 408 | //2 |
| 409 | m_player = gst_element_factory_make ("playbin", "play"); |
| 410 | |
| 411 | //3 |
| 412 | if (!m_player) |
| 413 | return false; |
| 414 | |
| 415 | //4 |
| 416 | g_signal_connect (m_player, "eos", G_CALLBACK (OnFinish), this); |
| 417 | g_signal_connect (m_player, "error", G_CALLBACK (OnError), this); |
| 418 | |
| 419 | //5 |
| 420 | GstElement* overlay = NULL; |
| 421 | GstElement* videosink; |
| 422 | |
| 423 | #if defined(__WXGTK__) && wxUSE_DYNLIB_CLASS |
| 424 | |
| 425 | //use gnome-specific gstreamer extensions |
| 426 | //if synthisis (?) file not found, it |
| 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); |
| 432 | LPgst_gconf_get_default_video_sink pGst_gconf_get_default_video_sink = |
| 433 | (LPgst_gconf_get_default_video_sink) |
| 434 | gstgconf.GetSymbol(wxT("gst_gconf_get_default_video_sink")); |
| 435 | |
| 436 | if (pGst_gconf_get_default_video_sink) |
| 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 | } |
| 443 | |
| 444 | gstgconf.Detach(); |
| 445 | } |
| 446 | |
| 447 | if ( ! GST_IS_X_OVERLAY(overlay) ) |
| 448 | { |
| 449 | #endif |
| 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; |
| 460 | #if defined(__WXGTK__) && wxUSE_DYNLIB_CLASS |
| 461 | } |
| 462 | #endif |
| 463 | |
| 464 | g_object_set (G_OBJECT (m_player), |
| 465 | "video-sink", videosink, |
| 466 | // "audio-sink", m_audiosink, |
| 467 | NULL); |
| 468 | |
| 469 | //6 |
| 470 | wxString locstring = location.BuildUnescapedURI(); |
| 471 | wxASSERT(gst_uri_protocol_is_valid("file")); |
| 472 | wxASSERT(gst_uri_is_valid(locstring.mb_str())); |
| 473 | |
| 474 | g_object_set (G_OBJECT (m_player), "uri", (const char*)locstring.mb_str(), NULL); |
| 475 | |
| 476 | //7 |
| 477 | #ifdef __WXGTK__ |
| 478 | if(!GTK_WIDGET_REALIZED(m_ctrl->m_wxwindow)) |
| 479 | { |
| 480 | //Not realized yet - set to connect at realization time |
| 481 | gtk_signal_connect( GTK_OBJECT(m_ctrl->m_wxwindow), |
| 482 | "realize", |
| 483 | GTK_SIGNAL_FUNC(wxGStreamerMediaBackend::OnGTKRealize), |
| 484 | (gpointer) this ); |
| 485 | } |
| 486 | else |
| 487 | { |
| 488 | wxYield(); //see realize callback... |
| 489 | GdkWindow *window = GTK_PIZZA(m_ctrl->m_wxwindow)->bin_window; |
| 490 | wxASSERT(window); |
| 491 | #endif |
| 492 | |
| 493 | |
| 494 | gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(overlay), |
| 495 | #ifdef __WXGTK__ |
| 496 | GDK_WINDOW_XWINDOW( window ) |
| 497 | #else |
| 498 | ctrl->GetHandle() |
| 499 | #endif |
| 500 | ); |
| 501 | |
| 502 | #ifdef __WXGTK__ |
| 503 | } //end else block |
| 504 | #endif |
| 505 | |
| 506 | //8 |
| 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 | } |
| 513 | |
| 514 | const GList *list = NULL; |
| 515 | g_object_get (G_OBJECT (m_player), "stream-info", &list, NULL); |
| 516 | |
| 517 | bool bVideoFound = false; |
| 518 | |
| 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"); |
| 538 | |
| 539 | if (!pspec) |
| 540 | g_object_get (info, "pad", &pad, NULL); |
| 541 | else |
| 542 | g_object_get (info, "object", &pad, NULL); |
| 543 | |
| 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( |
| 551 | pad, |
| 552 | "notify::caps", |
| 553 | G_CALLBACK(wxGStreamerMediaBackend::OnVideoCapsReady), |
| 554 | this); |
| 555 | } |
| 556 | |
| 557 | bVideoFound = true; |
| 558 | break; |
| 559 | }//end if video |
| 560 | else |
| 561 | { |
| 562 | m_videoSize = wxSize(0,0); |
| 563 | PostRecalcSize(); |
| 564 | } |
| 565 | }//end searching through info list |
| 566 | |
| 567 | if(!bVideoFound) |
| 568 | { |
| 569 | wxLogDebug(wxT("No video found for gstreamer stream")); |
| 570 | } |
| 571 | m_nPausedPos = 0; |
| 572 | |
| 573 | //send loaded event |
| 574 | wxMediaEvent theEvent(wxEVT_MEDIA_LOADED, |
| 575 | m_ctrl->GetId()); |
| 576 | m_ctrl->AddPendingEvent(theEvent); |
| 577 | |
| 578 | return true; |
| 579 | } |
| 580 | |
| 581 | //--------------------------------------------------------------------------- |
| 582 | // wxGStreamerMediaBackend::Play |
| 583 | // |
| 584 | // Sets the stream to a playing state |
| 585 | //--------------------------------------------------------------------------- |
| 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 | |
| 594 | //--------------------------------------------------------------------------- |
| 595 | // wxGStreamerMediaBackend::Pause |
| 596 | // |
| 597 | // Marks where we paused and pauses the stream |
| 598 | //--------------------------------------------------------------------------- |
| 599 | bool wxGStreamerMediaBackend::Pause() |
| 600 | { |
| 601 | m_nPausedPos = GetPosition(); |
| 602 | if (gst_element_set_state (m_player, GST_STATE_PAUSED) |
| 603 | != GST_STATE_SUCCESS) |
| 604 | return false; |
| 605 | return true; |
| 606 | } |
| 607 | |
| 608 | //--------------------------------------------------------------------------- |
| 609 | // wxGStreamerMediaBackend::Stop |
| 610 | // |
| 611 | // Pauses the stream and sets the position to 0 |
| 612 | //--------------------------------------------------------------------------- |
| 613 | bool wxGStreamerMediaBackend::Stop() |
| 614 | { |
| 615 | if (gst_element_set_state (m_player, |
| 616 | GST_STATE_PAUSED) != GST_STATE_SUCCESS) |
| 617 | return false; |
| 618 | return wxGStreamerMediaBackend::SetPosition(0); |
| 619 | } |
| 620 | |
| 621 | //--------------------------------------------------------------------------- |
| 622 | // wxGStreamerMediaBackend::GetState |
| 623 | // |
| 624 | // Gets the state of the stream |
| 625 | //--------------------------------------------------------------------------- |
| 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: |
| 633 | if (m_nPausedPos == 0) |
| 634 | return wxMEDIASTATE_STOPPED; |
| 635 | else |
| 636 | return wxMEDIASTATE_PAUSED; |
| 637 | default://case GST_STATE_READY: |
| 638 | return wxMEDIASTATE_STOPPED; |
| 639 | } |
| 640 | } |
| 641 | |
| 642 | //--------------------------------------------------------------------------- |
| 643 | // wxGStreamerMediaBackend::GetPosition |
| 644 | // |
| 645 | // If paused, returns our marked position - otherwise it queries the |
| 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 |
| 650 | //TODO: doesn't update the position sometimes, so we need to keep track of whether |
| 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 | //--------------------------------------------------------------------------- |
| 655 | wxLongLong wxGStreamerMediaBackend::GetPosition() |
| 656 | { |
| 657 | if(GetState() != wxMEDIASTATE_PLAYING) |
| 658 | return m_nPausedPos; |
| 659 | else |
| 660 | { |
| 661 | gint64 pos; |
| 662 | GstFormat fmtTime = GST_FORMAT_TIME; |
| 663 | |
| 664 | if (!gst_element_query (m_player, GST_QUERY_POSITION, &fmtTime, &pos)) |
| 665 | return 0; |
| 666 | return pos / GST_MSECOND ; |
| 667 | } |
| 668 | } |
| 669 | |
| 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 | // |
| 677 | // If paused marks where we seeked to |
| 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; |
| 687 | |
| 688 | return true; |
| 689 | } |
| 690 | |
| 691 | return false; |
| 692 | } |
| 693 | |
| 694 | //--------------------------------------------------------------------------- |
| 695 | // wxGStreamerMediaBackend::GetDuration |
| 696 | // |
| 697 | // Obtains the total time of our stream |
| 698 | //--------------------------------------------------------------------------- |
| 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 | |
| 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 | //--------------------------------------------------------------------------- |
| 715 | void wxGStreamerMediaBackend::Move(int x, int y, int w, int h) |
| 716 | { |
| 717 | } |
| 718 | |
| 719 | //--------------------------------------------------------------------------- |
| 720 | // wxGStreamerMediaBackend::GetVideoSize |
| 721 | // |
| 722 | // Returns our cached video size from Load/OnVideoCapsReady |
| 723 | //--------------------------------------------------------------------------- |
| 724 | wxSize wxGStreamerMediaBackend::GetVideoSize() const |
| 725 | { |
| 726 | return m_videoSize; |
| 727 | } |
| 728 | |
| 729 | //--------------------------------------------------------------------------- |
| 730 | // wxGStreamerMediaBackend::GetPlaybackRate |
| 731 | // wxGStreamerMediaBackend::SetPlaybackRate |
| 732 | // |
| 733 | // Obtains/Sets the playback rate of the stream |
| 734 | // |
| 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 | //--------------------------------------------------------------------------- |
| 749 | double wxGStreamerMediaBackend::GetPlaybackRate() |
| 750 | { |
| 751 | //not currently supported via playbin |
| 752 | return 1.0; |
| 753 | } |
| 754 | |
| 755 | bool wxGStreamerMediaBackend::SetPlaybackRate(double dRate) |
| 756 | { |
| 757 | //not currently supported via playbin |
| 758 | return false; |
| 759 | } |
| 760 | |
| 761 | #endif //wxUSE_GSTREAMER |
| 762 | |
| 763 | //in source file that contains stuff you don't directly use |
| 764 | #include "wx/html/forcelnk.h" |
| 765 | FORCE_LINK_ME(basewxmediabackends) |
| 766 | |
| 767 | #endif //wxUSE_MEDIACTRL |