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