Handle many more edge cases in dshowvideosink.
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.
This commit is contained in:
parent
e3fcf51e2c
commit
2e401cc71d
@ -1,281 +1,414 @@
|
|||||||
/* GStreamer
|
/* GStreamer
|
||||||
* Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
* Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
* License as published by the Free Software Foundation; either
|
* License as published by the Free Software Foundation; either
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* version 2 of the License, or (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* This library is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
* Library General Public License for more details.
|
* Library General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Library General Public
|
* You should have received a copy of the GNU Library General Public
|
||||||
* License along with this library; if not, write to the
|
* License along with this library; if not, write to the
|
||||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dshowvideofakesrc.h"
|
#include "dshowvideofakesrc.h"
|
||||||
|
|
||||||
// {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}
|
GST_DEBUG_CATEGORY_EXTERN (dshowvideosink_debug);
|
||||||
const GUID CLSID_VideoFakeSrc =
|
#define GST_CAT_DEFAULT dshowvideosink_debug
|
||||||
{ 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };
|
|
||||||
|
// {A0A5CF33-BD0C-4158-9A56-3011DEE3AF6B}
|
||||||
/* output pin*/
|
const GUID CLSID_VideoFakeSrc =
|
||||||
VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):
|
{ 0xa0a5cf33, 0xbd0c, 0x4158, { 0x9a, 0x56, 0x30, 0x11, 0xde, 0xe3, 0xaf, 0x6b } };
|
||||||
CBaseOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")
|
|
||||||
{
|
/* output pin*/
|
||||||
}
|
VideoFakeSrcPin::VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres):
|
||||||
|
CDynamicOutputPin("VideoFakeSrcPin", pFilter, sec, hres, L"output")
|
||||||
VideoFakeSrcPin::~VideoFakeSrcPin()
|
{
|
||||||
{
|
}
|
||||||
}
|
|
||||||
|
VideoFakeSrcPin::~VideoFakeSrcPin()
|
||||||
HRESULT VideoFakeSrcPin::GetMediaType(int iPosition, CMediaType *pMediaType)
|
{
|
||||||
{
|
}
|
||||||
GST_DEBUG ("GetMediaType(%d) called", iPosition);
|
|
||||||
if(iPosition == 0) {
|
HRESULT VideoFakeSrcPin::GetMediaType(int iPosition, CMediaType *pMediaType)
|
||||||
*pMediaType = m_MediaType;
|
{
|
||||||
return S_OK;
|
GST_DEBUG ("GetMediaType(%d) called", iPosition);
|
||||||
}
|
if(iPosition == 0) {
|
||||||
|
*pMediaType = m_MediaType;
|
||||||
return VFW_S_NO_MORE_ITEMS;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This seems to be called to notify us of the actual media type being used,
|
return VFW_S_NO_MORE_ITEMS;
|
||||||
* even though SetMediaType isn't called. How bizarre! */
|
}
|
||||||
HRESULT VideoFakeSrcPin::CheckMediaType(const CMediaType *pmt)
|
|
||||||
{
|
/* This seems to be called to notify us of the actual media type being used,
|
||||||
GST_DEBUG ("CheckMediaType called: %p", pmt);
|
* even though SetMediaType isn't called. How bizarre! */
|
||||||
|
HRESULT VideoFakeSrcPin::CheckMediaType(const CMediaType *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),
|
GST_DEBUG ("CheckMediaType called: %p", pmt);
|
||||||
* and require the rest of the media type to match
|
|
||||||
*/
|
/* The video renderer will request a different stride, which we must accept.
|
||||||
if (IsEqualGUID(pmt->majortype,m_MediaType.majortype) &&
|
* So, we accept arbitrary strides (and do memcpy() to convert if needed),
|
||||||
IsEqualGUID(pmt->subtype,m_MediaType.subtype) &&
|
* and require the rest of the media type to match
|
||||||
IsEqualGUID(pmt->formattype,m_MediaType.formattype) &&
|
*/
|
||||||
pmt->cbFormat >= m_MediaType.cbFormat)
|
if (IsEqualGUID(pmt->majortype,m_MediaType.majortype) &&
|
||||||
{
|
IsEqualGUID(pmt->subtype,m_MediaType.subtype) &&
|
||||||
if (IsEqualGUID(pmt->formattype, FORMAT_VideoInfo)) {
|
IsEqualGUID(pmt->formattype,m_MediaType.formattype) &&
|
||||||
VIDEOINFOHEADER *newvh = (VIDEOINFOHEADER *)pmt->pbFormat;
|
pmt->cbFormat >= m_MediaType.cbFormat)
|
||||||
VIDEOINFOHEADER *curvh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
|
{
|
||||||
|
if (IsEqualGUID(pmt->formattype, FORMAT_VideoInfo)) {
|
||||||
if ((memcmp ((void *)&newvh->rcSource, (void *)&curvh->rcSource, sizeof (RECT)) == 0) &&
|
VIDEOINFOHEADER *newvh = (VIDEOINFOHEADER *)pmt->pbFormat;
|
||||||
(memcmp ((void *)&newvh->rcTarget, (void *)&curvh->rcTarget, sizeof (RECT)) == 0) &&
|
VIDEOINFOHEADER *curvh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
|
||||||
(newvh->bmiHeader.biCompression == curvh->bmiHeader.biCompression) &&
|
|
||||||
(newvh->bmiHeader.biHeight == curvh->bmiHeader.biHeight) &&
|
if ((memcmp ((void *)&newvh->rcSource, (void *)&curvh->rcSource, sizeof (RECT)) == 0) &&
|
||||||
(newvh->bmiHeader.biWidth >= curvh->bmiHeader.biWidth))
|
(memcmp ((void *)&newvh->rcTarget, (void *)&curvh->rcTarget, sizeof (RECT)) == 0) &&
|
||||||
{
|
(newvh->bmiHeader.biCompression == curvh->bmiHeader.biCompression) &&
|
||||||
GST_DEBUG ("CheckMediaType has same media type, width %d (%d image)", newvh->bmiHeader.biWidth, curvh->bmiHeader.biWidth);
|
(newvh->bmiHeader.biHeight == curvh->bmiHeader.biHeight) &&
|
||||||
|
(newvh->bmiHeader.biWidth >= curvh->bmiHeader.biWidth))
|
||||||
/* OK, compatible! */
|
{
|
||||||
return S_OK;
|
GST_DEBUG ("CheckMediaType has same media type, width %d (%d image)", newvh->bmiHeader.biWidth, curvh->bmiHeader.biWidth);
|
||||||
}
|
|
||||||
else {
|
/* OK, compatible! */
|
||||||
GST_WARNING ("Looked similar, but aren't...");
|
return S_OK;
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
GST_WARNING ("Looked similar, but aren't...");
|
||||||
}
|
}
|
||||||
GST_WARNING ("Different media types, FAILING!");
|
}
|
||||||
return S_FALSE;
|
|
||||||
}
|
}
|
||||||
|
GST_WARNING ("Different media types, FAILING!");
|
||||||
HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
|
return S_FALSE;
|
||||||
{
|
}
|
||||||
ALLOCATOR_PROPERTIES properties;
|
|
||||||
GST_DEBUG ("Required allocator properties: %d, %d, %d, %d",
|
HRESULT VideoFakeSrcPin::DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest)
|
||||||
ppropInputRequest->cbAlign, ppropInputRequest->cbBuffer,
|
{
|
||||||
ppropInputRequest->cbPrefix, ppropInputRequest->cBuffers);
|
ALLOCATOR_PROPERTIES properties;
|
||||||
|
GST_DEBUG ("Required allocator properties: %d, %d, %d, %d",
|
||||||
ppropInputRequest->cbBuffer = m_SampleSize;
|
ppropInputRequest->cbAlign, ppropInputRequest->cbBuffer,
|
||||||
ppropInputRequest->cBuffers = 1;
|
ppropInputRequest->cbPrefix, ppropInputRequest->cBuffers);
|
||||||
|
|
||||||
/* First set the buffer descriptions we're interested in */
|
ppropInputRequest->cbBuffer = m_SampleSize;
|
||||||
HRESULT hres = pAlloc->SetProperties(ppropInputRequest, &properties);
|
ppropInputRequest->cBuffers = 1;
|
||||||
GST_DEBUG ("Actual Allocator properties: %d, %d, %d, %d",
|
|
||||||
properties.cbAlign, properties.cbBuffer,
|
/* First set the buffer descriptions we're interested in */
|
||||||
properties.cbPrefix, properties.cBuffers);
|
HRESULT hres = pAlloc->SetProperties(ppropInputRequest, &properties);
|
||||||
|
GST_DEBUG ("Actual Allocator properties: %d, %d, %d, %d",
|
||||||
/* Then actually allocate the buffers */
|
properties.cbAlign, properties.cbBuffer,
|
||||||
pAlloc->Commit();
|
properties.cbPrefix, properties.cBuffers);
|
||||||
|
|
||||||
return S_OK;
|
/* Then actually allocate the buffers */
|
||||||
}
|
hres = pAlloc->Commit();
|
||||||
|
GST_DEBUG ("Allocator commit returned %x", hres);
|
||||||
STDMETHODIMP
|
|
||||||
VideoFakeSrcPin::Notify(IBaseFilter * pSender, Quality q)
|
return S_OK;
|
||||||
{
|
}
|
||||||
/* Implementing this usefully is not required, but the base class
|
|
||||||
* has an assertion here... */
|
STDMETHODIMP
|
||||||
/* TODO: Map this to GStreamer QOS events? */
|
VideoFakeSrcPin::Notify(IBaseFilter * pSender, Quality q)
|
||||||
return E_NOTIMPL;
|
{
|
||||||
}
|
/* Implementing this usefully is not required, but the base class
|
||||||
|
* has an assertion here... */
|
||||||
STDMETHODIMP VideoFakeSrcPin::SetMediaType (AM_MEDIA_TYPE *pmt)
|
/* TODO: Map this to GStreamer QOS events? */
|
||||||
{
|
return E_NOTIMPL;
|
||||||
m_MediaType.Set (*pmt);
|
}
|
||||||
m_SampleSize = m_MediaType.GetSampleSize();
|
|
||||||
|
STDMETHODIMP VideoFakeSrcPin::SetMediaType (AM_MEDIA_TYPE *pmt)
|
||||||
GST_DEBUG ("SetMediaType called. SampleSize is %d", m_SampleSize);
|
{
|
||||||
|
m_MediaType.Set (*pmt);
|
||||||
return S_OK;
|
m_SampleSize = m_MediaType.GetSampleSize();
|
||||||
}
|
|
||||||
|
GST_DEBUG ("SetMediaType called. SampleSize is %d", m_SampleSize);
|
||||||
/* 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.
|
return S_OK;
|
||||||
*
|
}
|
||||||
* TODO: When possible (when these things DON'T differ), we should buffer-alloc the
|
|
||||||
* final output buffer, and not do this copy */
|
/* If the destination buffer is a different shape (strides, etc.) from the source
|
||||||
STDMETHODIMP VideoFakeSrcPin::CopyToDestinationBuffer (byte *srcbuf, byte *dstbuf)
|
* buffer, we have to copy. Do that here, for supported video formats.
|
||||||
{
|
*
|
||||||
VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
|
* TODO: When possible (when these things DON'T differ), we should buffer-alloc the
|
||||||
GST_DEBUG ("Rendering a frame");
|
* final output buffer, and not do this copy */
|
||||||
|
STDMETHODIMP VideoFakeSrcPin::CopyToDestinationBuffer (byte *srcbuf, byte *dstbuf)
|
||||||
byte *src, *dst;
|
{
|
||||||
int dststride, srcstride, rows;
|
VIDEOINFOHEADER *vh = (VIDEOINFOHEADER *)m_MediaType.pbFormat;
|
||||||
guint32 fourcc = vh->bmiHeader.biCompression;
|
GST_DEBUG ("Rendering a frame");
|
||||||
|
|
||||||
/* biHeight is always negative; we don't want that. */
|
byte *src, *dst;
|
||||||
int height = ABS (vh->bmiHeader.biHeight);
|
int dststride, srcstride, rows;
|
||||||
int width = vh->bmiHeader.biWidth;
|
guint32 fourcc = vh->bmiHeader.biCompression;
|
||||||
|
|
||||||
/* YUY2 is the preferred layout for DirectShow, so we will probably get this
|
/* biHeight is always negative; we don't want that. */
|
||||||
* most of the time */
|
int height = ABS (vh->bmiHeader.biHeight);
|
||||||
if ((fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')) ||
|
int width = vh->bmiHeader.biWidth;
|
||||||
(fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V')) ||
|
|
||||||
(fourcc == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y')))
|
/* YUY2 is the preferred layout for DirectShow, so we will probably get this
|
||||||
{
|
* most of the time */
|
||||||
/* Nice and simple */
|
if ((fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', '2')) ||
|
||||||
int srcstride = GST_ROUND_UP_4 (vh->rcSource.right * 2);
|
(fourcc == GST_MAKE_FOURCC ('Y', 'U', 'Y', 'V')) ||
|
||||||
int dststride = width * 2;
|
(fourcc == GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y')))
|
||||||
|
{
|
||||||
for (int i = 0; i < height; i++) {
|
/* Nice and simple */
|
||||||
memcpy (dstbuf + dststride * i, srcbuf + srcstride * i, srcstride);
|
int srcstride = GST_ROUND_UP_4 (vh->rcSource.right * 2);
|
||||||
}
|
int dststride = width * 2;
|
||||||
}
|
|
||||||
else if (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')) {
|
for (int i = 0; i < height; i++) {
|
||||||
for (int component = 0; component < 3; component++) {
|
memcpy (dstbuf + dststride * i, srcbuf + srcstride * i, srcstride);
|
||||||
// TODO: Get format properly rather than hard-coding it. Use gst_video_* APIs *?
|
}
|
||||||
if (component == 0) {
|
}
|
||||||
srcstride = GST_ROUND_UP_4 (vh->rcSource.right);
|
else if (fourcc == GST_MAKE_FOURCC ('Y', 'V', '1', '2')) {
|
||||||
src = srcbuf;
|
for (int component = 0; component < 3; component++) {
|
||||||
}
|
// TODO: Get format properly rather than hard-coding it. Use gst_video_* APIs *?
|
||||||
else {
|
if (component == 0) {
|
||||||
srcstride = GST_ROUND_UP_4 ( GST_ROUND_UP_2 (vh->rcSource.right) / 2);
|
srcstride = GST_ROUND_UP_4 (vh->rcSource.right);
|
||||||
if (component == 1)
|
src = srcbuf;
|
||||||
src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom);
|
}
|
||||||
else
|
else {
|
||||||
src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom) +
|
srcstride = GST_ROUND_UP_4 ( GST_ROUND_UP_2 (vh->rcSource.right) / 2);
|
||||||
srcstride * (GST_ROUND_UP_2 (vh->rcSource.bottom) / 2);
|
if (component == 1)
|
||||||
}
|
src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom);
|
||||||
|
else
|
||||||
/* Is there a better way to do this? This is ICK! */
|
src = srcbuf + GST_ROUND_UP_4 (vh->rcSource.right) * GST_ROUND_UP_2 (vh->rcSource.bottom) +
|
||||||
if (component == 0) {
|
srcstride * (GST_ROUND_UP_2 (vh->rcSource.bottom) / 2);
|
||||||
dststride = width;
|
}
|
||||||
dst = dstbuf;
|
|
||||||
rows = height;
|
/* Is there a better way to do this? This is ICK! */
|
||||||
} else if (component == 1) {
|
if (component == 0) {
|
||||||
dststride = width / 2;
|
dststride = width;
|
||||||
dst = dstbuf + width * height;
|
dst = dstbuf;
|
||||||
rows = height/2;
|
rows = height;
|
||||||
}
|
} else if (component == 1) {
|
||||||
else {
|
dststride = width / 2;
|
||||||
dststride = width / 2;
|
dst = dstbuf + width * height;
|
||||||
dst = dstbuf + width * height +
|
rows = height/2;
|
||||||
width/2 * height/2;
|
}
|
||||||
rows = height/2;
|
else {
|
||||||
}
|
dststride = width / 2;
|
||||||
|
dst = dstbuf + width * height +
|
||||||
for (int i = 0; i < rows; i++) {
|
width/2 * height/2;
|
||||||
memcpy (dst + i * dststride, src + i * srcstride, srcstride);
|
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)
|
return S_OK;
|
||||||
{
|
}
|
||||||
IMediaSample *pSample = NULL;
|
|
||||||
|
STDMETHODIMP VideoFakeSrcPin::Disconnect ()
|
||||||
byte *data = GST_BUFFER_DATA (buffer);
|
{
|
||||||
|
GST_DEBUG_OBJECT (this, "Disconnecting pin");
|
||||||
/* TODO: Use more of the arguments here? */
|
HRESULT hr = CDynamicOutputPin::Disconnect();
|
||||||
HRESULT hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
|
GST_DEBUG_OBJECT (this, "Pin disconnected");
|
||||||
if (SUCCEEDED (hres))
|
return hr;
|
||||||
{
|
}
|
||||||
BYTE *sample_buffer;
|
|
||||||
AM_MEDIA_TYPE *mediatype;
|
HRESULT VideoFakeSrcPin::Inactive ()
|
||||||
|
{
|
||||||
pSample->GetPointer(&sample_buffer);
|
GST_DEBUG_OBJECT (this, "Pin going inactive");
|
||||||
pSample->GetMediaType(&mediatype);
|
HRESULT hr = CDynamicOutputPin::Inactive();
|
||||||
if (mediatype)
|
GST_DEBUG_OBJECT (this, "Pin inactivated");
|
||||||
SetMediaType (mediatype);
|
return hr;
|
||||||
|
}
|
||||||
if(sample_buffer)
|
|
||||||
{
|
HRESULT VideoFakeSrcPin::BreakConnect ()
|
||||||
/* Copy to the destination stride.
|
{
|
||||||
* This is not just a simple memcpy because of the different strides.
|
GST_DEBUG_OBJECT (this, "Breaking connection");
|
||||||
* TODO: optimise for the same-stride case and avoid the copy entirely.
|
HRESULT hr = CDynamicOutputPin::BreakConnect();
|
||||||
*/
|
GST_DEBUG_OBJECT (this, "Connection broken");
|
||||||
CopyToDestinationBuffer (data, sample_buffer);
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
pSample->SetDiscontinuity(FALSE); /* Decoded frame; unimportant */
|
HRESULT VideoFakeSrcPin::CompleteConnect (IPin *pReceivePin)
|
||||||
pSample->SetSyncPoint(TRUE); /* Decoded frame; always a valid syncpoint */
|
{
|
||||||
pSample->SetPreroll(FALSE); /* For non-displayed frames.
|
GST_DEBUG_OBJECT (this, "Completing connection");
|
||||||
Not used in GStreamer */
|
HRESULT hr = CDynamicOutputPin::CompleteConnect(pReceivePin);
|
||||||
|
GST_DEBUG_OBJECT (this, "Completed connection: %x", hr);
|
||||||
/* Disable synchronising on this sample. We instead let GStreamer handle
|
return hr;
|
||||||
* this at a higher level, inside BaseSink. */
|
}
|
||||||
pSample->SetTime(NULL, NULL);
|
|
||||||
|
STDMETHODIMP VideoFakeSrcPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
|
||||||
hres = Deliver(pSample);
|
{
|
||||||
pSample->Release();
|
GST_DEBUG_OBJECT (this, "Calling Block()");
|
||||||
|
HRESULT hr = CDynamicOutputPin::Block (dwBlockFlags, hEvent);
|
||||||
if (SUCCEEDED (hres))
|
GST_DEBUG_OBJECT (this, "Called Block()");
|
||||||
return GST_FLOW_OK;
|
return hr;
|
||||||
else if (hres == VFW_E_NOT_CONNECTED)
|
}
|
||||||
return GST_FLOW_NOT_LINKED;
|
|
||||||
else
|
/* When moving the video to a different monitor, directshow stops and restarts the playback pipeline.
|
||||||
return GST_FLOW_ERROR;
|
* Unfortunately, it doesn't properly block pins or do anything special, so we racily just fail
|
||||||
}
|
* at this point.
|
||||||
else {
|
* So, we try multiple times in a loop, hoping that it'll have finished (we get no notifications at all!)
|
||||||
GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
|
* at some point.
|
||||||
return GST_FLOW_ERROR;
|
*/
|
||||||
}
|
#define MAX_ATTEMPTS 10
|
||||||
}
|
|
||||||
|
GstFlowReturn VideoFakeSrcPin::PushBuffer(GstBuffer *buffer)
|
||||||
STDMETHODIMP VideoFakeSrcPin::Flush ()
|
{
|
||||||
{
|
IMediaSample *pSample = NULL;
|
||||||
DeliverBeginFlush();
|
byte *data = GST_BUFFER_DATA (buffer);
|
||||||
DeliverEndFlush();
|
int attempts = 0;
|
||||||
return S_OK;
|
HRESULT hres;
|
||||||
}
|
BYTE *sample_buffer;
|
||||||
|
AM_MEDIA_TYPE *mediatype;
|
||||||
VideoFakeSrc::VideoFakeSrc() : CBaseFilter("VideoFakeSrc", NULL, &m_critsec, CLSID_VideoFakeSrc)
|
|
||||||
{
|
StartUsingOutputPin();
|
||||||
HRESULT hr = S_OK;
|
|
||||||
m_pOutputPin = new VideoFakeSrcPin ((CSource *)this, &m_critsec, &hr);
|
while (attempts < MAX_ATTEMPTS)
|
||||||
}
|
{
|
||||||
|
hres = GetDeliveryBuffer(&pSample, NULL, NULL, 0);
|
||||||
int VideoFakeSrc::GetPinCount()
|
if (SUCCEEDED (hres))
|
||||||
{
|
break;
|
||||||
return 1;
|
attempts++;
|
||||||
}
|
Sleep(100);
|
||||||
|
}
|
||||||
CBasePin *VideoFakeSrc::GetPin(int n)
|
|
||||||
{
|
if (FAILED (hres))
|
||||||
return (CBasePin *)m_pOutputPin;
|
{
|
||||||
}
|
StopUsingOutputPin();
|
||||||
|
GST_WARNING ("Could not get sample for delivery to sink: %x", hres);
|
||||||
VideoFakeSrcPin *VideoFakeSrc::GetOutputPin()
|
return GST_FLOW_ERROR;
|
||||||
{
|
}
|
||||||
return m_pOutputPin;
|
|
||||||
}
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#include <streams.h>
|
#include <streams.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
|
||||||
class VideoFakeSrcPin : public CBaseOutputPin
|
class VideoFakeSrcPin : public CDynamicOutputPin
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
/* members */
|
/* members */
|
||||||
@ -41,11 +41,14 @@ public:
|
|||||||
virtual HRESULT CheckMediaType(const CMediaType *pmt);
|
virtual HRESULT CheckMediaType(const CMediaType *pmt);
|
||||||
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
|
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
|
||||||
virtual HRESULT DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest);
|
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 (SetMediaType) (AM_MEDIA_TYPE *pmt);
|
||||||
STDMETHOD (Flush) ();
|
STDMETHOD (Flush) ();
|
||||||
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
|
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
|
||||||
|
STDMETHODIMP Disconnect();
|
||||||
|
STDMETHODIMP Block(DWORD dwBlockFlags, HANDLE hEvent);
|
||||||
};
|
};
|
||||||
|
|
||||||
class VideoFakeSrc : public CBaseFilter
|
class VideoFakeSrc : public CBaseFilter
|
||||||
@ -55,6 +58,8 @@ private:
|
|||||||
CCritSec m_critsec;
|
CCritSec m_critsec;
|
||||||
VideoFakeSrcPin *m_pOutputPin;
|
VideoFakeSrcPin *m_pOutputPin;
|
||||||
|
|
||||||
|
CAMEvent m_evFilterStoppingEvent;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* methods */
|
/* methods */
|
||||||
VideoFakeSrc (void);
|
VideoFakeSrc (void);
|
||||||
@ -65,6 +70,11 @@ public:
|
|||||||
/* Overrides */
|
/* Overrides */
|
||||||
int GetPinCount();
|
int GetPinCount();
|
||||||
CBasePin *GetPin(int n);
|
CBasePin *GetPin(int n);
|
||||||
|
|
||||||
|
STDMETHODIMP Run(REFERENCE_TIME tStart);
|
||||||
|
STDMETHODIMP Stop(void);
|
||||||
|
STDMETHODIMP Pause(void);
|
||||||
|
STDMETHODIMP JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* __DSHOWVIDEOFAKESRC_H__ */
|
#endif /* __DSHOWVIDEOFAKESRC_H__ */
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -65,6 +65,8 @@ struct _GstDshowVideoSink
|
|||||||
/* The filter graph (DirectShow equivalent to pipeline */
|
/* The filter graph (DirectShow equivalent to pipeline */
|
||||||
IFilterGraph *filter_graph;
|
IFilterGraph *filter_graph;
|
||||||
|
|
||||||
|
IMediaEventEx *filter_media_event;
|
||||||
|
|
||||||
/* Renderer wrapper (EVR, VMR9, or VMR) and support code */
|
/* Renderer wrapper (EVR, VMR9, or VMR) and support code */
|
||||||
RendererSupport *renderersupport;
|
RendererSupport *renderersupport;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user