Add a new win32 videosink. Uses the DirectShow renderers for high-performance video rendering on win32.

Original commit message from CVS:
* configure.ac:
* sys/Makefile.am:
* sys/dshowvideosink/Makefile.am:
* sys/dshowvideosink/README:
* sys/dshowvideosink/dshowvideofakesrc.cpp:
* sys/dshowvideosink/dshowvideofakesrc.h:
* sys/dshowvideosink/dshowvideosink.cpp:
* sys/dshowvideosink/dshowvideosink.h:
Add a new win32 videosink. Uses the DirectShow renderers for
high-performance video rendering on win32.
Currently only supports some YUV formats.
Rank PRIMARY, since it's much more useful for the common cases that the
directdraw sink (which only does RGB).
This commit is contained in:
Michael Smith 2008-06-02 18:23:54 +00:00
parent 1562bef1dc
commit d1d7fb884d
8 changed files with 2015 additions and 1 deletions

View file

@ -1188,6 +1188,7 @@ gst-libs/gst/dshow/Makefile
sys/Makefile
sys/dshowdecwrapper/Makefile
sys/dshowsrcwrapper/Makefile
sys/dshowvideosink/Makefile
sys/dvb/Makefile
sys/fbdev/Makefile
sys/oss4/Makefile
@ -1219,6 +1220,7 @@ ext/Makefile
ext/nas/Makefile
ext/mpeg2enc/Makefile
ext/mplex/Makefile
ext/mozilla/Makefile
ext/musepack/Makefile
ext/musicbrainz/Makefile
ext/mythtv/Makefile

View file

@ -54,5 +54,6 @@ endif
SUBDIRS = $(DVB_DIR) $(FBDEV_DIR) $(OSS4_DIR) $(QT_DIR) $(VCD_DIR) $(WININET_DIR)
DIST_SUBDIRS = dvb fbdev dshowdecwrapper dshowsrcwrapper oss4 qtwrapper vcd wininet
DIST_SUBDIRS = dvb fbdev dshowdecwrapper dshowsrcwrapper dshowvideosink \
oss4 qtwrapper vcd wininet

View file

@ -0,0 +1,9 @@
# This plugin isn't buildable with autotools at this point in time, so just
# ensure everything's listed in EXTRA_DIST
EXTRA_DIST = \
dshowvideofakesrc.cpp \
dshowvideofakesrc.h \
dshowvideosink.cpp \
dshowvideosink.h

View file

@ -0,0 +1,5 @@
To build this, you'll require the DirectShow base classes. These are supplied
in the Windows SDK, but under Samples\Multimedia\DirectShow\BaseClasses
Once you've built that, you should be able to figure out the rest...

View file

@ -0,0 +1,281 @@
/* GStreamer
* Copyright (C) 2008 Michael Smith <msmith@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;
}

View file

@ -0,0 +1,70 @@
/* GStreamer
* Copyright (C) 2008 Michael Smith <msmith@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.
*/
#ifndef __DSHOWVIDEOFAKESRC_H__
#define __DSHOWVIDEOFAKESRC_H__
#include <streams.h>
#include <gst/gst.h>
class VideoFakeSrcPin : public CBaseOutputPin
{
protected:
/* members */
CMediaType m_MediaType;
unsigned int m_SampleSize;
public:
/* methods */
VideoFakeSrcPin (CBaseFilter *pFilter, CCritSec *sec, HRESULT *hres);
~VideoFakeSrcPin ();
STDMETHODIMP CopyToDestinationBuffer (byte *src, byte *dst);
GstFlowReturn (PushBuffer) (GstBuffer *buf);
/* Overrides */
virtual HRESULT CheckMediaType(const CMediaType *pmt);
HRESULT GetMediaType(int iPosition, CMediaType *pMediaType);
virtual HRESULT DecideBufferSize (IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *ppropInputRequest);
STDMETHOD (SetMediaType) (AM_MEDIA_TYPE *pmt);
STDMETHOD (Flush) ();
STDMETHODIMP Notify(IBaseFilter * pSender, Quality q);
};
class VideoFakeSrc : public CBaseFilter
{
private:
/* members */
CCritSec m_critsec;
VideoFakeSrcPin *m_pOutputPin;
public:
/* methods */
VideoFakeSrc (void);
~VideoFakeSrc (void) {};
VideoFakeSrcPin *GetOutputPin();
/* Overrides */
int GetPinCount();
CBasePin *GetPin(int n);
};
#endif /* __DSHOWVIDEOFAKESRC_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,104 @@
/* GStreamer
* Copyright (C) 2008 Michael Smith <msmith@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.
*/
#ifndef __DSHOWVIDEOSINK_H__
#define __DSHOWVIDEOSINK_H__
#include <gst/gst.h>
#include <gst/base/gstbasesink.h>
#include "dshowvideofakesrc.h"
#include <dshow.h>
#include "d3d9.h"
#include "vmr9.h"
#pragma warning( disable : 4090 4024)
G_BEGIN_DECLS
#define GST_TYPE_DSHOWVIDEOSINK (gst_dshowvideosink_get_type())
#define GST_DSHOWVIDEOSINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DSHOWVIDEOSINK,GstDshowVideoSink))
#define GST_DSHOWVIDEOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DSHOWVIDEOSINK,GstDshowVideoSinkClass))
#define GST_IS_DSHOWVIDEOSINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DSHOWVIDEOSINK))
#define GST_IS_DSHOWVIDEOSINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DSHOWVIDEOSINK))
typedef struct _GstDshowVideoSink GstDshowVideoSink;
typedef struct _GstDshowVideoSinkClass GstDshowVideoSinkClass;
/* Renderer-specific support classes */
class RendererSupport
{
public:
virtual const char *GetName() = 0;
virtual IBaseFilter *GetFilter() = 0;
virtual gboolean Configure() = 0;
virtual gboolean SetRendererWindow(HWND window) = 0;
virtual void PaintWindow() = 0;
virtual void MoveWindow() = 0;
virtual void DestroyWindow() = 0;
virtual void DisplayModeChanged() = 0;
};
struct _GstDshowVideoSink
{
GstBaseSink sink;
/* Preferred renderer to use: VM9 or VMR */
char *preferredrenderer;
/* The filter graph (DirectShow equivalent to pipeline */
IFilterGraph *filter_graph;
/* Renderer wrapper (EVR, VMR9, or VMR) and support code */
RendererSupport *renderersupport;
/* Our fakesrc filter */
VideoFakeSrc *fakesrc;
/* DirectShow description of media type (equivalent of GstCaps) */
AM_MEDIA_TYPE mediatype;
gboolean keep_aspect_ratio;
gboolean full_screen;
/* If the window is closed, we set this and error out */
gboolean window_closed;
/* The video window set through GstXOverlay */
HWND window_id;
gboolean connected;
/* If we create our own window, we run it from another thread */
GThread *window_thread;
HANDLE window_created_signal;
/* If we use an app-supplied window, we need to hook its WNDPROC */
WNDPROC prevWndProc;
};
struct _GstDshowVideoSinkClass
{
GstBaseSinkClass parent_class;
};
GType gst_dshowvideosink_get_type (void);
G_END_DECLS
#endif /* __DSHOWVIDEOSINK_H__ */