summaryrefslogtreecommitdiff
path: root/sys/dshowvideosink
diff options
context:
space:
mode:
authorJulien Moutte <julien@fluendo.com>2010-07-23 19:41:29 +0200
committerJulien Moutte <julien@fluendo.com>2010-07-23 19:41:29 +0200
commita51d318759b07eca60811e3d47d8e81b6d12cb8c (patch)
tree2605edfc5eff8923164768a01d1c7fe00a7e98e6 /sys/dshowvideosink
parent0b3c6e4b9ed84557659171481a05168a662289a3 (diff)
downloadgstreamer-plugins-bad-a51d318759b07eca60811e3d47d8e81b6d12cb8c.tar.gz
dshowvideosink: Improvements contributed from the Moovida projet.
* Inherit from GstVideoSink * Implement GstNavigation interface * Proper COM initialization for threaded environments * Fix Window resource leak * Add EVR support for better video scaling on Windows Vista and above * Only apply PAR scaling when the keep_aspect_ratio property is set to stay consistent with the other Linux sinks * Prevent an infinite loop with the wndproc chain * Fix debugging messages to use the object instance
Diffstat (limited to 'sys/dshowvideosink')
-rw-r--r--sys/dshowvideosink/dshowvideofakesrc.cpp4
-rw-r--r--sys/dshowvideosink/dshowvideosink.cpp514
-rw-r--r--sys/dshowvideosink/dshowvideosink.h23
3 files changed, 417 insertions, 124 deletions
diff --git a/sys/dshowvideosink/dshowvideofakesrc.cpp b/sys/dshowvideosink/dshowvideofakesrc.cpp
index a2de4088b..1e332e53a 100644
--- a/sys/dshowvideosink/dshowvideofakesrc.cpp
+++ b/sys/dshowvideosink/dshowvideofakesrc.cpp
@@ -103,10 +103,6 @@ HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROP
properties.cbAlign, properties.cbBuffer,
properties.cbPrefix, properties.cBuffers);
- /* Then actually allocate the buffers */
- hres = pAlloc->Commit();
- GST_DEBUG ("Allocator commit returned %x", hres);
-
return S_OK;
}
diff --git a/sys/dshowvideosink/dshowvideosink.cpp b/sys/dshowvideosink/dshowvideosink.cpp
index 7a146649a..8e19e0091 100644
--- a/sys/dshowvideosink/dshowvideosink.cpp
+++ b/sys/dshowvideosink/dshowvideosink.cpp
@@ -1,5 +1,6 @@
/* GStreamer
* Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
+ * 2010 FLUENDO S.A. <support@fluendo.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -25,16 +26,18 @@
#include "dshowvideofakesrc.h"
#include <gst/interfaces/xoverlay.h>
+#include <gst/interfaces/navigation.h>
#include "windows.h"
-#define WM_GRAPH_NOTIFY WM_APP + 1
+#define WM_GRAPH_NOTIFY WM_APP + 1 /* Private message */
GST_DEBUG_CATEGORY (dshowvideosink_debug);
#define GST_CAT_DEFAULT dshowvideosink_debug
static GstCaps * gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype);
-static gboolean gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype);
+static gboolean gst_caps_to_directshow_media_type (GstDshowVideoSink * sink,
+ GstCaps *caps, AM_MEDIA_TYPE *mediatype);
/* TODO: Support RGB! */
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
@@ -50,8 +53,8 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
static void gst_dshowvideosink_init_interfaces (GType type);
-GST_BOILERPLATE_FULL (GstDshowVideoSink, gst_dshowvideosink, GstBaseSink,
- GST_TYPE_BASE_SINK, gst_dshowvideosink_init_interfaces);
+GST_BOILERPLATE_FULL (GstDshowVideoSink, gst_dshowvideosink, GstVideoSink,
+ GST_TYPE_VIDEO_SINK, gst_dshowvideosink_init_interfaces);
enum
{
@@ -78,11 +81,10 @@ static gboolean gst_dshowvideosink_unlock (GstBaseSink * bsink);
static gboolean gst_dshowvideosink_unlock_stop (GstBaseSink * bsink);
static gboolean gst_dshowvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps);
static GstCaps *gst_dshowvideosink_get_caps (GstBaseSink * bsink);
-static GstFlowReturn gst_dshowvideosink_render (GstBaseSink *sink, GstBuffer *buffer);
-
-/* GstXOverlay methods */
-static void gst_dshowvideosink_set_window_id (GstXOverlay * overlay, ULONG window_id);
+static GstFlowReturn gst_dshowvideosink_show_frame (GstVideoSink *sink, GstBuffer *buffer);
+/* COM initialization/uninitialization thread */
+static void gst_dshowvideosink_com_thread (GstDshowVideoSink * sink);
/* TODO: event, preroll, buffer_alloc?
* buffer_alloc won't generally be all that useful because the renderers require a
* different stride to GStreamer's implicit values.
@@ -92,7 +94,7 @@ static gboolean
gst_dshowvideosink_interface_supported (GstImplementsInterface * iface,
GType type)
{
- g_assert (type == GST_TYPE_X_OVERLAY);
+ g_assert (type == GST_TYPE_X_OVERLAY || type == GST_TYPE_NAVIGATION);
return TRUE;
}
@@ -102,11 +104,61 @@ gst_dshowvideosink_interface_init (GstImplementsInterfaceClass * klass)
klass->supported = gst_dshowvideosink_interface_supported;
}
+static void
+gst_dshowvideosink_set_window_id (GstXOverlay * overlay, ULONG window_id)
+{
+ GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (overlay);
+ HWND videowindow = (HWND)window_id;
+
+ if (videowindow == sink->window_id) {
+ GST_DEBUG_OBJECT (sink, "Window already set");
+ return;
+ }
+
+ /* TODO: What if we already have a window? What if we're already playing? */
+ sink->window_id = videowindow;
+}
+
+static void
+gst_dshowvideosink_expose (GstXOverlay * overlay)
+{
+ GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (overlay);
+
+ if (sink->renderersupport) {
+ sink->renderersupport->PaintWindow ();
+ }
+}
static void
gst_dshowvideosink_xoverlay_interface_init (GstXOverlayClass * iface)
{
iface->set_xwindow_id = gst_dshowvideosink_set_window_id;
+ iface->expose = gst_dshowvideosink_expose;
+}
+
+static void
+gst_dshowvideosink_navigation_send_event (GstNavigation * navigation,
+ GstStructure * structure)
+{
+ GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (navigation);
+ GstEvent *event = NULL;
+ GstPad *pad = NULL;
+
+ event = gst_event_new_navigation (structure);
+
+ pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (sink));
+
+ if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
+ gst_pad_send_event (pad, event);
+
+ gst_object_unref (pad);
+ }
+}
+
+static void
+gst_dshowvideosink_navigation_interface_init (GstNavigationInterface * iface)
+{
+ iface->send_event = gst_dshowvideosink_navigation_send_event;
}
static void
@@ -124,9 +176,16 @@ gst_dshowvideosink_init_interfaces (GType type)
NULL,
};
+ static const GInterfaceInfo navigation_info = {
+ (GInterfaceInitFunc) gst_dshowvideosink_navigation_interface_init,
+ NULL,
+ NULL,
+ };
+
g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
&iface_info);
g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);
+ g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
GST_DEBUG_CATEGORY_INIT (dshowvideosink_debug, "dshowvideosink", 0, \
"DirectShow video sink");
@@ -140,38 +199,39 @@ gst_dshowvideosink_base_init (gpointer klass)
gst_static_pad_template_get (&sink_template));
gst_element_class_set_details_simple (element_class, "DirectShow video sink",
- "Sink/Video",
- "Display data using a DirectShow video renderer",
- "Pioneers of the Inevitable <songbird@songbirdnest.com>");
+ "Sink/Video", "Display data using a DirectShow video renderer",
+ "Pioneers of the Inevitable <songbird@songbirdnest.com>, " \
+ "FLUENDO S.A. <support@fluendo.com>");
}
static void
gst_dshowvideosink_class_init (GstDshowVideoSinkClass * klass)
{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
- GstBaseSinkClass *gstbasesink_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
- gstbasesink_class = (GstBaseSinkClass *) klass;
-
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dshowvideosink_finalize);
- gobject_class->set_property =
- GST_DEBUG_FUNCPTR (gst_dshowvideosink_set_property);
- gobject_class->get_property =
- GST_DEBUG_FUNCPTR (gst_dshowvideosink_get_property);
-
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_dshowvideosink_change_state);
-
- gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosink_get_caps);
- gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosink_set_caps);
- gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dshowvideosink_start);
- gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dshowvideosink_stop);
- gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dshowvideosink_unlock);
- gstbasesink_class->unlock_stop =
+ GObjectClass *o_class;
+ GstElementClass *e_class;
+ GstBaseSinkClass *bs_class;
+ GstVideoSinkClass *vs_class;
+
+ o_class = (GObjectClass *) klass;
+ e_class = (GstElementClass *) klass;
+ bs_class = (GstBaseSinkClass *) klass;
+ vs_class = (GstVideoSinkClass *) klass;
+
+ o_class->finalize = gst_dshowvideosink_finalize;
+ o_class->set_property = gst_dshowvideosink_set_property;
+ o_class->get_property = gst_dshowvideosink_get_property;
+
+ e_class->change_state = GST_DEBUG_FUNCPTR (gst_dshowvideosink_change_state);
+
+ bs_class->get_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosink_get_caps);
+ bs_class->set_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosink_set_caps);
+ bs_class->start = GST_DEBUG_FUNCPTR (gst_dshowvideosink_start);
+ bs_class->stop = GST_DEBUG_FUNCPTR (gst_dshowvideosink_stop);
+ bs_class->unlock = GST_DEBUG_FUNCPTR (gst_dshowvideosink_unlock);
+ bs_class->unlock_stop =
GST_DEBUG_FUNCPTR (gst_dshowvideosink_unlock_stop);
- gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dshowvideosink_render);
+
+ vs_class->show_frame = GST_DEBUG_FUNCPTR (gst_dshowvideosink_show_frame);
/* Add properties */
g_object_class_install_property (G_OBJECT_CLASS (klass),
@@ -187,7 +247,7 @@ gst_dshowvideosink_class_init (GstDshowVideoSinkClass * klass)
g_object_class_install_property (G_OBJECT_CLASS (klass),
PROP_RENDERER, g_param_spec_string ("renderer", "Renderer",
- "Force usage of specific DirectShow renderer (VMR9 or VMR)",
+ "Force usage of specific DirectShow renderer (EVR, VMR9 or VMR7)",
NULL, (GParamFlags)G_PARAM_READWRITE));
}
@@ -204,25 +264,33 @@ gst_dshowvideosink_clear (GstDshowVideoSink *sink)
sink->window_closed = FALSE;
sink->window_id = NULL;
+ sink->is_new_window = FALSE;
sink->connected = FALSE;
+ sink->graph_running = FALSE;
}
static void
gst_dshowvideosink_init (GstDshowVideoSink * sink, GstDshowVideoSinkClass * klass)
{
- HRESULT hr;
-
gst_dshowvideosink_clear (sink);
- hr = CoInitialize (0);
- if (SUCCEEDED(hr))
- sink->comInitialized = TRUE;
+ sink->graph_lock = g_mutex_new();
+ sink->com_init_lock = g_mutex_new();
+ sink->com_deinit_lock = g_mutex_new();
+ sink->com_initialized = g_cond_new();
+ sink->com_uninitialize = g_cond_new();
+ sink->com_uninitialized = g_cond_new();
+
+ g_mutex_lock (sink->com_init_lock);
+
+ /* create the COM initialization thread */
+ g_thread_create ((GThreadFunc)gst_dshowvideosink_com_thread,
+ sink, FALSE, NULL);
- /* TODO: Copied from GstVideoSink; should we use that as base class? */
- /* 20ms is more than enough, 80-130ms is noticable */
- gst_base_sink_set_max_lateness (GST_BASE_SINK (sink), 20 * GST_MSECOND);
- gst_base_sink_set_qos_enabled (GST_BASE_SINK (sink), TRUE);
+ /* wait until the COM thread signals that COM has been initialized */
+ g_cond_wait (sink->com_initialized, sink->com_init_lock);
+ g_mutex_unlock (sink->com_init_lock);
}
static void
@@ -233,11 +301,22 @@ gst_dshowvideosink_finalize (GObject * gobject)
if (sink->preferredrenderer)
g_free (sink->preferredrenderer);
+ /* signal the COM thread that it sould uninitialize COM */
if (sink->comInitialized) {
- CoUninitialize ();
- sink->comInitialized = FALSE;
+ g_mutex_lock (sink->com_deinit_lock);
+ g_cond_signal (sink->com_uninitialize);
+ g_cond_wait (sink->com_uninitialized, sink->com_deinit_lock);
+ g_mutex_unlock (sink->com_deinit_lock);
}
+ g_mutex_free (sink->com_init_lock);
+ g_mutex_free (sink->com_deinit_lock);
+ g_cond_free (sink->com_initialized);
+ g_cond_free (sink->com_uninitialize);
+ g_cond_free (sink->com_uninitialized);
+
+ g_mutex_free (sink->graph_lock);
+
G_OBJECT_CLASS (parent_class)->finalize (gobject);
}
@@ -290,6 +369,43 @@ gst_dshowvideosink_get_property (GObject * object, guint prop_id,
}
}
+static void
+gst_dshowvideosink_com_thread (GstDshowVideoSink * sink)
+{
+ HRESULT res;
+
+ g_mutex_lock (sink->com_init_lock);
+
+ /* Initialize COM with a MTA for this process. This thread will
+ * be the first one to enter the apartement and the last one to leave
+ * it, unitializing COM properly */
+
+ res = CoInitializeEx (0, COINIT_MULTITHREADED);
+ if (res == S_FALSE)
+ GST_WARNING_OBJECT (sink, "COM has been already initialized in the same process");
+ else if (res == RPC_E_CHANGED_MODE)
+ GST_WARNING_OBJECT (sink, "The concurrency model of COM has changed.");
+ else
+ GST_INFO_OBJECT (sink, "COM intialized succesfully");
+
+ sink->comInitialized = TRUE;
+
+ /* Signal other threads waiting on this condition that COM was initialized */
+ g_cond_signal (sink->com_initialized);
+
+ g_mutex_unlock (sink->com_init_lock);
+
+ /* Wait until the unitialize condition is met to leave the COM apartement */
+ g_mutex_lock (sink->com_deinit_lock);
+ g_cond_wait (sink->com_uninitialize, sink->com_deinit_lock);
+
+ CoUninitialize ();
+ GST_INFO_OBJECT (sink, "COM unintialized succesfully");
+ sink->comInitialized = FALSE;
+ g_cond_signal (sink->com_uninitialized);
+ g_mutex_unlock (sink->com_deinit_lock);
+}
+
static GstCaps *
gst_dshowvideosink_get_caps (GstBaseSink * basesink)
{
@@ -478,6 +594,7 @@ WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
case WM_CLOSE:
sink->renderersupport->DestroyWindow ();
sink->window_closed = TRUE;
+ PostQuitMessage (WM_QUIT);
return 0;
}
@@ -528,7 +645,7 @@ gst_dshowvideosink_window_thread (GstDshowVideoSink * sink)
GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, ("Unknown media format"), (NULL));
return NULL;
}
-
+
/* rcTarget is the aspect-ratio-corrected size of the video. */
width = vi->rcTarget.right + GetSystemMetrics (SM_CXSIZEFRAME) * 2;
height = vi->rcTarget.bottom + GetSystemMetrics (SM_CYCAPTION) +
@@ -566,6 +683,8 @@ gst_dshowvideosink_window_thread (GstDshowVideoSink * sink)
return NULL;
}
+ sink->is_new_window = TRUE;
+
SetWindowLongPtr (video_window, GWLP_USERDATA, (LONG)sink);
sink->window_id = video_window;
@@ -632,22 +751,15 @@ failed:
return FALSE;
}
-static void gst_dshowvideosink_set_window_id (GstXOverlay * overlay, ULONG window_id)
+static void gst_dshowvideosink_set_window_for_renderer (GstDshowVideoSink *sink)
{
- GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (overlay);
- HWND videowindow = (HWND)window_id;
-
- if (videowindow == sink->window_id) {
- GST_DEBUG_OBJECT (sink, "Window already set");
+ WNDPROC prevWndProc = (WNDPROC)GetWindowLong (sink->window_id, GWL_WNDPROC);
+ if (prevWndProc == WndProcHook) {
+ /* The WndProc already points to our hook. Something has gone wrong
+ * somewhere else and this safety net prevents an infinite recursion */
return;
}
- /* TODO: What if we already have a window? What if we're already playing? */
- sink->window_id = videowindow;
-}
-
-static void gst_dshowvideosink_set_window_for_renderer (GstDshowVideoSink *sink)
-{
/* Application has requested a specific window ID */
sink->prevWndProc = (WNDPROC) SetWindowLong (sink->window_id, GWL_WNDPROC, (LONG)WndProcHook);
GST_DEBUG_OBJECT (sink, "Set wndproc to %p from %p", WndProcHook, sink->prevWndProc);
@@ -659,6 +771,7 @@ static void gst_dshowvideosink_set_window_for_renderer (GstDshowVideoSink *sink)
GST_WARNING_OBJECT (sink, "Failed to set HWND %x on renderer", sink->window_id);
return;
}
+ sink->is_new_window = FALSE;
/* This tells the renderer where the window is located, needed to
* start drawing in the right place. */
@@ -745,19 +858,6 @@ gst_dshowvideosink_start_graph (GstDshowVideoSink *sink)
GST_DEBUG_OBJECT (sink, "Connecting and starting DirectShow graph");
- if (!sink->connected) {
- /* This is fine; this just means we haven't connected yet.
- * That's normal for the first time this is called.
- * So, create a window (or start using an application-supplied
- * one, then connect the graph */
- gst_dshowvideosink_prepare_window (sink);
- if (!gst_dshowvideosink_connect_graph (sink)) {
- ret = GST_STATE_CHANGE_FAILURE;
- goto done;
- }
- sink->connected = TRUE;
- }
-
hres = sink->filter_graph->QueryInterface(
IID_IMediaControl, (void **) &control);
@@ -885,6 +985,7 @@ gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition
ret = gst_dshowvideosink_start_graph (sink);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
+ sink->graph_running = TRUE;
break;
}
@@ -892,14 +993,20 @@ gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ GST_DSHOWVIDEOSINK_GRAPH_LOCK(sink);
rettmp = gst_dshowvideosink_pause_graph (sink);
if (rettmp == GST_STATE_CHANGE_FAILURE)
ret = rettmp;
+ sink->graph_running = FALSE;
+ GST_DSHOWVIDEOSINK_GRAPH_UNLOCK(sink);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
+ GST_DSHOWVIDEOSINK_GRAPH_LOCK(sink);
rettmp = gst_dshowvideosink_stop_graph (sink);
if (rettmp == GST_STATE_CHANGE_FAILURE)
ret = rettmp;
+ sink->graph_running = FALSE;
+ GST_DSHOWVIDEOSINK_GRAPH_UNLOCK(sink);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
gst_dshowvideosink_clear (sink);
@@ -909,6 +1016,142 @@ gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition
return ret;
}
+class EVRSupport : public RendererSupport
+{
+private:
+ GstDshowVideoSink *sink;
+ IBaseFilter *filter;
+ IMFGetService *service;
+ IMFVideoDisplayControl *control;
+ HWND video_window;
+
+public:
+ EVRSupport (GstDshowVideoSink *sink) :
+ sink(sink),
+ filter(NULL),
+ service(NULL),
+ control(NULL)
+ {
+ }
+
+ ~EVRSupport() {
+ if (control)
+ control->Release();
+ if (service)
+ service->Release();
+ if (filter)
+ filter->Release();
+ }
+
+ const char *GetName() {
+ return "EnhancedVideoRenderer";
+ }
+
+ IBaseFilter *GetFilter() {
+ return filter;
+ }
+
+ gboolean CheckOS () {
+ OSVERSIONINFO info;
+ info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&info);
+
+ if (info.dwMajorVersion < 6) {
+ return false;
+ }
+ else {
+ return true;
+ }
+ }
+
+ gboolean Configure() {
+ HRESULT hres;
+
+ if (!this->CheckOS ()) {
+ GST_DEBUG_OBJECT (sink, "Windows Vista is required at least for EVR to work");
+ return FALSE;
+ }
+
+ hres = CoCreateInstance (CLSID_EnhancedVideoRenderer, NULL, CLSCTX_INPROC,
+ IID_IBaseFilter, (LPVOID *) &filter);
+ GST_DEBUG_OBJECT (sink, "cocreateinstance returned %d", hres);
+ if (FAILED (hres)) {
+ GST_ERROR_OBJECT (sink,
+ "Can't create an instance of renderer (error=%x)",
+ hres);
+ return FALSE;
+ }
+
+ hres = filter->QueryInterface (IID_IMFGetService,
+ (void **) &service);
+ if (FAILED (hres)) {
+ GST_WARNING_OBJECT (sink, "EVR service interface missing: %x", hres);
+ return FALSE;
+ }
+
+ hres = service->GetService (MR_VIDEO_RENDER_SERVICE,
+ IID_IMFVideoDisplayControl, (void **) &control);
+ if (FAILED (hres)) {
+ GST_WARNING_OBJECT (sink, "EVR control service missing: %x", hres);
+ return FALSE;
+ }
+
+ SetAspectRatioMode();
+ return TRUE;
+ }
+
+ void SetAspectRatioMode() {
+ if (sink->keep_aspect_ratio) {
+ control->SetAspectRatioMode(MFVideoARMode_PreservePicture);
+ }
+ else {
+ control->SetAspectRatioMode(MFVideoARMode_None);
+ }
+ }
+
+ gboolean SetRendererWindow(HWND window) {
+ video_window = window;
+ HRESULT hres = control->SetVideoWindow (video_window);
+ if (FAILED (hres)) {
+ GST_WARNING_OBJECT (sink, "Failed to set video clipping window on filter %p: %x", filter, hres);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ void PaintWindow()
+ {
+ HRESULT hr;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ RECT rcClient;
+
+ GetClientRect(video_window, &rcClient);
+ hdc = BeginPaint(video_window, &ps);
+
+ hr = control->RepaintVideo();
+
+ EndPaint(video_window, &ps);
+ }
+
+ void MoveWindow()
+ {
+ HRESULT hr;
+ RECT rect;
+
+ // Track the movement of the container window and resize as needed
+ GetClientRect(video_window, &rect);
+ hr = control->SetVideoPosition(NULL, &rect);
+ }
+
+ void DisplayModeChanged() {
+ }
+
+ void DestroyWindow() {
+ ::DestroyWindow (video_window);
+ }
+};
+
class VMR9Support : public RendererSupport
{
private:
@@ -1168,12 +1411,16 @@ public:
static gboolean
gst_dshowvideosink_create_renderer (GstDshowVideoSink *sink)
{
- GST_DEBUG_OBJECT (sink, "Trying to create renderer '%s'", "VMR9");
+ GST_DEBUG_OBJECT (sink, "Trying to create renderer '%s'", "EVR");
RendererSupport *support = NULL;
if (sink->preferredrenderer) {
- if (!strcmp (sink->preferredrenderer, "VMR9")) {
+ if (!strcmp (sink->preferredrenderer, "EVR")) {
+ GST_INFO_OBJECT (sink, "Forcing use of EVR");
+ support = new EVRSupport (sink);
+ }
+ else if (!strcmp (sink->preferredrenderer, "VMR9")) {
GST_INFO_OBJECT (sink, "Forcing use of VMR9");
support = new VMR9Support (sink);
}
@@ -1194,15 +1441,20 @@ gst_dshowvideosink_create_renderer (GstDshowVideoSink *sink)
goto done;
}
- support = new VMR9Support (sink);
- if (!support->Configure()) {
- GST_INFO_OBJECT (sink, "Failed to configure VMR9, trying VMR7");
+ support = new EVRSupport (sink);
+ if (!support->Configure ()) {
+ GST_INFO_OBJECT (sink, "Failed to configure EVR, trying VMR9");
delete support;
- support = new VMR7Support (sink);
+ support = new VMR9Support (sink);
if (!support->Configure()) {
- GST_ERROR_OBJECT (sink, "Failed to configure VMR9 or VMR7");
+ GST_INFO_OBJECT (sink, "Failed to configure VMR9, trying VMR7");
delete support;
- return FALSE;
+ support = new VMR7Support (sink);
+ if (!support->Configure()) {
+ GST_ERROR_OBJECT (sink, "Failed to configure VMR9 or VMR7");
+ delete support;
+ return FALSE;
+ }
}
}
@@ -1215,11 +1467,6 @@ static gboolean
gst_dshowvideosink_build_filtergraph (GstDshowVideoSink *sink)
{
HRESULT hres;
- gboolean comInit = FALSE;
-
- hres = CoInitialize(0);
- if (SUCCEEDED (hres))
- comInit = TRUE;
/* Build our DirectShow FilterGraph, looking like:
*
@@ -1271,8 +1518,6 @@ gst_dshowvideosink_build_filtergraph (GstDshowVideoSink *sink)
goto error;
}
- if (comInit)
- CoUninitialize();
return TRUE;
error:
@@ -1291,9 +1536,6 @@ error:
sink->filter_media_event = NULL;
}
- if (comInit)
- CoUninitialize();
-
return FALSE;
}
@@ -1313,13 +1555,15 @@ gst_dshowvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)
GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
if (sink->connected) {
- /* Look at the DShow APIs for dynamically modifying the pipeline and see
- * if we can make this work later... */
- GST_WARNING_OBJECT (sink, "Changing caps at runtime is not yet supported");
- return FALSE;
+ IPin *sinkpin;
+ sink->filter_graph->Disconnect(sink->fakesrc->GetOutputPin());
+ gst_dshow_get_pin_from_filter (sink->renderersupport->GetFilter(), PINDIR_INPUT,
+ &sinkpin);
+ sink->filter_graph->Disconnect(sinkpin);
+ sinkpin->Release();
}
- if (!gst_caps_to_directshow_media_type (caps, &sink->mediatype)) {
+ if (!gst_caps_to_directshow_media_type (sink, caps, &sink->mediatype)) {
GST_WARNING_OBJECT (sink, "Cannot convert caps to AM_MEDIA_TYPE, rejecting");
return FALSE;
}
@@ -1331,6 +1575,17 @@ gst_dshowvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)
sink->fakesrc->GetOutputPin()->SetMediaType (&sink->mediatype);
GST_DEBUG_OBJECT (sink, "Configured output pin media type");
+ /* We have configured the ouput pin media type.
+ * So, create a window (or start using an application-supplied
+ * one, then connect the graph */
+ gst_dshowvideosink_prepare_window (sink);
+ if (!gst_dshowvideosink_connect_graph (sink)) {
+ GST_ELEMENT_ERROR (sink, CORE, NEGOTIATION,
+ ("Failed to initialize DirectShow graph with the input caps"), (NULL));
+ return FALSE;
+ }
+ sink->connected = TRUE;
+
return TRUE;
}
@@ -1345,6 +1600,14 @@ gst_dshowvideosink_stop (GstBaseSink * bsink)
GST_WARNING_OBJECT (sink, "Cannot destroy filter graph; it doesn't exist");
return TRUE;
}
+
+ /* If we created a new window, send the close message and wait until
+ * it's closed in the window thread */
+ if (sink->is_new_window) {
+ SendMessage (sink->window_id, WM_CLOSE, NULL, NULL);
+ while (!sink->window_closed);
+ sink->is_new_window = FALSE;
+ }
/* Release the renderer */
if (sink->renderersupport) {
@@ -1372,19 +1635,32 @@ gst_dshowvideosink_stop (GstBaseSink * bsink)
return TRUE;
}
-static GstFlowReturn
-gst_dshowvideosink_render (GstBaseSink *bsink, GstBuffer *buffer)
+static GstFlowReturn
+gst_dshowvideosink_show_frame (GstVideoSink *vsink, GstBuffer *buffer)
{
- GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
+ GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (vsink);
GstFlowReturn ret;
+ GstStateChangeReturn retst;
if (sink->window_closed) {
- GST_WARNING_OBJECT (sink, "Window has been closed, stopping");
+ GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, ("Output window was closed"), (NULL));
return GST_FLOW_ERROR;
}
GST_DEBUG_OBJECT (sink, "Pushing buffer through fakesrc->renderer");
+ GST_DSHOWVIDEOSINK_GRAPH_LOCK(sink);
+ if (!sink->graph_running){
+ retst = gst_dshowvideosink_start_graph(sink);
+ if (retst == GST_STATE_CHANGE_FAILURE)
+ return GST_FLOW_WRONG_STATE;
+ }
ret = sink->fakesrc->GetOutputPin()->PushBuffer (buffer);
+ if (!sink->graph_running){
+ retst = gst_dshowvideosink_pause_graph(sink);
+ if (retst == GST_STATE_CHANGE_FAILURE)
+ return GST_FLOW_WRONG_STATE;
+ }
+ GST_DSHOWVIDEOSINK_GRAPH_UNLOCK(sink);
GST_DEBUG_OBJECT (sink, "Done pushing buffer through fakesrc->renderer: %s", gst_flow_get_name(ret));
return ret;
@@ -1480,8 +1756,9 @@ gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype)
else if (IsEqualGUID (mediatype->majortype, MEDIATYPE_Audio))
caps = audio_media_type_to_caps (mediatype);
else {
- GST_DEBUG ("Non audio/video media types not yet recognised, please add me: "
- GUID_FORMAT, GUID_ARGS(mediatype->majortype));
+ GST_DEBUG ("Non audio/video media types not yet " \
+ "recognised, please add me: " GUID_FORMAT,
+ GUID_ARGS(mediatype->majortype));
}
if (caps) {
@@ -1504,33 +1781,34 @@ gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype)
* Only operates on simple (single structure) caps.
*/
static gboolean
-gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype)
+gst_caps_to_directshow_media_type (GstDshowVideoSink * sink, GstCaps *caps,
+ AM_MEDIA_TYPE *mediatype)
{
GstStructure *s = gst_caps_get_structure (caps, 0);
const gchar *name = gst_structure_get_name (s);
gchar *capsstring = gst_caps_to_string (caps);
- GST_DEBUG ("Converting caps \"%s\" to AM_MEDIA_TYPE", capsstring);
+ GST_DEBUG_OBJECT (sink, "Converting caps \"%s\" to AM_MEDIA_TYPE", capsstring);
g_free (capsstring);
memset (mediatype, 0, sizeof (AM_MEDIA_TYPE));
if (!strcmp (name, "video/x-raw-yuv")) {
guint32 fourcc;
- int width, height;
+ int width, height;
int bpp;
if (!gst_structure_get_fourcc (s, "format", &fourcc)) {
- GST_WARNING ("Failed to convert caps, no fourcc");
+ GST_WARNING_OBJECT (sink, "Failed to convert caps, no fourcc");
return FALSE;
}
- if (!gst_structure_get_int (s, "width", &width)) {
- GST_WARNING ("Failed to convert caps, no width");
+ if (!gst_structure_get_int (s, "width", &width)) {
+ GST_WARNING_OBJECT (sink, "Failed to convert caps, no width");
return FALSE;
}
- if (!gst_structure_get_int (s, "height", &height)) {
- GST_WARNING ("Failed to convert caps, no height");
+ if (!gst_structure_get_int (s, "height", &height)) {
+ GST_WARNING_OBJECT (sink, "Failed to convert caps, no height");
return FALSE;
}
@@ -1553,7 +1831,7 @@ gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype)
bpp = 12;
break;
default:
- GST_WARNING ("Failed to convert caps, not a known fourcc");
+ GST_WARNING_OBJECT (sink, "Failed to convert caps, not a known fourcc");
return FALSE;
}
@@ -1571,7 +1849,8 @@ gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype)
mediatype->lSampleSize = width * height * bpp / 8;
- GST_INFO ("Set mediatype format: size %d, sample size %d", mediatype->cbFormat, mediatype->lSampleSize);
+ GST_INFO_OBJECT (sink, "Set mediatype format: size %d, sample size %d",
+ mediatype->cbFormat, mediatype->lSampleSize);
vi->rcSource.top = 0;
vi->rcSource.left = 0;
@@ -1580,16 +1859,17 @@ gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype)
vi->rcTarget.top = 0;
vi->rcTarget.left = 0;
- if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) {
+ if (sink->keep_aspect_ratio &&
+ gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) {
/* To handle non-square pixels, we set the target rectangle to a
* different size than the source rectangle.
* There might be a better way, but this seems to work. */
vi->rcTarget.bottom = height;
vi->rcTarget.right = width * par_n / par_d;
- GST_DEBUG ("Got PAR: set target right to %d from width %d", vi->rcTarget.right, width);
+ GST_DEBUG_OBJECT (sink, "Got PAR: set target right to %d from width %d",
+ vi->rcTarget.right, width);
}
else {
- GST_DEBUG ("No PAR found");
vi->rcTarget.bottom = height;
vi->rcTarget.right = width;
}
@@ -1609,11 +1889,11 @@ gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype)
vi->bmiHeader.biClrImportant = 0;
}
- GST_DEBUG ("Successfully built AM_MEDIA_TYPE from caps");
+ GST_DEBUG_OBJECT (sink, "Successfully built AM_MEDIA_TYPE from caps");
return TRUE;
}
- GST_WARNING ("Failed to convert caps, not a known caps type");
+ GST_WARNING_OBJECT (sink, "Failed to convert caps, not a known caps type");
/* Only YUV supported so far */
return FALSE;
diff --git a/sys/dshowvideosink/dshowvideosink.h b/sys/dshowvideosink/dshowvideosink.h
index 8b60087e0..c606019b2 100644
--- a/sys/dshowvideosink/dshowvideosink.h
+++ b/sys/dshowvideosink/dshowvideosink.h
@@ -21,7 +21,7 @@
#define __DSHOWVIDEOSINK_H__
#include <gst/gst.h>
-#include <gst/base/gstbasesink.h>
+#include <gst/video/gstvideosink.h>
#include "dshowvideofakesrc.h"
@@ -29,6 +29,8 @@
#include "d3d9.h"
#include "vmr9.h"
+#include "evr.h"
+#include "mfidl.h"
#pragma warning( disable : 4090 4024)
@@ -41,6 +43,9 @@ G_BEGIN_DECLS
typedef struct _GstDshowVideoSink GstDshowVideoSink;
typedef struct _GstDshowVideoSinkClass GstDshowVideoSinkClass;
+#define GST_DSHOWVIDEOSINK_GRAPH_LOCK(sink) g_mutex_lock (GST_DSHOWVIDEOSINK (sink)->graph_lock)
+#define GST_DSHOWVIDEOSINK_GRAPH_UNLOCK(clock) g_mutex_unlock (GST_DSHOWVIDEOSINK (sink)->graph_lock)
+
/* Renderer-specific support classes */
class RendererSupport
{
@@ -59,7 +64,7 @@ public:
struct _GstDshowVideoSink
{
- GstBaseSink sink;
+ GstVideoSink sink;
/* Preferred renderer to use: VM9 or VMR */
char *preferredrenderer;
@@ -86,8 +91,12 @@ struct _GstDshowVideoSink
/* The video window set through GstXOverlay */
HWND window_id;
+
+ /* If we created the window, it needs to be closed in ::stop() */
+ gboolean is_new_window;
gboolean connected;
+ gboolean graph_running;
/* If we create our own window, we run it from another thread */
GThread *window_thread;
@@ -96,12 +105,20 @@ struct _GstDshowVideoSink
/* If we use an app-supplied window, we need to hook its WNDPROC */
WNDPROC prevWndProc;
+ /* Lock for transitions */
+ GMutex *graph_lock;
+
gboolean comInitialized;
+ GMutex *com_init_lock;
+ GMutex *com_deinit_lock;
+ GCond *com_initialized;
+ GCond *com_uninitialize;
+ GCond *com_uninitialized;
};
struct _GstDshowVideoSinkClass
{
- GstBaseSinkClass parent_class;
+ GstVideoSinkClass parent_class;
};
GType gst_dshowvideosink_get_type (void);