+ g_object_get (info, "type", &type, NULL);
+ pspec = g_object_class_find_property (
+ G_OBJECT_GET_CLASS (info), "type");
+ val = g_enum_get_value (G_PARAM_SPEC_ENUM (pspec)->enum_class, type);
+
+ if (!strncasecmp(val->value_name, "video", 5) ||
+ !strncmp(val->value_name, "GST_STREAM_TYPE_VIDEO", 21))
+ {
+ // Newer gstreamer 0.8+ plugins are SUPPOSED to have "object"...
+ // but a lot of old plugins still use "pad" :)
+ pspec = g_object_class_find_property (
+ G_OBJECT_GET_CLASS (info), "object");
+
+ if (!pspec)
+ g_object_get (info, "pad", &pad, NULL);
+ else
+ g_object_get (info, "object", &pad, NULL);
+
+#if GST_VERSION_MAJOR == 0 && GST_VERSION_MINOR <= 8
+ // Killed in 0.9, presumely because events and such
+ // should be pushed on pads regardless of whether they
+ // are currently linked
+ pad = (GstPad *) GST_PAD_REALIZE (pad);
+ wxASSERT(pad);
+#endif
+
+ if(!QueryVideoSizeFromPad(pad))
+ {
+ // wait for those caps to get ready
+ g_signal_connect(
+ pad,
+ "notify::caps",
+ G_CALLBACK(gst_notify_caps_callback),
+ this);
+ }
+ break;
+ }// end if video
+ }// end searching through info list
+
+ // no video (or extremely delayed stream-info)
+ if(list == NULL)
+ {
+ m_videoSize = wxSize(0,0);
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// wxGStreamerMediaBackend::QueryVideoSizeFromPad
+//
+// Gets the size of our video (in wxSize) from a GstPad
+//-----------------------------------------------------------------------------
+bool wxGStreamerMediaBackend::QueryVideoSizeFromPad(GstPad* pad)
+{
+ const GstCaps* caps = GST_PAD_CAPS(pad);
+ if ( caps )
+ {
+ const GstStructure *s = gst_caps_get_structure (caps, 0);
+ wxASSERT(s);
+
+ gst_structure_get_int (s, "width", &m_videoSize.x);
+ gst_structure_get_int (s, "height", &m_videoSize.y);
+
+ const GValue *par;
+ par = gst_structure_get_value (s, "pixel-aspect-ratio");
+
+ if (par)
+ {
+ wxLogTrace(wxTRACE_GStreamer,
+ wxT("pixel-aspect-ratio found in pad"));
+ int num = par->data[0].v_int,
+ den = par->data[1].v_int;
+
+ // TODO: maybe better fraction normalization...
+ if (num > den)
+ m_videoSize.x = (int) ((float) num * m_videoSize.x / den);
+ else
+ m_videoSize.y = (int) ((float) den * m_videoSize.y / num);
+ }
+
+ wxLogTrace(wxTRACE_GStreamer, wxT("Adjusted video size: [%i,%i]"),
+ m_videoSize.x, m_videoSize.y);
+ return true;
+ } // end if caps
+
+ return false; // not ready/massive failure
+}
+
+//-----------------------------------------------------------------------------
+// wxGStreamerMediaBackend::SetupXOverlay
+//
+// Attempts to set the XWindow id of our GstXOverlay to tell it which
+// window to play video in.
+//-----------------------------------------------------------------------------
+void wxGStreamerMediaBackend::SetupXOverlay()
+{
+ // Use the xoverlay extension to tell gstreamer to play in our window
+#ifdef __WXGTK__
+ if(!GTK_WIDGET_REALIZED(m_ctrl->m_wxwindow))
+ {
+ // Not realized yet - set to connect at realization time
+ g_signal_connect (m_ctrl->m_wxwindow,
+ "realize",
+ G_CALLBACK (gtk_window_realize_callback),
+ this);
+ }
+ else
+ {
+ wxYield(); // see realize callback...
+ GdkWindow *window = GTK_PIZZA(m_ctrl->m_wxwindow)->bin_window;
+ wxASSERT(window);
+#endif
+
+ gst_x_overlay_set_xwindow_id( GST_X_OVERLAY(m_xoverlay),
+#ifdef __WXGTK__
+ GDK_WINDOW_XWINDOW( window )
+#else
+ ctrl->GetHandle()
+#endif
+ );
+
+#ifdef __WXGTK__
+ g_signal_connect (m_ctrl->m_wxwindow,
+ // m_ctrl->m_wxwindow/*m_ctrl->m_widget*/,
+ "expose_event",
+ G_CALLBACK(gtk_window_expose_callback), this);
+ } // end if GtkPizza realized
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// wxGStreamerMediaBackend::SyncStateChange
+//
+// This function is rather complex - basically the idea is that we
+// poll the GstBus of m_playbin until it has reached desiredstate, an error
+// is reached, or there are no more messages left in the GstBus queue.
+//
+// Returns true if there are no messages left in the queue or
+// the current state reaches the disired state.
+//
+// PRECONDITION: Assumes m_asynclock is Lock()ed
+//-----------------------------------------------------------------------------
+#if GST_VERSION_MAJOR > 0 || GST_VERSION_MINOR >= 10
+bool wxGStreamerMediaBackend::SyncStateChange(GstElement* element,
+ GstElementState desiredstate,
+ gint64 llTimeout)
+{
+ GstBus* bus = gst_element_get_bus(element);
+ GstMessage* message;
+ bool bBreak = false,
+ bSuccess = false;
+ gint64 llTimeWaited = 0;
+
+ do
+ {
+#if 1
+ // NB: The GStreamer gst_bus_poll is unfortunately broken and
+ // throws silly critical internal errors (for instance
+ // "message != NULL" when the whole point of it is to
+ // poll for the message in the first place!) so we implement
+ // our own "waiting mechinism"
+ if(gst_bus_have_pending(bus) == FALSE)
+ {
+ if(llTimeWaited >= llTimeout)
+ return true; // Reached timeout... assume success
+ llTimeWaited += 10*GST_MSECOND;
+ wxMilliSleep(10);
+ continue;
+ }
+
+ message = gst_bus_pop(bus);
+#else
+ message = gst_bus_poll(bus, (GstMessageType)
+ (GST_MESSAGE_STATE_CHANGED |
+ GST_MESSAGE_ERROR |
+ GST_MESSAGE_EOS), llTimeout);
+ if(!message)
+ return true;
+#endif
+ if(((GstElement*)GST_MESSAGE_SRC(message)) == element)
+ {
+ switch(GST_MESSAGE_TYPE(message))
+ {
+ case GST_MESSAGE_STATE_CHANGED:
+ {
+ GstState oldstate, newstate, pendingstate;
+ gst_message_parse_state_changed(message, &oldstate,
+ &newstate, &pendingstate);
+ if(newstate == desiredstate)
+ {
+ bSuccess = bBreak = true;
+ }
+ break;
+ }
+ case GST_MESSAGE_ERROR:
+ {
+ GError* error;
+ gchar* debug;
+ gst_message_parse_error(message, &error, &debug);
+ gst_error_callback(NULL, NULL, error, debug, this);
+ bBreak = true;
+ break;
+ }
+ case GST_MESSAGE_EOS:
+ wxLogSysError(wxT("Reached end of stream prematurely"));
+ bBreak = true;
+ break;
+ default:
+ break; // not handled
+ }
+ }
+
+ gst_message_unref(message);
+ }while(!bBreak);
+
+ return bSuccess;
+}
+#else // 0.8 implementation
+bool wxGStreamerMediaBackend::SyncStateChange(GstElement* element,
+ GstElementState desiredstate,
+ gint64 llTimeout)
+{
+ gint64 llTimeWaited = 0;
+ while(GST_STATE(element) != desiredstate)
+ {
+ if(llTimeWaited >= llTimeout)
+ break;
+ llTimeWaited += 10*GST_MSECOND;
+ wxMilliSleep(10);
+ }
+
+ return llTimeWaited != llTimeout;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// wxGStreamerMediaBackend::TryAudioSink
+// wxGStreamerMediaBackend::TryVideoSink
+//
+// Uses various means to determine whether a passed in video/audio sink
+// if suitable for us - if it is not we return false and unref the
+// inappropriate sink.
+//-----------------------------------------------------------------------------
+bool wxGStreamerMediaBackend::TryAudioSink(GstElement* audiosink)
+{
+ if( !GST_IS_ELEMENT(audiosink) )
+ {
+ if(G_IS_OBJECT(audiosink))
+ g_object_unref(audiosink);
+ return false;
+ }
+
+ return true;
+}
+
+bool wxGStreamerMediaBackend::TryVideoSink(GstElement* videosink)
+{
+ // Check if the video sink either is an xoverlay or might contain one...
+ if( !GST_IS_BIN(videosink) && !GST_IS_X_OVERLAY(videosink) )
+ {
+ if(G_IS_OBJECT(videosink))
+ g_object_unref(videosink);
+ return false;
+ }
+
+ // Make our video sink and make sure it supports the x overlay interface
+ // the x overlay enables us to put the video in our control window
+ // (i.e. we NEED it!) - also connect to the natural video size change event
+ if( GST_IS_BIN(videosink) )
+ m_xoverlay = (GstXOverlay*)
+ gst_bin_get_by_interface (GST_BIN (videosink),
+ GST_TYPE_X_OVERLAY);
+ else
+ m_xoverlay = (GstXOverlay*) videosink;
+
+ if ( !GST_IS_X_OVERLAY(m_xoverlay) )
+ {
+ g_object_unref(videosink);
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// wxGStreamerMediaEventHandler::OnMediaFinish
+//
+// Called when the media is about to stop
+//-----------------------------------------------------------------------------
+void wxGStreamerMediaEventHandler::OnMediaFinish(wxMediaEvent& event)
+{
+ // (RN - I have no idea why I thought this was good behaviour....
+ // maybe it made sense for streaming/nonseeking data but
+ // generally it seems like a really bad idea) -
+ if(m_be->SendStopEvent())
+ {
+ // Stop the media (we need to set it back to paused
+ // so that people can get the duration et al.
+ // and send the finish event (luckily we can "Sync" it out... LOL!)
+ // (We don't check return values here because we can't really do
+ // anything...)
+ wxMutexLocker lock(m_be->m_asynclock);
+
+ // Set element to ready+sync it
+ gst_element_set_state (m_be->m_playbin, GST_STATE_READY);
+ m_be->SyncStateChange(m_be->m_playbin, GST_STATE_READY);
+
+ // Now set it to paused + update pause pos to 0 and
+ // Sync that as well (note that we don't call Stop() here
+ // due to mutex issues)
+ gst_element_set_state (m_be->m_playbin, GST_STATE_PAUSED);
+ m_be->SyncStateChange(m_be->m_playbin, GST_STATE_PAUSED);
+ m_be->m_llPausedPos = 0;
+
+ // Finally, queue the finish event
+ m_be->QueueFinishEvent();
+ }
+}
+
+//-----------------------------------------------------------------------------
+//
+// Public methods
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// wxGStreamerMediaBackend Constructor
+//
+// Sets m_playbin to NULL signifying we havn't loaded anything yet
+//-----------------------------------------------------------------------------