diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 6700efe6022a04a6dc6c560a437730065837fd95..0000000000000000000000000000000000000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-/codestream.xml
-/statistic.xml
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-/discord.xml
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 0eb1189d82a596e5dbb3b91416da0e55dd9bfb1c..0000000000000000000000000000000000000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-LibAVQt
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index ba0f7e5ea16a2606db664148aefdeaa4437feca7..0000000000000000000000000000000000000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<component name="InspectionProjectProfileManager">
-  <profile version="1.0">
-    <option name="myName" value="Project Default" />
-    <inspection_tool class="Clazy" enabled="true" level="WARNING" enabled_by_default="true">
-      <option name="clazyChecks" value="level0,no-qt-macros" />
-    </inspection_tool>
-  </profile>
-</component>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 30681992d74a91f527acc5761a4f61338e84fb71..c2cdd87df10cd9fbc360e3e46c0dbae0a7afdc1b 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,12 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
-  <component name="CidrRootsConfiguration">
-    <excludeRoots>
-      <file path="$PROJECT_DIR$/build-qt59" />
-    </excludeRoots>
-  </component>
-  <component name="ProjectPlainTextFileTypeManager">
-    <file url="file://$PROJECT_DIR$/AVQt/mainpage.dox" />
+  <component name="DiscordProjectSettings">
+    <option name="show" value="ASK" />
+    <option name="description" value="" />
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
index 98c3ec3c4cf4388d48645f157f8ce3d7ad61b17a..407bd37b4d284a7a2073e032d004ba3afa31e6e9 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,7 +2,7 @@
 <project version="4">
   <component name="ProjectModuleManager">
     <modules>
-      <module fileurl="file://$PROJECT_DIR$/.idea/libAVQt.iml" filepath="$PROJECT_DIR$/.idea/libAVQt.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/LibAVQt.iml" filepath="$PROJECT_DIR$/.idea/LibAVQt.iml" />
     </modules>
   </component>
 </project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7f4cb416c083d265558da75d457237d671..02f8a3e58b8ada6b0074ddb759d0ca33fa83230b 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,5 +2,6 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/mfx_dispatch" vcs="Git" />
   </component>
 </project>
\ No newline at end of file
diff --git a/AVQt/CMakeLists.txt b/AVQt/CMakeLists.txt
index efdc106480f4508a01593a53b8ef66f937818a35..b55b5242f8a3335070286ada300c55b8d70dbd23 100644
--- a/AVQt/CMakeLists.txt
+++ b/AVQt/CMakeLists.txt
@@ -9,7 +9,16 @@ set(CMAKE_AUTOUIC on)
 
 set(CMAKE_INCLUDE_CURRENT_DIR on)
 
-include_directories(F:\\Dev\\mfx_dispatch-master)
+if (WIN32)
+    string(FIND "${CMAKE_SYSTEM_VERSION}" "." FIRST_DOT)
+    string(FIND "${CMAKE_SYSTEM_VERSION}" "." LAST_DOT REVERSE)
+    math(EXPR LAST_DOT "${LAST_DOT}+1") # We don't want the dot in the build number
+
+    string(SUBSTRING "${CMAKE_SYSTEM_VERSION}" 0 ${FIRST_DOT} WINDOWS_VERSION)
+    string(SUBSTRING "${CMAKE_SYSTEM_VERSION}" ${LAST_DOT} -1 WINDOWS_BUILD)
+    message("Windows ${WINDOWS_VERSION} with build number: ${WINDOWS_BUILD}")
+    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on)
+endif()
 
 #find_package(Qt6 COMPONENTS Core Gui Concurrent Widgets OpenGL OpenGLWidgets)
 find_package(Qt5 COMPONENTS Core Gui Concurrent Widgets OpenGL)
@@ -91,7 +100,8 @@ set(SOURCES_WINDOWS
         filter/DecoderDXVA2.cpp
         )
 
-if (WIN32)
+if (WIN32 AND ${WINDOWS_VERSION} GREATER_EQUAL 10 AND ${WINDOWS_BUILD} GREATER_EQUAL 17763) # LibAVQt only supports Windows 10 1809 and above
+#    add_compile_options(/EHa)
     add_library(AVQt SHARED ${SOURCES} ${SOURCES_WINDOWS})
     add_library(AVQtStatic STATIC ${SOURCES} ${SOURCES_WINDOWS})
 elseif (UNIX)
@@ -200,7 +210,7 @@ target_link_libraries(AVQt Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::
 target_link_libraries(AVQtStatic Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample)
 
 if (WIN32)
-    target_link_libraries(AVQt opengl32 OpenAL32 d3d9 windowsapp user32 kernel32 Dwmapi psapi d3d9 dxguid)
+    target_link_libraries(AVQt opengl32 OpenAL32 windowsapp user32 kernel32 Dwmapi psapi d3d9 dxguid)
     target_link_libraries(AVQtStatic opengl32 OpenAL32 windowsapp user32 kernel32 Dwmapi psapi d3d9 dxguid)
 else ()
     target_link_libraries(AVQt GL openal EGL GLU va)
diff --git a/AVQt/filter/DecoderQSV.cpp b/AVQt/filter/DecoderQSV.cpp
index 1107b8dc68164fc134d02cf3f4e076ca55797792..a3fa0cd676e8d969b737901e78311fd5b0e5c117 100644
--- a/AVQt/filter/DecoderQSV.cpp
+++ b/AVQt/filter/DecoderQSV.cpp
@@ -369,7 +369,7 @@ namespace AVQt {
                 frames_ctx->height = FFALIGN(pCodecCtx->coded_height, 32);
                 frames_ctx->initial_pool_size = 32;
 
-                frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;
+                frames_hwctx->frame_type = 0x00100; // 0x00100 is equivalent to MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET, but additional headers would be required to use this constant
 
                 ret = av_hwframe_ctx_init(pCodecCtx->hw_frames_ctx);
                 if (ret < 0)
@@ -385,4 +385,4 @@ namespace AVQt {
 
         return AV_PIX_FMT_NONE;
     }
-}
\ No newline at end of file
+}
diff --git a/AVQt/filter/EncoderQSV.cpp b/AVQt/filter/EncoderQSV.cpp
index 888521b59d24d394bb21b906bfea00bb0bd1fa6f..eb9e8e6c294067ca30bd6cabdb799a7d53b51780 100644
--- a/AVQt/filter/EncoderQSV.cpp
+++ b/AVQt/filter/EncoderQSV.cpp
@@ -272,6 +272,7 @@ namespace AVQt {
                 qDebug("Referencing frame");
                 av_frame_ref(queueFrame.first, frame);
                 break;
+            case AV_PIX_FMT_DXVA2_VLD:
             case AV_PIX_FMT_DRM_PRIME:
             case AV_PIX_FMT_VAAPI:
                 if (!d->m_pDeviceCtx) {
@@ -295,11 +296,11 @@ namespace AVQt {
                 }
                 av_frame_ref(queueFrame.first, frame);
                 break;
-            case AV_PIX_FMT_DXVA2_VLD:
-                qDebug("Transferring frame from GPU to CPU");
-                av_hwframe_transfer_data(queueFrame.first, frame, 0);
-                queueFrame.first->pts = frame->pts;
-                break;
+//            case AV_PIX_FMT_DXVA2_VLD:
+//                qDebug("Transferring frame from GPU to CPU");
+//                av_hwframe_transfer_data(queueFrame.first, frame, 0);
+//                queueFrame.first->pts = frame->pts;
+//                break;
             default:
                 qDebug("Referencing frame");
                 av_frame_ref(queueFrame.first, frame);
@@ -400,7 +401,8 @@ namespace AVQt {
                     for (const auto &cb: d->m_cbList) {
                         AVCodecParameters *parameters = avcodec_parameters_alloc();
                         avcodec_parameters_from_context(parameters, d->m_pCodecCtx);
-                        cb->init(this, d->m_framerate, d->m_pCodecCtx->time_base, 0, parameters, nullptr, nullptr);
+                        parameters->bit_rate = d->m_bitrate;
+                        cb->init(this, d->m_framerate, d->m_pCodecCtx->time_base, d->m_duration, parameters, nullptr, nullptr);
                         cb->start(this);
                     }
                 }
@@ -412,14 +414,19 @@ namespace AVQt {
                     }
                     if (frame.first->hw_frames_ctx) {
                         AVFrame *outFrame;
+                        std::chrono::time_point<std::chrono::high_resolution_clock> t1;
                         switch (frame.first->format) {
                             case AV_PIX_FMT_VAAPI:
                             case AV_PIX_FMT_DRM_PRIME:
-                                qDebug("[AVQt::EncoderQSV] Mapping VAAPI/DRM_PRIME frame to QSV");
+                            case AV_PIX_FMT_DXVA2_VLD:
+                                qDebug("[AVQt::EncoderQSV] Mapping VAAPI/DRM_PRIME/DXVA2 frame to QSV");
                                 outFrame = av_frame_alloc();
                                 outFrame->hw_frames_ctx = av_buffer_ref(d->m_pFramesCtx);
                                 outFrame->format = AV_PIX_FMT_QSV;
+                                t1 = std::chrono::high_resolution_clock::now();
                                 av_hwframe_map(outFrame, frame.first, AV_HWFRAME_MAP_DIRECT | AV_HWFRAME_MAP_READ);
+                                qDebug("Mapping time: %ld us", std::chrono::duration_cast<std::chrono::microseconds>(
+                                        std::chrono::high_resolution_clock::now() - t1).count());
                                 break;
                             case AV_PIX_FMT_QSV:
                                 outFrame = av_frame_clone(frame.first);
diff --git a/AVQt/filter/EncoderVAAPI.cpp b/AVQt/filter/EncoderVAAPI.cpp
index e001d48eaff91df8b2e1a28db4335d69d1943dfa..18d0f85fc9affa9c0c3f7a100a1035aab715ec6b 100644
--- a/AVQt/filter/EncoderVAAPI.cpp
+++ b/AVQt/filter/EncoderVAAPI.cpp
@@ -332,7 +332,9 @@ namespace AVQt {
                     for (const auto &cb: d->m_cbList) {
                         AVCodecParameters *parameters = avcodec_parameters_alloc();
                         avcodec_parameters_from_context(parameters, d->m_pCodecCtx);
-                        cb->init(this, av_make_q(0, 1), d->m_pCodecCtx->time_base, 0, parameters, nullptr, nullptr);
+                        parameters->bit_rate = d->m_bitrate;
+                        //TODO: Pass framerate
+                        cb->init(this, av_make_q(0, 1), d->m_pCodecCtx->time_base, d->m_duration, parameters, nullptr, nullptr);
                         cb->start(this);
                     }
                 }
diff --git a/AVQt/input/Demuxer.h b/AVQt/input/Demuxer.h
index 313c9bed4e5e1c22748c00c5fec91687b457c30e..6cc5a6a9ddac818dc0cb1884289b676c4794a57b 100644
--- a/AVQt/input/Demuxer.h
+++ b/AVQt/input/Demuxer.h
@@ -107,7 +107,7 @@ namespace AVQt {
         void paused(bool pause) Q_DECL_OVERRIDE;
 
     protected:
-        explicit Demuxer(DemuxerPrivate &p);
+        [[maybe_unused]] explicit Demuxer(DemuxerPrivate &p);
 
         DemuxerPrivate *d_ptr;
     };
diff --git a/AVQt/input/IAudioSource.h b/AVQt/input/IAudioSource.h
index 9d563e134a105deaf3531e306bad3f71ec8989eb..40e69af81b32ef1131e3c25234bc78ba47a5f958 100644
--- a/AVQt/input/IAudioSource.h
+++ b/AVQt/input/IAudioSource.h
@@ -18,9 +18,9 @@ namespace AVQt {
 
         virtual bool isPaused() = 0;
 
-        Q_INVOKABLE virtual qint64 registerCallback(IAudioSink *callback) = 0;
+        Q_INVOKABLE virtual qint64 registerCallback(AVQt::IAudioSink *callback) = 0;
 
-        Q_INVOKABLE virtual qint64 unregisterCallback(IAudioSink *callback) = 0;
+        Q_INVOKABLE virtual qint64 unregisterCallback(AVQt::IAudioSink *callback) = 0;
 
     public slots:
         Q_INVOKABLE virtual int init() = 0;
diff --git a/AVQt/input/IFrameSource.h b/AVQt/input/IFrameSource.h
index 2e3c88a44dfe3b0534b003ec3df72cbb972dac7f..39cc0292d38aaabbf96235431677040eda4e4795 100644
--- a/AVQt/input/IFrameSource.h
+++ b/AVQt/input/IFrameSource.h
@@ -18,32 +18,32 @@ namespace AVQt {
      */
     class IFrameSource {
     public:
-        /*!
-         * \enum CB_TYPE
-         * \brief Used to define accepted callback types of a frame source or filter.
-         *
-         * Can be linked with bitwise or to set multiple types
-         */
-        enum CB_TYPE : int8_t {
-            /*!
-             * \private
-             */
-            CB_NONE = -1,
-
-            /*!
-             * \brief Callback type: QImage. When registered with this type, the onFrame(QImage, ...) method will be called
-             */
-            CB_QIMAGE = 0b00000001,
-
-            /*!
-             * \brief Callback type: AVFrame. When registered with this type, the onFrame(AVFrame, ...) method will be called.
-             *
-             * ***Important:*** The AVFrame can be every possible software pixel format, so the frame sink/filter has to be able to
-             * convert the pixel format if necessary. It has to be freed after usage.
-             * If the source processes frames on the GPU, it has to be downloaded to CPU memory before passing to callbacks.
-             */
-            CB_AVFRAME = 0b00000010
-        };
+//        /*!
+//         * \enum CB_TYPE
+//         * \brief Used to define accepted callback types of a frame source or filter.
+//         *
+//         * Can be linked with bitwise or to set multiple types
+//         */
+//        enum CB_TYPE : int8_t {
+//            /*!
+//             * \private
+//             */
+//            CB_NONE = -1,
+//
+//            /*!
+//             * \brief Callback type: QImage. When registered with this type, the onFrame(QImage, ...) method will be called
+//             */
+//            CB_QIMAGE = 0b00000001,
+//
+//            /*!
+//             * \brief Callback type: AVFrame. When registered with this type, the onFrame(AVFrame, ...) method will be called.
+//             *
+//             * ***Important:*** The AVFrame can be every possible software pixel format, so the frame sink/filter has to be able to
+//             * convert the pixel format if necessary. It has to be freed after usage.
+//             * If the source processes frames on the GPU, it has to be downloaded to CPU memory before passing to callbacks.
+//             */
+//            CB_AVFRAME = 0b00000010
+//        };
 
         /*!
          * \private
@@ -63,14 +63,14 @@ namespace AVQt {
          * @param type Callback type, can be linked with bitwise or to set multiple options
          * @return
          */
-        Q_INVOKABLE virtual qint64 registerCallback(IFrameSink *frameSink) = 0;
+        Q_INVOKABLE virtual qint64 registerCallback(AVQt::IFrameSink *frameSink) = 0;
 
         /*!
          * \brief Removes frame callback \c frameSink from registry
          * @param frameSink Frame sink/filter to be removed
          * @return Previous position of the item, is -1 when not in registry
          */
-        Q_INVOKABLE virtual qint64 unregisterCallback(IFrameSink *frameSink) = 0;
+        Q_INVOKABLE virtual qint64 unregisterCallback(AVQt::IFrameSink *frameSink) = 0;
 
     public slots:
         /*!
diff --git a/AVQt/output/Muxer.cpp b/AVQt/output/Muxer.cpp
index d612c3110cfd16c10484e4d25035ca6cfa347cb1..a940eab1cb64bfc402a30f81c5d3b35003ae705e 100644
--- a/AVQt/output/Muxer.cpp
+++ b/AVQt/output/Muxer.cpp
@@ -92,7 +92,7 @@ namespace AVQt {
             d->m_pFormatContext = avformat_alloc_context();
             d->m_pFormatContext->oformat = av_guess_format(outputFormat.toLocal8Bit().data(), "", nullptr);
             d->m_pIOBuffer = static_cast<uint8_t *>(av_malloc(MuxerPrivate::IOBUF_SIZE));
-            d->m_pIOContext = avio_alloc_context(d->m_pIOBuffer, MuxerPrivate::IOBUF_SIZE, 1, d->m_outputDevice, nullptr,
+            d->m_pIOContext = avio_alloc_context(d->m_pIOBuffer, MuxerPrivate::IOBUF_SIZE, 1, d, nullptr,
                                                  &MuxerPrivate::writeToIO, &MuxerPrivate::seekIO);
             d->m_pIOContext->seekable = !d->m_outputDevice->isSequential();
             d->m_pFormatContext->pb = d->m_pIOContext;
@@ -121,12 +121,6 @@ namespace AVQt {
             subtitleStream->time_base = timebase;
             d->m_sourceStreamMap[source].insert(subtitleStream, timebase);
         }
-        int ret = avformat_write_header(d->m_pFormatContext, nullptr);
-        if (ret != 0) {
-            constexpr auto strBufSize = 32;
-            char strBuf[strBufSize];
-            qWarning("[AVQt::Muxer] %d: Couldn't init AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
-        }
         qDebug("[AVQt::Muxer] Initialized");
     }
 
@@ -250,6 +244,13 @@ namespace AVQt {
     void Muxer::run() {
         Q_D(AVQt::Muxer);
 
+        int ret = avformat_write_header(d->m_pFormatContext, nullptr);
+        if (ret != 0) {
+            constexpr auto strBufSize = 32;
+            char strBuf[strBufSize];
+            qWarning("[AVQt::Muxer] %d: Couldn't init AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+        }
+
         while (d->m_running.load()) {
             if (!d->m_paused.load() && d->m_inputQueue.size() > 5) {
                 QPair<AVPacket *, AVStream *> packet;
@@ -271,37 +272,39 @@ namespace AVQt {
     }
 
     int MuxerPrivate::writeToIO(void *opaque, uint8_t *buf, int buf_size) {
-        auto *outputDevice = reinterpret_cast<QIODevice *>(opaque);
+        auto *d = reinterpret_cast<MuxerPrivate *>(opaque);
+        QMutexLocker lock(&d->m_ioMutex);
 
-        auto bytesWritten = outputDevice->write(reinterpret_cast<const char *>(buf), buf_size);
+        auto bytesWritten = d->m_outputDevice->write(reinterpret_cast<const char *>(buf), buf_size);
         return bytesWritten == 0 ? AVERROR_UNKNOWN : static_cast<int>(bytesWritten);
     }
 
     int64_t MuxerPrivate::seekIO(void *opaque, int64_t offset, int whence) {
-        auto *outputDevice = reinterpret_cast<QIODevice *>(opaque);
+        auto *d = reinterpret_cast<MuxerPrivate *>(opaque);
+        QMutexLocker lock(&d->m_ioMutex);
 
-        if (outputDevice->isSequential()) {
+        if (d->m_outputDevice->isSequential()) {
             return AVERROR_UNKNOWN;
         }
 
         bool result;
         switch (whence) {
             case SEEK_SET:
-                result = outputDevice->seek(offset);
+                result = d->m_outputDevice->seek(offset);
                 break;
             case SEEK_CUR:
-                result = outputDevice->seek(outputDevice->pos() + offset);
+                result = d->m_outputDevice->seek(d->m_outputDevice->pos() + offset);
                 break;
             case SEEK_END:
-                result = outputDevice->seek(outputDevice->size() - offset);
+                result = d->m_outputDevice->seek(d->m_outputDevice->size() - offset);
                 break;
             case AVSEEK_SIZE:
-                return outputDevice->size();
+                return d->m_outputDevice->size();
             default:
                 return AVERROR_UNKNOWN;
         }
 
-        return result ? outputDevice->pos() : AVERROR_UNKNOWN;
+        return result ? d->m_outputDevice->pos() : AVERROR_UNKNOWN;
     }
 
     bool MuxerPrivate::packetQueueCompare(const QPair<AVPacket *, AVStream *> &packet1, const QPair<AVPacket *, AVStream *> &packet2) {
diff --git a/AVQt/output/OpenGLRenderer.cpp b/AVQt/output/OpenGLRenderer.cpp
index 8129ee2e23262d57263c41dceb608b9429ae5798..c9e5a8f80475c13b51e3d85fa9ba98b6c7d39105 100644
--- a/AVQt/output/OpenGLRenderer.cpp
+++ b/AVQt/output/OpenGLRenderer.cpp
@@ -5,21 +5,20 @@
 #include "private/OpenGLRenderer_p.h"
 #include "OpenGLRenderer.h"
 
-#include <QtGui>
-#include <QtConcurrent>
-#include <QImage>
-
 #ifdef Q_OS_LINUX
 
 #include <va/va.h>
 #include <va/va_drmcommon.h>
 #include <libdrm/drm_fourcc.h>
 #include <unistd.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <EGL/eglplatform.h>
+#include <GL/glu.h>
 #elif defined(Q_OS_WINDOWS)
 
-#include <Windows.h>
-#include <gl/GL.h>
-#include <d3d9helper.h>
+#include <d3d9.h>
 
 #define WGL_LOOKUP_FUNCTION(type, func) \
     type func = (type) wglGetProcAddress(#func); \
@@ -45,11 +44,16 @@ typedef BOOL (WINAPI *PFNWGLDXOBJECTACCESSNVPROC)(HANDLE hObject, GLenum access)
 #define WGL_ACCESS_READ_WRITE_NV 0x0001
 #define WGL_ACCESS_WRITE_DISCARD_NV 0x0002
 
+#undef ASSERT
+#define ASSERT(expr) if (!expr) { \
+    qFatal("Assertion" #expr "failed");                              \
+}
+
 std::string GetLastErrorAsString() {
     //Get the error message ID, if any.
     DWORD errorMessageID = ::GetLastError();
     if (errorMessageID == 0) {
-        return std::string(); //No error message has been recorded
+        return {}; //No error message has been recorded
     }
 
     LPSTR messageBuffer = nullptr;
@@ -57,7 +61,7 @@ std::string GetLastErrorAsString() {
     //Ask Win32 to give us the string version of that message ID.
     //The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
     size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
-                                 NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, NULL);
+                                 nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &messageBuffer, 0, nullptr);
 
     //Copy the error message into a std::string.
     std::string message(messageBuffer, size);
@@ -68,49 +72,89 @@ std::string GetLastErrorAsString() {
     return message;
 }
 
-QImage SurfaceToQImage(IDirect3DSurface9 *pSurface, int height) {
+[[maybe_unused]] QImage SurfaceToQImage(IDirect3DSurface9 *pSurface/*, IDirect3DDevice9 *pDevice*/, int height) {
     D3DSURFACE_DESC surfaceDesc;
     pSurface->GetDesc(&surfaceDesc);
+//    IDirect3DSurface9 *pRGBASurface;
+//    HANDLE hRGBASurface;
+//    ASSERT(SUCCEEDED(
+//            pDevice->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &pRGBASurface,
+//                                                 &hRGBASurface)));
+//    ASSERT(SUCCEEDED(pDevice->StretchRect(pSurface, nullptr, pRGBASurface, nullptr, D3DTEXF_LINEAR)));
+//    BYTE *dataPtr = reinterpret_cast<BYTE *>(locked.pBits);
+
+//    if (locked.Pitch == 0) {
+//        return {};
+//    }
+//    QThread::sleep(1);
+
+
+    QImage::Format imageFormat;
+    uint bpp;
+    switch (surfaceDesc.Format) {
+//        case D3DFMT_A16B16G16R16:
+//            imageFormat = QImage::Format_RGBA64;
+//            bpp = 64;
+//            break;
+//        case D3DFMT_A8R8G8B8:
+//            imageFormat = QImage::Format_ARGB32;
+//            bpp = 32;
+//            break;
+        default:
+            imageFormat = QImage::Format_Grayscale8;
+            bpp = 8;
+            break;
+    }
+    bpp /= 8;
+
+    QImage result(surfaceDesc.Width, surfaceDesc.Height, imageFormat);
+
     D3DLOCKED_RECT locked;
-    assert(SUCCEEDED(pSurface->LockRect(&locked, nullptr, D3DLOCK_READONLY)));
-    BYTE *dataPtr = reinterpret_cast<BYTE *>(locked.pBits);
+    ASSERT(SUCCEEDED(pSurface->LockRect(&locked, nullptr, D3DLOCK_READONLY)));
+    qDebug("Locked pitch: %d", locked.Pitch);
+//    locked.Pitch = 2048;
+    qDebug() << locked.pBits << locked.Pitch << surfaceDesc.Width;
+    for (uint y = 0; y < surfaceDesc.Height; ++y) {
+        qDebug() << y << ": From" << ((uint8_t *) locked.pBits) + y * locked.Pitch << ", To:"
+                 << result.scanLine(y) << ", Size:" << surfaceDesc.Width * bpp;
+        memcpy(result.scanLine(y), ((uint8_t *) locked.pBits) + y * locked.Pitch,
+               surfaceDesc.Width * bpp);
+    }
 
-//    uint8_t *data = new uint8_t[surfaceDesc.Width * surfaceDesc.Height * 4];
-//    for (uint y = 0; y < surfaceDesc.Height; ++y) {
-//        memcpy(data + y * surfaceDesc.Width * 4, dataPtr + y * locked.Pitch * 4, surfaceDesc.Width * 4);
+//    for (uint i = 0; i < 10; ++i) {
+//        qDebug() << i << ":" << locked.pBits << locked.Pitch << surfaceDesc.Width;
+//        QThread::msleep(20);
 //    }
 
-    QImage result = QImage(dataPtr, surfaceDesc.Width, height, locked.Pitch,
-                           (surfaceDesc.Format == D3DFMT_A8R8G8B8 ? QImage::Format_ARGB32 : QImage::Format_Grayscale8))
-            .copy();
-
-    qDebug() << result.size() << "using" << result.sizeInBytes() << "Byte";
+//    QImage result = QImage((uint8_t*)locked.pBits, locked.Pitch, surfaceDesc.Height, locked.Pitch, QImage::Format_Grayscale8);
+//    QFile out("output2.bmp");
+//    out.open(QIODevice::Truncate | QIODevice::ReadWrite);
+//    result.save(&out, "BMP");
+//    out.flush();
+//    out.close();
+//    qDebug() << result.size() << "using" << result.sizeInBytes() << "Byte";
 
 //    delete[] data;
+    ASSERT(SUCCEEDED(pSurface->UnlockRect()));
 
     return result;
 }
 
 #endif
 
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <EGL/eglplatform.h>
-#include <GL/glu.h>
-
 #include <iostream>
 
 #pragma clang diagnostic push
 #pragma ide diagnostic ignored "HidingNonVirtualFunction"
 extern "C" {
-#include <libavutil/frame.h>
+
 #include <libavcodec/avcodec.h>
 #include <libavutil/imgutils.h>
-#include <libavutil/hwcontext.h>
+
 #ifdef Q_OS_LINUX
 #include <libavutil/hwcontext_vaapi.h>
-#elif defined(Q_OS_WINDOWS)
-#include <libavutil/hwcontext_dxva2.h>
+//#elif defined(Q_OS_WINDOWS)
+//#include <libavutil/hwcontext_dxva2.h>
 #endif
 }
 
@@ -128,6 +172,9 @@ static int closefd(int fd) {
 }
 #endif
 
+#include <QtGui>
+#include <QtConcurrent>
+
 std::string eglErrorString(EGLint error) {
     switch (error) {
         case EGL_SUCCESS:
@@ -366,9 +413,24 @@ namespace AVQt {
                     bool shouldBe = true;
                     if (d->m_firstFrame.compare_exchange_strong(shouldBe, false)) {
                         if (input->format == AV_PIX_FMT_DXVA2_VLD) {
+//                            AVFrame *sw_frame = av_frame_alloc();
+//                            if (av_hwframe_transfer_data(sw_frame, input, 0)) {
+//                                qFatal("Could not map frame");
+//                            }
+//                            QImage result(sw_frame->data[0], sw_frame->width, sw_frame->height, sw_frame->linesize[0], QImage::Format_Grayscale8);
+//                            qDebug() << "Dimensions:" << result.size() << ", Size:" << result.sizeInBytes();
+//                            result.save("output3.bmp");
+//                            exit(0);
                             d->m_pDXVAContext = static_cast<AVDXVA2DeviceContext *>(reinterpret_cast<AVHWDeviceContext *>(pDeviceCtx->data)->hwctx);
                             d->m_pD3DManager = d->m_pDXVAContext->devmgr;
                             d->m_pD3DManager->AddRef();
+//                            HANDLE hDevice;
+//                            IDirect3DDevice9 *pDevice;
+//                            d->m_pD3DManager->OpenDeviceHandle(&hDevice);
+//                            d->m_pD3DManager->LockDevice(hDevice, &pDevice, TRUE);
+//                            SurfaceToQImage(reinterpret_cast<LPDIRECT3DSURFACE9>(input->data[3])/*, pDevice*/, input->height);
+//                            d->m_pD3DManager->UnlockDevice(hDevice, TRUE);
+//                            d->m_pD3DManager->CloseDeviceHandle(hDevice);
                         } else if (input->format == AV_PIX_FMT_D3D11) {
                             d->m_pD3D11VAContext = static_cast<AVD3D11VADeviceContext *>(reinterpret_cast<AVHWDeviceContext *>(pDeviceCtx->data)->hwctx);
                         }
@@ -495,15 +557,15 @@ namespace AVQt {
         LOOKUP_FUNCTION(PFNEGLDESTROYIMAGEKHRPROC, eglDestroyImageKHR)
         LOOKUP_FUNCTION(PFNGLEGLIMAGETARGETTEXTURE2DOESPROC, glEGLImageTargetTexture2DOES)
 #elif defined(Q_OS_WINDOWS)
-        WGL_LOOKUP_FUNCTION(PFNWGLDXOPENDEVICENVPROC, wglDXOpenDeviceNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXCLOSEDEVICENVPROC, wglDXCloseDeviceNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXREGISTEROBJECTNVPROC, wglDXRegisterObjectNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXUNREGISTEROBJECTNVPROC, wglDXUnregisterObjectNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXLOCKOBJECTSNVPROC, wglDXLockObjectsNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXUNLOCKOBJECTSNVPROC, wglDXUnlockObjectsNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXOBJECTACCESSNVPROC, wglDXObjectAccessNV);
-        WGL_LOOKUP_FUNCTION(PFNWGLDXSETRESOURCESHAREHANDLENVPROC, wglDXSetResourceShareHandleNV);
-        WGL_LOOKUP_FUNCTION(PFNGLACTIVETEXTUREPROC, glActiveTexture);
+        WGL_LOOKUP_FUNCTION(PFNWGLDXOPENDEVICENVPROC, wglDXOpenDeviceNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXCLOSEDEVICENVPROC, wglDXCloseDeviceNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXREGISTEROBJECTNVPROC, wglDXRegisterObjectNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXUNREGISTEROBJECTNVPROC, wglDXUnregisterObjectNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXLOCKOBJECTSNVPROC, wglDXLockObjectsNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXUNLOCKOBJECTSNVPROC, wglDXUnlockObjectsNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXOBJECTACCESSNVPROC, wglDXObjectAccessNV)
+        WGL_LOOKUP_FUNCTION(PFNWGLDXSETRESOURCESHAREHANDLENVPROC, wglDXSetResourceShareHandleNV)
+        WGL_LOOKUP_FUNCTION(PFNGLACTIVETEXTUREPROC, glActiveTexture)
 #endif
 
         if (d->m_currentFrame) {
@@ -691,9 +753,11 @@ namespace AVQt {
                             d->m_pD3DManager->OpenDeviceHandle(&hDevice);
                             d->m_pD3DManager->LockDevice(hDevice, &pDevice, TRUE);
 
-                            assert(SUCCEEDED(pDevice->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, D3DFMT_A8R8G8B8,
-                                                                                  D3DPOOL_DEFAULT, &d->m_pSharedSurface,
-                                                                                  &d->m_hSharedSurface)));
+                            qWarning("Creating surface with %dx%d pixels", surfaceDesc.Width, surfaceDesc.Height);
+
+                            pDevice->CreateOffscreenPlainSurface(surfaceDesc.Width, surfaceDesc.Height, D3DFMT_A8R8G8B8,
+                                                                 D3DPOOL_DEFAULT, &d->m_pSharedSurface,
+                                                                 &d->m_hSharedSurface);
 
                             d->m_hDXDevice = wglDXOpenDeviceNV(pDevice);
                             if (d->m_hDXDevice) {
@@ -705,6 +769,7 @@ namespace AVQt {
                             pDevice->Release();
                             d->m_pD3DManager->UnlockDevice(hDevice, TRUE);
                         } else if (d->m_currentFrame->format == AV_PIX_FMT_D3D11) {
+                            qFatal("D3D11VA -> OpenGL interop is being worked on");
                             glGenTextures(1, d->m_textures);
                             glBindTexture(GL_TEXTURE_2D, d->m_textures[0]);
                             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@@ -715,10 +780,8 @@ namespace AVQt {
                             d->m_program->setUniformValue("inputFormat", 0);
                             d->m_program->release();
 
-                            ID3D11Texture2D *texture = reinterpret_cast<ID3D11Texture2D*>(d->m_currentFrame->data[0]);
                             d->m_hDXDevice = wglDXOpenDeviceNV(d->m_pD3D11VAContext->device);
 
-                            qFatal("D3D11VA -> OpenGL interop is in progress");
                             //TODO: D3D11 render to texture for NV12/P010 -> RGB conversion, direct NV12/P010 OpenGL mapping not possible, because both planes are in one memory block
                         } else
 #endif
@@ -912,10 +975,19 @@ namespace AVQt {
                         d->m_pD3DManager->LockDevice(hDevice, &pDevice, true);
                         IDirect3D9 *d3d9;
                         pDevice->GetDirect3D(&d3d9);
-                        assert(SUCCEEDED(d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, surfaceDesc.Format,
+                        ASSERT(SUCCEEDED(d3d9->CheckDeviceFormatConversion(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, surfaceDesc.Format,
                                                                            D3DFMT_A8R8G8B8)));
-                        assert(SUCCEEDED(pDevice->StretchRect(surface, nullptr, d->m_pSharedSurface, nullptr, D3DTEXF_LINEAR)));
+                        if (0 != pDevice->StretchRect(surface, nullptr, d->m_pSharedSurface, nullptr, D3DTEXF_LINEAR)) {
+                            qFatal("Could not map NV12/P010 surface to RGB");
+                        }
+
+//                        auto image = SurfaceToQImage(surface/*, pDevice*/, d->m_currentFrame->height);
+//                        if (!image.isNull()) {
+//                            image.save("output.bmp");
+//                        }
+
                         d->m_pD3DManager->UnlockDevice(hDevice, TRUE);
+                        d->m_pD3DManager->CloseDeviceHandle(hDevice);
 //                        auto image = SurfaceToQImage(surface, frame->height);
 //                        qDebug() << "Decoded surface is using" << image.sizeInBytes() << "Byte of memory at" << image.size();
 //                        image.save("decoded.bmp");
@@ -1167,6 +1239,6 @@ namespace AVQt {
         int s = static_cast<int>((ts / 1000) % 60);
         int m = static_cast<int>((ts / 1000 / 60) % 60);
         int h = static_cast<int>(ts / 1000 / 60 / 60);
-        return QTime(h, m, s, ms);
+        return {h, m, s, ms};
     }
 }
\ No newline at end of file
diff --git a/AVQt/output/private/Muxer_p.h b/AVQt/output/private/Muxer_p.h
index 7aefc087e4a29b00bbe2f80c28231fd7e86529c9..6a097444d6674d88855972c15080e71936763156 100644
--- a/AVQt/output/private/Muxer_p.h
+++ b/AVQt/output/private/Muxer_p.h
@@ -30,7 +30,7 @@ namespace AVQt {
 
         Muxer::FORMAT m_format{Muxer::FORMAT::INVALID};
 
-        QMutex m_initMutex{};
+        QMutex m_initMutex{}, m_ioMutex{};
         static constexpr int64_t IOBUF_SIZE{4 * 1024};  // 4 KB
         uint8_t *m_pIOBuffer{nullptr};
         AVIOContext *m_pIOContext{nullptr};
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad04765f27265ee05af97c7128d92a21e2d5ead6..04d51c2cf8973d73e4d959670b721a49c37b67ba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -4,12 +4,21 @@ project(LibAVQt)
 set(CMAKE_CXX_STANDARD 20)
 
 if (WIN32)
-    include_directories(${OPENAL_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIR})
-    link_directories(${OPENAL_LIBRARY} ${FFMPEG_LIBRARY})
-    set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on)
-endif ()
+    if (NOT DEFINED ${OPENAL_INCLUDE_DIR})
+        set(OPENAL_INCLUDE_DIR "$ENV{OPENAL_INCLUDE_DIR}")
+    endif()
+    if (NOT DEFINED ${OPENAL_LIBRARY})
+        set(OPENAL_LIBRARY $ENV{OPENAL_LIBRARY})
+    endif()
 
-if (WIN32)
+    if (NOT DEFINED ${FFMPEG_INCLUDE_DIR})
+        set(FFMPEG_INCLUDE_DIR $ENV{FFMPEG_INCLUDE_DIR})
+    endif()
+    if (NOT DEFINED ${FFMPEG_LIBRARY})
+        set(FFMPEG_LIBRARY $ENV{FFMPEG_LIBRARY})
+    endif()
+    include_directories(${OPENAL_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIR} mfx_dispatch/)
+    link_directories(${OPENAL_LIBRARY} ${FFMPEG_LIBRARY})
     set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
     set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
 endif ()
@@ -20,4 +29,4 @@ set(CMAKE_AUTOUIC on)
 
 add_subdirectory(AVQt)
 include_directories(AVQt)
-add_subdirectory(Player)
\ No newline at end of file
+add_subdirectory(Player)
diff --git a/Player/CMakeLists.txt b/Player/CMakeLists.txt
index bdfae7fec8c76e2e732e415fc2fd71376f1069db..c5d7fb964aae328d031cd49de327be83a6e6e97b 100644
--- a/Player/CMakeLists.txt
+++ b/Player/CMakeLists.txt
@@ -58,9 +58,8 @@ endif ()
 target_link_libraries(Player avformat avfilter avutil avcodec avdevice swscale swresample AVQt)
 
 
-find_package(OpenAL)
-
 if (LINUX)
+    find_package(OpenAL)
     target_link_libraries(Player openal GL EGL GLU va)
 elseif (WIN32)
     target_link_libraries(Player OpenAL32 opengl32 dxguid d3d9)
diff --git a/Player/main.cpp b/Player/main.cpp
index 0fd139c018352129d10e41a327995907b4a325dd..65bffabbd1384fed22a943a3e23c6a9c5ac317a4 100644
--- a/Player/main.cpp
+++ b/Player/main.cpp
@@ -83,9 +83,9 @@ int main(int argc, char *argv[]) {
     AVQt::OpenGLRenderer renderer;
 
     demuxer.registerCallback(videoDecoder, AVQt::IPacketSource::CB_VIDEO);
-#ifdef ENABLE_QSV_ENCODE
-//    videoDecoder->registerCallback(videoEncoder);
-#endif
+//#ifdef ENABLE_QSV_ENCODE
+    videoDecoder->registerCallback(videoEncoder);
+//#endif
     QFile outputFile("output.mp4");
     outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate);
     outputFile.seek(0);