summaryrefslogtreecommitdiffstats
path: root/sys/dshowvideosink/dshowvideofakesrc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dshowvideosink/dshowvideofakesrc.cpp')
-rw-r--r--sys/dshowvideosink/dshowvideofakesrc.cpp695
1 files changed, 414 insertions, 281 deletions
diff --git a/sys/dshowvideosink/dshowvideofakesrc.cpp b/sys/dshowvideosink/dshowvideofakesrc.cpp
index 541e3885..a2de4088 100644
--- a/sys/dshowvideosink/dshowvideofakesrc.cpp
+++ b/sys/dshowvideosink/dshowvideofakesrc.cpp
@@ -1,281 +1,414 @@
-/* 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"
-
-// {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):
- CBaseOutputPin("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 */
- pAlloc->Commit();
-
- 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;
-}
-
-
-GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
-{
- IMediaSample *pSample = NULL;
-
- byte *data = GST_BUFFER_DATA (buffer);
-
- /* TODO: Use more of the arguments here? */
- HRESULT hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
- if (SUCCEEDED (hres))
- {
- BYTE *sample_buffer;
- AM_MEDIA_TYPE *mediatype;
-
- 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);
-
- hres = Deliver(pSample);
- pSample->Release();
-
- if (SUCCEEDED (hres))
- return GST_FLOW_OK;
- else if (hres == VFW_E_NOT_CONNECTED)
- return GST_FLOW_NOT_LINKED;
- else
- return GST_FLOW_ERROR;
- }
- else {
- GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
- return GST_FLOW_ERROR;
- }
-}
-
-STDMETHODIMP VideoFakeSrcPin::Flush ()
-{
- DeliverBeginFlush();
- DeliverEndFlush();
- return S_OK;
-}
-
-VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc)
-{
- 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;
-}
+/* 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;
+}
+