diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri
index c6b16da59ee9026fbc2fe1585aaa05416f18f919..6c67a8fe2ed59dc8aa151b10e9ed02764ff1e8fb 100644
--- a/src/plugins/directshow/camera/camera.pri
+++ b/src/plugins/directshow/camera/camera.pri
@@ -13,7 +13,7 @@ HEADERS += \
     $$PWD/dsvideodevicecontrol.h \
     $$PWD/dsimagecapturecontrol.h \
     $$PWD/dscamerasession.h \
-    $$PWD/directshowglobal.h \
+    $$PWD/directshowcameraglobal.h \
     $$PWD/dscameraviewfindersettingscontrol.h \
     $$PWD/dscameraimageprocessingcontrol.h
 
diff --git a/src/plugins/directshow/camera/directshowglobal.h b/src/plugins/directshow/camera/directshowcameraglobal.h
similarity index 99%
rename from src/plugins/directshow/camera/directshowglobal.h
rename to src/plugins/directshow/camera/directshowcameraglobal.h
index 46d161336e7151000c2131d872d45c48cde50854..75112a09019eab5d9d078451e9ef82bc11e824bc 100644
--- a/src/plugins/directshow/camera/directshowglobal.h
+++ b/src/plugins/directshow/camera/directshowcameraglobal.h
@@ -37,8 +37,8 @@
 **
 ****************************************************************************/
 
-#ifndef DIRECTSHOWGLOBAL_H
-#define DIRECTSHOWGLOBAL_H
+#ifndef DIRECTSHOWCAMERAGLOBAL_H
+#define DIRECTSHOWCAMERAGLOBAL_H
 
 #include <QtCore/qglobal.h>
 
diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp
index 9e1be960662a31833f0a985ddca8b4ce200d1814..4208f934f6cd023ec2319560c8273a0d424348fa 100644
--- a/src/plugins/directshow/camera/dscamerasession.cpp
+++ b/src/plugins/directshow/camera/dscamerasession.cpp
@@ -47,7 +47,7 @@
 
 #include "dscamerasession.h"
 #include "dsvideorenderer.h"
-#include "directshowglobal.h"
+#include "directshowcameraglobal.h"
 
 QT_BEGIN_NAMESPACE
 
diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro
index 280b52619b81f0ea5edb8c0fd79ec01d19650d3a..5cdf9d1b631fdcf7bec52e16fa34c73b047191fa 100644
--- a/src/plugins/directshow/directshow.pro
+++ b/src/plugins/directshow/directshow.pro
@@ -12,6 +12,7 @@ SOURCES += dsserviceplugin.cpp
 
 mingw: DEFINES += NO_DSHOW_STRSAFE
 
+include(helpers/helpers.pri)
 !config_wmf: include(player/player.pri)
 include(camera/camera.pri)
 
diff --git a/src/plugins/directshow/helpers/directshowbasefilter.cpp b/src/plugins/directshow/helpers/directshowbasefilter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fbf0f62041c93d9f71079b0d572cc0903f6ce7ee
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowbasefilter.cpp
@@ -0,0 +1,267 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "directshowbasefilter.h"
+
+#include "directshowpinenum.h"
+
+DirectShowBaseFilter::DirectShowBaseFilter()
+    : m_mutex(QMutex::Recursive)
+    , m_state(State_Stopped)
+    , m_graph(NULL)
+    , m_clock(NULL)
+    , m_sink(NULL)
+{
+
+}
+
+DirectShowBaseFilter::~DirectShowBaseFilter()
+{
+    if (m_clock) {
+        m_clock->Release();
+        m_clock = NULL;
+    }
+}
+
+HRESULT DirectShowBaseFilter::getInterface(REFIID riid, void **ppvObject)
+{
+    if (riid == IID_IPersist
+            || riid == IID_IMediaFilter
+            || riid == IID_IBaseFilter) {
+        return GetInterface(static_cast<IBaseFilter *>(this), ppvObject);
+    } else {
+        return DirectShowObject::getInterface(riid, ppvObject);
+    }
+}
+
+HRESULT DirectShowBaseFilter::GetClassID(CLSID *pClassID)
+{
+    *pClassID = CLSID_NULL;
+    return S_OK;
+}
+
+HRESULT DirectShowBaseFilter::NotifyEvent(long eventCode, LONG_PTR eventParam1, LONG_PTR eventParam2)
+{
+    IMediaEventSink *sink = m_sink;
+    if (sink) {
+        if (eventCode == EC_COMPLETE)
+            eventParam2 = (LONG_PTR)(IBaseFilter*)this;
+
+        return sink->Notify(eventCode, eventParam1, eventParam2);
+    } else {
+        return E_NOTIMPL;
+    }
+}
+
+HRESULT DirectShowBaseFilter::Run(REFERENCE_TIME tStart)
+{
+    Q_UNUSED(tStart)
+    QMutexLocker locker(&m_mutex);
+
+    m_startTime = tStart;
+
+    if (m_state == State_Stopped){
+        HRESULT hr = Pause();
+        if (FAILED(hr))
+            return hr;
+    }
+
+    m_state = State_Running;
+
+    return S_OK;
+}
+
+HRESULT DirectShowBaseFilter::Pause()
+{
+    QMutexLocker locker(&m_mutex);
+
+    if (m_state == State_Stopped) {
+        const QList<DirectShowPin *> pinList = pins();
+        for (DirectShowPin *pin : pinList) {
+            if (pin->isConnected()) {
+                HRESULT hr = pin->setActive(true);
+                if (FAILED(hr))
+                    return hr;
+            }
+        }
+    }
+
+    m_state = State_Paused;
+
+    return S_OK;
+}
+
+HRESULT DirectShowBaseFilter::Stop()
+{
+    QMutexLocker locker(&m_mutex);
+
+    HRESULT hr = S_OK;
+
+    if (m_state != State_Stopped) {
+        const QList<DirectShowPin *> pinList = pins();
+        for (DirectShowPin *pin : pinList) {
+            if (pin->isConnected()) {
+                HRESULT hrTmp = pin->setActive(false);
+                if (FAILED(hrTmp) && SUCCEEDED(hr))
+                    hr = hrTmp;
+            }
+        }
+    }
+
+    m_state = State_Stopped;
+
+    return hr;
+}
+
+HRESULT DirectShowBaseFilter::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
+{
+    Q_UNUSED(dwMilliSecsTimeout);
+
+    if (!pState) {
+        return E_POINTER;
+    } else {
+        QMutexLocker locker(&m_mutex);
+
+        *pState = m_state;
+
+        return S_OK;
+    }
+}
+
+HRESULT DirectShowBaseFilter::SetSyncSource(IReferenceClock *pClock)
+{
+    QMutexLocker locker(&m_mutex);
+
+    if (m_clock)
+        m_clock->Release();
+
+    m_clock = pClock;
+
+    if (m_clock)
+        m_clock->AddRef();
+
+    return S_OK;
+}
+
+HRESULT DirectShowBaseFilter::GetSyncSource(IReferenceClock **ppClock)
+{
+    if (!ppClock) {
+        return E_POINTER;
+    } else {
+        if (!m_clock) {
+            *ppClock = 0;
+
+            return S_FALSE;
+        } else {
+            m_clock->AddRef();
+
+            *ppClock = m_clock;
+
+            return S_OK;
+        }
+    }
+}
+
+HRESULT DirectShowBaseFilter::EnumPins(IEnumPins **ppEnum)
+{
+    if (!ppEnum) {
+        return E_POINTER;
+    } else {
+        *ppEnum = new DirectShowPinEnum(this);
+        return S_OK;
+    }
+}
+
+HRESULT DirectShowBaseFilter::FindPin(LPCWSTR Id, IPin **ppPin)
+{
+    if (!ppPin || !Id) {
+        return E_POINTER;
+    } else {
+        QMutexLocker locker(&m_mutex);
+        const QList<DirectShowPin *> pinList = pins();
+        for (DirectShowPin *pin : pinList) {
+            if (QString::fromWCharArray(Id) == pin->name()) {
+                pin->AddRef();
+                *ppPin = pin;
+                return S_OK;
+            }
+        }
+
+        *ppPin = 0;
+        return VFW_E_NOT_FOUND;
+    }
+}
+
+HRESULT DirectShowBaseFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
+{
+    QMutexLocker locker(&m_mutex);
+
+    m_filterName = QString::fromWCharArray(pName);
+    m_graph = pGraph;
+    m_sink = NULL;
+
+    if (m_graph) {
+        if (SUCCEEDED(m_graph->QueryInterface(IID_PPV_ARGS(&m_sink))))
+            m_sink->Release(); // we don't keep a reference on it
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowBaseFilter::QueryFilterInfo(FILTER_INFO *pInfo)
+{
+    if (!pInfo) {
+        return E_POINTER;
+    } else {
+        QString name = m_filterName;
+
+        if (name.length() >= MAX_FILTER_NAME)
+            name.truncate(MAX_FILTER_NAME - 1);
+
+        int length = name.toWCharArray(pInfo->achName);
+        pInfo->achName[length] = '\0';
+
+        if (m_graph)
+            m_graph->AddRef();
+
+        pInfo->pGraph = m_graph;
+
+        return S_OK;
+    }
+}
+
+HRESULT DirectShowBaseFilter::QueryVendorInfo(LPWSTR *pVendorInfo)
+{
+    Q_UNUSED(pVendorInfo);
+    return E_NOTIMPL;
+}
diff --git a/src/plugins/directshow/helpers/directshowbasefilter.h b/src/plugins/directshow/helpers/directshowbasefilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc8588044f7452229cd7695c148804eed05e8719
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowbasefilter.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DIRECTSHOWBASEFILTER_H
+#define DIRECTSHOWBASEFILTER_H
+
+#include "directshowpin.h"
+
+QT_USE_NAMESPACE
+
+class DirectShowBaseFilter : public DirectShowObject
+                           , public IBaseFilter
+{
+    DIRECTSHOW_OBJECT
+
+public:
+    DirectShowBaseFilter();
+    virtual ~DirectShowBaseFilter();
+
+    FILTER_STATE state() const { return m_state; }
+    HRESULT NotifyEvent(long eventCode, LONG_PTR eventParam1, LONG_PTR eventParam2);
+
+    virtual QList<DirectShowPin *> pins() = 0;
+
+    // DirectShowObject
+    HRESULT getInterface(const IID &riid, void **ppvObject);
+
+    // IPersist
+    STDMETHODIMP GetClassID(CLSID *pClassID);
+
+    // IMediaFilter
+    STDMETHODIMP Run(REFERENCE_TIME tStart);
+    STDMETHODIMP Pause();
+    STDMETHODIMP Stop();
+
+    STDMETHODIMP GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState);
+
+    STDMETHODIMP SetSyncSource(IReferenceClock *pClock);
+    STDMETHODIMP GetSyncSource(IReferenceClock **ppClock);
+
+    // IBaseFilter
+    STDMETHODIMP EnumPins(IEnumPins **ppEnum);
+    STDMETHODIMP FindPin(LPCWSTR Id, IPin **ppPin);
+
+    STDMETHODIMP JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName);
+
+    STDMETHODIMP QueryFilterInfo(FILTER_INFO *pInfo);
+    STDMETHODIMP QueryVendorInfo(LPWSTR *pVendorInfo);
+
+protected:
+    QMutex m_mutex;
+    FILTER_STATE m_state;
+    IFilterGraph *m_graph;
+    IReferenceClock *m_clock;
+    IMediaEventSink *m_sink;
+    QString m_filterName;
+    REFERENCE_TIME m_startTime;
+
+private:
+    Q_DISABLE_COPY(DirectShowBaseFilter)
+};
+
+#endif // DIRECTSHOWBASEFILTER_H
diff --git a/src/plugins/directshow/player/directshoweventloop.cpp b/src/plugins/directshow/helpers/directshoweventloop.cpp
similarity index 100%
rename from src/plugins/directshow/player/directshoweventloop.cpp
rename to src/plugins/directshow/helpers/directshoweventloop.cpp
diff --git a/src/plugins/directshow/player/directshoweventloop.h b/src/plugins/directshow/helpers/directshoweventloop.h
similarity index 100%
rename from src/plugins/directshow/player/directshoweventloop.h
rename to src/plugins/directshow/helpers/directshoweventloop.h
diff --git a/src/plugins/directshow/player/directshowglobal.h b/src/plugins/directshow/helpers/directshowglobal.h
similarity index 100%
rename from src/plugins/directshow/player/directshowglobal.h
rename to src/plugins/directshow/helpers/directshowglobal.h
diff --git a/src/plugins/directshow/player/directshowmediatype.cpp b/src/plugins/directshow/helpers/directshowmediatype.cpp
similarity index 85%
rename from src/plugins/directshow/player/directshowmediatype.cpp
rename to src/plugins/directshow/helpers/directshowmediatype.cpp
index cbe1753ae6592d72f2f5a21b053ec8f14cbbcd79..60c0ee040dae84d8375caa8310fdafaac2816420 100644
--- a/src/plugins/directshow/player/directshowmediatype.cpp
+++ b/src/plugins/directshow/helpers/directshowmediatype.cpp
@@ -62,12 +62,48 @@ namespace
         { QVideoFrame::Format_IMC4,    /*MEDIASUBTYPE_IMC4*/   {0x34434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} },
         { QVideoFrame::Format_YV12,    /*MEDIASUBTYPE_YV12*/   {0x32315659, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} },
         { QVideoFrame::Format_NV12,    /*MEDIASUBTYPE_NV12*/   {0x3231564E, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} },
-        { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_IYUV*/   {0x56555949, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }
+        { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_IYUV*/   {0x56555949, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} },
+        { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_I420*/   {0x30323449, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }
     };
 }
 
+bool DirectShowMediaType::isPartiallySpecified() const
+{
+    return majortype == GUID_NULL || formattype == GUID_NULL;
+}
+
+bool DirectShowMediaType::isCompatibleWith(const DirectShowMediaType *type) const
+{
+    if (type->majortype != GUID_NULL && majortype != type->majortype)
+        return false;
+
+    if (type->subtype != GUID_NULL && subtype != type->subtype)
+        return false;
+
+    if (type->formattype != GUID_NULL) {
+        if (formattype != type->formattype)
+            return false;
+        if (cbFormat != type->cbFormat)
+            return false;
+        if (cbFormat != 0 && memcmp(pbFormat, type->pbFormat, cbFormat) != 0)
+            return false;
+    }
+
+    return true;
+}
+
+void DirectShowMediaType::init(AM_MEDIA_TYPE *type)
+{
+    ZeroMemory((PVOID)type, sizeof(*type));
+    type->lSampleSize = 1;
+    type->bFixedSizeSamples = TRUE;
+}
+
 void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source)
 {
+    if (!target)
+        return;
+
     *target = source;
 
     if (source.cbFormat > 0) {
@@ -97,16 +133,13 @@ void DirectShowMediaType::freeData(AM_MEDIA_TYPE *type)
 
 GUID DirectShowMediaType::convertPixelFormat(QVideoFrame::PixelFormat format)
 {
-    // MEDIASUBTYPE_None;
-    static const GUID none = {
-        0xe436eb8e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} };
-
     const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup);
 
     for (int i = 0; i < count; ++i)
         if (qt_typeLookup[i].pixelFormat == format)
             return qt_typeLookup[i].mediaType;
-    return none;
+
+    return MEDIASUBTYPE_None;
 }
 
 QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &type)
@@ -147,6 +180,19 @@ QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &typ
     return QVideoSurfaceFormat();
 }
 
+QVideoFrame::PixelFormat DirectShowMediaType::pixelFormatFromType(const AM_MEDIA_TYPE &type)
+{
+    const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup);
+
+    for (int i = 0; i < count; ++i) {
+        if (IsEqualGUID(qt_typeLookup[i].mediaType, type.subtype)) {
+            return qt_typeLookup[i].pixelFormat;
+        }
+    }
+
+    return QVideoFrame::Format_Invalid;
+}
+
 #define PAD_TO_DWORD(x)  (((x) + 3) & ~3)
 int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format)
 {
@@ -165,14 +211,14 @@ int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format)
     case QVideoFrame::Format_UYVY:
         return PAD_TO_DWORD(format.frameWidth() * 2);
     // Planar formats.
+    case QVideoFrame::Format_YV12:
+    case QVideoFrame::Format_YUV420P:
     case QVideoFrame::Format_IMC1:
     case QVideoFrame::Format_IMC2:
     case QVideoFrame::Format_IMC3:
     case QVideoFrame::Format_IMC4:
-    case QVideoFrame::Format_YV12:
     case QVideoFrame::Format_NV12:
-    case QVideoFrame::Format_YUV420P:
-        return PAD_TO_DWORD(format.frameWidth());
+        return format.frameWidth();
     default:
         return 0;
     }
diff --git a/src/plugins/directshow/player/directshowmediatype.h b/src/plugins/directshow/helpers/directshowmediatype.h
similarity index 88%
rename from src/plugins/directshow/player/directshowmediatype.h
rename to src/plugins/directshow/helpers/directshowmediatype.h
index cf5ac73aaac0984cc9ecd78d2389432870f9f5d3..b2b074ccc2cb6d2cabf7706b0f71ed1bc9ae21cd 100644
--- a/src/plugins/directshow/player/directshowmediatype.h
+++ b/src/plugins/directshow/helpers/directshowmediatype.h
@@ -46,10 +46,12 @@
 
 #include <dvdmedia.h>
 
+QT_USE_NAMESPACE
+
 class DirectShowMediaType : public AM_MEDIA_TYPE
 {
 public:
-    DirectShowMediaType() { memset(this, 0, sizeof(DirectShowMediaType)); }
+    DirectShowMediaType() { init(this); }
     DirectShowMediaType(const AM_MEDIA_TYPE &type) { copy(this, type); }
     DirectShowMediaType(const DirectShowMediaType &other) { copy(this, other); }
     DirectShowMediaType &operator =(const AM_MEDIA_TYPE &type) {
@@ -58,14 +60,19 @@ public:
         freeData(this); copy(this, other); return *this; }
     ~DirectShowMediaType() { freeData(this); }
 
-    void clear() { freeData(this); memset(this, 0, sizeof(DirectShowMediaType)); }
+    void clear() { freeData(this); init(this); }
+
+    bool isPartiallySpecified() const;
+    bool isCompatibleWith(const DirectShowMediaType *type) const;
 
+    static void init(AM_MEDIA_TYPE *type);
     static void copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source);
     static void freeData(AM_MEDIA_TYPE *type);
     static void deleteType(AM_MEDIA_TYPE *type);
 
     static GUID convertPixelFormat(QVideoFrame::PixelFormat format);
     static QVideoSurfaceFormat formatFromType(const AM_MEDIA_TYPE &type);
+    static QVideoFrame::PixelFormat pixelFormatFromType(const AM_MEDIA_TYPE &type);
 
     static int bytesPerLine(const QVideoSurfaceFormat &format);
 
@@ -73,4 +80,6 @@ private:
     static QVideoSurfaceFormat::Direction scanLineDirection(QVideoFrame::PixelFormat pixelFormat, const BITMAPINFOHEADER &bmiHeader);
 };
 
+Q_DECLARE_TYPEINFO(DirectShowMediaType, Q_MOVABLE_TYPE);
+
 #endif
diff --git a/src/plugins/directshow/helpers/directshowmediatypeenum.cpp b/src/plugins/directshow/helpers/directshowmediatypeenum.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a1c8b23060a6f94994c3395165f32c9b8f405a05
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowmediatypeenum.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "directshowmediatypeenum.h"
+
+#include "directshowpin.h"
+
+DirectShowMediaTypeEnum::DirectShowMediaTypeEnum(DirectShowPin *pin)
+    : m_pin(pin)
+    , m_mediaTypes(pin->supportedMediaTypes())
+    , m_index(0)
+{
+    m_pin->AddRef();
+}
+
+DirectShowMediaTypeEnum::DirectShowMediaTypeEnum(const QList<DirectShowMediaType> &types)
+    : m_pin(NULL)
+    , m_mediaTypes(types)
+    , m_index(0)
+{
+}
+
+DirectShowMediaTypeEnum::~DirectShowMediaTypeEnum()
+{
+    if (m_pin)
+        m_pin->Release();
+}
+
+HRESULT DirectShowMediaTypeEnum::getInterface(REFIID riid, void **ppvObject)
+{
+    if (riid == IID_IEnumMediaTypes) {
+        return GetInterface(static_cast<IEnumMediaTypes *>(this), ppvObject);
+    } else {
+        return DirectShowObject::getInterface(riid, ppvObject);
+    }
+}
+
+HRESULT DirectShowMediaTypeEnum::Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched)
+{
+    if (ppMediaTypes && (pcFetched || cMediaTypes == 1)) {
+        ULONG count = qBound<ULONG>(0, cMediaTypes, m_mediaTypes.count() - m_index);
+
+        for (ULONG i = 0; i < count; ++i, ++m_index) {
+            ppMediaTypes[i] = reinterpret_cast<AM_MEDIA_TYPE *>(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)));
+            DirectShowMediaType::copy(ppMediaTypes[i], m_mediaTypes.at(m_index));
+        }
+
+        if (pcFetched)
+            *pcFetched = count;
+
+        return count == cMediaTypes ? S_OK : S_FALSE;
+    } else {
+        return E_POINTER;
+    }
+}
+
+HRESULT DirectShowMediaTypeEnum::Skip(ULONG cMediaTypes)
+{
+    m_index = qMin(int(m_index + cMediaTypes), m_mediaTypes.count());
+    return m_index < m_mediaTypes.count() ? S_OK : S_FALSE;
+}
+
+HRESULT DirectShowMediaTypeEnum::Reset()
+{
+    m_index = 0;
+    return S_OK;
+}
+
+HRESULT DirectShowMediaTypeEnum::Clone(IEnumMediaTypes **ppEnum)
+{
+    if (ppEnum) {
+        if (m_pin)
+            *ppEnum = new DirectShowMediaTypeEnum(m_pin);
+        else
+            *ppEnum = new DirectShowMediaTypeEnum(m_mediaTypes);
+        return S_OK;
+    } else {
+        return E_POINTER;
+    }
+}
+
diff --git a/src/plugins/directshow/helpers/directshowmediatypeenum.h b/src/plugins/directshow/helpers/directshowmediatypeenum.h
new file mode 100644
index 0000000000000000000000000000000000000000..050df0881ec0b2763e3c0a3f36adac6ef0a0d229
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowmediatypeenum.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DIRECTSHOWMEDIATYPEENUM_H
+#define DIRECTSHOWMEDIATYPEENUM_H
+
+#include "directshowobject.h"
+#include <qlist.h>
+
+QT_USE_NAMESPACE
+
+class DirectShowPin;
+class DirectShowMediaType;
+
+class DirectShowMediaTypeEnum : public DirectShowObject
+                              , public IEnumMediaTypes
+{
+    DIRECTSHOW_OBJECT
+
+public:
+    DirectShowMediaTypeEnum(DirectShowPin *pin);
+    DirectShowMediaTypeEnum(const QList<DirectShowMediaType> &types);
+    ~DirectShowMediaTypeEnum();
+
+    // DirectShowObject
+    HRESULT getInterface(REFIID riid, void **ppvObject);
+
+    // IEnumMediaTypes
+    STDMETHODIMP Next(ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched);
+    STDMETHODIMP Skip(ULONG cMediaTypes);
+    STDMETHODIMP Reset();
+    STDMETHODIMP Clone(IEnumMediaTypes **ppEnum);
+
+private:
+    Q_DISABLE_COPY(DirectShowMediaTypeEnum)
+
+    DirectShowPin *m_pin;
+    QList<DirectShowMediaType> m_mediaTypes;
+    int m_index;
+};
+
+#endif // DIRECTSHOWMEDIATYPEENUM_H
diff --git a/src/plugins/directshow/player/directshowmediatypelist.h b/src/plugins/directshow/helpers/directshowobject.cpp
similarity index 71%
rename from src/plugins/directshow/player/directshowmediatypelist.h
rename to src/plugins/directshow/helpers/directshowobject.cpp
index c6dac0e9ba257ced1550e47d908d835309936bec..b9d989f6e6a80488375f61e82075c755dddb56b8 100644
--- a/src/plugins/directshow/player/directshowmediatypelist.h
+++ b/src/plugins/directshow/helpers/directshowobject.cpp
@@ -37,32 +37,47 @@
 **
 ****************************************************************************/
 
-#ifndef DIRECTSHOWMEDIATYPELIST_H
-#define DIRECTSHOWMEDIATYPELIST_H
+#include "directshowobject.h"
 
-#include <dshow.h>
+DirectShowObject::DirectShowObject()
+    : m_ref(1)
+{
+}
 
-#include <QtCore/qvector.h>
+DirectShowObject::~DirectShowObject()
+{
+    Q_ASSERT(m_ref == 0);
+}
 
-class DirectShowMediaTypeList : public IUnknown
+HRESULT DirectShowObject::getInterface(const IID &riid, void **ppvObject)
 {
-public:
-    DirectShowMediaTypeList();
-    virtual ~DirectShowMediaTypeList();
+    Q_UNUSED(riid)
+    *ppvObject = NULL;
+    return E_NOINTERFACE;
+}
 
-    IEnumMediaTypes *createMediaTypeEnum();
+ULONG DirectShowObject::ref()
+{
+    return InterlockedIncrement(&m_ref);
+}
 
-    void setMediaTypes(const QVector<AM_MEDIA_TYPE> &types);
+ULONG DirectShowObject::unref()
+{
+    ULONG ref = InterlockedDecrement(&m_ref);
+    if (ref == 0)
+        delete this;
+
+    return ref;
+}
+
+HRESULT GetInterface(IUnknown *pUnk, void **ppv)
+{
+    if (!ppv)
+        return E_POINTER;
 
-    virtual int currentMediaTypeToken();
-    virtual HRESULT nextMediaType(
-            int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount);
-    virtual HRESULT skipMediaType(int token, int *index, ULONG count);
-    virtual HRESULT cloneMediaType(int token, int index, IEnumMediaTypes **enumeration);
+    *ppv = pUnk;
+    pUnk->AddRef();
 
-private:
-    int m_mediaTypeToken;
-    QVector<AM_MEDIA_TYPE> m_mediaTypes;
-};
+    return S_OK;
+}
 
-#endif
diff --git a/src/plugins/directshow/helpers/directshowobject.h b/src/plugins/directshow/helpers/directshowobject.h
new file mode 100644
index 0000000000000000000000000000000000000000..3aba06f4638db0301a6de928f70c4f58760ac951
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowobject.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DIRECTSHOWOBJECT_H
+#define DIRECTSHOWOBJECT_H
+
+#include "directshowglobal.h"
+
+QT_USE_NAMESPACE
+
+class DirectShowObject
+{
+public:
+    DirectShowObject();
+    virtual ~DirectShowObject();
+
+    virtual HRESULT getInterface(REFIID riid, void **ppvObject);
+    ULONG ref();
+    ULONG unref();
+
+private:
+    Q_DISABLE_COPY(DirectShowObject)
+
+    volatile LONG m_ref;
+};
+
+HRESULT GetInterface(IUnknown *pUnk, void **ppv);
+
+#define DIRECTSHOW_OBJECT \
+public: \
+    STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {          \
+        if (riid == IID_IUnknown)                                   \
+            return GetInterface(reinterpret_cast<IUnknown*>(this), ppv); \
+        else                                                        \
+            return getInterface(riid, ppv);                         \
+    };                                                              \
+    STDMETHODIMP_(ULONG) AddRef() {                                 \
+        return ref();                                               \
+    };                                                              \
+    STDMETHODIMP_(ULONG) Release() {                                \
+        return unref();                                             \
+    };
+
+#endif // DIRECTSHOWOBJECT_H
diff --git a/src/plugins/directshow/helpers/directshowpin.cpp b/src/plugins/directshow/helpers/directshowpin.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6cf4da321462236e4fd5403716ea2c214f11b5d5
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowpin.cpp
@@ -0,0 +1,733 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "directshowpin.h"
+
+#include "directshowmediatype.h"
+#include "directshowbasefilter.h"
+#include "directshowmediatypeenum.h"
+
+#include <qdebug.h>
+
+DirectShowPin::DirectShowPin(DirectShowBaseFilter *filter, const QString &name, PIN_DIRECTION direction)
+    : m_mutex(QMutex::Recursive)
+    , m_filter(filter)
+    , m_name(name)
+    , m_direction(direction)
+    , m_peerPin(NULL)
+{
+}
+
+DirectShowPin::~DirectShowPin()
+{
+
+}
+
+HRESULT DirectShowPin::getInterface(const IID &riid, void **ppvObject)
+{
+    if (riid == IID_IPin)
+        return GetInterface(static_cast<IPin*>(this), ppvObject);
+    else
+        return DirectShowObject::getInterface(riid, ppvObject);
+}
+
+
+
+HRESULT DirectShowPin::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
+{
+    if (!pReceivePin)
+        return E_POINTER;
+
+    HRESULT hr = E_FAIL;
+    QMutexLocker locker(&m_mutex);
+
+    if (m_peerPin)
+        return VFW_E_ALREADY_CONNECTED;
+    if (m_filter->state() != State_Stopped)
+        return VFW_E_NOT_STOPPED;
+
+    PIN_DIRECTION pd;
+    pReceivePin->QueryDirection(&pd);
+    if (pd == m_direction)
+        return VFW_E_INVALID_DIRECTION;
+
+    const DirectShowMediaType *type = reinterpret_cast<const DirectShowMediaType*>(pmt);
+
+    if (type != NULL && !type->isPartiallySpecified()) {
+        // If the type is fully specified, use it
+        hr = tryConnect(pReceivePin, type);
+    } else {
+        IEnumMediaTypes *enumMediaTypes = NULL;
+
+        // First, try the receiving pin's preferred types
+        if (SUCCEEDED(pReceivePin->EnumMediaTypes(&enumMediaTypes))) {
+            hr = tryMediaTypes(pReceivePin, type, enumMediaTypes);
+            enumMediaTypes->Release();
+        }
+        // Then, try this pin's preferred types
+        if (FAILED(hr) && SUCCEEDED(EnumMediaTypes(&enumMediaTypes))) {
+            hr = tryMediaTypes(pReceivePin, type, enumMediaTypes);
+            enumMediaTypes->Release();
+        }
+    }
+
+    if (FAILED(hr)) {
+        return ((hr != E_FAIL) && (hr != E_INVALIDARG) && (hr != VFW_E_TYPE_NOT_ACCEPTED))
+                ? hr : VFW_E_NO_ACCEPTABLE_TYPES;
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowPin::tryMediaTypes(IPin *pin, const DirectShowMediaType *partialType, IEnumMediaTypes *enumMediaTypes)
+{
+    HRESULT hr = enumMediaTypes->Reset();
+    if (FAILED(hr))
+        return hr;
+
+    DirectShowMediaType *mediaType = NULL;
+    ULONG mediaCount = 0;
+    HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
+
+    for (; enumMediaTypes->Next(1, reinterpret_cast<AM_MEDIA_TYPE**>(&mediaType), &mediaCount) == S_OK;) {
+
+        if (mediaType && (partialType == NULL || mediaType->isCompatibleWith(partialType))) {
+            hr = tryConnect(pin, mediaType);
+
+            if (FAILED(hr) && (hr != E_FAIL)
+                    && (hr != E_INVALIDARG)
+                    && (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
+                hrFailure = hr;
+            }
+        }
+
+        if (mediaType)
+            DirectShowMediaType::deleteType(mediaType);
+
+        if (SUCCEEDED(hr))
+            return S_OK;
+    }
+
+    return hrFailure;
+}
+
+HRESULT DirectShowPin::tryConnect(IPin *pin, const DirectShowMediaType *type)
+{
+    if (!isMediaTypeSupported(type))
+        return VFW_E_TYPE_NOT_ACCEPTED;
+
+    m_peerPin = pin;
+    m_peerPin->AddRef();
+
+    HRESULT hr;
+    if (!setMediaType(type)) {
+        hr = VFW_E_TYPE_NOT_ACCEPTED;
+    } else {
+        hr = pin->ReceiveConnection(this, type);
+        if (SUCCEEDED(hr)) {
+            hr = completeConnection(pin);
+            if (FAILED(hr))
+                pin->Disconnect();
+        }
+    }
+
+    if (FAILED(hr)) {
+        connectionEnded();
+        m_peerPin->Release();
+        m_peerPin = NULL;
+        setMediaType(NULL);
+        return hr;
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowPin::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
+{
+    if (!pConnector || !pmt)
+        return E_POINTER;
+
+    QMutexLocker locker(&m_mutex);
+
+    if (m_peerPin)
+        return VFW_E_ALREADY_CONNECTED;
+    if (m_filter->state() != State_Stopped)
+        return VFW_E_NOT_STOPPED;
+
+    PIN_DIRECTION pd;
+    pConnector->QueryDirection(&pd);
+    if (pd == m_direction)
+        return VFW_E_INVALID_DIRECTION;
+
+    const DirectShowMediaType *type = reinterpret_cast<const DirectShowMediaType*>(pmt);
+    if (!isMediaTypeSupported(type))
+        return VFW_E_TYPE_NOT_ACCEPTED;
+
+    m_peerPin = pConnector;
+    m_peerPin->AddRef();
+
+    HRESULT hr;
+    if (!setMediaType(type))
+        hr = VFW_E_TYPE_NOT_ACCEPTED;
+    else
+        hr = completeConnection(pConnector);
+
+    if (FAILED(hr)) {
+        connectionEnded();
+        m_peerPin->Release();
+        m_peerPin = NULL;
+        setMediaType(NULL);
+        return hr;
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowPin::Disconnect()
+{
+    QMutexLocker locker(&m_mutex);
+
+    if (m_filter->state() != State_Stopped)
+        return VFW_E_NOT_STOPPED;
+
+    if (m_peerPin) {
+        HRESULT hr = connectionEnded();
+        if (FAILED(hr))
+            return hr;
+
+        m_peerPin->Release();
+        m_peerPin = NULL;
+
+        setMediaType(NULL);
+
+        return S_OK;
+    }
+
+    return S_FALSE;
+}
+
+HRESULT DirectShowPin::ConnectedTo(IPin **ppPin)
+{
+    if (!ppPin) {
+        return E_POINTER;
+    } else {
+        QMutexLocker locker(&m_mutex);
+
+        if (!m_peerPin) {
+            *ppPin = 0;
+            return VFW_E_NOT_CONNECTED;
+        } else {
+            m_peerPin->AddRef();
+            *ppPin = m_peerPin;
+            return S_OK;
+        }
+    }
+}
+
+HRESULT DirectShowPin::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
+{
+    if (!pmt) {
+        return E_POINTER;
+    } else {
+        QMutexLocker locker(&m_mutex);
+
+        if (!m_peerPin) {
+            DirectShowMediaType::init(pmt);
+            return VFW_E_NOT_CONNECTED;
+        } else {
+            DirectShowMediaType::copy(pmt, m_mediaType);
+            return S_OK;
+        }
+    }
+}
+
+HRESULT DirectShowPin::QueryPinInfo(PIN_INFO *pInfo)
+{
+    if (!pInfo) {
+        return E_POINTER;
+    } else {
+        pInfo->pFilter = m_filter;
+        if (m_filter) {
+            m_filter->AddRef();
+        }
+        pInfo->dir = m_direction;
+
+        QString name = m_name;
+        if (name.length() >= MAX_PIN_NAME)
+            name.truncate(MAX_PIN_NAME - 1);
+        int length = name.toWCharArray(pInfo->achName);
+        pInfo->achName[length] = '\0';
+
+        return S_OK;
+    }
+}
+
+HRESULT DirectShowPin::QueryId(LPWSTR *Id)
+{
+    if (!Id) {
+        return E_POINTER;
+    } else {
+        const int bytes = (m_name.length() + 1) * 2;
+        *Id = static_cast<LPWSTR>(::CoTaskMemAlloc(bytes));
+        ::memcpy(*Id, m_name.utf16(), bytes);
+        return S_OK;
+    }
+}
+
+HRESULT DirectShowPin::QueryAccept(const AM_MEDIA_TYPE *pmt)
+{
+    if (!pmt)
+        return E_POINTER;
+
+    if (!isMediaTypeSupported(reinterpret_cast<const DirectShowMediaType*>(pmt)))
+        return S_FALSE;
+
+    return S_OK;
+}
+
+HRESULT DirectShowPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
+{
+    if (!ppEnum) {
+        return E_POINTER;
+    } else {
+        *ppEnum = new DirectShowMediaTypeEnum(this);
+        return S_OK;
+    }
+}
+
+HRESULT DirectShowPin::QueryInternalConnections(IPin **apPin, ULONG *nPin)
+{
+    Q_UNUSED(apPin);
+    Q_UNUSED(nPin);
+    return E_NOTIMPL;
+}
+
+HRESULT DirectShowPin::EndOfStream()
+{
+    return S_OK;
+}
+
+HRESULT DirectShowPin::BeginFlush()
+{
+    return E_UNEXPECTED;
+}
+
+HRESULT DirectShowPin::EndFlush()
+{
+    return E_UNEXPECTED;
+}
+
+HRESULT DirectShowPin::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
+{
+    Q_UNUSED(tStart);
+    Q_UNUSED(tStop);
+    Q_UNUSED(dRate);
+    return S_OK;
+}
+
+HRESULT DirectShowPin::QueryDirection(PIN_DIRECTION *pPinDir)
+{
+    if (!pPinDir) {
+        return E_POINTER;
+    } else {
+        *pPinDir = m_direction;
+        return S_OK;
+    }
+}
+
+QList<DirectShowMediaType> DirectShowPin::supportedMediaTypes()
+{
+    return QList<DirectShowMediaType>();
+}
+
+bool DirectShowPin::setMediaType(const DirectShowMediaType *type)
+{
+    if (!type)
+        m_mediaType.clear();
+    else
+        m_mediaType = *type;
+
+    return true;
+}
+
+HRESULT DirectShowPin::completeConnection(IPin *pin)
+{
+    Q_UNUSED(pin)
+    return S_OK;
+}
+
+HRESULT DirectShowPin::connectionEnded()
+{
+    return S_OK;
+}
+
+HRESULT DirectShowPin::setActive(bool active)
+{
+    Q_UNUSED(active)
+    return S_OK;
+}
+
+
+/* DirectShowOutputPin */
+
+DirectShowOutputPin::DirectShowOutputPin(DirectShowBaseFilter *filter, const QString &name)
+    : DirectShowPin(filter, name, PINDIR_OUTPUT)
+    , m_allocator(NULL)
+    , m_inputPin(NULL)
+{
+
+}
+
+DirectShowOutputPin::~DirectShowOutputPin()
+{
+}
+
+HRESULT DirectShowOutputPin::completeConnection(IPin *pin)
+{
+    if (!pin)
+        return E_POINTER;
+
+    Q_ASSERT(m_inputPin == NULL);
+    Q_ASSERT(m_allocator == NULL);
+
+    HRESULT hr = pin->QueryInterface(IID_PPV_ARGS(&m_inputPin));
+    if (FAILED(hr))
+        return hr;
+
+    ALLOCATOR_PROPERTIES prop;
+    ZeroMemory(&prop, sizeof(prop));
+    m_inputPin->GetAllocatorRequirements(&prop);
+    if (prop.cBuffers <= 0)
+        prop.cBuffers = 1;
+    if (prop.cbBuffer <= 0)
+        prop.cbBuffer = 1;
+    if (prop.cbAlign <= 0)
+        prop.cbAlign = 1;
+
+    // Use the connected input pin's allocator if it has one
+    hr = m_inputPin->GetAllocator(&m_allocator);
+    if (SUCCEEDED(hr)) {
+        ALLOCATOR_PROPERTIES actualProperties;
+        hr = m_allocator->SetProperties(&prop, &actualProperties);
+
+        if (SUCCEEDED(hr)) {
+            hr = m_inputPin->NotifyAllocator(m_allocator, FALSE);
+            if (SUCCEEDED(hr))
+                return S_OK;
+        }
+
+        m_allocator->Release();
+        m_allocator = NULL;
+    }
+
+    // Otherwise, allocate its own allocator
+    m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator);
+    if (!m_allocator) {
+        hr = E_OUTOFMEMORY;
+    } else {
+        ALLOCATOR_PROPERTIES actualProperties;
+        hr = m_allocator->SetProperties(&prop, &actualProperties);
+
+        if (SUCCEEDED(hr)) {
+            hr = m_inputPin->NotifyAllocator(m_allocator, FALSE);
+            if (SUCCEEDED(hr))
+                return S_OK;
+        }
+
+        m_allocator->Release();
+        m_allocator = NULL;
+    }
+
+    return hr;
+}
+
+HRESULT DirectShowOutputPin::connectionEnded()
+{
+    if (m_allocator) {
+        HRESULT hr = m_allocator->Decommit();
+        if (FAILED(hr))
+            return hr;
+
+        m_allocator->Release();
+        m_allocator = NULL;
+    }
+
+    if (m_inputPin) {
+        m_inputPin->Release();
+        m_inputPin = NULL;
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowOutputPin::setActive(bool active)
+{
+    if (!m_allocator)
+        return VFW_E_NO_ALLOCATOR;
+
+    return active ? m_allocator->Commit()
+                  : m_allocator->Decommit();
+}
+
+HRESULT DirectShowOutputPin::EndOfStream()
+{
+    return E_UNEXPECTED;
+}
+
+
+/* DirectShowInputPin */
+
+DirectShowInputPin::DirectShowInputPin(DirectShowBaseFilter *filter, const QString &name)
+    : DirectShowPin(filter, name, PINDIR_INPUT)
+    , m_allocator(NULL)
+    , m_flushing(false)
+    , m_inErrorState(false)
+{
+    ZeroMemory(&m_sampleProperties, sizeof(m_sampleProperties));
+}
+
+DirectShowInputPin::~DirectShowInputPin()
+{
+
+}
+
+HRESULT DirectShowInputPin::getInterface(const IID &riid, void **ppvObject)
+{
+    if (riid == IID_IMemInputPin)
+        return GetInterface(static_cast<IMemInputPin*>(this), ppvObject);
+    else
+        return DirectShowPin::getInterface(riid, ppvObject);
+}
+
+HRESULT DirectShowInputPin::connectionEnded()
+{
+    if (m_allocator) {
+        HRESULT hr = m_allocator->Decommit();
+        if (FAILED(hr))
+            return hr;
+
+        m_allocator->Release();
+        m_allocator = NULL;
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::setActive(bool active)
+{
+    if (!active) {
+        m_inErrorState = false;
+
+        if (!m_allocator)
+            return VFW_E_NO_ALLOCATOR;
+
+        m_flushing = false;
+        return m_allocator->Decommit();
+    }
+
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::EndOfStream()
+{
+    if (m_filter->state() == State_Stopped)
+        return VFW_E_WRONG_STATE;
+    if (m_flushing)
+        return S_FALSE;
+    if (m_inErrorState)
+        return VFW_E_RUNTIME_ERROR;
+
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::BeginFlush()
+{
+    QMutexLocker locker(&m_mutex);
+    m_flushing = true;
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::EndFlush()
+{
+    QMutexLocker locker(&m_mutex);
+    m_flushing = false;
+    m_inErrorState = false;
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::GetAllocator(IMemAllocator **ppAllocator)
+{
+    if (!ppAllocator)
+        return E_POINTER;
+
+    QMutexLocker locker(&m_mutex);
+
+    if (!m_allocator) {
+        m_allocator = com_new<IMemAllocator>(CLSID_MemoryAllocator);;
+        if (!m_allocator)
+            return E_OUTOFMEMORY;
+    }
+
+    *ppAllocator = m_allocator;
+    m_allocator->AddRef();
+
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly)
+{
+    Q_UNUSED(bReadOnly)
+
+    if (!pAllocator)
+        return E_POINTER;
+
+    QMutexLocker locker(&m_mutex);
+
+    if (m_allocator)
+        m_allocator->Release();
+
+    m_allocator = pAllocator;
+    m_allocator->AddRef();
+
+    return S_OK;
+}
+
+HRESULT DirectShowInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
+{
+    Q_UNUSED(pProps)
+    return E_NOTIMPL;
+}
+
+HRESULT DirectShowInputPin::Receive(IMediaSample *pSample)
+{
+    if (!pSample)
+        return E_POINTER;
+    if (m_filter->state() == State_Stopped)
+        return VFW_E_WRONG_STATE;
+    if (m_flushing)
+        return S_FALSE;
+    if (m_inErrorState)
+        return VFW_E_RUNTIME_ERROR;
+
+    HRESULT hr = S_OK;
+
+    IMediaSample2 *sample2;
+    if (SUCCEEDED(pSample->QueryInterface(IID_PPV_ARGS(&sample2)))) {
+        hr = sample2->GetProperties(sizeof(m_sampleProperties), (PBYTE)&m_sampleProperties);
+        sample2->Release();
+        if (FAILED(hr))
+            return hr;
+    } else {
+        m_sampleProperties.cbData = sizeof(m_sampleProperties);
+        m_sampleProperties.dwTypeSpecificFlags = 0;
+        m_sampleProperties.dwStreamId = AM_STREAM_MEDIA;
+        m_sampleProperties.dwSampleFlags = 0;
+        if (pSample->IsDiscontinuity() == S_OK)
+            m_sampleProperties.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
+        if (pSample->IsPreroll() == S_OK)
+            m_sampleProperties.dwSampleFlags |= AM_SAMPLE_PREROLL;
+        if (pSample->IsSyncPoint() == S_OK)
+            m_sampleProperties.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
+        if (SUCCEEDED(pSample->GetTime(&m_sampleProperties.tStart,
+                                       &m_sampleProperties.tStop))) {
+            m_sampleProperties.dwSampleFlags |= AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID;
+        }
+        if (pSample->GetMediaType(&m_sampleProperties.pMediaType) == S_OK)
+            m_sampleProperties.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
+        pSample->GetPointer(&m_sampleProperties.pbBuffer);
+        m_sampleProperties.lActual = pSample->GetActualDataLength();
+        m_sampleProperties.cbBuffer = pSample->GetSize();
+    }
+
+
+    if (!(m_sampleProperties.dwSampleFlags & AM_SAMPLE_TYPECHANGED))
+        return S_OK;
+
+    if (isMediaTypeSupported(reinterpret_cast<DirectShowMediaType*>(m_sampleProperties.pMediaType)))
+        return S_OK;
+
+    m_inErrorState = true;
+    EndOfStream();
+    m_filter->NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0);
+    return VFW_E_INVALIDMEDIATYPE;
+}
+
+HRESULT DirectShowInputPin::ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed)
+{
+    if (!pSamples || !nSamplesProcessed)
+        return E_POINTER;
+
+    HRESULT hr = S_OK;
+    *nSamplesProcessed = 0;
+    while (nSamples-- > 0) {
+         hr = Receive(pSamples[*nSamplesProcessed]);
+         if (hr != S_OK)
+             break;
+         (*nSamplesProcessed)++;
+    }
+    return hr;
+}
+
+HRESULT DirectShowInputPin::ReceiveCanBlock()
+{
+    int outputPins = 0;
+
+    const QList<DirectShowPin *> pinList = m_filter->pins();
+    for (DirectShowPin *pin : pinList) {
+        PIN_DIRECTION pd;
+        HRESULT hr = pin->QueryDirection(&pd);
+        if (FAILED(hr))
+            return hr;
+
+        if (pd == PINDIR_OUTPUT) {
+            IPin *connected;
+            hr = pin->ConnectedTo(&connected);
+            if (SUCCEEDED(hr)) {
+                ++outputPins;
+                IMemInputPin *inputPin;
+                hr = connected->QueryInterface(IID_PPV_ARGS(&inputPin));
+                connected->Release();
+                if (SUCCEEDED(hr)) {
+                    hr = inputPin->ReceiveCanBlock();
+                    inputPin->Release();
+                    if (hr != S_FALSE)
+                        return S_OK;
+                } else {
+                    return S_OK;
+                }
+            }
+        }
+    }
+
+    return outputPins == 0 ? S_OK : S_FALSE;
+}
diff --git a/src/plugins/directshow/helpers/directshowpin.h b/src/plugins/directshow/helpers/directshowpin.h
new file mode 100644
index 0000000000000000000000000000000000000000..82322395677da7456e048cb54036bd6eed46488e
--- /dev/null
+++ b/src/plugins/directshow/helpers/directshowpin.h
@@ -0,0 +1,184 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DIRECTSHOWPIN_H
+#define DIRECTSHOWPIN_H
+
+#include "directshowobject.h"
+
+#include "directshowmediatype.h"
+#include <qstring.h>
+#include <qmutex.h>
+
+QT_USE_NAMESPACE
+
+class DirectShowBaseFilter;
+
+class DirectShowPin : public DirectShowObject
+                    , public IPin
+{
+    DIRECTSHOW_OBJECT
+
+public:
+    virtual ~DirectShowPin();
+
+    QString name() const { return m_name; }
+    bool isConnected() const { return m_peerPin != NULL; }
+
+    virtual bool isMediaTypeSupported(const DirectShowMediaType *type) = 0;
+    virtual QList<DirectShowMediaType> supportedMediaTypes();
+    virtual bool setMediaType(const DirectShowMediaType *type);
+
+    virtual HRESULT completeConnection(IPin *pin);
+    virtual HRESULT connectionEnded();
+
+    virtual HRESULT setActive(bool active);
+
+    // DirectShowObject
+    HRESULT getInterface(REFIID riid, void **ppvObject);
+
+    // IPin
+    STDMETHODIMP Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt);
+    STDMETHODIMP ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt);
+    STDMETHODIMP Disconnect();
+    STDMETHODIMP ConnectedTo(IPin **ppPin);
+
+    STDMETHODIMP ConnectionMediaType(AM_MEDIA_TYPE *pmt);
+
+    STDMETHODIMP QueryPinInfo(PIN_INFO *pInfo);
+    STDMETHODIMP QueryId(LPWSTR *Id);
+
+    STDMETHODIMP QueryAccept(const AM_MEDIA_TYPE *pmt);
+
+    STDMETHODIMP EnumMediaTypes(IEnumMediaTypes **ppEnum);
+
+    STDMETHODIMP QueryInternalConnections(IPin **apPin, ULONG *nPin);
+
+    STDMETHODIMP EndOfStream();
+
+    STDMETHODIMP BeginFlush();
+    STDMETHODIMP EndFlush();
+
+    STDMETHODIMP NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
+
+    STDMETHODIMP QueryDirection(PIN_DIRECTION *pPinDir);
+
+protected:
+    DirectShowPin(DirectShowBaseFilter *filter, const QString &name, PIN_DIRECTION direction);
+
+    QMutex m_mutex;
+
+    DirectShowBaseFilter *m_filter;
+    QString m_name;
+    PIN_DIRECTION m_direction;
+
+    IPin *m_peerPin;
+    DirectShowMediaType m_mediaType;
+
+private:
+    Q_DISABLE_COPY(DirectShowPin)
+    HRESULT tryMediaTypes(IPin *pin, const DirectShowMediaType *type, IEnumMediaTypes *enumMediaTypes);
+    HRESULT tryConnect(IPin *pin, const DirectShowMediaType *type);
+};
+
+
+class DirectShowOutputPin : public DirectShowPin
+{
+    DIRECTSHOW_OBJECT
+
+public:
+    virtual ~DirectShowOutputPin();
+
+    // DirectShowPin
+    virtual HRESULT completeConnection(IPin *pin);
+    virtual HRESULT connectionEnded();
+    virtual HRESULT setActive(bool active);
+
+    // IPin
+    STDMETHODIMP EndOfStream();
+
+protected:
+    DirectShowOutputPin(DirectShowBaseFilter *filter, const QString &name);
+
+    IMemAllocator *m_allocator;
+    IMemInputPin *m_inputPin;
+
+private:
+    Q_DISABLE_COPY(DirectShowOutputPin)
+};
+
+
+class DirectShowInputPin : public DirectShowPin
+                         , public IMemInputPin
+{
+    DIRECTSHOW_OBJECT
+
+public:
+    virtual ~DirectShowInputPin();
+
+    const AM_SAMPLE2_PROPERTIES *currentSampleProperties() const { return &m_sampleProperties; }
+
+    // DirectShowObject
+    HRESULT getInterface(REFIID riid, void **ppvObject);
+
+    // DirectShowPin
+    HRESULT connectionEnded();
+    HRESULT setActive(bool active);
+
+    // IPin
+    STDMETHODIMP EndOfStream();
+    STDMETHODIMP BeginFlush();
+    STDMETHODIMP EndFlush();
+
+    // IMemInputPin
+    STDMETHODIMP GetAllocator(IMemAllocator **ppAllocator);
+    STDMETHODIMP NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly);
+    STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
+
+    STDMETHODIMP Receive(IMediaSample *pSample);
+    STDMETHODIMP ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed);
+    STDMETHODIMP ReceiveCanBlock();
+
+protected:
+    DirectShowInputPin(DirectShowBaseFilter *filter, const QString &name);
+
+    IMemAllocator *m_allocator;
+    bool m_flushing;
+    bool m_inErrorState;
+    AM_SAMPLE2_PROPERTIES m_sampleProperties;
+
+private:
+    Q_DISABLE_COPY(DirectShowInputPin)
+};
+
+#endif // DIRECTSHOWPIN_H
diff --git a/src/plugins/directshow/player/directshowpinenum.cpp b/src/plugins/directshow/helpers/directshowpinenum.cpp
similarity index 80%
rename from src/plugins/directshow/player/directshowpinenum.cpp
rename to src/plugins/directshow/helpers/directshowpinenum.cpp
index 7ef986a2689a909160f1c4ebcec67bf607720f63..61aca8b9c7b145c3ec75acf10e83cfda0709fcbc 100644
--- a/src/plugins/directshow/player/directshowpinenum.cpp
+++ b/src/plugins/directshow/helpers/directshowpinenum.cpp
@@ -38,10 +38,22 @@
 ****************************************************************************/
 
 #include "directshowpinenum.h"
+#include "directshowbasefilter.h"
 
+DirectShowPinEnum::DirectShowPinEnum(DirectShowBaseFilter *filter)
+    : m_filter(filter)
+    , m_index(0)
+{
+    m_filter->AddRef();
+    const QList<DirectShowPin *> pinList = filter->pins();
+    for (DirectShowPin *pin : pinList) {
+        pin->AddRef();
+        m_pins.append(pin);
+    }
+}
 
 DirectShowPinEnum::DirectShowPinEnum(const QList<IPin *> &pins)
-    : m_ref(1)
+    : m_filter(NULL)
     , m_pins(pins)
     , m_index(0)
 {
@@ -53,40 +65,19 @@ DirectShowPinEnum::~DirectShowPinEnum()
 {
     for (IPin *pin : qAsConst(m_pins))
         pin->Release();
+    if (m_filter)
+        m_filter->Release();
 }
 
-HRESULT DirectShowPinEnum::QueryInterface(REFIID riid, void **ppvObject)
+HRESULT DirectShowPinEnum::getInterface(REFIID riid, void **ppvObject)
 {
-    if (riid == IID_IUnknown
-            || riid == IID_IEnumPins) {
-        AddRef();
-
-        *ppvObject = static_cast<IEnumPins *>(this);
-
-        return S_OK;
+    if (riid == IID_IEnumPins) {
+        return GetInterface(static_cast<IEnumPins *>(this), ppvObject);
     } else {
-        *ppvObject = 0;
-
-        return E_NOINTERFACE;
+        return DirectShowObject::getInterface(riid, ppvObject);
     }
 }
 
-ULONG DirectShowPinEnum::AddRef()
-{
-    return InterlockedIncrement(&m_ref);
-}
-
-ULONG DirectShowPinEnum::Release()
-{
-    ULONG ref = InterlockedDecrement(&m_ref);
-
-    if (ref == 0) {
-        delete this;
-    }
-
-    return ref;
-}
-
 HRESULT DirectShowPinEnum::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched)
 {
     if (ppPins && (pcFetched || cPins == 1)) {
@@ -123,7 +114,10 @@ HRESULT DirectShowPinEnum::Reset()
 HRESULT DirectShowPinEnum::Clone(IEnumPins **ppEnum)
 {
     if (ppEnum) {
-        *ppEnum = new DirectShowPinEnum(m_pins);
+        if (m_filter)
+            *ppEnum = new DirectShowPinEnum(m_filter);
+        else
+            *ppEnum = new DirectShowPinEnum(m_pins);
 
         return S_OK;
     } else {
diff --git a/src/plugins/directshow/player/directshowpinenum.h b/src/plugins/directshow/helpers/directshowpinenum.h
similarity index 78%
rename from src/plugins/directshow/player/directshowpinenum.h
rename to src/plugins/directshow/helpers/directshowpinenum.h
index 8859f49a584942e5cd9c9138f574e890e2f77dd6..84c2a7579550f436e8f20d7757d754a9e0321715 100644
--- a/src/plugins/directshow/player/directshowpinenum.h
+++ b/src/plugins/directshow/helpers/directshowpinenum.h
@@ -43,27 +43,35 @@
 #include <dshow.h>
 
 #include <QtCore/qlist.h>
+#include "directshowpin.h"
 
+QT_USE_NAMESPACE
 
-class DirectShowPinEnum : public IEnumPins
+class DirectShowBaseFilter;
+
+class DirectShowPinEnum : public DirectShowObject
+                        , public IEnumPins
 {
+    DIRECTSHOW_OBJECT
+
 public:
+    DirectShowPinEnum(DirectShowBaseFilter *filter);
     DirectShowPinEnum(const QList<IPin *> &pins);
-    virtual ~DirectShowPinEnum();
+    ~DirectShowPinEnum();
 
-    // IUnknown
-    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
-    ULONG STDMETHODCALLTYPE AddRef();
-    ULONG STDMETHODCALLTYPE Release();
+    // DirectShowObject
+    HRESULT getInterface(REFIID riid, void **ppvObject);
 
     // IEnumPins
-    HRESULT STDMETHODCALLTYPE Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched);
-    HRESULT STDMETHODCALLTYPE Skip(ULONG cPins);
-    HRESULT STDMETHODCALLTYPE Reset();
-    HRESULT STDMETHODCALLTYPE Clone(IEnumPins **ppEnum);
+    STDMETHODIMP Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched);
+    STDMETHODIMP Skip(ULONG cPins);
+    STDMETHODIMP Reset();
+    STDMETHODIMP Clone(IEnumPins **ppEnum);
 
 private:
-    LONG m_ref;
+    Q_DISABLE_COPY(DirectShowPinEnum)
+
+    DirectShowBaseFilter *m_filter;
     QList<IPin *> m_pins;
     int m_index;
 };
diff --git a/src/plugins/directshow/player/mediasamplevideobuffer.cpp b/src/plugins/directshow/helpers/directshowvideobuffer.cpp
similarity index 87%
rename from src/plugins/directshow/player/mediasamplevideobuffer.cpp
rename to src/plugins/directshow/helpers/directshowvideobuffer.cpp
index 58b146a89bc188f1c567345019d42d07bb252f65..3204e9f7eabdb0bbef794e781e468406f53e7981 100644
--- a/src/plugins/directshow/player/mediasamplevideobuffer.cpp
+++ b/src/plugins/directshow/helpers/directshowvideobuffer.cpp
@@ -37,9 +37,9 @@
 **
 ****************************************************************************/
 
-#include "mediasamplevideobuffer.h"
+#include "directshowvideobuffer.h"
 
-MediaSampleVideoBuffer::MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine)
+DirectShowVideoBuffer::DirectShowVideoBuffer(IMediaSample *sample, int bytesPerLine)
     : QAbstractVideoBuffer(NoHandle)
     , m_sample(sample)
     , m_bytesPerLine(bytesPerLine)
@@ -48,12 +48,12 @@ MediaSampleVideoBuffer::MediaSampleVideoBuffer(IMediaSample *sample, int bytesPe
     m_sample->AddRef();
 }
 
-MediaSampleVideoBuffer::~MediaSampleVideoBuffer()
+DirectShowVideoBuffer::~DirectShowVideoBuffer()
 {
     m_sample->Release();
 }
 
-uchar *MediaSampleVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
+uchar *DirectShowVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine)
 {
     if (m_mapMode == NotMapped && mode != NotMapped) {
         if (numBytes)
@@ -73,12 +73,12 @@ uchar *MediaSampleVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLin
     return 0;
 }
 
-void MediaSampleVideoBuffer::unmap()
+void DirectShowVideoBuffer::unmap()
 {
     m_mapMode = NotMapped;
 }
 
-QAbstractVideoBuffer::MapMode MediaSampleVideoBuffer::mapMode() const
+QAbstractVideoBuffer::MapMode DirectShowVideoBuffer::mapMode() const
 {
     return m_mapMode;
 }
diff --git a/src/plugins/directshow/player/mediasamplevideobuffer.h b/src/plugins/directshow/helpers/directshowvideobuffer.h
similarity index 90%
rename from src/plugins/directshow/player/mediasamplevideobuffer.h
rename to src/plugins/directshow/helpers/directshowvideobuffer.h
index 6ec1470c568db2e643eb32e931359c3a317ead91..10089c75ac5c3317e614b043585bbc2fdf928019 100644
--- a/src/plugins/directshow/player/mediasamplevideobuffer.h
+++ b/src/plugins/directshow/helpers/directshowvideobuffer.h
@@ -37,18 +37,18 @@
 **
 ****************************************************************************/
 
-#ifndef MEDIASAMPLEVIDEOBUFFER_H
-#define MEDIASAMPLEVIDEOBUFFER_H
+#ifndef DIRECTSHOWVIDEOBUFFER_H
+#define DIRECTSHOWVIDEOBUFFER_H
 
 #include <dshow.h>
 
 #include <qabstractvideobuffer.h>
 
-class MediaSampleVideoBuffer : public QAbstractVideoBuffer
+class DirectShowVideoBuffer : public QAbstractVideoBuffer
 {
 public:
-    MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine);
-    ~MediaSampleVideoBuffer();
+    DirectShowVideoBuffer(IMediaSample *sample, int bytesPerLine);
+    ~DirectShowVideoBuffer();
 
     IMediaSample *sample() { return m_sample; }
 
diff --git a/src/plugins/directshow/helpers/helpers.pri b/src/plugins/directshow/helpers/helpers.pri
new file mode 100644
index 0000000000000000000000000000000000000000..b3743a680c58eded43f5cd3570ec442b211493b9
--- /dev/null
+++ b/src/plugins/directshow/helpers/helpers.pri
@@ -0,0 +1,22 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+    $$PWD/directshowbasefilter.h \
+    $$PWD/directshoweventloop.h \
+    $$PWD/directshowglobal.h \
+    $$PWD/directshowmediatype.h \
+    $$PWD/directshowmediatypeenum.h \
+    $$PWD/directshowobject.h \
+    $$PWD/directshowpin.h \
+    $$PWD/directshowpinenum.h \
+    $$PWD/directshowvideobuffer.h
+
+SOURCES += \
+    $$PWD/directshowbasefilter.cpp \
+    $$PWD/directshoweventloop.cpp \
+    $$PWD/directshowmediatype.cpp \
+    $$PWD/directshowmediatypeenum.cpp \
+    $$PWD/directshowobject.cpp \
+    $$PWD/directshowpin.cpp \
+    $$PWD/directshowpinenum.cpp \
+    $$PWD/directshowvideobuffer.cpp
diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp
index 6dee14fb0510356f65a630c4c6cc52298bff4144..9e71f34d00ad2875973a1939c8e667edf19a186e 100644
--- a/src/plugins/directshow/player/directshowiosource.cpp
+++ b/src/plugins/directshow/player/directshowiosource.cpp
@@ -41,6 +41,7 @@
 
 #include "directshowglobal.h"
 #include "directshowmediatype.h"
+#include "directshowmediatypeenum.h"
 #include "directshowpinenum.h"
 
 #include <QtCore/qcoreapplication.h>
@@ -64,8 +65,6 @@ DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
     , m_peerPin(0)
     , m_pinId(QLatin1String("Data"))
 {
-    QVector<AM_MEDIA_TYPE> mediaTypes;
-
     AM_MEDIA_TYPE type =
     {
         MEDIATYPE_Stream,  // majortype
@@ -83,10 +82,8 @@ DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop)
 
     for (int i = 0; i < count; ++i) {
         type.subtype = directshow_subtypes[i];
-        mediaTypes.append(type);
+        m_supportedMediaTypes.append(type);
     }
-
-    setMediaTypes(mediaTypes);
 }
 
 DirectShowIOSource::~DirectShowIOSource()
@@ -566,7 +563,7 @@ HRESULT DirectShowIOSource::EnumMediaTypes(IEnumMediaTypes **ppEnum)
     if (!ppEnum) {
         return E_POINTER;
     } else {
-        *ppEnum = createMediaTypeEnum();
+        *ppEnum = new DirectShowMediaTypeEnum(m_supportedMediaTypes);
 
         return S_OK;
     }
diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h
index bb90ba398d25588a0b638aa9f766ad5a18f52431..c2add606200e580fb712dd3e2322e8c1370def08 100644
--- a/src/plugins/directshow/player/directshowiosource.h
+++ b/src/plugins/directshow/player/directshowiosource.h
@@ -43,13 +43,11 @@
 #include "directshowglobal.h"
 #include "directshowioreader.h"
 #include "directshowmediatype.h"
-#include "directshowmediatypelist.h"
 
 #include <QtCore/qfile.h>
 
 class DirectShowIOSource
-    : public DirectShowMediaTypeList
-    , public IBaseFilter
+    : public IBaseFilter
     , public IAMFilterMiscFlags
     , public IPin
 {
@@ -128,6 +126,7 @@ private:
     IMemAllocator *m_allocator;
     IPin *m_peerPin;
     DirectShowMediaType m_mediaType;
+    QList<DirectShowMediaType> m_supportedMediaTypes;
     QString m_filterName;
     const QString m_pinId;
     QMutex m_mutex;
diff --git a/src/plugins/directshow/player/directshowmediatypelist.cpp b/src/plugins/directshow/player/directshowmediatypelist.cpp
deleted file mode 100644
index 8d5e572caef36d9b56998150553c9dceac331d3b..0000000000000000000000000000000000000000
--- a/src/plugins/directshow/player/directshowmediatypelist.cpp
+++ /dev/null
@@ -1,228 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "directshowmediatypelist.h"
-
-#include "directshowmediatype.h"
-#include "videosurfacefilter.h"
-
-
-class DirectShowMediaTypeEnum : public IEnumMediaTypes
-{
-public:
-    DirectShowMediaTypeEnum(DirectShowMediaTypeList *list, int token, int index = 0);
-    virtual ~DirectShowMediaTypeEnum();
-
-    // IUnknown
-    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
-    ULONG STDMETHODCALLTYPE AddRef();
-    ULONG STDMETHODCALLTYPE Release();
-
-    // IEnumMediaTypes
-    HRESULT STDMETHODCALLTYPE Next(
-            ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched);
-    HRESULT STDMETHODCALLTYPE Skip(ULONG cMediaTypes);
-    HRESULT STDMETHODCALLTYPE Reset();
-
-    HRESULT STDMETHODCALLTYPE Clone(IEnumMediaTypes **ppEnum);
-
-private:
-    LONG m_ref;
-    DirectShowMediaTypeList *m_list;
-    int m_mediaTypeToken;
-    int m_index;
-};
-
-
-DirectShowMediaTypeEnum::DirectShowMediaTypeEnum(
-        DirectShowMediaTypeList *list, int token, int index)
-    : m_ref(1)
-    , m_list(list)
-    , m_mediaTypeToken(token)
-    , m_index(index)
-{
-    m_list->AddRef();
-}
-
-DirectShowMediaTypeEnum::~DirectShowMediaTypeEnum()
-{
-    m_list->Release();
-}
-
-HRESULT DirectShowMediaTypeEnum::QueryInterface(REFIID riid, void **ppvObject)
-{
-    if (!ppvObject) {
-        return E_POINTER;
-    } else if (riid == IID_IUnknown
-            || riid == IID_IEnumMediaTypes) {
-        *ppvObject = static_cast<IEnumMediaTypes *>(this);
-    } else {
-        *ppvObject = 0;
-
-        return E_NOINTERFACE;
-    }
-
-    AddRef();
-
-    return S_OK;
-}
-
-ULONG DirectShowMediaTypeEnum::AddRef()
-{
-    return InterlockedIncrement(&m_ref);
-}
-
-ULONG DirectShowMediaTypeEnum::Release()
-{
-    ULONG ref = InterlockedDecrement(&m_ref);
-
-    if (ref == 0) {
-        delete this;
-    }
-
-    return ref;
-}
-
-HRESULT DirectShowMediaTypeEnum::Next(
-        ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched)
-{
-    return m_list->nextMediaType(m_mediaTypeToken, &m_index, cMediaTypes, ppMediaTypes, pcFetched);
-}
-
-HRESULT DirectShowMediaTypeEnum::Skip(ULONG cMediaTypes)
-{
-    return m_list->skipMediaType(m_mediaTypeToken, &m_index, cMediaTypes);
-}
-
-HRESULT DirectShowMediaTypeEnum::Reset()
-{
-    m_mediaTypeToken = m_list->currentMediaTypeToken();
-    m_index = 0;
-
-    return S_OK;
-}
-
-HRESULT DirectShowMediaTypeEnum::Clone(IEnumMediaTypes **ppEnum)
-{
-    return m_list->cloneMediaType(m_mediaTypeToken, m_index, ppEnum);
-}
-
-
-DirectShowMediaTypeList::DirectShowMediaTypeList()
-    : m_mediaTypeToken(0)
-{
-}
-
-DirectShowMediaTypeList::~DirectShowMediaTypeList()
-{
-}
-
-IEnumMediaTypes *DirectShowMediaTypeList::createMediaTypeEnum()
-{
-    return new DirectShowMediaTypeEnum(this, m_mediaTypeToken, 0);
-}
-
-
-void DirectShowMediaTypeList::setMediaTypes(const QVector<AM_MEDIA_TYPE> &types)
-{
-    ++m_mediaTypeToken;
-
-    m_mediaTypes = types;
-}
-
-
-int DirectShowMediaTypeList::currentMediaTypeToken()
-{
-    return m_mediaTypeToken;
-}
-
-HRESULT DirectShowMediaTypeList::nextMediaType(
-        int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount)
-{
-    if (!types || (count != 1 && !fetchedCount)) {
-        return E_POINTER;
-    } else if (m_mediaTypeToken != token) {
-        return VFW_E_ENUM_OUT_OF_SYNC;
-    } else {
-        int boundedCount = qBound<int>(0, count, m_mediaTypes.count() - *index);
-
-        for (int i = 0; i < boundedCount; ++i, ++(*index)) {
-            types[i] = reinterpret_cast<AM_MEDIA_TYPE *>(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)));
-
-            if (types[i]) {
-                DirectShowMediaType::copy(types[i], m_mediaTypes.at(*index));
-            } else {
-                for (--i; i >= 0; --i)
-                    CoTaskMemFree(types[i]);
-
-                if (fetchedCount)
-                    *fetchedCount = 0;
-
-                return E_OUTOFMEMORY;
-            }
-        }
-        if (fetchedCount)
-            *fetchedCount = boundedCount;
-
-        return boundedCount == int(count) ? S_OK : S_FALSE;
-    }
-}
-
-HRESULT DirectShowMediaTypeList::skipMediaType(int token, int *index, ULONG count)
-{
-    if (m_mediaTypeToken != token) {
-        return VFW_E_ENUM_OUT_OF_SYNC;
-    } else {
-        *index = qMin<int>(*index + count, m_mediaTypes.size());
-
-        return *index < m_mediaTypes.size() ? S_OK : S_FALSE;
-    }
-}
-
-HRESULT DirectShowMediaTypeList::cloneMediaType(int token, int index, IEnumMediaTypes **enumeration)
-{
-    if (m_mediaTypeToken != token) {
-        return VFW_E_ENUM_OUT_OF_SYNC;
-    } else {
-        *enumeration = new DirectShowMediaTypeEnum(this, token, index);
-
-        return S_OK;
-    }
-}
-
diff --git a/src/plugins/directshow/player/directshowsamplescheduler.cpp b/src/plugins/directshow/player/directshowsamplescheduler.cpp
deleted file mode 100644
index 0aa257f7f5f8a3bed2abb3541ad78ee634b6bac8..0000000000000000000000000000000000000000
--- a/src/plugins/directshow/player/directshowsamplescheduler.cpp
+++ /dev/null
@@ -1,435 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "directshowsamplescheduler.h"
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qcoreevent.h>
-
-class DirectShowTimedSample
-{
-public:
-    DirectShowTimedSample(IMediaSample *sample)
-        : m_next(0)
-        , m_sample(sample)
-        , m_cookie(0)
-        , m_lastSample(false)
-    {
-        m_sample->AddRef();
-    }
-
-    ~DirectShowTimedSample()
-    {
-        m_sample->Release();
-    }
-
-    IMediaSample *sample() const { return m_sample; }
-
-    DirectShowTimedSample *nextSample() const { return m_next; }
-    void setNextSample(DirectShowTimedSample *sample) { Q_ASSERT(!m_next); m_next = sample; }
-
-    DirectShowTimedSample *remove() {
-        DirectShowTimedSample *next = m_next; delete this; return next; }
-
-    bool schedule(IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle);
-    void unschedule(IReferenceClock *clock);
-
-    bool isReady(IReferenceClock *clock) const;
-
-    bool isLast() const { return m_lastSample; }
-    void setLast() { m_lastSample = true; }
-
-private:
-    DirectShowTimedSample *m_next;
-    IMediaSample *m_sample;
-    DWORD_PTR m_cookie;
-    bool m_lastSample;
-};
-
-bool DirectShowTimedSample::schedule(
-        IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle)
-{
-    REFERENCE_TIME sampleStartTime;
-    REFERENCE_TIME sampleEndTime;
-    if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) {
-        if (clock->AdviseTime(
-                startTime, sampleStartTime, reinterpret_cast<HEVENT>(handle), &m_cookie) == S_OK) {
-            return true;
-        }
-    }
-    return false;
-}
-
-void DirectShowTimedSample::unschedule(IReferenceClock *clock)
-{
-    clock->Unadvise(m_cookie);
-}
-
-bool DirectShowTimedSample::isReady(IReferenceClock *clock) const
-{
-    REFERENCE_TIME sampleStartTime;
-    REFERENCE_TIME sampleEndTime;
-    REFERENCE_TIME currentTime;
-    if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) {
-        if (clock->GetTime(&currentTime) == S_OK)
-            return currentTime >= sampleStartTime;
-    }
-    return true;
-}
-
-DirectShowSampleScheduler::DirectShowSampleScheduler(IUnknown *pin, QObject *parent)
-    : QObject(parent)
-    , m_pin(pin)
-    , m_clock(0)
-    , m_allocator(0)
-    , m_head(0)
-    , m_tail(0)
-    , m_maximumSamples(1)
-    , m_state(Stopped)
-    , m_startTime(0)
-    , m_timeoutEvent(::CreateEvent(0, 0, 0, 0))
-    , m_flushEvent(::CreateEvent(0, 0, 0, 0))
-{
-    m_semaphore.release(m_maximumSamples);
-}
-
-DirectShowSampleScheduler::~DirectShowSampleScheduler()
-{
-    ::CloseHandle(m_timeoutEvent);
-    ::CloseHandle(m_flushEvent);
-
-    Q_ASSERT(!m_clock);
-    Q_ASSERT(!m_allocator);
-}
-
-HRESULT DirectShowSampleScheduler::QueryInterface(REFIID riid, void **ppvObject)
-{
-    return m_pin->QueryInterface(riid, ppvObject);
-}
-
-ULONG DirectShowSampleScheduler::AddRef()
-{
-    return m_pin->AddRef();
-}
-
-ULONG DirectShowSampleScheduler::Release()
-{
-    return m_pin->Release();
-}
-
-// IMemInputPin
-HRESULT DirectShowSampleScheduler::GetAllocator(IMemAllocator **ppAllocator)
-{
-    if (!ppAllocator) {
-        return E_POINTER;
-    } else {
-        QMutexLocker locker(&m_mutex);
-
-        if (!m_allocator) {
-            return VFW_E_NO_ALLOCATOR;
-        } else {
-            *ppAllocator = m_allocator;
-
-            return S_OK;
-        }
-    }
-}
-
-HRESULT DirectShowSampleScheduler::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly)
-{
-    Q_UNUSED(bReadOnly);
-
-    HRESULT hr;
-    ALLOCATOR_PROPERTIES properties;
-
-    if (!pAllocator) {
-        if (m_allocator)
-            m_allocator->Release();
-
-        m_allocator = 0;
-
-        return S_OK;
-    } else if ((hr = pAllocator->GetProperties(&properties)) != S_OK) {
-        return hr;
-    } else {
-        if (properties.cBuffers == 1) {
-            ALLOCATOR_PROPERTIES actual;
-
-            properties.cBuffers = 2;
-            if ((hr = pAllocator->SetProperties(&properties, &actual)) != S_OK)
-                return hr;
-        }
-
-        QMutexLocker locker(&m_mutex);
-
-        if (m_allocator)
-            m_allocator->Release();
-
-        m_allocator = pAllocator;
-        m_allocator->AddRef();
-
-        return S_OK;
-    }
-}
-
-HRESULT DirectShowSampleScheduler::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
-{
-    if (!pProps)
-        return E_POINTER;
-
-    pProps->cBuffers = 2;
-
-    return S_OK;
-}
-
-HRESULT DirectShowSampleScheduler::Receive(IMediaSample *pSample)
-{
-    if (!pSample)
-        return E_POINTER;
-
-    m_semaphore.acquire(1);
-
-    QMutexLocker locker(&m_mutex);
-
-    if (m_state & Flushing) {
-        m_semaphore.release(1);
-
-        return S_FALSE;
-    } else if (m_state == Stopped) {
-        m_semaphore.release();
-
-        return VFW_E_WRONG_STATE;
-    } else {
-        DirectShowTimedSample *timedSample = new DirectShowTimedSample(pSample);
-
-        if (m_tail)
-            m_tail->setNextSample(timedSample);
-        else
-            m_head = timedSample;
-
-        m_tail = timedSample;
-
-        if (m_state == Running) {
-            if (!timedSample->schedule(m_clock, m_startTime, m_timeoutEvent)) {
-                // Timing information is unavailable, so schedule frames immediately.
-                QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
-            } else {
-                locker.unlock();
-                HANDLE handles[] = { m_flushEvent, m_timeoutEvent };
-                DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
-                locker.relock();
-
-                if (result == WAIT_OBJECT_0 + 1)
-                    QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
-            }
-        } else if (m_tail == m_head) {
-            // If this is the first frame make it available.
-            QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
-
-            if (m_state == Paused) {
-                ::ResetEvent(m_timeoutEvent);
-
-                locker.unlock();
-                HANDLE handles[] = { m_flushEvent, m_timeoutEvent };
-                ::WaitForMultipleObjects(2, handles, FALSE, INFINITE);
-                locker.relock();
-            }
-        }
-
-        return S_OK;
-    }
-}
-
-HRESULT DirectShowSampleScheduler::ReceiveMultiple(
-        IMediaSample **pSamples, long nSamples, long *nSamplesProcessed)
-{
-    if (!pSamples || !nSamplesProcessed)
-        return E_POINTER;
-
-    for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; ++(*nSamplesProcessed)) {
-        HRESULT hr = Receive(pSamples[*nSamplesProcessed]);
-
-        if (hr != S_OK)
-            return hr;
-    }
-    return S_OK;
-}
-
-HRESULT DirectShowSampleScheduler::ReceiveCanBlock()
-{
-    return S_OK;
-}
-
-void DirectShowSampleScheduler::run(REFERENCE_TIME startTime)
-{
-    QMutexLocker locker(&m_mutex);
-
-    m_state = (m_state & Flushing) | Running;
-    m_startTime = startTime;
-
-    for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) {
-        sample->schedule(m_clock, m_startTime, m_timeoutEvent);
-    }
-
-    if (!(m_state & Flushing))
-        ::ResetEvent(m_flushEvent);
-
-    if (!m_head)
-        ::SetEvent(m_timeoutEvent);
-
-}
-
-void DirectShowSampleScheduler::pause()
-{
-    QMutexLocker locker(&m_mutex);
-
-    m_state = (m_state & Flushing) | Paused;
-
-    for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample())
-        sample->unschedule(m_clock);
-
-    if (!(m_state & Flushing))
-        ::ResetEvent(m_flushEvent);
-}
-
-void DirectShowSampleScheduler::stop()
-{
-    QMutexLocker locker(&m_mutex);
-
-    m_state = m_state & Flushing;
-
-    for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) {
-        sample->unschedule(m_clock);
-
-        m_semaphore.release(1);
-    }
-
-    m_head = 0;
-    m_tail = 0;
-
-    ::SetEvent(m_flushEvent);
-}
-
-void DirectShowSampleScheduler::setFlushing(bool flushing)
-{
-    QMutexLocker locker(&m_mutex);
-
-    const bool isFlushing = m_state & Flushing;
-
-    if (isFlushing != flushing) {
-        if (flushing) {
-            m_state |= Flushing;
-
-            for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) {
-                sample->unschedule(m_clock);
-
-                m_semaphore.release(1);
-            }
-            m_head = 0;
-            m_tail = 0;
-
-            ::SetEvent(m_flushEvent);
-        } else {
-            m_state &= ~Flushing;
-
-            if (m_state != Stopped)
-                ::ResetEvent(m_flushEvent);
-        }
-    }
-}
-
-void DirectShowSampleScheduler::setClock(IReferenceClock *clock)
-{
-    QMutexLocker locker(&m_mutex);
-
-    if (m_clock)
-        m_clock->Release();
-
-    m_clock = clock;
-
-    if (m_clock)
-        m_clock->AddRef();
-}
-
-IMediaSample *DirectShowSampleScheduler::takeSample(bool *eos)
-{
-    QMutexLocker locker(&m_mutex);
-
-    if (m_head && m_head->isReady(m_clock)) {
-        IMediaSample *sample = m_head->sample();
-        sample->AddRef();
-
-        *eos =  m_head->isLast();
-
-        m_head = m_head->remove();
-
-        if (!m_head)
-            m_tail = 0;
-
-        m_semaphore.release(1);
-
-        return sample;
-    } else {
-        return 0;
-    }
-}
-
-bool DirectShowSampleScheduler::scheduleEndOfStream()
-{
-    QMutexLocker locker(&m_mutex);
-
-    if (m_tail) {
-        m_tail->setLast();
-
-        return true;
-    } else {
-        return false;
-    }
-}
-
-bool DirectShowSampleScheduler::event(QEvent *event)
-{
-    if (event->type() == QEvent::UpdateRequest) {
-        emit sampleReady();
-
-        return true;
-    } else {
-        return QObject::event(event);
-    }
-}
diff --git a/src/plugins/directshow/player/directshowsamplescheduler.h b/src/plugins/directshow/player/directshowsamplescheduler.h
deleted file mode 100644
index 1670d23ed03cbf3d36f60e61edf86582b1373969..0000000000000000000000000000000000000000
--- a/src/plugins/directshow/player/directshowsamplescheduler.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef DIRECTSHOWSAMPLESCHEDULER_H
-#define DIRECTSHOWSAMPLESCHEDULER_H
-
-#include <dshow.h>
-
-#include <QtCore/qmutex.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qsemaphore.h>
-
-class DirectShowTimedSample;
-
-class DirectShowSampleScheduler : public QObject, public IMemInputPin
-{
-    Q_OBJECT
-public:
-
-    enum State
-    {
-        Stopped  = 0x00,
-        Running  = 0x01,
-        Paused   = 0x02,
-        RunMask  = 0x03,
-        Flushing = 0x04
-    };
-
-    DirectShowSampleScheduler(IUnknown *pin, QObject *parent = 0);
-    ~DirectShowSampleScheduler();
-
-    // IUnknown
-    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
-    ULONG STDMETHODCALLTYPE AddRef();
-    ULONG STDMETHODCALLTYPE Release();
-
-    // IMemInputPin
-    HRESULT STDMETHODCALLTYPE GetAllocator(IMemAllocator **ppAllocator);
-    HRESULT STDMETHODCALLTYPE NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly);
-    HRESULT STDMETHODCALLTYPE GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
-
-    HRESULT STDMETHODCALLTYPE Receive(IMediaSample *pSample);
-    HRESULT STDMETHODCALLTYPE ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed);
-    HRESULT STDMETHODCALLTYPE ReceiveCanBlock();
-
-    void run(REFERENCE_TIME startTime);
-    void pause();
-    void stop();
-    void setFlushing(bool flushing);
-
-    IReferenceClock *clock() const { return m_clock; }
-    void setClock(IReferenceClock *clock);
-
-    bool schedule(IMediaSample *sample);
-    bool scheduleEndOfStream();
-
-    IMediaSample *takeSample(bool *eos);
-
-    bool event(QEvent *event);
-
-Q_SIGNALS:
-    void sampleReady();
-
-private:
-    IUnknown *m_pin;
-    IReferenceClock *m_clock;
-    IMemAllocator *m_allocator;
-    DirectShowTimedSample *m_head;
-    DirectShowTimedSample *m_tail;
-    int m_maximumSamples;
-    int m_state;
-    REFERENCE_TIME m_startTime;
-    HANDLE m_timeoutEvent;
-    HANDLE m_flushEvent;
-    QSemaphore m_semaphore;
-    QMutex m_mutex;
-};
-
-#endif
diff --git a/src/plugins/directshow/player/player.pri b/src/plugins/directshow/player/player.pri
index 0bc9c6d0a1827a381e667e596b96dc13e33a763f..8b4bb70b74bc29c72edaed06e4bcea1aebc435f7 100644
--- a/src/plugins/directshow/player/player.pri
+++ b/src/plugins/directshow/player/player.pri
@@ -8,35 +8,22 @@ qtHaveModule(widgets): QT += widgets
 DEFINES += QMEDIA_DIRECTSHOW_PLAYER
 
 HEADERS += \
-        $$PWD/directshoweventloop.h \
-        $$PWD/directshowglobal.h \
         $$PWD/directshowioreader.h \
         $$PWD/directshowiosource.h \
-        $$PWD/directshowmediatype.h \
-        $$PWD/directshowmediatypelist.h \
-        $$PWD/directshowpinenum.h \
         $$PWD/directshowplayercontrol.h \
         $$PWD/directshowplayerservice.h \
-        $$PWD/directshowsamplescheduler.h \
         $$PWD/directshowvideorenderercontrol.h \
-        $$PWD/mediasamplevideobuffer.h \
         $$PWD/videosurfacefilter.h \
         $$PWD/directshowaudioendpointcontrol.h \
         $$PWD/directshowmetadatacontrol.h \
         $$PWD/vmr9videowindowcontrol.h
 
 SOURCES += \
-        $$PWD/directshoweventloop.cpp \
         $$PWD/directshowioreader.cpp \
         $$PWD/directshowiosource.cpp \
-        $$PWD/directshowmediatype.cpp \
-        $$PWD/directshowmediatypelist.cpp \
-        $$PWD/directshowpinenum.cpp \
         $$PWD/directshowplayercontrol.cpp \
         $$PWD/directshowplayerservice.cpp \
-        $$PWD/directshowsamplescheduler.cpp \
         $$PWD/directshowvideorenderercontrol.cpp \
-        $$PWD/mediasamplevideobuffer.cpp \
         $$PWD/videosurfacefilter.cpp \
         $$PWD/directshowaudioendpointcontrol.cpp \
         $$PWD/directshowmetadatacontrol.cpp \
diff --git a/src/plugins/directshow/player/videosurfacefilter.cpp b/src/plugins/directshow/player/videosurfacefilter.cpp
index b7b0d3aa856b3c61fe460276c6894935e5f0babc..4cb97be3966a8198cb4bdbb41a67043ee86615bd 100644
--- a/src/plugins/directshow/player/videosurfacefilter.cpp
+++ b/src/plugins/directshow/player/videosurfacefilter.cpp
@@ -41,226 +41,220 @@
 
 #include "directshoweventloop.h"
 #include "directshowglobal.h"
-#include "directshowpinenum.h"
-#include "mediasamplevideobuffer.h"
+#include "directshowvideobuffer.h"
 
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qcoreevent.h>
 #include <QtCore/qthread.h>
+#include <QtCore/qloggingcategory.h>
 #include <qabstractvideosurface.h>
 
 #include <initguid.h>
 
+Q_LOGGING_CATEGORY(qLcRenderFilter, "qt.multimedia.plugins.directshow.renderfilter")
+
 // { e23cad72-153d-406c-bf3f-4c4b523d96f2 }
 DEFINE_GUID(CLSID_VideoSurfaceFilter,
 0xe23cad72, 0x153d, 0x406c, 0xbf, 0x3f, 0x4c, 0x4b, 0x52, 0x3d, 0x96, 0xf2);
 
-VideoSurfaceFilter::VideoSurfaceFilter(
-        QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent)
-    : QObject(parent)
-    , m_ref(1)
-    , m_state(State_Stopped)
-    , m_surface(surface)
-    , m_loop(loop)
-    , m_graph(0)
-    , m_peerPin(0)
-    , m_bytesPerLine(0)
-    , m_startResult(S_OK)
-    , m_pinId(QString::fromLatin1("reference"))
-    , m_sampleScheduler(static_cast<IPin *>(this))
+class VideoSurfaceInputPin : public DirectShowInputPin
 {
-    connect(surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged()));
-    connect(&m_sampleScheduler, SIGNAL(sampleReady()), this, SLOT(sampleReady()));
-}
+    DIRECTSHOW_OBJECT
 
-VideoSurfaceFilter::~VideoSurfaceFilter()
-{
-    Q_ASSERT(m_ref == 0);
-}
+public:
+    VideoSurfaceInputPin(VideoSurfaceFilter *filter);
 
-HRESULT VideoSurfaceFilter::QueryInterface(REFIID riid, void **ppvObject)
-{
-    // 2dd74950-a890-11d1-abe8-00a0c905f375
-    static const GUID iid_IAmFilterMiscFlags = {
-        0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75} };
+    // DirectShowPin
+    bool isMediaTypeSupported(const DirectShowMediaType *type);
+    bool setMediaType(const DirectShowMediaType *type);
 
-    if (!ppvObject) {
-        return E_POINTER;
-    } else if (riid == IID_IUnknown
-            || riid == IID_IPersist
-            || riid == IID_IMediaFilter
-            || riid == IID_IBaseFilter) {
-        *ppvObject = static_cast<IBaseFilter *>(this);
-    } else if (riid == iid_IAmFilterMiscFlags) {
-        *ppvObject = static_cast<IAMFilterMiscFlags *>(this);
-    } else if (riid == IID_IPin) {
-        *ppvObject = static_cast<IPin *>(this);
-    } else if (riid == IID_IMemInputPin) {
-        *ppvObject = static_cast<IMemInputPin *>(&m_sampleScheduler);
-    } else {
-        *ppvObject = 0;
+    HRESULT completeConnection(IPin *pin);
+    HRESULT connectionEnded();
 
-        return E_NOINTERFACE;
-    }
+    // IPin
+    STDMETHODIMP ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt);
+    STDMETHODIMP Disconnect();
+    STDMETHODIMP EndOfStream();
+    STDMETHODIMP BeginFlush();
+    STDMETHODIMP EndFlush();
 
-    AddRef();
+    // IMemInputPin
+    STDMETHODIMP GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps);
+    STDMETHODIMP Receive(IMediaSample *pMediaSample);
 
-    return S_OK;
+private:
+    VideoSurfaceFilter *m_videoSurfaceFilter;
+};
+
+VideoSurfaceInputPin::VideoSurfaceInputPin(VideoSurfaceFilter *filter)
+    : DirectShowInputPin(filter, QStringLiteral("Input"))
+    , m_videoSurfaceFilter(filter)
+{
 }
 
-ULONG VideoSurfaceFilter::AddRef()
+bool VideoSurfaceInputPin::isMediaTypeSupported(const DirectShowMediaType *type)
 {
-    return InterlockedIncrement(&m_ref);
+    return m_videoSurfaceFilter->isMediaTypeSupported(type);
 }
 
-ULONG VideoSurfaceFilter::Release()
+bool VideoSurfaceInputPin::setMediaType(const DirectShowMediaType *type)
 {
-    ULONG ref = InterlockedDecrement(&m_ref);
-    if (ref == 0)
-        delete this;
+    if (!DirectShowInputPin::setMediaType(type))
+        return false;
 
-    return ref;
+    return m_videoSurfaceFilter->setMediaType(type);
 }
 
-HRESULT VideoSurfaceFilter::GetClassID(CLSID *pClassID)
+HRESULT VideoSurfaceInputPin::completeConnection(IPin *pin)
 {
-    *pClassID = CLSID_VideoSurfaceFilter;
+    HRESULT hr = DirectShowInputPin::completeConnection(pin);
+    if (FAILED(hr))
+        return hr;
 
-    return S_OK;
+    return m_videoSurfaceFilter->completeConnection(pin);
 }
 
-HRESULT VideoSurfaceFilter::Run(REFERENCE_TIME tStart)
+HRESULT VideoSurfaceInputPin::connectionEnded()
 {
-    m_state = State_Running;
-
-    m_sampleScheduler.run(tStart);
+    HRESULT hr = DirectShowInputPin::connectionEnded();
+    if (FAILED(hr))
+        return hr;
 
-    return S_OK;
+    return m_videoSurfaceFilter->connectionEnded();
 }
 
-HRESULT VideoSurfaceFilter::Pause()
+HRESULT VideoSurfaceInputPin::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
 {
-    m_state = State_Paused;
-
-    m_sampleScheduler.pause();
+    QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
+    return DirectShowInputPin::ReceiveConnection(pConnector, pmt);
+}
 
-    return S_OK;
+HRESULT VideoSurfaceInputPin::Disconnect()
+{
+    QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
+    return DirectShowInputPin::Disconnect();
 }
 
-HRESULT VideoSurfaceFilter::Stop()
+HRESULT VideoSurfaceInputPin::EndOfStream()
 {
-    m_state = State_Stopped;
+    QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
+    QMutexLocker renderLock(&m_videoSurfaceFilter->m_renderMutex);
 
-    m_sampleScheduler.stop();
+    HRESULT hr = DirectShowInputPin::EndOfStream();
+    if (hr != S_OK)
+        return hr;
 
-    if (thread() == QThread::currentThread()) {
-        flush();
-    } else {
-        QMutexLocker locker(&m_mutex);
-        m_loop->postEvent(this, new QEvent(QEvent::Type(FlushSurface)));
-        m_wait.wait(&m_mutex);
+    return m_videoSurfaceFilter->EndOfStream();
+}
+
+HRESULT VideoSurfaceInputPin::BeginFlush()
+{
+    QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
+    {
+        QMutexLocker renderLock(&m_videoSurfaceFilter->m_renderMutex);
+        DirectShowInputPin::BeginFlush();
+        m_videoSurfaceFilter->BeginFlush();
     }
+    m_videoSurfaceFilter->resetEOS();
 
     return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState)
+HRESULT VideoSurfaceInputPin::EndFlush()
 {
-    Q_UNUSED(dwMilliSecsTimeout)
-    if (!pState)
-        return E_POINTER;
-
-    *pState = m_state;
+    QMutexLocker lock(&m_videoSurfaceFilter->m_mutex);
+    QMutexLocker renderLock(&m_videoSurfaceFilter->m_renderMutex);
 
-    return S_OK;
+    HRESULT hr = m_videoSurfaceFilter->EndFlush();
+    if (SUCCEEDED(hr))
+        hr = DirectShowInputPin::EndFlush();
+    return hr;
 }
 
-HRESULT VideoSurfaceFilter::SetSyncSource(IReferenceClock *pClock)
+HRESULT VideoSurfaceInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps)
 {
+    if (!pProps)
+        return E_POINTER;
 
-    m_sampleScheduler.setClock(pClock);
+    // We need at least two allocated buffers, one for holding the frame currently being
+    // rendered and another one to decode the following frame at the same time.
+    pProps->cBuffers = 2;
 
     return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::GetSyncSource(IReferenceClock **ppClock)
+HRESULT VideoSurfaceInputPin::Receive(IMediaSample *pMediaSample)
 {
-    if (!ppClock) {
-        return E_POINTER;
-    } else {
-        *ppClock = m_sampleScheduler.clock();
-
-        if (*ppClock) {
-            (*ppClock)->AddRef();
-
-            return S_OK;
-        } else {
-            return S_FALSE;
+    HRESULT hr = m_videoSurfaceFilter->Receive(pMediaSample);
+    if (FAILED(hr)) {
+        QMutexLocker locker(&m_videoSurfaceFilter->m_mutex);
+        if (m_videoSurfaceFilter->state() != State_Stopped && !m_flushing && !m_inErrorState) {
+            m_videoSurfaceFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
+            {
+                QMutexLocker renderLocker(&m_videoSurfaceFilter->m_renderMutex);
+                if (m_videoSurfaceFilter->m_running && !m_videoSurfaceFilter->m_EOSDelivered)
+                    m_videoSurfaceFilter->notifyEOS();
+            }
+            m_inErrorState = true;
         }
     }
+
+    return hr;
 }
 
-HRESULT VideoSurfaceFilter::EnumPins(IEnumPins **ppEnum)
-{
-    if (ppEnum) {
-        *ppEnum = new DirectShowPinEnum(QList<IPin *>() << this);
 
-        return S_OK;
-    } else {
-        return E_POINTER;
-    }
+VideoSurfaceFilter::VideoSurfaceFilter(QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent)
+    : QObject(parent)
+    , m_loop(loop)
+    , m_pin(NULL)
+    , m_surface(surface)
+    , m_bytesPerLine(0)
+    , m_surfaceStarted(false)
+    , m_renderMutex(QMutex::Recursive)
+    , m_running(false)
+    , m_pendingSample(NULL)
+    , m_pendingSampleEndTime(0)
+    , m_renderEvent(CreateEvent(NULL, FALSE, FALSE, NULL))
+    , m_flushEvent(CreateEvent(NULL, TRUE, FALSE, NULL))
+    , m_adviseCookie(0)
+    , m_EOS(false)
+    , m_EOSDelivered(false)
+    , m_EOSTimer(0)
+{
+    supportedFormatsChanged();
+    connect(surface, &QAbstractVideoSurface::supportedFormatsChanged,
+            this, &VideoSurfaceFilter::supportedFormatsChanged);
 }
 
-HRESULT VideoSurfaceFilter::FindPin(LPCWSTR pId, IPin **ppPin)
+VideoSurfaceFilter::~VideoSurfaceFilter()
 {
-    if (!ppPin || !pId) {
-        return E_POINTER;
-    } else if (QString::fromWCharArray(pId) == m_pinId) {
-        AddRef();
+    clearPendingSample();
 
-        *ppPin = this;
+    if (m_pin)
+        m_pin->Release();
 
-        return S_OK;
-    } else {
-        return VFW_E_NOT_FOUND;
-    }
+    CloseHandle(m_flushEvent);
+    CloseHandle(m_renderEvent);
 }
 
-HRESULT VideoSurfaceFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName)
+HRESULT VideoSurfaceFilter::getInterface(const IID &riid, void **ppvObject)
 {
-    m_graph = pGraph;
-    m_name = QString::fromWCharArray(pName);
-
-    return S_OK;
+    if (riid == IID_IAMFilterMiscFlags)
+        return GetInterface(static_cast<IAMFilterMiscFlags*>(this), ppvObject);
+    else
+        return DirectShowBaseFilter::getInterface(riid, ppvObject);
 }
 
-HRESULT VideoSurfaceFilter::QueryFilterInfo(FILTER_INFO *pInfo)
+QList<DirectShowPin *> VideoSurfaceFilter::pins()
 {
-    if (pInfo) {
-        QString name = m_name;
-
-        if (name.length() >= MAX_FILTER_NAME)
-            name.truncate(MAX_FILTER_NAME - 1);
-
-        int length = name.toWCharArray(pInfo->achName);
-        pInfo->achName[length] = '\0';
-
-        if (m_graph)
-            m_graph->AddRef();
-
-        pInfo->pGraph = m_graph;
+    if (!m_pin)
+        m_pin = new VideoSurfaceInputPin(this);
 
-        return S_OK;
-    } else {
-        return E_POINTER;
-    }
+    return QList<DirectShowPin *>() << m_pin;
 }
 
-HRESULT VideoSurfaceFilter::QueryVendorInfo(LPWSTR *pVendorInfo)
+HRESULT VideoSurfaceFilter::GetClassID(CLSID *pClassID)
 {
-    Q_UNUSED(pVendorInfo);
-
-    return E_NOTIMPL;
+    *pClassID = CLSID_VideoSurfaceFilter;
+    return S_OK;
 }
 
 ULONG VideoSurfaceFilter::GetMiscFlags()
@@ -268,388 +262,527 @@ ULONG VideoSurfaceFilter::GetMiscFlags()
     return AM_FILTER_MISC_FLAGS_IS_RENDERER;
 }
 
-
-HRESULT VideoSurfaceFilter::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
-{
-    Q_UNUSED(pReceivePin)
-    Q_UNUSED(pmt)
-    // This is an input pin, you shouldn't be calling Connect on it.
-    return E_POINTER;
-}
-
-HRESULT VideoSurfaceFilter::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt)
+void VideoSurfaceFilter::supportedFormatsChanged()
 {
-    if (!pConnector) {
-        return E_POINTER;
-    } else if (!pmt) {
-        return E_POINTER;
-    } else {
-        HRESULT hr;
-        QMutexLocker locker(&m_mutex);
+    QWriteLocker writeLocker(&m_typesLock);
 
-        if (m_peerPin) {
-            hr = VFW_E_ALREADY_CONNECTED;
-        } else if (pmt->majortype != MEDIATYPE_Video) {
-            hr = VFW_E_TYPE_NOT_ACCEPTED;
-        } else {
-            m_surfaceFormat = DirectShowMediaType::formatFromType(*pmt);
-            m_bytesPerLine = DirectShowMediaType::bytesPerLine(m_surfaceFormat);
+    qCDebug(qLcRenderFilter, "supportedFormatChanged");
 
-            if (thread() == QThread::currentThread()) {
-                hr = start();
-            } else {
-                m_loop->postEvent(this, new QEvent(QEvent::Type(StartSurface)));
+    m_supportedTypes.clear();
 
-                m_wait.wait(&m_mutex);
+    const QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats();
+    m_supportedTypes.reserve(formats.count());
 
-                hr = m_startResult;
-            }
+    for (QVideoFrame::PixelFormat format : formats) {
+        GUID subtype = DirectShowMediaType::convertPixelFormat(format);
+        if (!IsEqualGUID(subtype, MEDIASUBTYPE_None)) {
+            qCDebug(qLcRenderFilter) << "  " << format;
+            m_supportedTypes.append(subtype);
         }
-        if (hr == S_OK) {
-           m_peerPin = pConnector;
-           m_peerPin->AddRef();
+    }
+}
 
-           DirectShowMediaType::copy(&m_mediaType, *pmt);
-        }
-        return hr;
+bool VideoSurfaceFilter::isMediaTypeSupported(const DirectShowMediaType *type)
+{
+    if (type->majortype != MEDIATYPE_Video || type->bFixedSizeSamples == FALSE)
+        return false;
+
+    QReadLocker readLocker(&m_typesLock);
+
+    for (const GUID &supportedType : m_supportedTypes) {
+        if (IsEqualGUID(supportedType, type->subtype))
+            return true;
     }
+
+    return false;
 }
 
-HRESULT VideoSurfaceFilter::start()
+bool VideoSurfaceFilter::setMediaType(const DirectShowMediaType *type)
 {
-    if (!m_surface->isFormatSupported(m_surfaceFormat)) {
-        return VFW_E_TYPE_NOT_ACCEPTED;
+    if (!type) {
+        qCDebug(qLcRenderFilter, "clear media type");
+        m_surfaceFormat = QVideoSurfaceFormat();
+        m_bytesPerLine = 0;
+        return true;
+    } else {
+        m_surfaceFormat = DirectShowMediaType::formatFromType(*type);
+        m_bytesPerLine = DirectShowMediaType::bytesPerLine(m_surfaceFormat);
+        qCDebug(qLcRenderFilter) << "setMediaType -->" << m_surfaceFormat;
+        return m_surfaceFormat.isValid();
     }
-    if (!m_surface->start(m_surfaceFormat)) {
+}
+
+HRESULT VideoSurfaceFilter::completeConnection(IPin *pin)
+{
+    Q_UNUSED(pin);
+
+    qCDebug(qLcRenderFilter, "completeConnection");
+
+    if (!startSurface())
         return VFW_E_TYPE_NOT_ACCEPTED;
-    } else {
+    else
         return S_OK;
-    }
 }
 
-HRESULT VideoSurfaceFilter::Disconnect()
+HRESULT VideoSurfaceFilter::connectionEnded()
+{
+    qCDebug(qLcRenderFilter, "connectionEnded");
+
+    stopSurface();
+
+    return S_OK;
+}
+
+HRESULT VideoSurfaceFilter::Run(REFERENCE_TIME tStart)
 {
     QMutexLocker locker(&m_mutex);
 
-    if (!m_peerPin)
-        return S_FALSE;
+    if (m_state == State_Running)
+        return S_OK;
 
-    if (thread() == QThread::currentThread()) {
-        stop();
-    } else {
-        m_loop->postEvent(this, new QEvent(QEvent::Type(StopSurface)));
+    qCDebug(qLcRenderFilter, "Run (start=%lli)", tStart);
+
+    HRESULT hr = DirectShowBaseFilter::Run(tStart);
+    if (FAILED(hr))
+        return hr;
+
+    ResetEvent(m_flushEvent);
 
-        m_wait.wait(&m_mutex);
+    IMemAllocator *allocator;
+    if (SUCCEEDED(m_pin->GetAllocator(&allocator))) {
+        allocator->Commit();
+        allocator->Release();
     }
 
-    m_mediaType.clear();
+    QMutexLocker renderLocker(&m_renderMutex);
 
-    m_sampleScheduler.NotifyAllocator(0, FALSE);
+    m_running = true;
 
-    m_peerPin->Release();
-    m_peerPin = 0;
+    if (!m_pendingSample)
+        checkEOS();
+    else if (!scheduleSample(m_pendingSample))
+        SetEvent(m_renderEvent); // render immediately
 
     return S_OK;
 }
 
-void VideoSurfaceFilter::stop()
+HRESULT VideoSurfaceFilter::Pause()
 {
-    m_surface->stop();
-}
+    QMutexLocker locker(&m_mutex);
 
-HRESULT VideoSurfaceFilter::ConnectedTo(IPin **ppPin)
-{
-    if (!ppPin) {
-        return E_POINTER;
-    } else {
-        QMutexLocker locker(&m_mutex);
+    if (m_state == State_Paused)
+        return S_OK;
 
-        if (!m_peerPin) {
-            return VFW_E_NOT_CONNECTED;
-        } else {
-            m_peerPin->AddRef();
+    qCDebug(qLcRenderFilter, "Pause");
 
-            *ppPin = m_peerPin;
+    HRESULT hr = DirectShowBaseFilter::Pause();
+    if (FAILED(hr))
+        return hr;
 
-            return S_OK;
-        }
+    m_renderMutex.lock();
+    m_EOSDelivered = false;
+    m_running = false;
+    m_renderMutex.unlock();
+
+    resetEOSTimer();
+    ResetEvent(m_flushEvent);
+    unscheduleSample();
+
+    IMemAllocator *allocator;
+    if (SUCCEEDED(m_pin->GetAllocator(&allocator))) {
+        allocator->Commit();
+        allocator->Release();
     }
+
+    return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::ConnectionMediaType(AM_MEDIA_TYPE *pmt)
+HRESULT VideoSurfaceFilter::Stop()
 {
-    if (!pmt) {
-        return E_POINTER;
-    } else {
-        QMutexLocker locker(&m_mutex);
+    QMutexLocker locker(&m_mutex);
 
-        if (!m_peerPin) {
-            return VFW_E_NOT_CONNECTED;
-        } else {
-            DirectShowMediaType::copy(pmt, m_mediaType);
+    if (m_state == State_Stopped)
+        return S_OK;
 
-            return S_OK;
-        }
-    }
-}
+    qCDebug(qLcRenderFilter, "Stop");
 
-HRESULT VideoSurfaceFilter::QueryPinInfo(PIN_INFO *pInfo)
-{
-    if (!pInfo) {
-        return E_POINTER;
-    } else {
-        AddRef();
+    DirectShowBaseFilter::Stop();
 
-        pInfo->pFilter = this;
-        pInfo->dir = PINDIR_INPUT;
+    clearPendingSample();
 
-        const int bytes = qMin(MAX_FILTER_NAME, (m_pinId.length() + 1) * 2);
+    m_renderMutex.lock();
+    m_EOSDelivered = false;
+    m_running = false;
+    m_renderMutex.unlock();
 
-        ::memcpy(pInfo->achName, m_pinId.utf16(), bytes);
+    SetEvent(m_flushEvent);
+    resetEOS();
+    unscheduleSample();
+    flushSurface();
 
-        return S_OK;
+    IMemAllocator *allocator;
+    if (SUCCEEDED(m_pin->GetAllocator(&allocator))) {
+        allocator->Decommit();
+        allocator->Release();
     }
+
+    return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::QueryId(LPWSTR *Id)
+HRESULT VideoSurfaceFilter::EndOfStream()
 {
-    if (!Id) {
-        return E_POINTER;
-    } else {
-        const int bytes = (m_pinId.length() + 1) * 2;
+    QMutexLocker renderLocker(&m_renderMutex);
 
-        *Id = static_cast<LPWSTR>(::CoTaskMemAlloc(bytes));
+    qCDebug(qLcRenderFilter, "EndOfStream");
 
-        ::memcpy(*Id, m_pinId.utf16(), bytes);
+    m_EOS = true;
 
-        return S_OK;
-    }
-}
+    if (!m_pendingSample && m_running)
+        checkEOS();
 
-HRESULT VideoSurfaceFilter::QueryAccept(const AM_MEDIA_TYPE *pmt)
-{
-    return !m_surface->isFormatSupported(DirectShowMediaType::formatFromType(*pmt))
-            ? S_OK
-            : S_FALSE;
+    return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::EnumMediaTypes(IEnumMediaTypes **ppEnum)
+HRESULT VideoSurfaceFilter::BeginFlush()
 {
-    if (!ppEnum) {
-        return E_POINTER;
-    } else {
-        QMutexLocker locker(&m_mutex);
+    qCDebug(qLcRenderFilter, "BeginFlush");
 
-        *ppEnum = createMediaTypeEnum();
+    SetEvent(m_flushEvent);
+    unscheduleSample();
+    clearPendingSample();
 
-        return S_OK;
-    }
+    return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::QueryInternalConnections(IPin **apPin, ULONG *nPin)
+HRESULT VideoSurfaceFilter::EndFlush()
 {
-    Q_UNUSED(apPin);
-    Q_UNUSED(nPin);
+    qCDebug(qLcRenderFilter, "EndFlush");
 
-    return E_NOTIMPL;
+    ResetEvent(m_flushEvent);
+    return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::EndOfStream()
+HRESULT VideoSurfaceFilter::Receive(IMediaSample *pMediaSample)
 {
-    QMutexLocker locker(&m_mutex);
+    {
+        QMutexLocker locker(&m_mutex);
 
-    if (!m_sampleScheduler.scheduleEndOfStream()) {
-        if (IMediaEventSink *sink = com_cast<IMediaEventSink>(m_graph, IID_IMediaEventSink)) {
-            sink->Notify(
-                    EC_COMPLETE,
-                    S_OK,
-                    reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter *>(this)));
-            sink->Release();
+        qCDebug(qLcRenderFilter, "Receive (sample=%p)", pMediaSample);
+
+        HRESULT hr = m_pin->DirectShowInputPin::Receive(pMediaSample);
+        if (hr != S_OK) {
+            qCDebug(qLcRenderFilter, "  can't receive sample (error %X)", uint(hr));
+            return E_FAIL;
         }
-    }
 
-    return S_OK;
-}
+        // If the format dynamically changed, the sample contains information about the new format.
+        // We need to reset the format and restart the QAbstractVideoSurface.
+        if (m_pin->currentSampleProperties()->pMediaType
+                && (!m_pin->setMediaType(reinterpret_cast<const DirectShowMediaType *>(m_pin->currentSampleProperties()->pMediaType))
+                    || !restartSurface())) {
+                qCWarning(qLcRenderFilter, "  dynamic format change failed, aborting rendering");
+                NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0);
+                return VFW_E_INVALIDMEDIATYPE;
+        }
 
-HRESULT VideoSurfaceFilter::BeginFlush()
-{
-    QMutexLocker locker(&m_mutex);
+        {
+            QMutexLocker locker(&m_renderMutex);
 
-    m_sampleScheduler.setFlushing(true);
+            if (m_pendingSample || m_EOS)
+                return E_UNEXPECTED;
 
-    if (thread() == QThread::currentThread()) {
-        flush();
-    } else {
-        m_loop->postEvent(this, new QEvent(QEvent::Type(FlushSurface)));
+            if (m_running && !scheduleSample(pMediaSample)) {
+                qCWarning(qLcRenderFilter, "  sample can't be scheduled, discarding it");
+                return S_OK;
+            }
+
+            m_pendingSample = pMediaSample;
+            m_pendingSample->AddRef();
+            m_pendingSampleEndTime = m_pin->currentSampleProperties()->tStop;
+        }
 
-        m_wait.wait(&m_mutex);
+        if (m_state == State_Paused) // Render immediately
+            renderPendingSample();
     }
 
-    return S_OK;
-}
+    qCDebug(qLcRenderFilter, "  waiting for render time");
 
-HRESULT VideoSurfaceFilter::EndFlush()
-{
-    QMutexLocker locker(&m_mutex);
+    // Wait for render time. The clock will wake us up whenever the time comes.
+    // It can also be interrupted by a flush, pause or stop.
+    HANDLE waitObjects[] = { m_flushEvent, m_renderEvent };
+    DWORD result = WAIT_TIMEOUT;
+    while (result == WAIT_TIMEOUT)
+        result = WaitForMultipleObjects(2, waitObjects, FALSE, INFINITE);
 
-    m_sampleScheduler.setFlushing(false);
+    if (result == WAIT_OBJECT_0) {
+        // render interrupted (flush, pause, stop)
+        qCDebug(qLcRenderFilter, " rendering of sample %p interrupted", pMediaSample);
+        return S_OK;
+    }
 
-    return S_OK;
-}
+    m_adviseCookie = 0;
 
-void VideoSurfaceFilter::flush()
-{
-    m_surface->present(QVideoFrame());
+    QMutexLocker locker(&m_mutex);
 
-    m_wait.wakeAll();
-}
+    // State might have changed just before the lock
+    if (m_state == State_Stopped) {
+        qCDebug(qLcRenderFilter, "  state changed to Stopped, discarding sample (%p)", pMediaSample);
+        return S_OK;
+    }
 
-HRESULT VideoSurfaceFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
-{
-    Q_UNUSED(tStart);
-    Q_UNUSED(tStop);
-    Q_UNUSED(dRate);
+    QMutexLocker renderLock(&m_renderMutex);
+
+    // Flush or pause might have happened just before the lock
+    if (m_pendingSample && m_running) {
+        renderLock.unlock();
+        renderPendingSample();
+        renderLock.relock();
+    } else {
+        qCDebug(qLcRenderFilter, "  discarding sample (%p)", pMediaSample);
+    }
+
+    clearPendingSample();
+    checkEOS();
+    ResetEvent(m_renderEvent);
 
     return S_OK;
 }
 
-HRESULT VideoSurfaceFilter::QueryDirection(PIN_DIRECTION *pPinDir)
+bool VideoSurfaceFilter::scheduleSample(IMediaSample *sample)
 {
-    if (!pPinDir) {
-        return E_POINTER;
-    } else {
-        *pPinDir = PINDIR_INPUT;
+    if (!sample)
+        return false;
 
-        return S_OK;
+    qCDebug(qLcRenderFilter, "scheduleSample (sample=%p)", sample);
+
+    REFERENCE_TIME sampleStart, sampleEnd;
+    if (FAILED(sample->GetTime(&sampleStart, &sampleEnd)) || !m_clock) {
+        qCDebug(qLcRenderFilter, "  render now");
+        SetEvent(m_renderEvent); // Render immediately
+        return true;
     }
-}
 
-int VideoSurfaceFilter::currentMediaTypeToken()
-{
-    QMutexLocker locker(&m_mutex);
+    if (sampleEnd < sampleStart) { // incorrect times
+        qCWarning(qLcRenderFilter, "  invalid sample times (start=%lli, end=%lli)", sampleStart, sampleEnd);
+        return false;
+    }
+
+    HRESULT hr = m_clock->AdviseTime(m_startTime, sampleStart, (HEVENT)m_renderEvent, &m_adviseCookie);
+    if (FAILED(hr)) {
+        qCWarning(qLcRenderFilter, "  clock failed to advise time (error=%X)", uint(hr));
+        return false;
+    }
 
-    return DirectShowMediaTypeList::currentMediaTypeToken();
+    return true;
 }
 
-HRESULT VideoSurfaceFilter::nextMediaType(
-        int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount)
+void VideoSurfaceFilter::unscheduleSample()
 {
-    QMutexLocker locker(&m_mutex);
-
-    return DirectShowMediaTypeList::nextMediaType(token, index, count, types, fetchedCount);
+    if (m_adviseCookie) {
+        qCDebug(qLcRenderFilter, "unscheduleSample");
+        m_clock->Unadvise(m_adviseCookie);
+        m_adviseCookie = 0;
+    }
 
+    ResetEvent(m_renderEvent);
 }
 
-HRESULT VideoSurfaceFilter::skipMediaType(int token, int *index, ULONG count)
+void VideoSurfaceFilter::clearPendingSample()
 {
-    QMutexLocker locker(&m_mutex);
+    QMutexLocker locker(&m_renderMutex);
+    if (m_pendingSample) {
+        qCDebug(qLcRenderFilter, "clearPendingSample");
+        m_pendingSample->Release();
+        m_pendingSample = NULL;
+    }
+}
 
-    return DirectShowMediaTypeList::skipMediaType(token, index, count);
+void QT_WIN_CALLBACK EOSTimerCallback(UINT, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR)
+{
+    VideoSurfaceFilter *that = reinterpret_cast<VideoSurfaceFilter *>(dwUser);
+    that->onEOSTimerTimeout();
 }
 
-HRESULT VideoSurfaceFilter::cloneMediaType(int token, int index, IEnumMediaTypes **enumeration)
+void VideoSurfaceFilter::onEOSTimerTimeout()
 {
-    QMutexLocker locker(&m_mutex);
+    QMutexLocker locker(&m_renderMutex);
 
-    return DirectShowMediaTypeList::cloneMediaType(token, index, enumeration);
+    if (m_EOSTimer) {
+        m_EOSTimer = 0;
+        checkEOS();
+    }
 }
 
-void VideoSurfaceFilter::customEvent(QEvent *event)
+void VideoSurfaceFilter::checkEOS()
 {
-    const int type = event->type();
-    if (type == StartSurface) {
-        QMutexLocker locker(&m_mutex);
+    QMutexLocker locker(&m_renderMutex);
 
-        m_startResult = start();
+    if (!m_EOS || m_EOSDelivered || m_EOSTimer)
+        return;
 
-        m_wait.wakeAll();
-    } else if (type == StopSurface) {
-        QMutexLocker locker(&m_mutex);
+    if (!m_clock) {
+        notifyEOS();
+        return;
+    }
 
-        stop();
+    REFERENCE_TIME eosTime = m_startTime + m_pendingSampleEndTime;
+    REFERENCE_TIME currentTime;
+    m_clock->GetTime(&currentTime);
+    LONG delay = LONG((eosTime - currentTime) / 10000);
 
-        m_wait.wakeAll();
-    } else if (type == FlushSurface) {
-        QMutexLocker locker(&m_mutex);
+    if (delay < 1) {
+        notifyEOS();
+    } else {
+        qCDebug(qLcRenderFilter, "will trigger EOS in %li", delay);
 
-        flush();
+        m_EOSTimer = timeSetEvent(delay,
+                                  1,
+                                  EOSTimerCallback,
+                                  reinterpret_cast<DWORD_PTR>(this),
+                                  TIME_ONESHOT | TIME_CALLBACK_FUNCTION | TIME_KILL_SYNCHRONOUS);
 
-        m_wait.wakeAll();
-    } else {
-       QObject::customEvent(event);
+        if (!m_EOSTimer) {
+            qDebug("Error with timer");
+            notifyEOS();
+        }
     }
 }
 
-void VideoSurfaceFilter::supportedFormatsChanged()
+void VideoSurfaceFilter::notifyEOS()
 {
-    QMutexLocker locker(&m_mutex);
+    QMutexLocker locker(&m_renderMutex);
 
-    // MEDIASUBTYPE_None;
-    static const GUID none = {
-        0xe436eb8e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} };
+    if (!m_running)
+        return;
 
-    const QList<QVideoFrame::PixelFormat> formats = m_surface->supportedPixelFormats();
+    qCDebug(qLcRenderFilter, "notifyEOS, delivering EC_COMPLETE event");
 
-    QVector<AM_MEDIA_TYPE> mediaTypes;
-    mediaTypes.reserve(formats.count());
+    m_EOSTimer = 0;
+    m_EOSDelivered = true;
+    NotifyEvent(EC_COMPLETE, S_OK, (LONG_PTR)(IBaseFilter *)this);
+}
 
-    AM_MEDIA_TYPE type;
-    type.majortype = MEDIATYPE_Video;
-    type.bFixedSizeSamples = TRUE;
-    type.bTemporalCompression = FALSE;
-    type.lSampleSize = 0;
-    type.formattype = GUID_NULL;
-    type.pUnk = 0;
-    type.cbFormat = 0;
-    type.pbFormat = 0;
+void VideoSurfaceFilter::resetEOS()
+{
+    resetEOSTimer();
 
-    for (QVideoFrame::PixelFormat format : formats) {
-        type.subtype = DirectShowMediaType::convertPixelFormat(format);
+    QMutexLocker locker(&m_renderMutex);
 
-        if (type.subtype != none)
-            mediaTypes.append(type);
-    }
+    if (m_EOS)
+        qCDebug(qLcRenderFilter, "resetEOS (delivered=%s)", m_EOSDelivered ? "true" : "false");
 
-    setMediaTypes(mediaTypes);
+    m_EOS = false;
+    m_EOSDelivered = false;
+    m_pendingSampleEndTime = 0;
 }
 
-void VideoSurfaceFilter::sampleReady()
+void VideoSurfaceFilter::resetEOSTimer()
 {
-    bool eos = false;
+    if (m_EOSTimer) {
+        timeKillEvent(m_EOSTimer);
+        m_EOSTimer = 0;
+    }
+}
 
-    IMediaSample *sample = m_sampleScheduler.takeSample(&eos);
+bool VideoSurfaceFilter::startSurface()
+{
+    if (QThread::currentThread() != thread()) {
+        m_loop->postEvent(this, new QEvent(QEvent::Type(StartSurface)));
+        m_waitSurface.wait(&m_mutex);
+        return m_surfaceStarted;
+    } else {
+        m_surfaceStarted = m_surface->start(m_surfaceFormat);
+        qCDebug(qLcRenderFilter, "startSurface %s", m_surfaceStarted ? "succeeded" : "failed");
+        return m_surfaceStarted;
+    }
+}
 
-    if (sample) {
-        QVideoFrame frame(new MediaSampleVideoBuffer(sample, m_bytesPerLine),
-                          m_surfaceFormat.frameSize(),
-                          m_surfaceFormat.pixelFormat());
+void VideoSurfaceFilter::stopSurface()
+{
+    if (!m_surfaceStarted)
+        return;
 
-        if (IMediaSeeking *seeking = com_cast<IMediaSeeking>(m_graph, IID_IMediaSeeking)) {
-            LONGLONG position = 0;
-            seeking->GetCurrentPosition(&position);
-            seeking->Release();
+    if (QThread::currentThread() != thread()) {
+        m_loop->postEvent(this, new QEvent(QEvent::Type(StopSurface)));
+        m_waitSurface.wait(&m_mutex);
+    } else {
+        qCDebug(qLcRenderFilter, "stopSurface");
+        m_surface->stop();
+        m_surfaceStarted = false;
+    }
+}
 
-            frame.setStartTime(position * 0.1);
+bool VideoSurfaceFilter::restartSurface()
+{
+    if (QThread::currentThread() != thread()) {
+        m_loop->postEvent(this, new QEvent(QEvent::Type(RestartSurface)));
+        m_waitSurface.wait(&m_mutex);
+        return m_surfaceStarted;
+    } else {
+        m_surface->stop();
+        m_surfaceStarted = m_surface->start(m_surfaceFormat);
+        qCDebug(qLcRenderFilter, "restartSurface %s", m_surfaceStarted ? "succeeded" : "failed");
+        return m_surfaceStarted;
+    }
+}
 
-            REFERENCE_TIME startTime = -1;
-            REFERENCE_TIME endTime = -1;
-            if (sample->GetTime(&startTime, &endTime) == S_OK)
-                frame.setEndTime(frame.startTime() + (endTime - startTime) * 0.1);
-        }
+void VideoSurfaceFilter::flushSurface()
+{
+    if (QThread::currentThread() != thread()) {
+        m_loop->postEvent(this, new QEvent(QEvent::Type(FlushSurface)));
+        m_waitSurface.wait(&m_mutex);
+    } else {
+        qCDebug(qLcRenderFilter, "flushSurface");
+        m_surface->present(QVideoFrame());
+    }
+}
 
-        m_surface->present(frame);
+void VideoSurfaceFilter::renderPendingSample()
+{
+    if (QThread::currentThread() != thread()) {
+        m_loop->postEvent(this, new QEvent(QEvent::Type(RenderSample)));
+        m_waitSurface.wait(&m_mutex);
+    } else {
+        QMutexLocker locker(&m_renderMutex);
+        if (!m_pendingSample)
+            return;
 
-        sample->Release();
+        qCDebug(qLcRenderFilter, "presentSample (sample=%p)", m_pendingSample);
 
-        if (eos) {
-            if (IMediaEventSink *sink = com_cast<IMediaEventSink>(m_graph, IID_IMediaEventSink)) {
-                sink->Notify(
-                        EC_COMPLETE,
-                        S_OK,
-                        reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter *>(this)));
-                sink->Release();
-            }
-        }
+        m_surface->present(QVideoFrame(new DirectShowVideoBuffer(m_pendingSample, m_bytesPerLine),
+                                       m_surfaceFormat.frameSize(),
+                                       m_surfaceFormat.pixelFormat()));
     }
 }
 
+bool VideoSurfaceFilter::event(QEvent *e)
+{
+    if (e->type() == QEvent::Type(StartSurface)) {
+        QMutexLocker locker(&m_mutex);
+        startSurface();
+        m_waitSurface.wakeAll();
+        return true;
+    } else if (e->type() == QEvent::Type(StopSurface)) {
+        QMutexLocker locker(&m_mutex);
+        stopSurface();
+        m_waitSurface.wakeAll();
+        return true;
+    } else if (e->type() == QEvent::Type(RestartSurface)) {
+        QMutexLocker locker(&m_mutex);
+        restartSurface();
+        m_waitSurface.wakeAll();
+        return true;
+    } else if (e->type() == QEvent::Type(FlushSurface)) {
+        QMutexLocker locker(&m_mutex);
+        flushSurface();
+        m_waitSurface.wakeAll();
+        return true;
+    } else if (e->type() == QEvent::Type(RenderSample)) {
+        QMutexLocker locker(&m_mutex);
+        renderPendingSample();
+        m_waitSurface.wakeAll();
+        return true;
+    }
+
+    return QObject::event(e);
+}
diff --git a/src/plugins/directshow/player/videosurfacefilter.h b/src/plugins/directshow/player/videosurfacefilter.h
index 001e2804b0fc810aad26a5b58370a1ec35fb933f..581e33c704cecb58248c240ac7449c2306773b80 100644
--- a/src/plugins/directshow/player/videosurfacefilter.h
+++ b/src/plugins/directshow/player/videosurfacefilter.h
@@ -40,135 +40,121 @@
 #ifndef VIDEOSURFACEFILTER_H
 #define VIDEOSURFACEFILTER_H
 
-#include "directshowglobal.h"
-#include "directshowmediatypelist.h"
-#include "directshowsamplescheduler.h"
-#include "directshowmediatype.h"
+#include "directshowbasefilter.h"
 
-#include <QtCore/qbasictimer.h>
 #include <QtCore/qcoreevent.h>
 #include <QtCore/qmutex.h>
-#include <QtCore/qsemaphore.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qwaitcondition.h>
-
-#include <dshow.h>
+#include <qreadwritelock.h>
+#include <qsemaphore.h>
+#include <qwaitcondition.h>
 
 QT_BEGIN_NAMESPACE
 class QAbstractVideoSurface;
 QT_END_NAMESPACE
 
 class DirectShowEventLoop;
+class VideoSurfaceInputPin;
 
-class VideoSurfaceFilter
-    : public QObject
-    , public DirectShowMediaTypeList
-    , public IBaseFilter
-    , public IAMFilterMiscFlags
-    , public IPin
+class VideoSurfaceFilter : public QObject
+                         , public DirectShowBaseFilter
+                         , public IAMFilterMiscFlags
 {
     Q_OBJECT
+    DIRECTSHOW_OBJECT
 public:
-    VideoSurfaceFilter(
-            QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent = 0);
+    VideoSurfaceFilter(QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent = 0);
     ~VideoSurfaceFilter();
 
-    // IUnknown
-    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject);
-    ULONG STDMETHODCALLTYPE AddRef();
-    ULONG STDMETHODCALLTYPE Release();
+    // DirectShowObject
+    HRESULT getInterface(REFIID riid, void **ppvObject);
+
+    // DirectShowBaseFilter
+    QList<DirectShowPin *> pins();
 
     // IPersist
-    HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID);
+    STDMETHODIMP GetClassID(CLSID *pClassID);
 
     // IMediaFilter
-    HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart);
-    HRESULT STDMETHODCALLTYPE Pause();
-    HRESULT STDMETHODCALLTYPE Stop();
-
-    HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState);
-
-    HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock);
-    HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock **ppClock);
-
-    // IBaseFilter
-    HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins **ppEnum);
-    HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin **ppPin);
-
-    HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName);
-
-    HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO *pInfo);
-    HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR *pVendorInfo);
+    STDMETHODIMP Run(REFERENCE_TIME tStart);
+    STDMETHODIMP Pause();
+    STDMETHODIMP Stop();
 
     // IAMFilterMiscFlags
-    ULONG STDMETHODCALLTYPE GetMiscFlags();
+    STDMETHODIMP_(ULONG) GetMiscFlags();
 
-    // IPin
-    HRESULT STDMETHODCALLTYPE Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt);
-    HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt);
-    HRESULT STDMETHODCALLTYPE Disconnect();
-    HRESULT STDMETHODCALLTYPE ConnectedTo(IPin **ppPin);
+    // DirectShowPin (delegate)
+    bool isMediaTypeSupported(const DirectShowMediaType *type);
+    bool setMediaType(const DirectShowMediaType *type);
+    HRESULT completeConnection(IPin *pin);
+    HRESULT connectionEnded();
 
-    HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE *pmt);
+    // IPin (delegate)
+    HRESULT EndOfStream();
+    HRESULT BeginFlush();
+    HRESULT EndFlush();
 
-    HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO *pInfo);
-    HRESULT STDMETHODCALLTYPE QueryId(LPWSTR *Id);
+    // IMemInputPin (delegate)
+    HRESULT Receive(IMediaSample *pMediaSample);
 
-    HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE *pmt);
+private Q_SLOTS:
+    void supportedFormatsChanged();
+    void checkEOS();
 
-    HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes **ppEnum);
+private:
+    enum Events {
+        StartSurface = QEvent::User,
+        StopSurface = QEvent::User + 1,
+        RestartSurface = QEvent::User + 2,
+        FlushSurface = QEvent::User + 3,
+        RenderSample = QEvent::User + 4
+    };
+
+    bool event(QEvent *);
 
-    HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin **apPin, ULONG *nPin);
+    bool startSurface();
+    void stopSurface();
+    bool restartSurface();
+    void flushSurface();
 
-    HRESULT STDMETHODCALLTYPE EndOfStream();
+    bool scheduleSample(IMediaSample *sample);
+    void unscheduleSample();
+    void renderPendingSample();
+    void clearPendingSample();
 
-    HRESULT STDMETHODCALLTYPE BeginFlush();
-    HRESULT STDMETHODCALLTYPE EndFlush();
+    void notifyEOS();
+    void resetEOS();
+    void resetEOSTimer();
+    void onEOSTimerTimeout();
 
-    HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate);
+    friend void QT_WIN_CALLBACK EOSTimerCallback(UINT, UINT, DWORD_PTR dwUser, DWORD_PTR, DWORD_PTR);
 
-    HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir);
+    QMutex m_mutex;
 
-    int currentMediaTypeToken();
-    HRESULT nextMediaType(
-            int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount);
-    HRESULT skipMediaType(int token, int *index, ULONG count);
-    HRESULT cloneMediaType(int token, int index, IEnumMediaTypes **enumeration);
+    DirectShowEventLoop *m_loop;
+    VideoSurfaceInputPin *m_pin;
 
-protected:
-    void customEvent(QEvent *event);
+    QWaitCondition m_waitSurface;
+    QAbstractVideoSurface *m_surface;
+    QVideoSurfaceFormat m_surfaceFormat;
+    int m_bytesPerLine;
+    bool m_surfaceStarted;
 
-private Q_SLOTS:
-    void supportedFormatsChanged();
-    void sampleReady();
+    QList<GUID> m_supportedTypes;
+    QReadWriteLock m_typesLock;
 
-private:
-    HRESULT start();
-    void stop();
-    void flush();
+    QMutex m_renderMutex;
+    bool m_running;
+    IMediaSample *m_pendingSample;
+    REFERENCE_TIME m_pendingSampleEndTime;
+    HANDLE m_renderEvent;
+    HANDLE m_flushEvent;
+    DWORD_PTR m_adviseCookie;
 
-    enum
-    {
-        StartSurface = QEvent::User,
-        StopSurface,
-        FlushSurface
-    };
+    bool m_EOS;
+    bool m_EOSDelivered;
+    UINT m_EOSTimer;
 
-    LONG m_ref;
-    FILTER_STATE m_state;
-    QAbstractVideoSurface *m_surface;
-    DirectShowEventLoop *m_loop;
-    IFilterGraph *m_graph;
-    IPin *m_peerPin;
-    int m_bytesPerLine;
-    HRESULT m_startResult;
-    QString m_name;
-    QString m_pinId;
-    DirectShowMediaType m_mediaType;
-    QVideoSurfaceFormat m_surfaceFormat;
-    QMutex m_mutex;
-    QWaitCondition m_wait;
-    DirectShowSampleScheduler m_sampleScheduler;
+    friend class VideoSurfaceInputPin;
 };
 
 #endif