2 * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
20 #include "dshowvideofakesrc.h"
22 GST_DEBUG_CATEGORY_EXTERN (dshowvideosink_debug);
23 #define GST_CAT_DEFAULT dshowvideosink_debug
25 // {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}
26 const GUID CLSID_VideoFakeSrc =
27 { 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };
30 VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):
31 CDynamicOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")
35 VideoFakeSrcPin::~VideoFakeSrcPin()
39 HRESULT VideoFakeSrcPin::GetMediaType(int iPosition, CMediaType *pMediaType)
41 GST_DEBUG ("GetMediaType(%d) called", iPosition);
43 *pMediaType = m_MediaType;
47 return VFW_S_NO_MORE_ITEMS;
50 /* This seems to be called to notify us of the actual media type being used,
51 * even though SetMediaType isn't called. How bizarre! */
52 HRESULT VideoFakeSrcPin::CheckMediaType(const CMediaType *pmt)
54 GST_DEBUG ("CheckMediaType called: %p", pmt);
56 /* The video renderer will request a different stride, which we must accept.
57 * So, we accept arbitrary strides (and do memcpy() to convert if needed),
58 * and require the rest of the media type to match
60 if (IsEqualGUID(pmt->majortype,m_MediaType.majortype) &&
61 IsEqualGUID(pmt->subtype,m_MediaType.subtype) &&
62 IsEqualGUID(pmt->formattype,m_MediaType.formattype) &&
63 pmt->cbFormat >= m_MediaType.cbFormat)
65 if (IsEqualGUID(pmt->formattype, FORMAT_VideoInfo)) {
66 VIDEOINFOHEADER *newvh = (VIDEOINFOHEADER *)pmt->pbFormat;
67 VIDEOINFOHEADER *curvh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
69 if ((memcmp ((void *)&newvh->rcSource, (void *)&curvh->rcSource, sizeof (RECT)) == 0) &&
70 (memcmp ((void *)&newvh->rcTarget, (void *)&curvh->rcTarget, sizeof (RECT)) == 0) &&
71 (newvh->bmiHeader.biCompression == curvh->bmiHeader.biCompression) &&
72 (newvh->bmiHeader.biHeight == curvh->bmiHeader.biHeight) &&
73 (newvh->bmiHeader.biWidth >= curvh->bmiHeader.biWidth))
75 GST_DEBUG ("CheckMediaType has same media type, width %d (%d image)", newvh->bmiHeader.biWidth, curvh->bmiHeader.biWidth);
81 GST_WARNING ("Looked similar, but aren't...");
86 GST_WARNING ("Different media types, FAILING!");
90 HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
92 ALLOCATOR_PROPERTIES properties;
93 GST_DEBUG ("Required allocator properties: %d, %d, %d, %d",
94 ppropInputRequest->cbAlign, ppropInputRequest->cbBuffer,
95 ppropInputRequest->cbPrefix, ppropInputRequest->cBuffers);
97 ppropInputRequest->cbBuffer = m_SampleSize;
98 ppropInputRequest->cBuffers = 1;
100 /* First set the buffer descriptions we're interested in */
101 HRESULT hres = pAlloc->SetProperties(ppropInputRequest, &properties);
102 GST_DEBUG ("Actual Allocator properties: %d, %d, %d, %d",
103 properties.cbAlign, properties.cbBuffer,
104 properties.cbPrefix, properties.cBuffers);
110 VideoFakeSrcPin::Notify(IBaseFilter * pSender, Quality q)
112 /* Implementing this usefully is not required, but the base class
113 * has an assertion here... */
114 /* TODO: Map this to GStreamer QOS events? */
118 STDMETHODIMP VideoFakeSrcPin::SetMediaType (AM_MEDIA_TYPE *pmt)
120 m_MediaType.Set (*pmt);
121 m_SampleSize = m_MediaType.GetSampleSize();
123 GST_DEBUG ("SetMediaType called. SampleSize is %d", m_SampleSize);
128 /* If the destination buffer is a different shape (strides, etc.) from the source
129 * buffer, we have to copy. Do that here, for supported video formats.
131 * TODO: When possible (when these things DON'T differ), we should buffer-alloc the
132 * final output buffer, and not do this copy */
133 STDMETHODIMP VideoFakeSrcPin::CopyToDestinationBuffer (byte *srcbuf, byte *dstbuf)
135 VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
136 GST_DEBUG ("Rendering a frame");
139 int dststride, srcstride, rows;
140 guint32 fourcc = vh->bmiHeader.biCompression;
142 /* biHeight is always negative; we don't want that. */
143 int height = ABS (vh->bmiHeader.biHeight);
144 int width = vh->bmiHeader.biWidth;
146 /* YUY2 is the preferred layout for DirectShow, so we will probably get this
147 * most of the time */
148 if ((fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')) ||
149 (fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V')) ||
150 (fourcc == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y')))
152 /* Nice and simple */
153 int srcstride = GST_ROUND_UP_4 (vh->rcSource.right * 2);
154 int dststride = width * 2;
156 for (int i = 0; i < height; i++) {
157 memcpy (dstbuf + dststride * i, srcbuf + srcstride * i, srcstride);
160 else if (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')) {
161 for (int component = 0; component < 3; component++) {
162 // TODO: Get format properly rather than hard-coding it. Use gst_video_* APIs *?
163 if (component == 0) {
164 srcstride = GST_ROUND_UP_4 (vh->rcSource.right);
168 srcstride = GST_ROUND_UP_4 ( GST_ROUND_UP_2 (vh->rcSource.right) / 2);
170 src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom);
172 src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom) +
173 srcstride * (GST_ROUND_UP_2 (vh->rcSource.bottom) / 2);
176 /* Is there a better way to do this? This is ICK! */
177 if (component == 0) {
181 } else if (component == 1) {
182 dststride = width / 2;
183 dst = dstbuf + width * height;
187 dststride = width / 2;
188 dst = dstbuf + width * height +
193 for (int i = 0; i < rows; i++) {
194 memcpy (dst + i * dststride, src + i * srcstride, srcstride);
202 STDMETHODIMP VideoFakeSrcPin::Disconnect ()
204 GST_DEBUG_OBJECT (this, "Disconnecting pin");
205 HRESULT hr = CDynamicOutputPin::Disconnect();
206 GST_DEBUG_OBJECT (this, "Pin disconnected");
210 HRESULT VideoFakeSrcPin::Inactive ()
212 GST_DEBUG_OBJECT (this, "Pin going inactive");
213 HRESULT hr = CDynamicOutputPin::Inactive();
214 GST_DEBUG_OBJECT (this, "Pin inactivated");
218 HRESULT VideoFakeSrcPin::BreakConnect ()
220 GST_DEBUG_OBJECT (this, "Breaking connection");
221 HRESULT hr = CDynamicOutputPin::BreakConnect();
222 GST_DEBUG_OBJECT (this, "Connection broken");
226 HRESULT VideoFakeSrcPin::CompleteConnect (IPin *pReceivePin)
228 GST_DEBUG_OBJECT (this, "Completing connection");
229 HRESULT hr = CDynamicOutputPin::CompleteConnect(pReceivePin);
230 GST_DEBUG_OBJECT (this, "Completed connection: %x", hr);
234 STDMETHODIMP VideoFakeSrcPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
236 GST_DEBUG_OBJECT (this, "Calling Block()");
237 HRESULT hr = CDynamicOutputPin::Block (dwBlockFlags, hEvent);
238 GST_DEBUG_OBJECT (this, "Called Block()");
242 /* When moving the video to a different monitor, directshow stops and restarts the playback pipeline.
243 * Unfortunately, it doesn't properly block pins or do anything special, so we racily just fail
245 * So, we try multiple times in a loop, hoping that it'll have finished (we get no notifications at all!)
248 #define MAX_ATTEMPTS 10
250 GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
252 IMediaSample *pSample = NULL;
258 AM_MEDIA_TYPE *mediatype;
260 /* FIXME: check return value. */
261 gst_buffer_map (buffer, &map, GST_MAP_READ);
263 StartUsingOutputPin();
265 while (attempts < MAX_ATTEMPTS)
267 hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
268 if (SUCCEEDED (hres))
276 StopUsingOutputPin();
277 GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
278 return GST_FLOW_ERROR;
281 pSample->GetPointer(&sample_buffer);
282 pSample->GetMediaType(&mediatype);
284 SetMediaType (mediatype);
288 /* Copy to the destination stride.
289 * This is not just a simple memcpy because of the different strides.
290 * TODO: optimise for the same-stride case and avoid the copy entirely.
292 CopyToDestinationBuffer (data, sample_buffer);
294 gst_buffer_unmap (buffer, &map);
296 pSample->SetDiscontinuity(FALSE); /* Decoded frame; unimportant */
297 pSample->SetSyncPoint(TRUE); /* Decoded frame; always a valid syncpoint */
298 pSample->SetPreroll(FALSE); /* For non-displayed frames.
299 Not used in GStreamer */
301 /* Disable synchronising on this sample. We instead let GStreamer handle
302 * this at a higher level, inside BaseSink. */
303 pSample->SetTime(NULL, NULL);
305 while (attempts < MAX_ATTEMPTS)
307 hres = Deliver(pSample);
308 if (SUCCEEDED (hres))
316 StopUsingOutputPin();
318 if (SUCCEEDED (hres))
321 GST_WARNING_OBJECT (this, "Failed to deliver sample: %x", hres);
322 if (hres == VFW_E_NOT_CONNECTED)
323 return GST_FLOW_NOT_LINKED;
325 return GST_FLOW_ERROR;
329 STDMETHODIMP VideoFakeSrcPin::Flush ()
336 VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc),
337 m_evFilterStoppingEvent(TRUE)
340 m_pOutputPin = new VideoFakeSrcPin ((CSource *)this, &m_critsec, &hr);
343 int VideoFakeSrc::GetPinCount()
348 CBasePin *VideoFakeSrc::GetPin(int n)
350 return (CBasePin *)m_pOutputPin;
353 VideoFakeSrcPin *VideoFakeSrc::GetOutputPin()
358 STDMETHODIMP VideoFakeSrc::Stop(void)
360 GST_DEBUG_OBJECT (this, "Stop()");
361 m_evFilterStoppingEvent.Set();
363 return CBaseFilter::Stop();
366 STDMETHODIMP VideoFakeSrc::Pause(void)
368 GST_DEBUG_OBJECT (this, "Pause()");
370 m_evFilterStoppingEvent.Reset();
372 return CBaseFilter::Pause();
375 STDMETHODIMP VideoFakeSrc::Run(REFERENCE_TIME tStart)
377 GST_DEBUG_OBJECT (this, "Run()");
379 return CBaseFilter::Run(tStart);
382 STDMETHODIMP VideoFakeSrc::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName)
386 // The filter is joining the filter graph.
389 IGraphConfig* pGraphConfig = NULL;
390 hr = pGraph->QueryInterface(IID_IGraphConfig, (void**)&pGraphConfig);
394 hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
397 pGraphConfig->Release();
401 m_pOutputPin->SetConfigInfo(pGraphConfig, m_evFilterStoppingEvent);
402 pGraphConfig->Release();
406 hr = CBaseFilter::JoinFilterGraph(pGraph, pName);
410 m_pOutputPin->SetConfigInfo(NULL, NULL);