Handle many more edge cases in dshowvideosink.
authorMichael Smith <msmith@songbirdnest.com>
Thu, 5 Feb 2009 01:50:51 +0000 (17:50 -0800)
committerMichael Smith <msmith@songbirdnest.com>
Thu, 5 Feb 2009 01:50:51 +0000 (17:50 -0800)
Instrument various codepaths with debug messages.
Handle (as best as I could see how - it's pretty nasty) moving a video
window to another monitor.
Add listening for directshow events.

sys/dshowvideosink/dshowvideofakesrc.cpp
sys/dshowvideosink/dshowvideofakesrc.h
sys/dshowvideosink/dshowvideosink.cpp
sys/dshowvideosink/dshowvideosink.h

index 541e3885986054bff9e5419abaf660fa964fc6a3..a2de4088bb3ffeccc610463f7849d95f43757eec 100644 (file)
-/* GStreamer\r
- * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Library General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * Library General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Library General Public\r
- * License along with this library; if not, write to the\r
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
- * Boston, MA 02111-1307, USA.\r
- */\r
-\r
-#include "dshowvideofakesrc.h"\r
-\r
-// {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}\r
-const GUID CLSID_VideoFakeSrc = \r
-{ 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };\r
-\r
-/* output pin*/\r
-VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):\r
-  CBaseOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")\r
-{\r
-}\r
-\r
-VideoFakeSrcPin::~VideoFakeSrcPin()\r
-{\r
-}\r
-\r
-HRESULT VideoFakeSrcPin::GetMediaType(int iPosition, CMediaType *pMediaType)\r
-{\r
-  GST_DEBUG ("GetMediaType(%d) called", iPosition);\r
-  if(iPosition == 0) {\r
-    *pMediaType = m_MediaType;\r
-    return S_OK;\r
-  }\r
-  \r
-  return VFW_S_NO_MORE_ITEMS;\r
-}\r
-\r
-/* This seems to be called to notify us of the actual media type being used,\r
- * even though SetMediaType isn't called. How bizarre! */\r
-HRESULT VideoFakeSrcPin::CheckMediaType(const CMediaType *pmt)\r
-{\r
-    GST_DEBUG ("CheckMediaType called: %p", pmt);\r
-\r
-    /* The video renderer will request a different stride, which we must accept.\r
-     * So, we accept arbitrary strides (and do memcpy() to convert if needed),\r
-     * and require the rest of the media type to match\r
-     */\r
-    if (IsEqualGUID(pmt->majortype,m_MediaType.majortype) &&\r
-        IsEqualGUID(pmt->subtype,m_MediaType.subtype) &&\r
-        IsEqualGUID(pmt->formattype,m_MediaType.formattype) &&\r
-        pmt->cbFormat >= m_MediaType.cbFormat)\r
-    {\r
-      if (IsEqualGUID(pmt->formattype, FORMAT_VideoInfo)) {\r
-        VIDEOINFOHEADER *newvh = (VIDEOINFOHEADER *)pmt->pbFormat;\r
-        VIDEOINFOHEADER *curvh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;\r
-\r
-        if ((memcmp ((void *)&newvh->rcSource, (void *)&curvh->rcSource, sizeof (RECT)) == 0) &&\r
-            (memcmp ((void *)&newvh->rcTarget, (void *)&curvh->rcTarget, sizeof (RECT)) == 0) &&\r
-            (newvh->bmiHeader.biCompression == curvh->bmiHeader.biCompression) &&\r
-            (newvh->bmiHeader.biHeight == curvh->bmiHeader.biHeight) &&\r
-            (newvh->bmiHeader.biWidth >= curvh->bmiHeader.biWidth))\r
-        {\r
-          GST_DEBUG ("CheckMediaType has same media type, width %d (%d image)", newvh->bmiHeader.biWidth, curvh->bmiHeader.biWidth);\r
-        \r
-          /* OK, compatible! */\r
-          return S_OK;\r
-        }\r
-        else {\r
-          GST_WARNING ("Looked similar, but aren't...");\r
-        }\r
-      }\r
-      \r
-    }\r
-    GST_WARNING ("Different media types, FAILING!");\r
-    return S_FALSE;\r
-}\r
-\r
-HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)\r
-{\r
-  ALLOCATOR_PROPERTIES properties;\r
-  GST_DEBUG ("Required allocator properties: %d, %d, %d, %d", \r
-        ppropInputRequest->cbAlign, ppropInputRequest->cbBuffer, \r
-        ppropInputRequest->cbPrefix, ppropInputRequest->cBuffers);\r
-\r
-  ppropInputRequest->cbBuffer = m_SampleSize;\r
-  ppropInputRequest->cBuffers = 1;\r
-\r
-  /* First set the buffer descriptions we're interested in */\r
-  HRESULT hres = pAlloc->SetProperties(ppropInputRequest, &properties);\r
-  GST_DEBUG ("Actual Allocator properties: %d, %d, %d, %d", \r
-        properties.cbAlign, properties.cbBuffer, \r
-        properties.cbPrefix, properties.cBuffers);\r
-\r
-  /* Then actually allocate the buffers */\r
-  pAlloc->Commit();\r
-\r
-  return S_OK;\r
-}\r
-\r
-STDMETHODIMP\r
-VideoFakeSrcPin::Notify(IBaseFilter * pSender, Quality q)\r
-{\r
-  /* Implementing this usefully is not required, but the base class\r
-   * has an assertion here... */\r
-  /* TODO: Map this to GStreamer QOS events? */\r
-  return E_NOTIMPL;\r
-}\r
-\r
-STDMETHODIMP VideoFakeSrcPin::SetMediaType (AM_MEDIA_TYPE *pmt)\r
-{\r
-  m_MediaType.Set (*pmt);\r
-  m_SampleSize = m_MediaType.GetSampleSize();\r
-\r
-  GST_DEBUG ("SetMediaType called. SampleSize is %d", m_SampleSize);\r
-\r
-  return S_OK;\r
-}\r
-\r
-/* If the destination buffer is a different shape (strides, etc.) from the source\r
- * buffer, we have to copy. Do that here, for supported video formats.\r
- *\r
- * TODO: When possible (when these things DON'T differ), we should buffer-alloc the\r
- * final output buffer, and not do this copy */\r
-STDMETHODIMP VideoFakeSrcPin::CopyToDestinationBuffer (byte *srcbuf, byte *dstbuf)\r
-{\r
-  VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;\r
-  GST_DEBUG ("Rendering a frame");\r
-\r
-  byte *src, *dst;\r
-  int dststride, srcstride, rows;\r
-  guint32 fourcc = vh->bmiHeader.biCompression;\r
-\r
-  /* biHeight is always negative; we don't want that. */\r
-  int height = ABS (vh->bmiHeader.biHeight);\r
-  int width = vh->bmiHeader.biWidth;\r
-\r
-  /* YUY2 is the preferred layout for DirectShow, so we will probably get this\r
-   * most of the time */\r
-  if ((fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')) ||\r
-      (fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V')) ||\r
-      (fourcc == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'))) \r
-  {\r
-    /* Nice and simple */\r
-    int srcstride = GST_ROUND_UP_4 (vh->rcSource.right * 2);\r
-    int dststride = width * 2;\r
-\r
-    for (int i = 0; i < height; i++) {\r
-      memcpy (dstbuf + dststride * i, srcbuf + srcstride * i, srcstride);\r
-    }\r
-  }\r
-  else if (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')) {\r
-    for (int component = 0; component < 3; component++) {\r
-      // TODO: Get format properly rather than hard-coding it. Use gst_video_* APIs *?\r
-      if (component == 0) {\r
-        srcstride = GST_ROUND_UP_4 (vh->rcSource.right);\r
-        src = srcbuf;\r
-      }\r
-      else {\r
-        srcstride = GST_ROUND_UP_4 ( GST_ROUND_UP_2 (vh->rcSource.right) / 2);\r
-        if (component == 1)\r
-          src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom);\r
-        else\r
-          src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom) +\r
-                  srcstride * (GST_ROUND_UP_2 (vh->rcSource.bottom) / 2);\r
-      }\r
-\r
-      /* Is there a better way to do this? This is ICK! */\r
-      if (component == 0) {\r
-        dststride = width;\r
-        dst = dstbuf;\r
-        rows = height;\r
-      } else if (component == 1) {\r
-        dststride = width / 2;\r
-        dst = dstbuf + width * height;\r
-        rows = height/2;\r
-      }\r
-      else {\r
-        dststride = width / 2;\r
-        dst = dstbuf + width * height +\r
-                       width/2 * height/2;\r
-        rows = height/2;\r
-      }\r
-\r
-      for (int i = 0; i < rows; i++) {\r
-        memcpy (dst + i * dststride, src + i * srcstride, srcstride);\r
-      }\r
-    }\r
-  }\r
-\r
-  return S_OK;\r
-}\r
-\r
-\r
-GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)\r
-{\r
-  IMediaSample *pSample = NULL;\r
-\r
-  byte *data = GST_BUFFER_DATA (buffer);\r
-  \r
-  /* TODO: Use more of the arguments here? */\r
-  HRESULT hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);\r
-  if (SUCCEEDED (hres))\r
-  {\r
-    BYTE *sample_buffer;\r
-    AM_MEDIA_TYPE *mediatype;\r
-\r
-    pSample->GetPointer(&sample_buffer);\r
-    pSample->GetMediaType(&mediatype);\r
-    if (mediatype)\r
-      SetMediaType (mediatype);\r
-\r
-    if(sample_buffer)\r
-    {\r
-      /* Copy to the destination stride. \r
-       * This is not just a simple memcpy because of the different strides. \r
-       * TODO: optimise for the same-stride case and avoid the copy entirely. \r
-       */\r
-      CopyToDestinationBuffer (data, sample_buffer);\r
-    }\r
-\r
-    pSample->SetDiscontinuity(FALSE); /* Decoded frame; unimportant */\r
-    pSample->SetSyncPoint(TRUE); /* Decoded frame; always a valid syncpoint */\r
-    pSample->SetPreroll(FALSE); /* For non-displayed frames. \r
-                                   Not used in GStreamer */\r
-\r
-    /* Disable synchronising on this sample. We instead let GStreamer handle \r
-     * this at a higher level, inside BaseSink. */\r
-    pSample->SetTime(NULL, NULL);\r
-\r
-    hres = Deliver(pSample);\r
-    pSample->Release();\r
-\r
-    if (SUCCEEDED (hres))\r
-      return GST_FLOW_OK;\r
-    else if (hres == VFW_E_NOT_CONNECTED)\r
-      return GST_FLOW_NOT_LINKED;\r
-    else\r
-      return GST_FLOW_ERROR;\r
-  }\r
-  else {\r
-    GST_WARNING ("Could not get sample for delivery to sink: %x", hres);\r
-    return GST_FLOW_ERROR;\r
-  }\r
-}\r
-\r
-STDMETHODIMP VideoFakeSrcPin::Flush ()\r
-{\r
-  DeliverBeginFlush();\r
-  DeliverEndFlush();\r
-  return S_OK;\r
-}\r
-\r
-VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc)\r
-{\r
-  HRESULT hr = S_OK;\r
-  m_pOutputPin = new VideoFakeSrcPin ((CSource *)this, &m_critsec, &hr);\r
-}\r
-\r
-int VideoFakeSrc::GetPinCount()\r
-{\r
-  return 1;\r
-}\r
-\r
-CBasePin *VideoFakeSrc::GetPin(int n)\r
-{\r
-  return (CBasePin *)m_pOutputPin;\r
-}\r
-\r
-VideoFakeSrcPin *VideoFakeSrc::GetOutputPin()\r
-{\r
-  return m_pOutputPin;\r
-}\r
+/* GStreamer
+ * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "dshowvideofakesrc.h"
+
+GST_DEBUG_CATEGORY_EXTERN (dshowvideosink_debug);
+#define GST_CAT_DEFAULT dshowvideosink_debug
+
+// {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}
+const GUID CLSID_VideoFakeSrc = 
+{ 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };
+
+/* output pin*/
+VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):
+  CDynamicOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")
+{
+}
+
+VideoFakeSrcPin::~VideoFakeSrcPin()
+{
+}
+
+HRESULT VideoFakeSrcPin::GetMediaType(int iPosition, CMediaType *pMediaType)
+{
+  GST_DEBUG ("GetMediaType(%d) called", iPosition);
+  if(iPosition == 0) {
+    *pMediaType = m_MediaType;
+    return S_OK;
+  }
+  
+  return VFW_S_NO_MORE_ITEMS;
+}
+
+/* This seems to be called to notify us of the actual media type being used,
+ * even though SetMediaType isn't called. How bizarre! */
+HRESULT VideoFakeSrcPin::CheckMediaType(const CMediaType *pmt)
+{
+    GST_DEBUG ("CheckMediaType called: %p", pmt);
+
+    /* The video renderer will request a different stride, which we must accept.
+     * So, we accept arbitrary strides (and do memcpy() to convert if needed),
+     * and require the rest of the media type to match
+     */
+    if (IsEqualGUID(pmt->majortype,m_MediaType.majortype) &&
+        IsEqualGUID(pmt->subtype,m_MediaType.subtype) &&
+        IsEqualGUID(pmt->formattype,m_MediaType.formattype) &&
+        pmt->cbFormat >= m_MediaType.cbFormat)
+    {
+      if (IsEqualGUID(pmt->formattype, FORMAT_VideoInfo)) {
+        VIDEOINFOHEADER *newvh = (VIDEOINFOHEADER *)pmt->pbFormat;
+        VIDEOINFOHEADER *curvh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
+
+        if ((memcmp ((void *)&newvh->rcSource, (void *)&curvh->rcSource, sizeof (RECT)) == 0) &&
+            (memcmp ((void *)&newvh->rcTarget, (void *)&curvh->rcTarget, sizeof (RECT)) == 0) &&
+            (newvh->bmiHeader.biCompression == curvh->bmiHeader.biCompression) &&
+            (newvh->bmiHeader.biHeight == curvh->bmiHeader.biHeight) &&
+            (newvh->bmiHeader.biWidth >= curvh->bmiHeader.biWidth))
+        {
+          GST_DEBUG ("CheckMediaType has same media type, width %d (%d image)", newvh->bmiHeader.biWidth, curvh->bmiHeader.biWidth);
+        
+          /* OK, compatible! */
+          return S_OK;
+        }
+        else {
+          GST_WARNING ("Looked similar, but aren't...");
+        }
+      }
+      
+    }
+    GST_WARNING ("Different media types, FAILING!");
+    return S_FALSE;
+}
+
+HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
+{
+  ALLOCATOR_PROPERTIES properties;
+  GST_DEBUG ("Required allocator properties: %d, %d, %d, %d", 
+        ppropInputRequest->cbAlign, ppropInputRequest->cbBuffer, 
+        ppropInputRequest->cbPrefix, ppropInputRequest->cBuffers);
+
+  ppropInputRequest->cbBuffer = m_SampleSize;
+  ppropInputRequest->cBuffers = 1;
+
+  /* First set the buffer descriptions we're interested in */
+  HRESULT hres = pAlloc->SetProperties(ppropInputRequest, &properties);
+  GST_DEBUG ("Actual Allocator properties: %d, %d, %d, %d", 
+        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;
+}
+
+STDMETHODIMP
+VideoFakeSrcPin::Notify(IBaseFilter * pSender, Quality q)
+{
+  /* Implementing this usefully is not required, but the base class
+   * has an assertion here... */
+  /* TODO: Map this to GStreamer QOS events? */
+  return E_NOTIMPL;
+}
+
+STDMETHODIMP VideoFakeSrcPin::SetMediaType (AM_MEDIA_TYPE *pmt)
+{
+  m_MediaType.Set (*pmt);
+  m_SampleSize = m_MediaType.GetSampleSize();
+
+  GST_DEBUG ("SetMediaType called. SampleSize is %d", m_SampleSize);
+
+  return S_OK;
+}
+
+/* If the destination buffer is a different shape (strides, etc.) from the source
+ * buffer, we have to copy. Do that here, for supported video formats.
+ *
+ * TODO: When possible (when these things DON'T differ), we should buffer-alloc the
+ * final output buffer, and not do this copy */
+STDMETHODIMP VideoFakeSrcPin::CopyToDestinationBuffer (byte *srcbuf, byte *dstbuf)
+{
+  VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
+  GST_DEBUG ("Rendering a frame");
+
+  byte *src, *dst;
+  int dststride, srcstride, rows;
+  guint32 fourcc = vh->bmiHeader.biCompression;
+
+  /* biHeight is always negative; we don't want that. */
+  int height = ABS (vh->bmiHeader.biHeight);
+  int width = vh->bmiHeader.biWidth;
+
+  /* YUY2 is the preferred layout for DirectShow, so we will probably get this
+   * most of the time */
+  if ((fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')) ||
+      (fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V')) ||
+      (fourcc == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'))) 
+  {
+    /* Nice and simple */
+    int srcstride = GST_ROUND_UP_4 (vh->rcSource.right * 2);
+    int dststride = width * 2;
+
+    for (int i = 0; i < height; i++) {
+      memcpy (dstbuf + dststride * i, srcbuf + srcstride * i, srcstride);
+    }
+  }
+  else if (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')) {
+    for (int component = 0; component < 3; component++) {
+      // TODO: Get format properly rather than hard-coding it. Use gst_video_* APIs *?
+      if (component == 0) {
+        srcstride = GST_ROUND_UP_4 (vh->rcSource.right);
+        src = srcbuf;
+      }
+      else {
+        srcstride = GST_ROUND_UP_4 ( GST_ROUND_UP_2 (vh->rcSource.right) / 2);
+        if (component == 1)
+          src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom);
+        else
+          src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom) +
+                  srcstride * (GST_ROUND_UP_2 (vh->rcSource.bottom) / 2);
+      }
+
+      /* Is there a better way to do this? This is ICK! */
+      if (component == 0) {
+        dststride = width;
+        dst = dstbuf;
+        rows = height;
+      } else if (component == 1) {
+        dststride = width / 2;
+        dst = dstbuf + width * height;
+        rows = height/2;
+      }
+      else {
+        dststride = width / 2;
+        dst = dstbuf + width * height +
+                       width/2 * height/2;
+        rows = height/2;
+      }
+
+      for (int i = 0; i < rows; i++) {
+        memcpy (dst + i * dststride, src + i * srcstride, srcstride);
+      }
+    }
+  }
+
+  return S_OK;
+}
+
+STDMETHODIMP VideoFakeSrcPin::Disconnect ()
+{
+  GST_DEBUG_OBJECT (this, "Disconnecting pin");
+  HRESULT hr = CDynamicOutputPin::Disconnect();
+  GST_DEBUG_OBJECT (this, "Pin disconnected");
+  return hr;
+}
+
+HRESULT VideoFakeSrcPin::Inactive ()
+{
+  GST_DEBUG_OBJECT (this, "Pin going inactive");
+  HRESULT hr = CDynamicOutputPin::Inactive();
+  GST_DEBUG_OBJECT (this, "Pin inactivated");
+  return hr;
+}
+
+HRESULT VideoFakeSrcPin::BreakConnect ()
+{
+  GST_DEBUG_OBJECT (this, "Breaking connection");
+  HRESULT hr = CDynamicOutputPin::BreakConnect();
+  GST_DEBUG_OBJECT (this, "Connection broken");
+  return hr;
+}
+
+HRESULT VideoFakeSrcPin::CompleteConnect (IPin *pReceivePin)
+{
+  GST_DEBUG_OBJECT (this, "Completing connection");
+  HRESULT hr = CDynamicOutputPin::CompleteConnect(pReceivePin);
+  GST_DEBUG_OBJECT (this, "Completed connection: %x", hr);
+  return hr;
+}
+
+STDMETHODIMP VideoFakeSrcPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
+{
+  GST_DEBUG_OBJECT (this, "Calling Block()");
+  HRESULT hr = CDynamicOutputPin::Block (dwBlockFlags, hEvent);
+  GST_DEBUG_OBJECT (this, "Called Block()");
+  return hr;
+}
+
+/* When moving the video to a different monitor, directshow stops and restarts the playback pipeline.
+ * Unfortunately, it doesn't properly block pins or do anything special, so we racily just fail
+ * at this point.
+ * So, we try multiple times in a loop, hoping that it'll have finished (we get no notifications at all!)
+ * at some point.
+ */
+#define MAX_ATTEMPTS 10
+
+GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
+{
+  IMediaSample *pSample = NULL;
+  byte *data = GST_BUFFER_DATA (buffer);
+  int attempts = 0;
+  HRESULT hres;
+  BYTE *sample_buffer;
+  AM_MEDIA_TYPE *mediatype;
+
+  StartUsingOutputPin();
+
+  while (attempts < MAX_ATTEMPTS)
+  {
+    hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
+    if (SUCCEEDED (hres))
+      break;
+    attempts++;
+    Sleep(100);
+  }
+
+  if (FAILED (hres)) 
+  {
+    StopUsingOutputPin();
+    GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
+    return GST_FLOW_ERROR;
+  }
+
+  pSample->GetPointer(&sample_buffer);
+  pSample->GetMediaType(&mediatype);
+  if (mediatype)
+    SetMediaType (mediatype);
+
+  if(sample_buffer)
+  {
+    /* Copy to the destination stride. 
+     * This is not just a simple memcpy because of the different strides. 
+     * TODO: optimise for the same-stride case and avoid the copy entirely. 
+     */
+    CopyToDestinationBuffer (data, sample_buffer);
+  }
+
+  pSample->SetDiscontinuity(FALSE); /* Decoded frame; unimportant */
+  pSample->SetSyncPoint(TRUE); /* Decoded frame; always a valid syncpoint */
+  pSample->SetPreroll(FALSE); /* For non-displayed frames. 
+                                 Not used in GStreamer */
+
+  /* Disable synchronising on this sample. We instead let GStreamer handle 
+   * this at a higher level, inside BaseSink. */
+  pSample->SetTime(NULL, NULL);
+
+  while (attempts < MAX_ATTEMPTS)
+  {
+    hres = Deliver(pSample);
+    if (SUCCEEDED (hres))
+      break;
+    attempts++;
+    Sleep(100);
+  }
+
+  pSample->Release();
+
+  StopUsingOutputPin();
+
+  if (SUCCEEDED (hres))
+    return GST_FLOW_OK;
+  else {
+    GST_WARNING_OBJECT (this, "Failed to deliver sample: %x", hres);
+    if (hres == VFW_E_NOT_CONNECTED)
+      return GST_FLOW_NOT_LINKED;
+    else
+      return GST_FLOW_ERROR;
+  }
+}
+
+STDMETHODIMP VideoFakeSrcPin::Flush ()
+{
+  DeliverBeginFlush();
+  DeliverEndFlush();
+  return S_OK;
+}
+
+VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc),
+       m_evFilterStoppingEvent(TRUE)
+{
+  HRESULT hr = S_OK;
+  m_pOutputPin = new VideoFakeSrcPin ((CSource *)this, &m_critsec, &hr);
+}
+
+int VideoFakeSrc::GetPinCount()
+{
+  return 1;
+}
+
+CBasePin *VideoFakeSrc::GetPin(int n)
+{
+  return (CBasePin *)m_pOutputPin;
+}
+
+VideoFakeSrcPin *VideoFakeSrc::GetOutputPin()
+{
+  return m_pOutputPin;
+}
+
+STDMETHODIMP VideoFakeSrc::Stop(void)
+{
+  GST_DEBUG_OBJECT (this, "Stop()");
+  m_evFilterStoppingEvent.Set();
+
+  return CBaseFilter::Stop();
+}
+
+STDMETHODIMP VideoFakeSrc::Pause(void)
+{
+  GST_DEBUG_OBJECT (this, "Pause()");
+
+  m_evFilterStoppingEvent.Reset();
+
+  return CBaseFilter::Pause();
+}
+
+STDMETHODIMP VideoFakeSrc::Run(REFERENCE_TIME tStart)
+{
+  GST_DEBUG_OBJECT (this, "Run()");
+
+  return CBaseFilter::Run(tStart);
+}
+
+STDMETHODIMP VideoFakeSrc::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName)
+{
+  HRESULT hr;
+
+  // The filter is joining the filter graph.
+  if(NULL != pGraph)
+  {
+    IGraphConfig* pGraphConfig = NULL;
+    hr = pGraph->QueryInterface(IID_IGraphConfig, (void**)&pGraphConfig);
+    if(FAILED(hr))
+      return hr;
+
+    hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
+    if(FAILED(hr))
+    {
+      pGraphConfig->Release();
+      return hr;
+    }
+
+    m_pOutputPin->SetConfigInfo(pGraphConfig, m_evFilterStoppingEvent);
+    pGraphConfig->Release();
+  }
+  else
+  {
+    hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
+    if(FAILED(hr))
+      return hr;
+
+    m_pOutputPin->SetConfigInfo(NULL, NULL);
+  }
+
+  return S_OK;
+}
+
index a6b032432893e846eb85ebee3e58a652e5c626ce..ac2248da361ab6acd103afb91c7859288ddb9373 100644 (file)
@@ -22,7 +22,7 @@
 #include <streams.h>
 #include <gst/gst.h>
 
-class VideoFakeSrcPin : public CBaseOutputPin
+class VideoFakeSrcPin : public CDynamicOutputPin
 {
 protected:
   /* members */
@@ -41,11 +41,14 @@ public:
   virtual HRESULT CheckMediaType(const CMediaType *pmt);
   HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
   virtual HRESULT DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest);
+  virtual HRESULT BreakConnect();
+  virtual HRESULT CompleteConnect(IPin *pReceivePin);
+  virtual HRESULT Inactive();
   STDMETHOD (SetMediaType) (AM_MEDIA_TYPE *pmt);
   STDMETHOD (Flush) ();
   STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
-
-
+  STDMETHODIMP Disconnect();
+  STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);
 };
 
 class VideoFakeSrc : public CBaseFilter
@@ -55,6 +58,8 @@ private:
   CCritSec m_critsec;
   VideoFakeSrcPin *m_pOutputPin;
 
+  CAMEvent m_evFilterStoppingEvent;
+
 public:
   /* methods */
   VideoFakeSrc  (void);
@@ -65,6 +70,11 @@ public:
   /* Overrides */
   int       GetPinCount();
   CBasePin *GetPin(int n);
+
+  STDMETHODIMP Run(REFERENCE_TIME tStart);
+  STDMETHODIMP Stop(void);
+  STDMETHODIMP Pause(void);
+  STDMETHODIMP JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName);
 };
 
 #endif /* __DSHOWVIDEOFAKESRC_H__ */
index 788dbdd5cce99185319ebd52316f9a5c85d5a859..0745921c0d98fc754c1c48e39421eec682beca33 100644 (file)
-/* GStreamer\r
- * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>\r
- *\r
- * This library is free software; you can redistribute it and/or\r
- * modify it under the terms of the GNU Library General Public\r
- * License as published by the Free Software Foundation; either\r
- * version 2 of the License, or (at your option) any later version.\r
- *\r
- * This library is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
- * Library General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU Library General Public\r
- * License along with this library; if not, write to the\r
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,\r
- * Boston, MA 02111-1307, USA.\r
- */\r
-\r
-#ifdef HAVE_CONFIG_H\r
-#include "config.h"\r
-#endif\r
-\r
-#include "dshowvideosink.h"\r
-#include "dshowvideofakesrc.h"\r
-\r
-#include <gst/interfaces/xoverlay.h>\r
-\r
-#include "windows.h"\r
-\r
-static const GstElementDetails gst_dshowvideosink_details =\r
-GST_ELEMENT_DETAILS ("DirectShow video sink",\r
-    "Sink/Video",\r
-    "Display data using a DirectShow video renderer",\r
-    "Pioneers of the Inevitable <songbird@songbirdnest.com>");\r
-\r
-GST_DEBUG_CATEGORY_STATIC (dshowvideosink_debug);\r
-#define GST_CAT_DEFAULT dshowvideosink_debug\r
-\r
-static GstCaps * gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype);\r
-static gboolean gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype);\r
-\r
-/* TODO: Support RGB! */\r
-static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",\r
-    GST_PAD_SINK,\r
-    GST_PAD_ALWAYS,\r
-    GST_STATIC_CAPS (\r
-        "video/x-raw-yuv,"\r
-        "width = (int) [ 1, MAX ],"\r
-        "height = (int) [ 1, MAX ],"\r
-        "framerate = (fraction) [ 0, MAX ]," \r
-        "format = {(fourcc)YUY2, (fourcc)UYVY, (fourcc) YUVY, (fourcc)YV12 }")\r
-    );\r
-\r
-static void gst_dshowvideosink_init_interfaces (GType type);\r
-\r
-GST_BOILERPLATE_FULL (GstDshowVideoSink, gst_dshowvideosink, GstBaseSink,\r
-    GST_TYPE_BASE_SINK, gst_dshowvideosink_init_interfaces);\r
-\r
-enum\r
-{\r
-  PROP_0,\r
-  PROP_KEEP_ASPECT_RATIO,\r
-  PROP_FULL_SCREEN,\r
-  PROP_RENDERER\r
-};\r
-\r
-/* GObject methods */\r
-static void gst_dshowvideosink_finalize (GObject * gobject);\r
-static void gst_dshowvideosink_set_property (GObject * object, guint prop_id,\r
-    const GValue * value, GParamSpec * pspec);\r
-static void gst_dshowvideosink_get_property (GObject * object, guint prop_id,\r
-    GValue * value, GParamSpec * pspec);\r
-\r
-/* GstElement methods */\r
-static GstStateChangeReturn gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition);\r
-\r
-/* GstBaseSink methods */\r
-static gboolean gst_dshowvideosink_start (GstBaseSink * bsink);\r
-static gboolean gst_dshowvideosink_stop (GstBaseSink * bsink);\r
-static gboolean gst_dshowvideosink_unlock (GstBaseSink * bsink);\r
-static gboolean gst_dshowvideosink_unlock_stop (GstBaseSink * bsink);\r
-static gboolean gst_dshowvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps);\r
-static GstCaps *gst_dshowvideosink_get_caps (GstBaseSink * bsink);\r
-static GstFlowReturn gst_dshowvideosink_render (GstBaseSink *sink, GstBuffer *buffer);\r
-\r
-/* GstXOverlay methods */\r
-static void gst_dshowvideosink_set_window_id (GstXOverlay * overlay, ULONG window_id);\r
-\r
-/* TODO: event, preroll, buffer_alloc? \r
- * buffer_alloc won't generally be all that useful because the renderers require a \r
- * different stride to GStreamer's implicit values. \r
- */\r
-\r
-static gboolean\r
-gst_dshowvideosink_interface_supported (GstImplementsInterface * iface,\r
-    GType type)\r
-{\r
-  g_assert (type == GST_TYPE_X_OVERLAY);\r
-  return TRUE;\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_interface_init (GstImplementsInterfaceClass * klass)\r
-{\r
-  klass->supported = gst_dshowvideosink_interface_supported;\r
-}\r
-\r
-\r
-static void\r
-gst_dshowvideosink_xoverlay_interface_init (GstXOverlayClass * iface)\r
-{\r
-  iface->set_xwindow_id = gst_dshowvideosink_set_window_id;\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_init_interfaces (GType type)\r
-{\r
-  static const GInterfaceInfo iface_info = {\r
-    (GInterfaceInitFunc) gst_dshowvideosink_interface_init,\r
-    NULL,\r
-    NULL,\r
-  };\r
-\r
-  static const GInterfaceInfo xoverlay_info = {\r
-    (GInterfaceInitFunc) gst_dshowvideosink_xoverlay_interface_init,\r
-    NULL,\r
-    NULL,\r
-  };\r
-\r
-  g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,\r
-      &iface_info);\r
-  g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &xoverlay_info);\r
-\r
-  GST_DEBUG_CATEGORY_INIT (dshowvideosink_debug, "dshowvideosink", 0, \\r
-      "DirectShow video sink");\r
-}\r
-static void\r
-gst_dshowvideosink_base_init (gpointer klass)\r
-{\r
-  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);\r
-\r
-  gst_element_class_add_pad_template (element_class,\r
-      gst_static_pad_template_get (&sink_template));\r
-\r
-  gst_element_class_set_details (element_class, &gst_dshowvideosink_details);\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_class_init (GstDshowVideoSinkClass * klass)\r
-{\r
-  GObjectClass *gobject_class;\r
-  GstElementClass *gstelement_class;\r
-  GstBaseSinkClass *gstbasesink_class;\r
-\r
-  gobject_class = (GObjectClass *) klass;\r
-  gstelement_class = (GstElementClass *) klass;\r
-  gstbasesink_class = (GstBaseSinkClass *) klass;\r
-\r
-  gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_dshowvideosink_finalize);\r
-  gobject_class->set_property =\r
-      GST_DEBUG_FUNCPTR (gst_dshowvideosink_set_property);\r
-  gobject_class->get_property =\r
-      GST_DEBUG_FUNCPTR (gst_dshowvideosink_get_property);\r
-\r
-  gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_dshowvideosink_change_state);\r
-\r
-  gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosink_get_caps);\r
-  gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dshowvideosink_set_caps);\r
-  gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dshowvideosink_start);\r
-  gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dshowvideosink_stop);\r
-  gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dshowvideosink_unlock);\r
-  gstbasesink_class->unlock_stop =\r
-      GST_DEBUG_FUNCPTR (gst_dshowvideosink_unlock_stop);\r
-  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dshowvideosink_render);\r
-\r
-  /* Add properties */\r
-  g_object_class_install_property (G_OBJECT_CLASS (klass),\r
-      PROP_KEEP_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio",\r
-          "Force aspect ratio",\r
-          "When enabled, scaling will respect original aspect ratio", FALSE,\r
-          (GParamFlags)G_PARAM_READWRITE));\r
-  g_object_class_install_property (G_OBJECT_CLASS (klass),\r
-      PROP_FULL_SCREEN, g_param_spec_boolean ("fullscreen",\r
-          "Full screen mode",\r
-          "Use full-screen mode (not available when using XOverlay)", FALSE,\r
-          (GParamFlags)G_PARAM_READWRITE));\r
-\r
-  g_object_class_install_property (G_OBJECT_CLASS (klass),\r
-      PROP_RENDERER, g_param_spec_string ("renderer", "Renderer", \r
-      "Force usage of specific DirectShow renderer (VMR9 or VMR)",\r
-      NULL, (GParamFlags)G_PARAM_READWRITE));\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_clear (GstDshowVideoSink *sink)\r
-{\r
-  sink->renderersupport = NULL;\r
-  sink->fakesrc = NULL;\r
-  sink->filter_graph = NULL;\r
-\r
-  sink->keep_aspect_ratio = FALSE;\r
-  sink->full_screen = FALSE;\r
-\r
-  sink->window_closed = FALSE;\r
-  sink->window_id = NULL;\r
-\r
-  sink->connected = FALSE;\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_init (GstDshowVideoSink * sink, GstDshowVideoSinkClass * klass)\r
-{\r
-  HRESULT hr;\r
-\r
-  gst_dshowvideosink_clear (sink);\r
-\r
-  hr = CoInitialize (0);\r
-  if (SUCCEEDED(hr))\r
-    sink->comInitialized = TRUE;\r
-\r
-  /* TODO: Copied from GstVideoSink; should we use that as base class? */\r
-  /* 20ms is more than enough, 80-130ms is noticable */\r
-  gst_base_sink_set_max_lateness (GST_BASE_SINK (sink), 20 * GST_MSECOND);\r
-  gst_base_sink_set_qos_enabled (GST_BASE_SINK (sink), TRUE);\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_finalize (GObject * gobject)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (gobject);\r
-\r
-  if (sink->preferredrenderer)\r
-    g_free (sink->preferredrenderer);\r
-\r
-  if (sink->comInitialized) {\r
-    CoUninitialize ();\r
-    sink->comInitialized = FALSE;\r
-  }\r
-\r
-  G_OBJECT_CLASS (parent_class)->finalize (gobject);\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_set_property (GObject * object, guint prop_id,\r
-    const GValue * value, GParamSpec * pspec)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (object);\r
-\r
-  switch (prop_id) {\r
-    case PROP_RENDERER:\r
-      if (sink->preferredrenderer)\r
-        g_free (sink->preferredrenderer);\r
-\r
-      sink->preferredrenderer = g_value_dup_string (value);\r
-      break;\r
-    case PROP_KEEP_ASPECT_RATIO:\r
-      sink->keep_aspect_ratio = g_value_get_boolean (value);\r
-      break;\r
-    case PROP_FULL_SCREEN:\r
-      sink->full_screen = g_value_get_boolean (value);\r
-      break;\r
-    default:\r
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);\r
-      break;\r
-  }\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_get_property (GObject * object, guint prop_id,\r
-    GValue * value, GParamSpec * pspec)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (object);\r
-\r
-  switch (prop_id) {\r
-    case PROP_RENDERER:\r
-      g_value_take_string (value, sink->preferredrenderer);\r
-      break;\r
-    case PROP_KEEP_ASPECT_RATIO:\r
-      g_value_set_boolean (value, sink->keep_aspect_ratio);\r
-      break;\r
-    case PROP_FULL_SCREEN:\r
-      g_value_set_boolean (value, sink->full_screen);\r
-      break;\r
-    default:\r
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);\r
-      break;\r
-  }\r
-}\r
-\r
-static GstCaps *\r
-gst_dshowvideosink_get_caps (GstBaseSink * basesink)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (basesink);\r
-\r
-  return NULL;\r
-}\r
-\r
-static void dump_available_media_types (IPin *pin)\r
-{\r
-  /* Enumerate all media types on this pin, output info about them */\r
-  IEnumMediaTypes *enumerator = NULL;\r
-  AM_MEDIA_TYPE *type;\r
-  GstCaps *caps;\r
-  int i = 0;\r
-\r
-  GST_INFO ("Enumerating media types on pin %p", pin);\r
-\r
-  pin->EnumMediaTypes (&enumerator);\r
-\r
-  while (enumerator->Next (1, &type, NULL) == S_OK) {\r
-    i++;\r
-    caps = gst_directshow_media_type_to_caps (type);\r
-\r
-    if (caps) {\r
-      gchar *str = gst_caps_to_string (caps);\r
-      GST_INFO ("Type %d: converted to caps \"%s\"", i, str);\r
-      g_free (str);\r
-\r
-      gst_caps_unref (caps);\r
-    }\r
-    else\r
-      GST_INFO ("Failed to convert type to GstCaps");\r
-\r
-    DeleteMediaType (type);\r
-  }\r
-  GST_INFO ("Enumeration complete");\r
-\r
-  enumerator->Release();\r
-}\r
-\r
-static void\r
-dump_all_pin_media_types (IBaseFilter *filter)\r
-{\r
-  IEnumPins *enumpins = NULL;\r
-  IPin *pin = NULL;\r
-  HRESULT hres; \r
-\r
-  hres = filter->EnumPins (&enumpins);\r
-  if (FAILED(hres)) {\r
-    GST_WARNING ("Cannot enumerate pins on filter");\r
-    return;\r
-  }\r
-\r
-  GST_INFO ("Enumerating pins on filter %p", filter);\r
-  while (enumpins->Next (1, &pin, NULL) == S_OK)\r
-  {\r
-    IMemInputPin *meminputpin;\r
-    PIN_DIRECTION pindir;\r
-    hres = pin->QueryDirection (&pindir);\r
-\r
-    GST_INFO ("Found a pin with direction: %s", (pindir == PINDIR_INPUT)? "input": "output");\r
-    dump_available_media_types (pin);\r
-\r
-    hres = pin->QueryInterface (\r
-            IID_IMemInputPin, (void **) &meminputpin);\r
-    if (hres == S_OK) {\r
-      GST_INFO ("Pin is a MemInputPin (push mode): %p", meminputpin);\r
-      meminputpin->Release();\r
-    }\r
-    else\r
-      GST_INFO ("Pin is not a MemInputPin (pull mode?): %p", pin);\r
-\r
-    pin->Release();\r
-  }\r
-  enumpins->Release();\r
-}\r
-\r
-gboolean \r
-gst_dshow_get_pin_from_filter (IBaseFilter *filter, PIN_DIRECTION pindir, IPin **pin)\r
-{\r
-  gboolean ret = FALSE;\r
-  IEnumPins *enumpins = NULL;\r
-  IPin *pintmp = NULL;\r
-  HRESULT hres; \r
-  *pin = NULL;\r
-\r
-  hres = filter->EnumPins (&enumpins);\r
-  if (FAILED(hres)) {\r
-    return ret;\r
-  }\r
-\r
-  while (enumpins->Next (1, &pintmp, NULL) == S_OK)\r
-  {\r
-    PIN_DIRECTION pindirtmp;\r
-    hres = pintmp->QueryDirection (&pindirtmp);\r
-    if (hres == S_OK && pindir == pindirtmp) {\r
-      *pin = pintmp;\r
-      ret = TRUE;\r
-      break;\r
-    }\r
-    pintmp->Release ();\r
-  }\r
-  enumpins->Release ();\r
-\r
-  return ret;\r
-}\r
-\r
-/* WNDPROC for application-supplied windows */\r
-LRESULT APIENTRY WndProcHook (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
-{\r
-  /* Handle certain actions specially on the window passed to us.\r
-   * Then forward back to the original window.\r
-   */\r
-  GstDshowVideoSink *sink = (GstDshowVideoSink *)GetProp (hWnd, L"GstDShowVideoSink");\r
-\r
-  switch (message) {\r
-    case WM_PAINT:\r
-      sink->renderersupport->PaintWindow ();\r
-      break;\r
-    case WM_MOVE:\r
-    case WM_SIZE:\r
-      sink->renderersupport->MoveWindow ();\r
-      break;\r
-    case WM_DISPLAYCHANGE:\r
-      sink->renderersupport->DisplayModeChanged();\r
-      break;\r
-    case WM_ERASEBKGND:\r
-      /* DirectShow docs recommend ignoring this message to avoid flicker */\r
-      return TRUE;\r
-    case WM_CLOSE:\r
-      sink->window_closed = TRUE;\r
-  }\r
-  return CallWindowProc (sink->prevWndProc, hWnd, message, wParam, lParam);\r
-}\r
-\r
-/* WndProc for our default window, if the application didn't supply one */\r
-LRESULT APIENTRY \r
-WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\r
-{\r
-  GstDshowVideoSink *sink = (GstDshowVideoSink *)GetWindowLongPtr (hWnd, GWLP_USERDATA);\r
-\r
-  if (!sink) {\r
-    /* I think these happen before we have a chance to set our userdata pointer */\r
-    GST_DEBUG ("No sink!");\r
-    return DefWindowProc (hWnd, message, wParam, lParam);\r
-  }\r
-\r
-  GST_DEBUG_OBJECT (sink, "Got a window message for %x, %x", hWnd, message);\r
-\r
-  switch (message) {\r
-    case WM_PAINT:\r
-      sink->renderersupport->PaintWindow ();\r
-      break;\r
-    case WM_MOVE:\r
-    case WM_SIZE:\r
-      sink->renderersupport->MoveWindow ();\r
-      break;\r
-    case WM_DISPLAYCHANGE:\r
-      sink->renderersupport->DisplayModeChanged();\r
-      break;\r
-    case WM_ERASEBKGND:\r
-      /* DirectShow docs recommend ignoring this message */\r
-      return TRUE;\r
-    case WM_CLOSE:\r
-      sink->renderersupport->DestroyWindow ();\r
-      sink->window_closed = TRUE;\r
-      return 0;\r
-  }\r
-\r
-  return DefWindowProc (hWnd, message, wParam, lParam);\r
-}\r
-\r
-static gpointer\r
-gst_dshowvideosink_window_thread (GstDshowVideoSink * sink)\r
-{\r
-  WNDCLASS WndClass;\r
-  int width, height;\r
-  int offx, offy;\r
-  DWORD exstyle, style;\r
-\r
-  memset (&WndClass, 0, sizeof (WNDCLASS));\r
-  WndClass.style = CS_HREDRAW | CS_VREDRAW;\r
-  WndClass.hInstance = GetModuleHandle (NULL);\r
-  WndClass.lpszClassName = L"GST-DShowSink";\r
-  WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);\r
-  WndClass.cbClsExtra = 0;\r
-  WndClass.cbWndExtra = 0;\r
-  WndClass.lpfnWndProc = WndProc;\r
-  WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);\r
-  RegisterClass (&WndClass);\r
-\r
-  if (sink->full_screen) {\r
-    /* This doesn't seem to work, it returns the wrong values! But when we\r
-     * later use ShowWindow to show it maximized, it goes to full-screen\r
-     * anyway. TODO: Figure out why. */\r
-    width = GetSystemMetrics (SM_CXFULLSCREEN);\r
-    height = GetSystemMetrics (SM_CYFULLSCREEN);\r
-    offx = 0;\r
-    offy = 0;\r
-\r
-    style = WS_POPUP; /* No window decorations */\r
-    exstyle = 0;\r
-  }\r
-  else {\r
-    /* By default, create a normal top-level window, the size \r
-     * of the video.\r
-     */\r
-    RECT rect;\r
-    VIDEOINFOHEADER *vi = (VIDEOINFOHEADER *)sink->mediatype.pbFormat;\r
-\r
-    /* rcTarget is the aspect-ratio-corrected size of the video. */\r
-    width = vi->rcTarget.right + GetSystemMetrics (SM_CXSIZEFRAME) * 2;\r
-    height = vi->rcTarget.bottom + GetSystemMetrics (SM_CYCAPTION) +\r
-        (GetSystemMetrics (SM_CYSIZEFRAME) * 2);\r
-\r
-    SystemParametersInfo (SPI_GETWORKAREA, NULL, &rect, 0);\r
-    int screenwidth = rect.right - rect.left;\r
-    int screenheight = rect.bottom - rect.top;\r
-    offx = rect.left;\r
-    offy = rect.top;\r
-\r
-    /* Make it fit into the screen without changing the\r
-     * aspect ratio. */\r
-    if (width > screenwidth) {\r
-      double ratio = (double)screenwidth/(double)width;\r
-      width = screenwidth;\r
-      height = (int)(height * ratio);\r
-    }\r
-    if (height > screenheight) {\r
-      double ratio = (double)screenheight/(double)height;\r
-      height = screenheight;\r
-      width = (int)(width * ratio);\r
-    }\r
-\r
-    style = WS_OVERLAPPEDWINDOW; /* Normal top-level window */\r
-    exstyle = 0;\r
-  }\r
-\r
-  HWND video_window = CreateWindowEx (exstyle, L"GST-DShowSink",\r
-      L"GStreamer DirectShow sink default window",\r
-      style, offx, offy, width, height, NULL, NULL,\r
-      WndClass.hInstance, NULL);\r
-  if (video_window == NULL) {\r
-    GST_ERROR_OBJECT (sink, "Failed to create window!");\r
-    return NULL;\r
-  }\r
-\r
-  SetWindowLongPtr (video_window, GWLP_USERDATA, (LONG)sink);\r
-\r
-  /* signal application we created a window */\r
-  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (sink),\r
-      (gulong)video_window);\r
-\r
-  /* Set the renderer's clipping window */\r
-  if (!sink->renderersupport->SetRendererWindow (video_window)) {\r
-    GST_WARNING_OBJECT (sink, "Failed to set video clipping window on filter %p", sink->renderersupport);\r
-  }\r
-\r
-  /* Now show the window, as appropriate */\r
-  if (sink->full_screen) {\r
-    ShowWindow (video_window, SW_SHOWMAXIMIZED);\r
-    ShowCursor (FALSE);\r
-  }\r
-  else\r
-    ShowWindow (video_window, SW_SHOWNORMAL);\r
-\r
-  /* Trigger the initial paint of the window */\r
-  UpdateWindow (video_window);\r
-\r
-  ReleaseSemaphore (sink->window_created_signal, 1, NULL);\r
-\r
-  /* start message loop processing our default window messages */\r
-  while (1) {\r
-    MSG msg;\r
-\r
-    if (GetMessage (&msg, video_window, 0, 0) <= 0) {\r
-      GST_LOG_OBJECT (sink, "our window received WM_QUIT or error.");\r
-      break;\r
-    }\r
-    DispatchMessage (&msg);\r
-  }\r
-\r
-  return NULL;\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_create_default_window (GstDshowVideoSink * sink)\r
-{\r
-  sink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);\r
-  if (sink->window_created_signal == NULL)\r
-    goto failed;\r
-\r
-  sink->window_thread = g_thread_create (\r
-      (GThreadFunc) gst_dshowvideosink_window_thread, sink, TRUE, NULL);\r
-\r
-  /* wait maximum 10 seconds for window to be created */\r
-  if (WaitForSingleObject (sink->window_created_signal,\r
-          10000) != WAIT_OBJECT_0)\r
-    goto failed;\r
-\r
-  CloseHandle (sink->window_created_signal);\r
-  return TRUE;\r
-\r
-failed:\r
-  CloseHandle (sink->window_created_signal);\r
-  GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,\r
-      ("Error creating our default window"), (NULL));\r
-\r
-  return FALSE;\r
-}\r
-\r
-static void gst_dshowvideosink_set_window_id (GstXOverlay * overlay, ULONG window_id)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (overlay);\r
-  HWND videowindow = (HWND)window_id;\r
-\r
-  if (videowindow == sink->window_id) {\r
-    GST_DEBUG_OBJECT (sink, "Window already set");\r
-    return;\r
-  }\r
-\r
-  /* TODO: What if we already have a window? What if we're already playing? */\r
-  sink->window_id = videowindow;\r
-}\r
-\r
-static void gst_dshowvideosink_set_window_for_renderer (GstDshowVideoSink *sink)\r
-{\r
-  /* Application has requested a specific window ID */\r
-  sink->prevWndProc = (WNDPROC) SetWindowLong (sink->window_id, GWL_WNDPROC, (LONG)WndProcHook);\r
-  GST_DEBUG_OBJECT (sink, "Set wndproc to %p from %p", WndProcHook, sink->prevWndProc);\r
-  SetProp (sink->window_id, L"GstDShowVideoSink", sink);\r
-  /* This causes the new WNDPROC to become active */\r
-  SetWindowPos (sink->window_id, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);\r
-\r
-  if (!sink->renderersupport->SetRendererWindow (sink->window_id)) {\r
-    GST_WARNING_OBJECT (sink, "Failed to set HWND %x on renderer", sink->window_id);\r
-    return;\r
-  }\r
-\r
-  /* This tells the renderer where the window is located, needed to \r
-   * start drawing in the right place.  */\r
-  sink->renderersupport->MoveWindow();\r
-  GST_INFO_OBJECT (sink, "Set renderer window to %x", sink->window_id);\r
-}\r
-\r
-static void\r
-gst_dshowvideosink_prepare_window (GstDshowVideoSink *sink)\r
-{\r
-  /* Give the app a last chance to supply a window id */\r
-  if (!sink->window_id) {\r
-    gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sink));\r
-  }\r
-\r
-  /* If the app supplied one, use it. Otherwise, go ahead\r
-   * and create (and use) our own window */\r
-  if (sink->window_id) {\r
-    gst_dshowvideosink_set_window_for_renderer (sink);\r
-  }\r
-  else {\r
-    gst_dshowvideosink_create_default_window (sink);\r
-  }\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_connect_graph (GstDshowVideoSink *sink)\r
-{\r
-  HRESULT hres;\r
-  IPin *srcpin;\r
-  IPin *sinkpin;\r
-\r
-  GST_INFO_OBJECT (sink, "Connecting DirectShow pins");\r
-\r
-  srcpin = sink->fakesrc->GetOutputPin();\r
-\r
-  gst_dshow_get_pin_from_filter (sink->renderersupport->GetFilter(), PINDIR_INPUT, \r
-      &sinkpin);\r
-  if (!sinkpin) {\r
-    GST_WARNING_OBJECT (sink, "Cannot get input pin from Renderer");\r
-    return FALSE;\r
-  }\r
-\r
-  /* Be warned that this call WILL deadlock unless you call it from\r
-   * the main thread. Thus, we call this from the state change, not from\r
-   * setcaps (which happens in a streaming thread).\r
-   */\r
-  hres = sink->filter_graph->ConnectDirect (\r
-           srcpin, sinkpin, NULL);\r
-  if (FAILED (hres)) {\r
-    GST_WARNING_OBJECT (sink, "Could not connect pins: %x", hres);\r
-    sinkpin->Release();\r
-    return FALSE;\r
-  }\r
-  sinkpin->Release();\r
-  return TRUE;\r
-}\r
-\r
-static GstStateChangeReturn\r
-gst_dshowvideosink_start_graph (GstDshowVideoSink *sink)\r
-{\r
-  IMediaControl *control = NULL;\r
-  HRESULT hres;\r
-  GstStateChangeReturn ret;\r
-\r
-  GST_DEBUG_OBJECT (sink, "Connecting and starting DirectShow graph");\r
-\r
-  if (!sink->connected) {\r
-    /* This is fine; this just means we haven't connected yet.\r
-     * That's normal for the first time this is called. \r
-     * So, create a window (or start using an application-supplied\r
-     * one, then connect the graph */\r
-    gst_dshowvideosink_prepare_window (sink);\r
-    if (!gst_dshowvideosink_connect_graph (sink)) {\r
-      ret = GST_STATE_CHANGE_FAILURE;\r
-      goto done;\r
-    }\r
-    sink->connected = TRUE;\r
-  }\r
-\r
-  hres = sink->filter_graph->QueryInterface(\r
-          IID_IMediaControl, (void **) &control);\r
-\r
-  if (FAILED (hres)) {\r
-    GST_WARNING_OBJECT (sink, "Failed to get IMediaControl interface");\r
-    ret = GST_STATE_CHANGE_FAILURE;\r
-    goto done;\r
-  }\r
-\r
-  GST_INFO_OBJECT (sink, "Running DirectShow graph");\r
-  hres = control->Run();\r
-  if (FAILED (hres)) {\r
-    GST_ERROR_OBJECT (sink,\r
-        "Failed to run the directshow graph (error=%x)", hres);\r
-    ret = GST_STATE_CHANGE_FAILURE;\r
-    goto done;\r
-  }\r
-  \r
-  GST_DEBUG_OBJECT (sink, "DirectShow graph is now running");\r
-  ret = GST_STATE_CHANGE_SUCCESS;\r
-\r
-done:\r
-  if (control)\r
-    control->Release();\r
-\r
-  return ret;\r
-}\r
-static GstStateChangeReturn\r
-gst_dshowvideosink_pause_graph (GstDshowVideoSink *sink)\r
-{\r
-  IMediaControl *control = NULL;\r
-  GstStateChangeReturn ret;\r
-  HRESULT hres;\r
-\r
-  hres = sink->filter_graph->QueryInterface(\r
-          IID_IMediaControl, (void **) &control);\r
-  if (FAILED (hres)) {\r
-    GST_WARNING_OBJECT (sink, "Failed to get IMediaControl interface");\r
-    ret = GST_STATE_CHANGE_FAILURE;\r
-    goto done;\r
-  }\r
-\r
-  GST_INFO_OBJECT (sink, "Pausing DirectShow graph");\r
-  hres = control->Pause();\r
-  if (FAILED (hres)) {\r
-    GST_WARNING_OBJECT (sink,\r
-        "Can't pause the directshow graph (error=%x)", hres);\r
-    ret = GST_STATE_CHANGE_FAILURE;\r
-    goto done;\r
-  }\r
-\r
-  ret = GST_STATE_CHANGE_SUCCESS;\r
-\r
-done:\r
-  if (control)\r
-    control->Release();\r
-\r
-  return ret;\r
-}\r
-\r
-static GstStateChangeReturn\r
-gst_dshowvideosink_stop_graph (GstDshowVideoSink *sink)\r
-{\r
-  IMediaControl *control = NULL;\r
-  GstStateChangeReturn ret;\r
-  HRESULT hres;\r
-  IPin *sinkpin;\r
-\r
-  hres = sink->filter_graph->QueryInterface(\r
-          IID_IMediaControl, (void **) &control);\r
-  if (FAILED (hres)) {\r
-    GST_WARNING_OBJECT (sink, "Failed to get IMediaControl interface");\r
-    ret = GST_STATE_CHANGE_FAILURE;\r
-    goto done;\r
-  }\r
-\r
-  GST_INFO_OBJECT (sink, "Stopping DirectShow graph");\r
-  hres = control->Stop();\r
-  if (FAILED (hres)) {\r
-    GST_WARNING_OBJECT (sink,\r
-        "Can't stop the directshow graph (error=%x)", hres);\r
-    ret = GST_STATE_CHANGE_FAILURE;\r
-    goto done;\r
-  }\r
-\r
-  sink->filter_graph->Disconnect(sink->fakesrc->GetOutputPin());\r
-\r
-  gst_dshow_get_pin_from_filter (sink->renderersupport->GetFilter(), PINDIR_INPUT, \r
-      &sinkpin);\r
-  sink->filter_graph->Disconnect(sinkpin);\r
-  sinkpin->Release();\r
-\r
-  GST_DEBUG_OBJECT (sink, "DirectShow graph has stopped");\r
-\r
-  if (sink->window_id) {\r
-    /* Return control of application window */\r
-    SetWindowLong (sink->window_id, GWL_WNDPROC, (LONG)sink->prevWndProc);\r
-    RemoveProp (sink->window_id, L"GstDShowVideoSink");\r
-    SetWindowPos (sink->window_id, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);\r
-    sink->prevWndProc = NULL;\r
-  }\r
-  sink->connected = FALSE;\r
-\r
-  ret = GST_STATE_CHANGE_SUCCESS;\r
-\r
-done:\r
-  if (control)\r
-    control->Release();\r
-\r
-  return ret;\r
-}\r
-\r
-static GstStateChangeReturn\r
-gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (element);\r
-  GstStateChangeReturn ret;\r
-\r
-  switch (transition) {\r
-    case GST_STATE_CHANGE_NULL_TO_READY:\r
-      break;\r
-    case GST_STATE_CHANGE_READY_TO_PAUSED:\r
-      break;\r
-    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:\r
-      ret = gst_dshowvideosink_start_graph (sink);\r
-      if (ret != GST_STATE_CHANGE_SUCCESS)\r
-        return ret;\r
-      break;\r
-  }\r
-\r
-  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);\r
-\r
-  switch (transition) {\r
-    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:\r
-      ret = gst_dshowvideosink_pause_graph (sink);\r
-      break;\r
-    case GST_STATE_CHANGE_PAUSED_TO_READY:\r
-      ret = gst_dshowvideosink_stop_graph (sink);\r
-      break;\r
-    case GST_STATE_CHANGE_READY_TO_NULL:\r
-      gst_dshowvideosink_clear (sink);\r
-      break;\r
-  }\r
-\r
-  return ret;\r
-}\r
-\r
-class VMR9Support : public RendererSupport\r
-{\r
-private:\r
-  GstDshowVideoSink *sink;\r
-  IBaseFilter *filter;\r
-  IVMRWindowlessControl9 *control;\r
-  IVMRFilterConfig9 *config;\r
-  HWND video_window;\r
-\r
-public:\r
-  VMR9Support (GstDshowVideoSink *sink) : \r
-      sink(sink), \r
-      filter(NULL),\r
-      control(NULL),\r
-      config(NULL)\r
-  {\r
-  }\r
-\r
-  ~VMR9Support() {\r
-    if (control)\r
-      control->Release();\r
-    if (config)\r
-      config->Release();\r
-    if (filter)\r
-      filter->Release();\r
-  }\r
-\r
-  const char *GetName() {\r
-    return "VideoMixingRenderer9";\r
-  }\r
-\r
-  IBaseFilter *GetFilter() {\r
-    return filter;\r
-  }\r
-\r
-  gboolean Configure() {\r
-    HRESULT hres;\r
-\r
-    hres = CoCreateInstance (CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC,\r
-        IID_IBaseFilter, (LPVOID *) &filter);\r
-    if (FAILED (hres)) {\r
-      GST_ERROR_OBJECT (sink, \r
-          "Can't create an instance of renderer (error=%x)",\r
-          hres);\r
-      return FALSE;\r
-    }\r
-\r
-    hres = filter->QueryInterface (\r
-          IID_IVMRFilterConfig9, (void **) &config);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "VMR9 filter config interface missing: %x", hres);\r
-      return FALSE;\r
-    }\r
-\r
-    hres = config->SetRenderingMode (VMR9Mode_Windowless);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "VMR9 couldn't be set to windowless mode: %x", hres);\r
-      return FALSE;\r
-    }\r
-    else {\r
-      GST_DEBUG_OBJECT (sink, "Set VMR9 (%p) to windowless mode!", filter);\r
-    }\r
-\r
-    /* We can't QI to this until _after_ we've been set to windowless mode. \r
-     * Apparently this is against the rules in COM, but that's how it is... */\r
-    hres = filter->QueryInterface (\r
-          IID_IVMRWindowlessControl9, (void **) &control);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "VMR9 windowless control interface missing: %x", hres);\r
-      return FALSE;\r
-    }\r
-\r
-    if (sink->keep_aspect_ratio) {\r
-      control->SetAspectRatioMode(VMR9ARMode_LetterBox);\r
-    }\r
-    else {\r
-      control->SetAspectRatioMode(VMR9ARMode_None);\r
-    }\r
-    return TRUE;\r
-  }\r
-\r
-  gboolean SetRendererWindow(HWND window) {\r
-    video_window = window;\r
-    HRESULT hres = control->SetVideoClippingWindow (video_window);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "Failed to set video clipping window on filter %p: %x", filter, hres);\r
-      return FALSE;\r
-    }\r
-    return TRUE;\r
-  }\r
-\r
-  void PaintWindow()\r
-  {\r
-    HRESULT hr;\r
-    PAINTSTRUCT ps;\r
-    HDC         hdc;\r
-    RECT        rcClient;\r
-\r
-    GetClientRect(video_window, &rcClient);\r
-    hdc = BeginPaint(video_window, &ps);\r
-\r
-    hr = control->RepaintVideo(video_window, hdc);\r
-\r
-    EndPaint(video_window, &ps);\r
-  }\r
-\r
-  void MoveWindow()\r
-  {\r
-    HRESULT hr;\r
-    RECT rect;\r
-\r
-    // Track the movement of the container window and resize as needed\r
-    GetClientRect(video_window, &rect);\r
-    hr = control->SetVideoPosition(NULL, &rect);\r
-  }\r
-\r
-  void DisplayModeChanged() {\r
-    control->DisplayModeChanged();\r
-  }\r
-\r
-  void DestroyWindow() {\r
-    ::DestroyWindow (video_window);\r
-  }\r
-};\r
-\r
-class VMR7Support : public RendererSupport\r
-{\r
-private:\r
-  GstDshowVideoSink *sink;\r
-  IBaseFilter *filter;\r
-  IVMRWindowlessControl *control;\r
-  IVMRFilterConfig *config;\r
-  HWND video_window;\r
-\r
-public:\r
-  VMR7Support (GstDshowVideoSink *sink) : \r
-      sink(sink), \r
-      filter(NULL),\r
-      control(NULL),\r
-      config(NULL)\r
-  {\r
-  }\r
-\r
-  ~VMR7Support() {\r
-    if (control)\r
-      control->Release();\r
-    if (config)\r
-      config->Release();\r
-    if (filter)\r
-      filter->Release();\r
-  }\r
-\r
-  const char *GetName() {\r
-    return "VideoMixingRenderer";\r
-  }\r
-\r
-  IBaseFilter *GetFilter() {\r
-    return filter;\r
-  }\r
-\r
-  gboolean Configure() {\r
-    HRESULT hres;\r
-\r
-    hres = CoCreateInstance (CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC,\r
-        IID_IBaseFilter, (LPVOID *) &filter);\r
-    if (FAILED (hres)) {\r
-      GST_ERROR_OBJECT (sink, \r
-          "Can't create an instance of renderer (error=%x)",\r
-          hres);\r
-      return FALSE;\r
-    }\r
-\r
-    hres = filter->QueryInterface (\r
-          IID_IVMRFilterConfig, (void **) &config);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "VMR filter config interface missing: %x", hres);\r
-      return FALSE;\r
-    }\r
-\r
-    hres = config->SetRenderingMode (VMRMode_Windowless);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "VMR couldn't be set to windowless mode: %x", hres);\r
-      return FALSE;\r
-    }\r
-    else {\r
-      GST_DEBUG_OBJECT (sink, "Set VMR (%p) to windowless mode!", filter);\r
-    }\r
-\r
-    hres = filter->QueryInterface (\r
-          IID_IVMRWindowlessControl, (void **) &control);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "VMR windowless control interface missing: %x", hres);\r
-      return FALSE;\r
-    }\r
-\r
-    if (sink->keep_aspect_ratio) {\r
-      control->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);\r
-    }\r
-    else {\r
-      control->SetAspectRatioMode(VMR_ARMODE_NONE);\r
-    }\r
-    return TRUE;\r
-  }\r
-\r
-  gboolean SetRendererWindow(HWND window) {\r
-    video_window = window;\r
-    HRESULT hres = control->SetVideoClippingWindow (video_window);\r
-    if (FAILED (hres)) {\r
-      GST_WARNING_OBJECT (sink, "Failed to set video clipping window on filter %p: %x", filter, hres);\r
-      return FALSE;\r
-    }\r
-    return TRUE;\r
-  }\r
-\r
-  void PaintWindow()\r
-  {\r
-    HRESULT hr;\r
-    PAINTSTRUCT ps;\r
-    HDC         hdc;\r
-    RECT        rcClient;\r
-\r
-    GetClientRect(video_window, &rcClient);\r
-    hdc = BeginPaint(video_window, &ps);\r
-\r
-    hr = control->RepaintVideo(video_window, hdc);\r
-\r
-    EndPaint(video_window, &ps);\r
-  }\r
-\r
-  void MoveWindow()\r
-  {\r
-    HRESULT hr;\r
-    RECT rect;\r
-\r
-    // Track the movement of the container window and resize as needed\r
-    GetClientRect(video_window, &rect);\r
-    hr = control->SetVideoPosition(NULL, &rect);\r
-  }\r
-\r
-  void DisplayModeChanged() {\r
-    control->DisplayModeChanged();\r
-  }\r
-\r
-  void DestroyWindow() {\r
-    ::DestroyWindow (video_window);\r
-  }\r
-};\r
-\r
-static gboolean \r
-gst_dshowvideosink_create_renderer (GstDshowVideoSink *sink) \r
-{\r
-  GST_DEBUG_OBJECT (sink, "Trying to create renderer '%s'", "VMR9");\r
-\r
-  RendererSupport *support = NULL;\r
-\r
-  if (sink->preferredrenderer) {\r
-    if (!strcmp (sink->preferredrenderer, "VMR9")) {\r
-      GST_INFO_OBJECT (sink, "Forcing use of VMR9");\r
-      support = new VMR9Support (sink);\r
-    }\r
-    else if (!strcmp (sink->preferredrenderer, "VMR")) {\r
-      GST_INFO_OBJECT (sink, "Forcing use of VMR");\r
-      support = new VMR7Support (sink);\r
-    }\r
-    else {\r
-      GST_ERROR_OBJECT (sink, "Unknown sink type '%s'", sink->preferredrenderer);\r
-      return FALSE;\r
-    }\r
-\r
-    if (!support->Configure()) {\r
-      GST_ERROR_OBJECT (sink, "Couldn't configure selected renderer");\r
-      delete support;\r
-      return FALSE;\r
-    }\r
-    goto done;\r
-  }\r
-\r
-  support = new VMR9Support (sink);\r
-  if (!support->Configure()) {\r
-    GST_INFO_OBJECT (sink, "Failed to configure VMR9, trying VMR7");\r
-    delete support;\r
-    support = new VMR7Support (sink);\r
-    if (!support->Configure()) {\r
-      GST_ERROR_OBJECT (sink, "Failed to configure VMR9 or VMR7");\r
-      delete support;\r
-      return FALSE;\r
-    }\r
-  }\r
-\r
-done:\r
-  sink->renderersupport = support;\r
-  return TRUE;\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_build_filtergraph (GstDshowVideoSink *sink)\r
-{\r
-  HRESULT hres;\r
-\r
-  /* Build our DirectShow FilterGraph, looking like: \r
-   *\r
-   *    [ fakesrc ] -> [ sink filter ]\r
-   *\r
-   * so we can feed data in through the fakesrc.\r
-   *\r
-   * The sink filter can be one of our supported filters: VMR9 (VMR7?, EMR?)\r
-   */\r
-\r
-  hres = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,\r
-      IID_IFilterGraph, (LPVOID *) & sink->filter_graph);\r
-  if (FAILED (hres)) {\r
-    GST_ERROR_OBJECT (sink, \r
-          "Can't create an instance of the dshow graph manager (error=%x)", hres);\r
-    goto error;\r
-  }\r
-\r
-  sink->fakesrc = new VideoFakeSrc();\r
-\r
-  IBaseFilter *filter;\r
-  hres = sink->fakesrc->QueryInterface (\r
-          IID_IBaseFilter, (void **) &filter);\r
-  if (FAILED (hres)) {\r
-    GST_ERROR_OBJECT (sink, "Could not QI fakesrc to IBaseFilter");\r
-    goto error;\r
-  }\r
-\r
-  hres = sink->filter_graph->AddFilter (filter, L"fakesrc");\r
-  if (FAILED (hres)) {\r
-    GST_ERROR_OBJECT (sink,\r
-        "Can't add our fakesrc filter to the graph (error=%x)", hres);\r
-    goto error;\r
-  }\r
-\r
-  if (!gst_dshowvideosink_create_renderer (sink)) {\r
-    GST_ERROR_OBJECT (sink, "Could not create a video renderer");\r
-    goto error;\r
-  }\r
-\r
-  /* dump_all_pin_media_types (sink->renderer); */\r
-\r
-  hres =\r
-      sink->filter_graph->AddFilter (sink->renderersupport->GetFilter(),\r
-      L"renderer");\r
-  if (FAILED (hres)) {\r
-    GST_ERROR_OBJECT (sink, \r
-          "Can't add renderer to the graph (error=%x)", hres);\r
-    goto error;\r
-  }\r
-\r
-  return TRUE;\r
-\r
-error:\r
-  if (sink->fakesrc) {\r
-    sink->fakesrc->Release();\r
-    sink->fakesrc = NULL;\r
-  }\r
-\r
-  if (sink->filter_graph) {\r
-    sink->filter_graph->Release();\r
-    sink->filter_graph = NULL;\r
-  }\r
-\r
-  return FALSE;\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_start (GstBaseSink * bsink)\r
-{\r
-  HRESULT hres = S_FALSE;\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);\r
-\r
-  /* Just build the filtergraph; we don't link or otherwise configure it yet */\r
-  return gst_dshowvideosink_build_filtergraph (sink);\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_set_caps (GstBaseSink * bsink, GstCaps * caps)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);\r
-\r
-  /* TODO: What do we want to do if the caps change while we're running?\r
-   * Find out if we can handle this or not... */\r
-\r
-  if (!gst_caps_to_directshow_media_type (caps, &sink->mediatype)) {\r
-    GST_WARNING_OBJECT (sink, "Cannot convert caps to AM_MEDIA_TYPE, rejecting");\r
-    return FALSE;\r
-  }\r
-\r
-  /* Now we have an AM_MEDIA_TYPE describing what we're going to send.\r
-   * We set this on our DirectShow fakesrc's output pin. \r
-   */\r
-  sink->fakesrc->GetOutputPin()->SetMediaType (&sink->mediatype);\r
-\r
-  return TRUE;\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_stop (GstBaseSink * bsink)\r
-{\r
-  IPin *input_pin = NULL, *output_pin = NULL;\r
-  HRESULT hres = S_FALSE;\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);\r
-\r
-  if (!sink->filter_graph) {\r
-    GST_WARNING_OBJECT (sink, "Cannot destroy filter graph; it doesn't exist");\r
-    return TRUE;\r
-  }\r
-\r
-  /* Release the renderer */\r
-  if (sink->renderersupport) {\r
-    delete sink->renderersupport;\r
-    sink->renderersupport = NULL;\r
-  }\r
-\r
-  /* Release our dshow fakesrc */\r
-  if (sink->fakesrc) {\r
-    sink->fakesrc->Release();\r
-    sink->fakesrc = NULL;\r
-  }\r
-\r
-  /* Release the filter graph manager */\r
-  if (sink->filter_graph) {\r
-    sink->filter_graph->Release();\r
-    sink->filter_graph = NULL;\r
-  }\r
-\r
-  return TRUE;\r
-}\r
-\r
-static GstFlowReturn \r
-gst_dshowvideosink_render (GstBaseSink *bsink, GstBuffer *buffer)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);\r
-  GstFlowReturn ret;\r
-\r
-  if (sink->window_closed) {\r
-    GST_WARNING_OBJECT (sink, "Window has been closed, stopping");\r
-    return GST_FLOW_ERROR;\r
-  }\r
-\r
-  GST_DEBUG_OBJECT (sink, "Pushing buffer through fakesrc->renderer");\r
-  ret = sink->fakesrc->GetOutputPin()->PushBuffer (buffer);\r
-  GST_DEBUG_OBJECT (sink, "Done pushing buffer through fakesrc->renderer");\r
-\r
-  return ret;\r
-}\r
-\r
-/* TODO: How can we implement these? Figure that out... */\r
-static gboolean\r
-gst_dshowvideosink_unlock (GstBaseSink * bsink)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);\r
-\r
-  return TRUE;\r
-}\r
-\r
-static gboolean\r
-gst_dshowvideosink_unlock_stop (GstBaseSink * bsink)\r
-{\r
-  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);\r
-\r
-  return TRUE;\r
-}\r
-\r
-/* TODO: Move all of this into generic code? */\r
-\r
-/* Helpers to format GUIDs the same way we find them in the source */\r
-#define GUID_FORMAT "{%.8x, %.4x, %.4x, { %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x }}"\r
-#define GUID_ARGS(guid) \\r
-    guid.Data1, guid.Data2, guid.Data3, \\r
-    guid.Data4[0], guid.Data4[1], guid.Data4[3], guid.Data4[4], \\r
-    guid.Data4[5], guid.Data4[6], guid.Data4[7], guid.Data4[8]\r
-\r
-static GstCaps *\r
-audio_media_type_to_caps (AM_MEDIA_TYPE *mediatype)\r
-{\r
-  return NULL;\r
-}\r
-\r
-static GstCaps *\r
-video_media_type_to_caps (AM_MEDIA_TYPE *mediatype)\r
-{\r
-  GstCaps *caps = NULL;\r
-\r
-  /* TODO: Add  RGB types. */\r
-  if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_YUY2))\r
-    caps = gst_caps_new_simple ("video/x-raw-yuv", \r
-            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), NULL);\r
-  else if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_UYVY))\r
-    caps = gst_caps_new_simple ("video/x-raw-yuv", \r
-            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'), NULL);\r
-  else if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_YUYV))\r
-    caps = gst_caps_new_simple ("video/x-raw-yuv", \r
-            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V'), NULL);\r
-  else if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_YV12))\r
-    caps = gst_caps_new_simple ("video/x-raw-yuv", \r
-            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'V', '1', '2'), NULL);\r
-\r
-  if (!caps) {\r
-    GST_DEBUG ("No subtype known; cannot continue");\r
-    return NULL;\r
-  }\r
-\r
-  if (IsEqualGUID (mediatype->formattype, FORMAT_VideoInfo) &&\r
-          mediatype->cbFormat >= sizeof(VIDEOINFOHEADER))\r
-  {\r
-    VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)mediatype->pbFormat;\r
-\r
-       /* TODO: Set PAR here. Based on difference between source and target RECTs? \r
-     *       Do we want framerate? Based on AvgTimePerFrame? */\r
-    gst_caps_set_simple (caps, \r
-            "width", G_TYPE_INT, vh->bmiHeader.biWidth,\r
-            "height", G_TYPE_INT, vh->bmiHeader.biHeight,\r
-                       NULL);\r
-  }\r
-\r
-  return caps;\r
-}\r
-\r
-\r
-/* Create a GstCaps object representing the same media type as\r
- * this AM_MEDIA_TYPE.\r
- *\r
- * Returns NULL if no corresponding GStreamer type is known.\r
- *\r
- * May modify mediatype.\r
- */\r
-static GstCaps *\r
-gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype)\r
-{\r
-  GstCaps *caps = NULL;\r
-\r
-  if (IsEqualGUID (mediatype->majortype, MEDIATYPE_Video))\r
-    caps = video_media_type_to_caps (mediatype);\r
-  else if (IsEqualGUID (mediatype->majortype, MEDIATYPE_Audio))\r
-    caps = audio_media_type_to_caps (mediatype);\r
-  else {\r
-    GST_DEBUG ("Non audio/video media types not yet recognised, please add me: "\r
-            GUID_FORMAT, GUID_ARGS(mediatype->majortype));\r
-  }\r
-\r
-  if (caps) {\r
-    gchar *capsstring = gst_caps_to_string (caps);\r
-    GST_DEBUG ("Converted AM_MEDIA_TYPE to \"%s\"", capsstring);\r
-    g_free (capsstring);\r
-  }\r
-  else {\r
-    GST_WARNING ("Failed to convert AM_MEDIA_TYPE to caps");\r
-  }\r
-\r
-  return caps;\r
-}\r
-\r
-/* Fill in a DirectShow AM_MEDIA_TYPE structure representing the same media\r
- * type as this GstCaps object.\r
- *\r
- * Returns FALSE if no corresponding type is known.\r
- *\r
- * Only operates on simple (single structure) caps.\r
- */\r
-static gboolean\r
-gst_caps_to_directshow_media_type (GstCaps *caps, AM_MEDIA_TYPE *mediatype)\r
-{\r
-  GstStructure *s = gst_caps_get_structure (caps, 0);\r
-  const gchar *name = gst_structure_get_name (s);\r
-\r
-  gchar *capsstring = gst_caps_to_string (caps);\r
-  GST_DEBUG ("Converting caps \"%s\" to AM_MEDIA_TYPE", capsstring);\r
-  g_free (capsstring);\r
-\r
-  memset (mediatype, 0, sizeof (AM_MEDIA_TYPE));\r
-\r
-  if (!strcmp (name, "video/x-raw-yuv")) {\r
-    guint32 fourcc;\r
-       int width, height;\r
-    int bpp;\r
-\r
-    if (!gst_structure_get_fourcc (s, "format", &fourcc)) {\r
-      GST_WARNING ("Failed to convert caps, no fourcc");\r
-      return FALSE;\r
-    }\r
-\r
-       if (!gst_structure_get_int (s, "width", &width)) {\r
-      GST_WARNING ("Failed to convert caps, no width");\r
-      return FALSE;\r
-    }\r
-       if (!gst_structure_get_int (s, "height", &height)) {\r
-      GST_WARNING ("Failed to convert caps, no height");\r
-      return FALSE;\r
-    }\r
-\r
-    mediatype->majortype = MEDIATYPE_Video;\r
-    switch (fourcc) {\r
-      case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):\r
-        mediatype->subtype = MEDIASUBTYPE_YUY2;\r
-        bpp = 16;\r
-        break;\r
-      case GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V'):\r
-        mediatype->subtype = MEDIASUBTYPE_YUYV;\r
-        bpp = 16;\r
-        break;\r
-      case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):\r
-        mediatype->subtype = MEDIASUBTYPE_UYVY;\r
-        bpp = 16;\r
-        break;\r
-      case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):\r
-        mediatype->subtype = MEDIASUBTYPE_YV12;\r
-        bpp = 12;\r
-        break;\r
-      default:\r
-        GST_WARNING ("Failed to convert caps, not a known fourcc");\r
-        return FALSE;\r
-    }\r
-\r
-    mediatype->bFixedSizeSamples = TRUE; /* Always true for raw video */\r
-    mediatype->bTemporalCompression = FALSE; /* Likewise, always false */\r
-\r
-    {\r
-      int par_n, par_d;\r
-      VIDEOINFOHEADER *vi = (VIDEOINFOHEADER *)CoTaskMemAlloc (sizeof (VIDEOINFOHEADER));\r
-      memset (vi, 0, sizeof (VIDEOINFOHEADER));\r
-\r
-      mediatype->formattype = FORMAT_VideoInfo;\r
-      mediatype->cbFormat = sizeof (VIDEOINFOHEADER);\r
-      mediatype->pbFormat = (BYTE *)vi;\r
-\r
-      mediatype->lSampleSize = width * height * bpp / 8;\r
-\r
-      GST_INFO ("Set mediatype format: size %d, sample size %d", mediatype->cbFormat, mediatype->lSampleSize);\r
-\r
-      vi->rcSource.top = 0;\r
-      vi->rcSource.left = 0;\r
-      vi->rcSource.bottom = height;\r
-      vi->rcSource.right = width;\r
-\r
-      vi->rcTarget.top = 0;\r
-      vi->rcTarget.left = 0;\r
-      if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) {\r
-        /* To handle non-square pixels, we set the target rectangle to a \r
-         * different size than the source rectangle.\r
-         * There might be a better way, but this seems to work. */\r
-        vi->rcTarget.bottom = height;\r
-        vi->rcTarget.right = width * par_n / par_d;\r
-        GST_DEBUG ("Got PAR: set target right to %d from width %d", vi->rcTarget.right, width);\r
-      }\r
-      else {\r
-        GST_DEBUG ("No PAR found");\r
-        vi->rcTarget.bottom = height;\r
-        vi->rcTarget.right = width;\r
-      }\r
-\r
-      vi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);\r
-      vi->bmiHeader.biWidth = width;\r
-      vi->bmiHeader.biHeight = -height; /* Required to be negative. */\r
-      vi->bmiHeader.biPlanes = 1; /* Required to be 1 */\r
-      vi->bmiHeader.biBitCount = bpp;\r
-      vi->bmiHeader.biCompression = fourcc;\r
-      vi->bmiHeader.biSizeImage = width * height * bpp / 8;\r
-\r
-      /* We can safely zero these; they don't matter for our uses */\r
-      vi->bmiHeader.biXPelsPerMeter = 0;\r
-      vi->bmiHeader.biYPelsPerMeter = 0;\r
-      vi->bmiHeader.biClrUsed = 0;\r
-      vi->bmiHeader.biClrImportant = 0;\r
-    }\r
-\r
-    GST_DEBUG ("Successfully built AM_MEDIA_TYPE from caps");\r
-    return TRUE;\r
-  }\r
-\r
-  GST_WARNING ("Failed to convert caps, not a known caps type");\r
-  /* Only YUV supported so far */\r
-\r
-  return FALSE;\r
-}\r
-\r
-/* Plugin entry point */\r
-extern "C" static gboolean\r
-plugin_init (GstPlugin * plugin)\r
-{\r
-  /* PRIMARY: this is the best videosink to use on windows */\r
-  if (!gst_element_register (plugin, "dshowvideosink",\r
-          GST_RANK_PRIMARY, GST_TYPE_DSHOWVIDEOSINK))\r
-    return FALSE;\r
-\r
-  return TRUE;\r
-}\r
-\r
-extern "C" GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,\r
-    GST_VERSION_MINOR,\r
-    "dshowsinkwrapper",\r
-    "DirectShow sink wrapper plugin",\r
-    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)\r
+/* GStreamer
+ * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "dshowvideosink.h"
+#include "dshowvideofakesrc.h"
+
+#include <gst/interfaces/xoverlay.h>
+
+#include "windows.h"
+
+#define WM_GRAPH_NOTIFY WM_APP + 1 /* Private message */
+
+static const GstElementDetails gst_dshowvideosink_details =
+GST_ELEMENT_DETAILS ("DirectShow video sink",
+    "Sink/Video",
+    "Display data using a DirectShow video renderer",
+    "Pioneers of the Inevitable <songbird@songbirdnest.com>");
+
+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);
+
+/* TODO: Support RGB! */
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (
+        "video/x-raw-yuv,"
+        "width = (int) [ 1, MAX ],"
+        "height = (int) [ 1, MAX ],"
+        "framerate = (fraction) [ 0, MAX ]," 
+        "format = {(fourcc)YUY2, (fourcc)UYVY, (fourcc) YUVY, (fourcc)YV12 }")
+    );
+
+static void gst_dshowvideosink_init_interfaces (GType type);
+
+GST_BOILERPLATE_FULL (GstDshowVideoSink, gst_dshowvideosink, GstBaseSink,
+    GST_TYPE_BASE_SINK, gst_dshowvideosink_init_interfaces);
+
+enum
+{
+  PROP_0,
+  PROP_KEEP_ASPECT_RATIO,
+  PROP_FULL_SCREEN,
+  PROP_RENDERER
+};
+
+/* GObject methods */
+static void gst_dshowvideosink_finalize (GObject * gobject);
+static void gst_dshowvideosink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec);
+static void gst_dshowvideosink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec);
+
+/* GstElement methods */
+static GstStateChangeReturn gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition);
+
+/* GstBaseSink methods */
+static gboolean gst_dshowvideosink_start (GstBaseSink * bsink);
+static gboolean gst_dshowvideosink_stop (GstBaseSink * bsink);
+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);
+
+/* 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. 
+ */
+
+static gboolean
+gst_dshowvideosink_interface_supported (GstImplementsInterface * iface,
+    GType type)
+{
+  g_assert (type == GST_TYPE_X_OVERLAY);
+  return TRUE;
+}
+
+static void
+gst_dshowvideosink_interface_init (GstImplementsInterfaceClass * klass)
+{
+  klass->supported = gst_dshowvideosink_interface_supported;
+}
+
+
+static void
+gst_dshowvideosink_xoverlay_interface_init (GstXOverlayClass * iface)
+{
+  iface->set_xwindow_id = gst_dshowvideosink_set_window_id;
+}
+
+static void
+gst_dshowvideosink_init_interfaces (GType type)
+{
+  static const GInterfaceInfo iface_info = {
+    (GInterfaceInitFunc) gst_dshowvideosink_interface_init,
+    NULL,
+    NULL,
+  };
+
+  static const GInterfaceInfo xoverlay_info = {
+    (GInterfaceInitFunc) gst_dshowvideosink_xoverlay_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);
+
+  GST_DEBUG_CATEGORY_INIT (dshowvideosink_debug, "dshowvideosink", 0, \
+      "DirectShow video sink");
+}
+static void
+gst_dshowvideosink_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template));
+
+  gst_element_class_set_details (element_class, &gst_dshowvideosink_details);
+}
+
+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 =
+      GST_DEBUG_FUNCPTR (gst_dshowvideosink_unlock_stop);
+  gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dshowvideosink_render);
+
+  /* Add properties */
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_KEEP_ASPECT_RATIO, g_param_spec_boolean ("force-aspect-ratio",
+          "Force aspect ratio",
+          "When enabled, scaling will respect original aspect ratio", FALSE,
+          (GParamFlags)G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass),
+      PROP_FULL_SCREEN, g_param_spec_boolean ("fullscreen",
+          "Full screen mode",
+          "Use full-screen mode (not available when using XOverlay)", FALSE,
+          (GParamFlags)G_PARAM_READWRITE));
+
+  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)",
+      NULL, (GParamFlags)G_PARAM_READWRITE));
+}
+
+static void
+gst_dshowvideosink_clear (GstDshowVideoSink *sink)
+{
+  sink->renderersupport = NULL;
+  sink->fakesrc = NULL;
+  sink->filter_graph = NULL;
+  sink->filter_media_event = NULL;
+
+  sink->keep_aspect_ratio = FALSE;
+  sink->full_screen = FALSE;
+
+  sink->window_closed = FALSE;
+  sink->window_id = NULL;
+
+  sink->connected = 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;
+
+  /* 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);
+}
+
+static void
+gst_dshowvideosink_finalize (GObject * gobject)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (gobject);
+
+  if (sink->preferredrenderer)
+    g_free (sink->preferredrenderer);
+
+  if (sink->comInitialized) {
+    CoUninitialize ();
+    sink->comInitialized = FALSE;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (gobject);
+}
+
+static void
+gst_dshowvideosink_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (object);
+
+  switch (prop_id) {
+    case PROP_RENDERER:
+      if (sink->preferredrenderer)
+        g_free (sink->preferredrenderer);
+
+      sink->preferredrenderer = g_value_dup_string (value);
+      break;
+    case PROP_KEEP_ASPECT_RATIO:
+      sink->keep_aspect_ratio = g_value_get_boolean (value);
+      break;
+    case PROP_FULL_SCREEN:
+      sink->full_screen = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static void
+gst_dshowvideosink_get_property (GObject * object, guint prop_id,
+    GValue * value, GParamSpec * pspec)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (object);
+
+  switch (prop_id) {
+    case PROP_RENDERER:
+      g_value_take_string (value, sink->preferredrenderer);
+      break;
+    case PROP_KEEP_ASPECT_RATIO:
+      g_value_set_boolean (value, sink->keep_aspect_ratio);
+      break;
+    case PROP_FULL_SCREEN:
+      g_value_set_boolean (value, sink->full_screen);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static GstCaps *
+gst_dshowvideosink_get_caps (GstBaseSink * basesink)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (basesink);
+
+  return NULL;
+}
+
+static void dump_available_media_types (IPin *pin)
+{
+  /* Enumerate all media types on this pin, output info about them */
+  IEnumMediaTypes *enumerator = NULL;
+  AM_MEDIA_TYPE *type;
+  GstCaps *caps;
+  int i = 0;
+
+  GST_INFO ("Enumerating media types on pin %p", pin);
+
+  pin->EnumMediaTypes (&enumerator);
+
+  while (enumerator->Next (1, &type, NULL) == S_OK) {
+    i++;
+    caps = gst_directshow_media_type_to_caps (type);
+
+    if (caps) {
+      gchar *str = gst_caps_to_string (caps);
+      GST_INFO ("Type %d: converted to caps \"%s\"", i, str);
+      g_free (str);
+
+      gst_caps_unref (caps);
+    }
+    else
+      GST_INFO ("Failed to convert type to GstCaps");
+
+    DeleteMediaType (type);
+  }
+  GST_INFO ("Enumeration complete");
+
+  enumerator->Release();
+}
+
+static void
+dump_all_pin_media_types (IBaseFilter *filter)
+{
+  IEnumPins *enumpins = NULL;
+  IPin *pin = NULL;
+  HRESULT hres; 
+
+  hres = filter->EnumPins (&enumpins);
+  if (FAILED(hres)) {
+    GST_WARNING ("Cannot enumerate pins on filter");
+    return;
+  }
+
+  GST_INFO ("Enumerating pins on filter %p", filter);
+  while (enumpins->Next (1, &pin, NULL) == S_OK)
+  {
+    IMemInputPin *meminputpin;
+    PIN_DIRECTION pindir;
+    hres = pin->QueryDirection (&pindir);
+
+    GST_INFO ("Found a pin with direction: %s", (pindir == PINDIR_INPUT)? "input": "output");
+    dump_available_media_types (pin);
+
+    hres = pin->QueryInterface (
+            IID_IMemInputPin, (void **) &meminputpin);
+    if (hres == S_OK) {
+      GST_INFO ("Pin is a MemInputPin (push mode): %p", meminputpin);
+      meminputpin->Release();
+    }
+    else
+      GST_INFO ("Pin is not a MemInputPin (pull mode?): %p", pin);
+
+    pin->Release();
+  }
+  enumpins->Release();
+}
+
+gboolean 
+gst_dshow_get_pin_from_filter (IBaseFilter *filter, PIN_DIRECTION pindir, IPin **pin)
+{
+  gboolean ret = FALSE;
+  IEnumPins *enumpins = NULL;
+  IPin *pintmp = NULL;
+  HRESULT hres; 
+  *pin = NULL;
+
+  hres = filter->EnumPins (&enumpins);
+  if (FAILED(hres)) {
+    return ret;
+  }
+
+  while (enumpins->Next (1, &pintmp, NULL) == S_OK)
+  {
+    PIN_DIRECTION pindirtmp;
+    hres = pintmp->QueryDirection (&pindirtmp);
+    if (hres == S_OK && pindir == pindirtmp) {
+      *pin = pintmp;
+      ret = TRUE;
+      break;
+    }
+    pintmp->Release ();
+  }
+  enumpins->Release ();
+
+  return ret;
+}
+
+static void 
+gst_dshowvideosink_handle_event (GstDshowVideoSink *sink)
+{
+  if (sink->filter_media_event) {
+    long evCode;
+    LONG_PTR param1, param2;
+    HRESULT hr;
+    while (SUCCEEDED (sink->filter_media_event->GetEvent(&evCode, &param1, &param2, 0)))
+    {
+      GST_INFO_OBJECT (sink, "Received DirectShow graph event code 0x%x", evCode);
+      sink->filter_media_event->FreeEventParams(evCode, param1, param2);
+    }
+  }
+}
+
+/* WNDPROC for application-supplied windows */
+LRESULT APIENTRY WndProcHook (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  /* Handle certain actions specially on the window passed to us.
+   * Then forward back to the original window.
+   */
+  GstDshowVideoSink *sink = (GstDshowVideoSink *)GetProp (hWnd, L"GstDShowVideoSink");
+
+  switch (message) {
+    case WM_GRAPH_NOTIFY:
+      gst_dshowvideosink_handle_event (sink);
+      return 0;
+    case WM_PAINT:
+      sink->renderersupport->PaintWindow ();
+      break;
+    case WM_MOVE:
+    case WM_SIZE:
+      sink->renderersupport->MoveWindow ();
+      break;
+    case WM_DISPLAYCHANGE:
+      sink->renderersupport->DisplayModeChanged();
+      break;
+    case WM_ERASEBKGND:
+      /* DirectShow docs recommend ignoring this message to avoid flicker */
+      return TRUE;
+    case WM_CLOSE:
+      sink->window_closed = TRUE;
+  }
+  return CallWindowProc (sink->prevWndProc, hWnd, message, wParam, lParam);
+}
+
+/* WndProc for our default window, if the application didn't supply one */
+LRESULT APIENTRY 
+WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+  GstDshowVideoSink *sink = (GstDshowVideoSink *)GetWindowLongPtr (hWnd, GWLP_USERDATA);
+
+  if (!sink) {
+    /* I think these happen before we have a chance to set our userdata pointer */
+    GST_DEBUG ("No sink!");
+    return DefWindowProc (hWnd, message, wParam, lParam);
+  }
+
+  //GST_DEBUG_OBJECT (sink, "Got a window message for %x, %x", hWnd, message);
+
+  switch (message) {
+    case WM_GRAPH_NOTIFY:
+      GST_LOG_OBJECT (sink, "GRAPH_NOTIFY WINDOW MESSAGE");
+      gst_dshowvideosink_handle_event (sink);
+      return 0;
+    case WM_PAINT:
+      sink->renderersupport->PaintWindow ();
+      break;
+    case WM_MOVE:
+    case WM_SIZE:
+      sink->renderersupport->MoveWindow ();
+      break;
+    case WM_DISPLAYCHANGE:
+      sink->renderersupport->DisplayModeChanged();
+      break;
+    case WM_ERASEBKGND:
+      /* DirectShow docs recommend ignoring this message */
+      return TRUE;
+    case WM_CLOSE:
+      sink->renderersupport->DestroyWindow ();
+      sink->window_closed = TRUE;
+      return 0;
+  }
+
+  return DefWindowProc (hWnd, message, wParam, lParam);
+}
+
+static gpointer
+gst_dshowvideosink_window_thread (GstDshowVideoSink * sink)
+{
+  WNDCLASS WndClass;
+  int width, height;
+  int offx, offy;
+  DWORD exstyle, style;
+
+  memset (&WndClass, 0, sizeof (WNDCLASS));
+  WndClass.style = CS_HREDRAW | CS_VREDRAW;
+  WndClass.hInstance = GetModuleHandle (NULL);
+  WndClass.lpszClassName = L"GST-DShowSink";
+  WndClass.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
+  WndClass.cbClsExtra = 0;
+  WndClass.cbWndExtra = 0;
+  WndClass.lpfnWndProc = WndProc;
+  WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
+  RegisterClass (&WndClass);
+
+  if (sink->full_screen) {
+    /* This doesn't seem to work, it returns the wrong values! But when we
+     * later use ShowWindow to show it maximized, it goes to full-screen
+     * anyway. TODO: Figure out why. */
+    width = GetSystemMetrics (SM_CXFULLSCREEN);
+    height = GetSystemMetrics (SM_CYFULLSCREEN);
+    offx = 0;
+    offy = 0;
+
+    style = WS_POPUP; /* No window decorations */
+    exstyle = 0;
+  }
+  else {
+    /* By default, create a normal top-level window, the size 
+     * of the video.
+     */
+    RECT rect;
+    VIDEOINFOHEADER *vi = (VIDEOINFOHEADER *)sink->mediatype.pbFormat;
+
+    /* 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) +
+        (GetSystemMetrics (SM_CYSIZEFRAME) * 2);
+
+    SystemParametersInfo (SPI_GETWORKAREA, NULL, &rect, 0);
+    int screenwidth = rect.right - rect.left;
+    int screenheight = rect.bottom - rect.top;
+    offx = rect.left;
+    offy = rect.top;
+
+    /* Make it fit into the screen without changing the
+     * aspect ratio. */
+    if (width > screenwidth) {
+      double ratio = (double)screenwidth/(double)width;
+      width = screenwidth;
+      height = (int)(height * ratio);
+    }
+    if (height > screenheight) {
+      double ratio = (double)screenheight/(double)height;
+      height = screenheight;
+      width = (int)(width * ratio);
+    }
+
+    style = WS_OVERLAPPEDWINDOW; /* Normal top-level window */
+    exstyle = 0;
+  }
+
+  HWND video_window = CreateWindowEx (exstyle, L"GST-DShowSink",
+      L"GStreamer DirectShow sink default window",
+      style, offx, offy, width, height, NULL, NULL,
+      WndClass.hInstance, NULL);
+  if (video_window == NULL) {
+    GST_ERROR_OBJECT (sink, "Failed to create window!");
+    return NULL;
+  }
+
+  SetWindowLongPtr (video_window, GWLP_USERDATA, (LONG)sink);
+
+  sink->window_id = video_window;
+
+  /* signal application we created a window */
+  gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (sink),
+      (gulong)video_window);
+
+  /* Set the renderer's clipping window */
+  if (!sink->renderersupport->SetRendererWindow (video_window)) {
+    GST_WARNING_OBJECT (sink, "Failed to set video clipping window on filter %p", sink->renderersupport);
+  }
+
+  /* Now show the window, as appropriate */
+  if (sink->full_screen) {
+    ShowWindow (video_window, SW_SHOWMAXIMIZED);
+    ShowCursor (FALSE);
+  }
+  else
+    ShowWindow (video_window, SW_SHOWNORMAL);
+
+  /* Trigger the initial paint of the window */
+  UpdateWindow (video_window);
+
+  ReleaseSemaphore (sink->window_created_signal, 1, NULL);
+
+  /* start message loop processing our default window messages */
+  while (1) {
+    MSG msg;
+
+    if (GetMessage (&msg, video_window, 0, 0) <= 0) {
+      GST_LOG_OBJECT (sink, "our window received WM_QUIT or error.");
+      break;
+    }
+    DispatchMessage (&msg);
+  }
+
+  return NULL;
+}
+
+static gboolean
+gst_dshowvideosink_create_default_window (GstDshowVideoSink * sink)
+{
+  sink->window_created_signal = CreateSemaphore (NULL, 0, 1, NULL);
+  if (sink->window_created_signal == NULL)
+    goto failed;
+
+  sink->window_thread = g_thread_create (
+      (GThreadFunc) gst_dshowvideosink_window_thread, sink, TRUE, NULL);
+
+  /* wait maximum 10 seconds for window to be created */
+  if (WaitForSingleObject (sink->window_created_signal,
+          10000) != WAIT_OBJECT_0)
+    goto failed;
+
+  CloseHandle (sink->window_created_signal);
+  return TRUE;
+
+failed:
+  CloseHandle (sink->window_created_signal);
+  GST_ELEMENT_ERROR (sink, RESOURCE, WRITE,
+      ("Error creating our default window"), (NULL));
+
+  return FALSE;
+}
+
+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_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);
+  SetProp (sink->window_id, L"GstDShowVideoSink", sink);
+  /* This causes the new WNDPROC to become active */
+  SetWindowPos (sink->window_id, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+  if (!sink->renderersupport->SetRendererWindow (sink->window_id)) {
+    GST_WARNING_OBJECT (sink, "Failed to set HWND %x on renderer", sink->window_id);
+    return;
+  }
+
+  /* This tells the renderer where the window is located, needed to 
+   * start drawing in the right place.  */
+  sink->renderersupport->MoveWindow();
+  GST_INFO_OBJECT (sink, "Set renderer window to %x", sink->window_id);
+}
+
+static void
+gst_dshowvideosink_prepare_window (GstDshowVideoSink *sink)
+{
+  HRESULT hres;
+
+  /* Give the app a last chance to supply a window id */
+  if (!sink->window_id) {
+    gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (sink));
+  }
+
+  /* If the app supplied one, use it. Otherwise, go ahead
+   * and create (and use) our own window */
+  if (sink->window_id) {
+    gst_dshowvideosink_set_window_for_renderer (sink);
+  }
+  else {
+    gst_dshowvideosink_create_default_window (sink);
+  }
+
+  if (sink->filter_media_event) {
+    sink->filter_media_event->Release();
+    sink->filter_media_event = NULL;
+  }
+
+  hres = sink->filter_graph->QueryInterface(
+          IID_IMediaEventEx, (void **) &sink->filter_media_event);
+
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink, "Failed to get IMediaEventEx");
+  }
+  else {
+    hres = sink->filter_media_event->SetNotifyWindow ((OAHWND)sink->window_id,
+            WM_GRAPH_NOTIFY, 0);
+    GST_DEBUG_OBJECT (sink, "SetNotifyWindow(%p) returned %x", sink->window_id, hres);
+  }
+}
+
+static gboolean
+gst_dshowvideosink_connect_graph (GstDshowVideoSink *sink)
+{
+  HRESULT hres;
+  IPin *srcpin;
+  IPin *sinkpin;
+
+  GST_INFO_OBJECT (sink, "Connecting DirectShow pins");
+
+  srcpin = sink->fakesrc->GetOutputPin();
+
+  gst_dshow_get_pin_from_filter (sink->renderersupport->GetFilter(), PINDIR_INPUT, 
+      &sinkpin);
+  if (!sinkpin) {
+    GST_WARNING_OBJECT (sink, "Cannot get input pin from Renderer");
+    return FALSE;
+  }
+
+  /* Be warned that this call WILL deadlock unless you call it from
+   * the main thread. Thus, we call this from the state change, not from
+   * setcaps (which happens in a streaming thread).
+   */
+  hres = sink->filter_graph->ConnectDirect (
+           srcpin, sinkpin, NULL);
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink, "Could not connect pins: %x", hres);
+    sinkpin->Release();
+    return FALSE;
+  }
+  sinkpin->Release();
+  return TRUE;
+}
+
+static GstStateChangeReturn
+gst_dshowvideosink_start_graph (GstDshowVideoSink *sink)
+{
+  IMediaControl *control = NULL;
+  HRESULT hres;
+  GstStateChangeReturn ret;
+
+  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);
+
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink, "Failed to get IMediaControl interface");
+    ret = GST_STATE_CHANGE_FAILURE;
+    goto done;
+  }
+
+  GST_INFO_OBJECT (sink, "Running DirectShow graph");
+  hres = control->Run();
+  if (FAILED (hres)) {
+    GST_ERROR_OBJECT (sink,
+        "Failed to run the directshow graph (error=%x)", hres);
+    ret = GST_STATE_CHANGE_FAILURE;
+    goto done;
+  }
+  
+  GST_DEBUG_OBJECT (sink, "DirectShow graph is now running");
+  ret = GST_STATE_CHANGE_SUCCESS;
+
+done:
+  if (control)
+    control->Release();
+
+  return ret;
+}
+static GstStateChangeReturn
+gst_dshowvideosink_pause_graph (GstDshowVideoSink *sink)
+{
+  IMediaControl *control = NULL;
+  GstStateChangeReturn ret;
+  HRESULT hres;
+
+  hres = sink->filter_graph->QueryInterface(
+          IID_IMediaControl, (void **) &control);
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink, "Failed to get IMediaControl interface");
+    ret = GST_STATE_CHANGE_FAILURE;
+    goto done;
+  }
+
+  GST_INFO_OBJECT (sink, "Pausing DirectShow graph");
+  hres = control->Pause();
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink,
+        "Can't pause the directshow graph (error=%x)", hres);
+    ret = GST_STATE_CHANGE_FAILURE;
+    goto done;
+  }
+
+  ret = GST_STATE_CHANGE_SUCCESS;
+
+done:
+  if (control)
+    control->Release();
+
+  return ret;
+}
+
+static GstStateChangeReturn
+gst_dshowvideosink_stop_graph (GstDshowVideoSink *sink)
+{
+  IMediaControl *control = NULL;
+  GstStateChangeReturn ret;
+  HRESULT hres;
+  IPin *sinkpin;
+
+  hres = sink->filter_graph->QueryInterface(
+          IID_IMediaControl, (void **) &control);
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink, "Failed to get IMediaControl interface");
+    ret = GST_STATE_CHANGE_FAILURE;
+    goto done;
+  }
+
+  GST_INFO_OBJECT (sink, "Stopping DirectShow graph");
+  hres = control->Stop();
+  if (FAILED (hres)) {
+    GST_WARNING_OBJECT (sink,
+        "Can't stop the directshow graph (error=%x)", hres);
+    ret = GST_STATE_CHANGE_FAILURE;
+    goto done;
+  }
+
+  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();
+
+  GST_DEBUG_OBJECT (sink, "DirectShow graph has stopped");
+
+  if (sink->window_id) {
+    /* Return control of application window */
+    SetWindowLong (sink->window_id, GWL_WNDPROC, (LONG)sink->prevWndProc);
+    RemoveProp (sink->window_id, L"GstDShowVideoSink");
+    SetWindowPos (sink->window_id, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+    sink->prevWndProc = NULL;
+  }
+  sink->connected = FALSE;
+
+  ret = GST_STATE_CHANGE_SUCCESS;
+
+done:
+  if (control)
+    control->Release();
+
+  return ret;
+}
+
+static GstStateChangeReturn
+gst_dshowvideosink_change_state (GstElement * element, GstStateChange transition)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (element);
+  GstStateChangeReturn ret;
+
+  switch (transition) {
+    case GST_STATE_CHANGE_NULL_TO_READY:
+      break;
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+      ret = gst_dshowvideosink_start_graph (sink);
+      if (ret != GST_STATE_CHANGE_SUCCESS)
+        return ret;
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+      ret = gst_dshowvideosink_pause_graph (sink);
+      break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      ret = gst_dshowvideosink_stop_graph (sink);
+      break;
+    case GST_STATE_CHANGE_READY_TO_NULL:
+      gst_dshowvideosink_clear (sink);
+      break;
+  }
+
+  return ret;
+}
+
+class VMR9Support : public RendererSupport
+{
+private:
+  GstDshowVideoSink *sink;
+  IBaseFilter *filter;
+  IVMRWindowlessControl9 *control;
+  IVMRFilterConfig9 *config;
+  HWND video_window;
+
+public:
+  VMR9Support (GstDshowVideoSink *sink) : 
+      sink(sink), 
+      filter(NULL),
+      control(NULL),
+      config(NULL)
+  {
+  }
+
+  ~VMR9Support() {
+    if (control)
+      control->Release();
+    if (config)
+      config->Release();
+    if (filter)
+      filter->Release();
+  }
+
+  const char *GetName() {
+    return "VideoMixingRenderer9";
+  }
+
+  IBaseFilter *GetFilter() {
+    return filter;
+  }
+
+  gboolean Configure() {
+    HRESULT hres;
+
+    hres = CoCreateInstance (CLSID_VideoMixingRenderer9, NULL, CLSCTX_INPROC,
+        IID_IBaseFilter, (LPVOID *) &filter);
+    if (FAILED (hres)) {
+      GST_ERROR_OBJECT (sink, 
+          "Can't create an instance of renderer (error=%x)",
+          hres);
+      return FALSE;
+    }
+
+    hres = filter->QueryInterface (
+          IID_IVMRFilterConfig9, (void **) &config);
+    if (FAILED (hres)) {
+      GST_WARNING_OBJECT (sink, "VMR9 filter config interface missing: %x", hres);
+      return FALSE;
+    }
+
+    hres = config->SetRenderingMode (VMR9Mode_Windowless);
+    if (FAILED (hres)) {
+      GST_WARNING_OBJECT (sink, "VMR9 couldn't be set to windowless mode: %x", hres);
+      return FALSE;
+    }
+    else {
+      GST_DEBUG_OBJECT (sink, "Set VMR9 (%p) to windowless mode!", filter);
+    }
+
+    /* We can't QI to this until _after_ we've been set to windowless mode. 
+     * Apparently this is against the rules in COM, but that's how it is... */
+    hres = filter->QueryInterface (
+          IID_IVMRWindowlessControl9, (void **) &control);
+    if (FAILED (hres)) {
+      GST_WARNING_OBJECT (sink, "VMR9 windowless control interface missing: %x", hres);
+      return FALSE;
+    }
+
+    if (sink->keep_aspect_ratio) {
+      control->SetAspectRatioMode(VMR9ARMode_LetterBox);
+    }
+    else {
+      control->SetAspectRatioMode(VMR9ARMode_None);
+    }
+    return TRUE;
+  }
+
+  gboolean SetRendererWindow(HWND window) {
+    video_window = window;
+    HRESULT hres = control->SetVideoClippingWindow (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(video_window, hdc);
+
+    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() {
+    control->DisplayModeChanged();
+  }
+
+  void DestroyWindow() {
+    ::DestroyWindow (video_window);
+  }
+};
+
+class VMR7Support : public RendererSupport
+{
+private:
+  GstDshowVideoSink *sink;
+  IBaseFilter *filter;
+  IVMRWindowlessControl *control;
+  IVMRFilterConfig *config;
+  HWND video_window;
+
+public:
+  VMR7Support (GstDshowVideoSink *sink) : 
+      sink(sink), 
+      filter(NULL),
+      control(NULL),
+      config(NULL)
+  {
+  }
+
+  ~VMR7Support() {
+    if (control)
+      control->Release();
+    if (config)
+      config->Release();
+    if (filter)
+      filter->Release();
+  }
+
+  const char *GetName() {
+    return "VideoMixingRenderer";
+  }
+
+  IBaseFilter *GetFilter() {
+    return filter;
+  }
+
+  gboolean Configure() {
+    HRESULT hres;
+
+    hres = CoCreateInstance (CLSID_VideoMixingRenderer, NULL, CLSCTX_INPROC,
+        IID_IBaseFilter, (LPVOID *) &filter);
+    if (FAILED (hres)) {
+      GST_ERROR_OBJECT (sink, 
+          "Can't create an instance of renderer (error=%x)",
+          hres);
+      return FALSE;
+    }
+
+    hres = filter->QueryInterface (
+          IID_IVMRFilterConfig, (void **) &config);
+    if (FAILED (hres)) {
+      GST_WARNING_OBJECT (sink, "VMR filter config interface missing: %x", hres);
+      return FALSE;
+    }
+
+    hres = config->SetRenderingMode (VMRMode_Windowless);
+    if (FAILED (hres)) {
+      GST_WARNING_OBJECT (sink, "VMR couldn't be set to windowless mode: %x", hres);
+      return FALSE;
+    }
+    else {
+      GST_DEBUG_OBJECT (sink, "Set VMR (%p) to windowless mode!", filter);
+    }
+
+    hres = filter->QueryInterface (
+          IID_IVMRWindowlessControl, (void **) &control);
+    if (FAILED (hres)) {
+      GST_WARNING_OBJECT (sink, "VMR windowless control interface missing: %x", hres);
+      return FALSE;
+    }
+
+    if (sink->keep_aspect_ratio) {
+      control->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
+    }
+    else {
+      control->SetAspectRatioMode(VMR_ARMODE_NONE);
+    }
+    return TRUE;
+  }
+
+  gboolean SetRendererWindow(HWND window) {
+    video_window = window;
+    HRESULT hres = control->SetVideoClippingWindow (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(video_window, hdc);
+
+    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() {
+    control->DisplayModeChanged();
+  }
+
+  void DestroyWindow() {
+    ::DestroyWindow (video_window);
+  }
+};
+
+static gboolean 
+gst_dshowvideosink_create_renderer (GstDshowVideoSink *sink) 
+{
+  GST_DEBUG_OBJECT (sink, "Trying to create renderer '%s'", "VMR9");
+
+  RendererSupport *support = NULL;
+
+  if (sink->preferredrenderer) {
+    if (!strcmp (sink->preferredrenderer, "VMR9")) {
+      GST_INFO_OBJECT (sink, "Forcing use of VMR9");
+      support = new VMR9Support (sink);
+    }
+    else if (!strcmp (sink->preferredrenderer, "VMR")) {
+      GST_INFO_OBJECT (sink, "Forcing use of VMR");
+      support = new VMR7Support (sink);
+    }
+    else {
+      GST_ERROR_OBJECT (sink, "Unknown sink type '%s'", sink->preferredrenderer);
+      return FALSE;
+    }
+
+    if (!support->Configure()) {
+      GST_ERROR_OBJECT (sink, "Couldn't configure selected renderer");
+      delete support;
+      return FALSE;
+    }
+    goto done;
+  }
+
+  support = new VMR9Support (sink);
+  if (!support->Configure()) {
+    GST_INFO_OBJECT (sink, "Failed to configure VMR9, trying VMR7");
+    delete support;
+    support = new VMR7Support (sink);
+    if (!support->Configure()) {
+      GST_ERROR_OBJECT (sink, "Failed to configure VMR9 or VMR7");
+      delete support;
+      return FALSE;
+    }
+  }
+
+done:
+  sink->renderersupport = support;
+  return TRUE;
+}
+
+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: 
+   *
+   *    [ fakesrc ] -> [ sink filter ]
+   *
+   * so we can feed data in through the fakesrc.
+   *
+   * The sink filter can be one of our supported filters: VMR9 (VMR7?, EMR?)
+   */
+
+  hres = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
+      IID_IFilterGraph, (LPVOID *) & sink->filter_graph);
+  if (FAILED (hres)) {
+    GST_ERROR_OBJECT (sink, 
+          "Can't create an instance of the dshow graph manager (error=%x)", hres);
+    goto error;
+  }
+
+  sink->fakesrc = new VideoFakeSrc();
+
+  IBaseFilter *filter;
+  hres = sink->fakesrc->QueryInterface (
+          IID_IBaseFilter, (void **) &filter);
+  if (FAILED (hres)) {
+    GST_ERROR_OBJECT (sink, "Could not QI fakesrc to IBaseFilter");
+    goto error;
+  }
+
+  hres = sink->filter_graph->AddFilter (filter, L"fakesrc");
+  if (FAILED (hres)) {
+    GST_ERROR_OBJECT (sink,
+        "Can't add our fakesrc filter to the graph (error=%x)", hres);
+    goto error;
+  }
+
+  if (!gst_dshowvideosink_create_renderer (sink)) {
+    GST_ERROR_OBJECT (sink, "Could not create a video renderer");
+    goto error;
+  }
+
+  /* dump_all_pin_media_types (sink->renderer); */
+
+  hres =
+      sink->filter_graph->AddFilter (sink->renderersupport->GetFilter(),
+      L"renderer");
+  if (FAILED (hres)) {
+    GST_ERROR_OBJECT (sink, 
+          "Can't add renderer to the graph (error=%x)", hres);
+    goto error;
+  }
+
+  if (comInit)
+    CoUninitialize();
+  return TRUE;
+
+error:
+  if (sink->fakesrc) {
+    sink->fakesrc->Release();
+    sink->fakesrc = NULL;
+  }
+
+  if (sink->filter_graph) {
+    sink->filter_graph->Release();
+    sink->filter_graph = NULL;
+  }
+
+  if (sink->filter_media_event) {
+    sink->filter_media_event->Release();
+    sink->filter_media_event = NULL;
+  }
+
+  if (comInit)
+    CoUninitialize();
+
+  return FALSE;
+}
+
+static gboolean
+gst_dshowvideosink_start (GstBaseSink * bsink)
+{
+  HRESULT hres = S_FALSE;
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
+
+  /* Just build the filtergraph; we don't link or otherwise configure it yet */
+  return gst_dshowvideosink_build_filtergraph (sink);
+}
+
+static gboolean
+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;
+  }
+
+  if (!gst_caps_to_directshow_media_type (caps, &sink->mediatype)) {
+    GST_WARNING_OBJECT (sink, "Cannot convert caps to AM_MEDIA_TYPE, rejecting");
+    return FALSE;
+  }
+
+  GST_DEBUG_OBJECT (sink, "Configuring output pin media type");
+  /* Now we have an AM_MEDIA_TYPE describing what we're going to send.
+   * We set this on our DirectShow fakesrc's output pin. 
+   */
+  sink->fakesrc->GetOutputPin()->SetMediaType (&sink->mediatype);
+  GST_DEBUG_OBJECT (sink, "Configured output pin media type");
+
+  return TRUE;
+}
+
+static gboolean
+gst_dshowvideosink_stop (GstBaseSink * bsink)
+{
+  IPin *input_pin = NULL, *output_pin = NULL;
+  HRESULT hres = S_FALSE;
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
+
+  if (!sink->filter_graph) {
+    GST_WARNING_OBJECT (sink, "Cannot destroy filter graph; it doesn't exist");
+    return TRUE;
+  }
+
+  /* Release the renderer */
+  if (sink->renderersupport) {
+    delete sink->renderersupport;
+    sink->renderersupport = NULL;
+  }
+
+  /* Release our dshow fakesrc */
+  if (sink->fakesrc) {
+    sink->fakesrc->Release();
+    sink->fakesrc = NULL;
+  }
+
+  /* Release the filter graph manager */
+  if (sink->filter_graph) {
+    sink->filter_graph->Release();
+    sink->filter_graph = NULL;
+  }
+
+  return TRUE;
+}
+
+static GstFlowReturn 
+gst_dshowvideosink_render (GstBaseSink *bsink, GstBuffer *buffer)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
+  GstFlowReturn ret;
+
+  if (sink->window_closed) {
+    GST_WARNING_OBJECT (sink, "Window has been closed, stopping");
+    return GST_FLOW_ERROR;
+  }
+
+  GST_DEBUG_OBJECT (sink, "Pushing buffer through fakesrc->renderer");
+  ret = sink->fakesrc->GetOutputPin()->PushBuffer (buffer);
+  GST_DEBUG_OBJECT (sink, "Done pushing buffer through fakesrc->renderer: %s", gst_flow_get_name(ret));
+
+  return ret;
+}
+
+/* TODO: How can we implement these? Figure that out... */
+static gboolean
+gst_dshowvideosink_unlock (GstBaseSink * bsink)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
+
+  return TRUE;
+}
+
+static gboolean
+gst_dshowvideosink_unlock_stop (GstBaseSink * bsink)
+{
+  GstDshowVideoSink *sink = GST_DSHOWVIDEOSINK (bsink);
+
+  return TRUE;
+}
+
+/* TODO: Move all of this into generic code? */
+
+/* Helpers to format GUIDs the same way we find them in the source */
+#define GUID_FORMAT "{%.8x, %.4x, %.4x, { %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x, %.2x }}"
+#define GUID_ARGS(guid) \
+    guid.Data1, guid.Data2, guid.Data3, \
+    guid.Data4[0], guid.Data4[1], guid.Data4[3], guid.Data4[4], \
+    guid.Data4[5], guid.Data4[6], guid.Data4[7], guid.Data4[8]
+
+static GstCaps *
+audio_media_type_to_caps (AM_MEDIA_TYPE *mediatype)
+{
+  return NULL;
+}
+
+static GstCaps *
+video_media_type_to_caps (AM_MEDIA_TYPE *mediatype)
+{
+  GstCaps *caps = NULL;
+
+  /* TODO: Add  RGB types. */
+  if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_YUY2))
+    caps = gst_caps_new_simple ("video/x-raw-yuv", 
+            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'), NULL);
+  else if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_UYVY))
+    caps = gst_caps_new_simple ("video/x-raw-yuv", 
+            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'), NULL);
+  else if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_YUYV))
+    caps = gst_caps_new_simple ("video/x-raw-yuv", 
+            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V'), NULL);
+  else if (IsEqualGUID (mediatype->subtype, MEDIASUBTYPE_YV12))
+    caps = gst_caps_new_simple ("video/x-raw-yuv", 
+            "format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('Y', 'V', '1', '2'), NULL);
+
+  if (!caps) {
+    GST_DEBUG ("No subtype known; cannot continue");
+    return NULL;
+  }
+
+  if (IsEqualGUID (mediatype->formattype, FORMAT_VideoInfo) &&
+          mediatype->cbFormat >= sizeof(VIDEOINFOHEADER))
+  {
+    VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)mediatype->pbFormat;
+
+       /* TODO: Set PAR here. Based on difference between source and target RECTs? 
+     *       Do we want framerate? Based on AvgTimePerFrame? */
+    gst_caps_set_simple (caps, 
+            "width", G_TYPE_INT, vh->bmiHeader.biWidth,
+            "height", G_TYPE_INT, vh->bmiHeader.biHeight,
+                       NULL);
+  }
+
+  return caps;
+}
+
+
+/* Create a GstCaps object representing the same media type as
+ * this AM_MEDIA_TYPE.
+ *
+ * Returns NULL if no corresponding GStreamer type is known.
+ *
+ * May modify mediatype.
+ */
+static GstCaps *
+gst_directshow_media_type_to_caps (AM_MEDIA_TYPE *mediatype)
+{
+  GstCaps *caps = NULL;
+
+  if (IsEqualGUID (mediatype->majortype, MEDIATYPE_Video))
+    caps = video_media_type_to_caps (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));
+  }
+
+  if (caps) {
+    gchar *capsstring = gst_caps_to_string (caps);
+    GST_DEBUG ("Converted AM_MEDIA_TYPE to \"%s\"", capsstring);
+    g_free (capsstring);
+  }
+  else {
+    GST_WARNING ("Failed to convert AM_MEDIA_TYPE to caps");
+  }
+
+  return caps;
+}
+
+/* Fill in a DirectShow AM_MEDIA_TYPE structure representing the same media
+ * type as this GstCaps object.
+ *
+ * Returns FALSE if no corresponding type is known.
+ *
+ * Only operates on simple (single structure) caps.
+ */
+static gboolean
+gst_caps_to_directshow_media_type (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);
+  g_free (capsstring);
+
+  memset (mediatype, 0, sizeof (AM_MEDIA_TYPE));
+
+  if (!strcmp (name, "video/x-raw-yuv")) {
+    guint32 fourcc;
+       int width, height;
+    int bpp;
+
+    if (!gst_structure_get_fourcc (s, "format", &fourcc)) {
+      GST_WARNING ("Failed to convert caps, no fourcc");
+      return FALSE;
+    }
+
+       if (!gst_structure_get_int (s, "width", &width)) {
+      GST_WARNING ("Failed to convert caps, no width");
+      return FALSE;
+    }
+       if (!gst_structure_get_int (s, "height", &height)) {
+      GST_WARNING ("Failed to convert caps, no height");
+      return FALSE;
+    }
+
+    mediatype->majortype = MEDIATYPE_Video;
+    switch (fourcc) {
+      case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
+        mediatype->subtype = MEDIASUBTYPE_YUY2;
+        bpp = 16;
+        break;
+      case GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V'):
+        mediatype->subtype = MEDIASUBTYPE_YUYV;
+        bpp = 16;
+        break;
+      case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
+        mediatype->subtype = MEDIASUBTYPE_UYVY;
+        bpp = 16;
+        break;
+      case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
+        mediatype->subtype = MEDIASUBTYPE_YV12;
+        bpp = 12;
+        break;
+      default:
+        GST_WARNING ("Failed to convert caps, not a known fourcc");
+        return FALSE;
+    }
+
+    mediatype->bFixedSizeSamples = TRUE; /* Always true for raw video */
+    mediatype->bTemporalCompression = FALSE; /* Likewise, always false */
+
+    {
+      int par_n, par_d;
+      VIDEOINFOHEADER *vi = (VIDEOINFOHEADER *)CoTaskMemAlloc (sizeof (VIDEOINFOHEADER));
+      memset (vi, 0, sizeof (VIDEOINFOHEADER));
+
+      mediatype->formattype = FORMAT_VideoInfo;
+      mediatype->cbFormat = sizeof (VIDEOINFOHEADER);
+      mediatype->pbFormat = (BYTE *)vi;
+
+      mediatype->lSampleSize = width * height * bpp / 8;
+
+      GST_INFO ("Set mediatype format: size %d, sample size %d", mediatype->cbFormat, mediatype->lSampleSize);
+
+      vi->rcSource.top = 0;
+      vi->rcSource.left = 0;
+      vi->rcSource.bottom = height;
+      vi->rcSource.right = width;
+
+      vi->rcTarget.top = 0;
+      vi->rcTarget.left = 0;
+      if (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);
+      }
+      else {
+        GST_DEBUG ("No PAR found");
+        vi->rcTarget.bottom = height;
+        vi->rcTarget.right = width;
+      }
+
+      vi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
+      vi->bmiHeader.biWidth = width;
+      vi->bmiHeader.biHeight = -height; /* Required to be negative. */
+      vi->bmiHeader.biPlanes = 1; /* Required to be 1 */
+      vi->bmiHeader.biBitCount = bpp;
+      vi->bmiHeader.biCompression = fourcc;
+      vi->bmiHeader.biSizeImage = width * height * bpp / 8;
+
+      /* We can safely zero these; they don't matter for our uses */
+      vi->bmiHeader.biXPelsPerMeter = 0;
+      vi->bmiHeader.biYPelsPerMeter = 0;
+      vi->bmiHeader.biClrUsed = 0;
+      vi->bmiHeader.biClrImportant = 0;
+    }
+
+    GST_DEBUG ("Successfully built AM_MEDIA_TYPE from caps");
+    return TRUE;
+  }
+
+  GST_WARNING ("Failed to convert caps, not a known caps type");
+  /* Only YUV supported so far */
+
+  return FALSE;
+}
+
+/* Plugin entry point */
+extern "C" static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  /* PRIMARY: this is the best videosink to use on windows */
+  if (!gst_element_register (plugin, "dshowvideosink",
+          GST_RANK_PRIMARY, GST_TYPE_DSHOWVIDEOSINK))
+    return FALSE;
+
+  return TRUE;
+}
+
+extern "C" GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+    GST_VERSION_MINOR,
+    "dshowsinkwrapper",
+    "DirectShow sink wrapper plugin",
+    plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
index fd03c2b7e39c7b72e0956026ea3d60be8f62e243..d5ea9e916aaac6d9099730dfbbae623b01c3577d 100644 (file)
@@ -65,6 +65,8 @@ struct _GstDshowVideoSink
    /* The filter graph (DirectShow equivalent to pipeline */
   IFilterGraph *filter_graph;
 
+  IMediaEventEx *filter_media_event;
+
   /* Renderer wrapper (EVR, VMR9, or VMR) and support code */
   RendererSupport *renderersupport;