diff --git a/.gitignore b/.gitignore
index 8a9d35c887947b2618399e6cd64808e7df019e67..996ee8959f07b650ea45516a96e71080b7a8d204 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 *.user
+build*
diff --git a/.idea/misc.xml b/.idea/misc.xml
index cc6c7696d5eb7b96c5ce365fa4bae2d7a8f66da5..30681992d74a91f527acc5761a4f61338e84fb71 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,11 @@
 <?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>
diff --git a/AVQt/AVQt b/AVQt/AVQt
index c554c7d4ce0ce513832a76995db98a626ba97981..876471bc02b7109fa473e60c7142796d7bf5acf8 100644
--- a/AVQt/AVQt
+++ b/AVQt/AVQt
@@ -6,11 +6,13 @@
 #include "filter/DecoderVAAPI.h"
 #include "filter/DecoderQSV.h"
 #include "filter/DecoderDXVA2.h"
+#include "filter/DecoderMMAL.h"
 #include "filter/AudioDecoder.h"
+#include "filter/EncoderVAAPI.h"
 #include "output/IPacketSink.h"
 #include "output/IFrameSink.h"
 #include "output/IAudioSink.h"
 #include "output/OpenGLRenderer.h"
 #include "output/OpenALAudioOutput.h"
-#include "filter/EncoderVAAPI.h"
-//#include "output/FrameFileSaver.h"
\ No newline at end of file
+#include "output/Muxer.h"
+//#include "output/FrameFileSaver.h"
diff --git a/AVQt/CMakeLists.txt b/AVQt/CMakeLists.txt
index 2d92e7ac4d5e2bf2dd1dbdb869e17b52f9dbbdda..4584f5d4d87f7aa088ef2044aa14346534f7e2a7 100644
--- a/AVQt/CMakeLists.txt
+++ b/AVQt/CMakeLists.txt
@@ -9,8 +9,8 @@ set(CMAKE_AUTOUIC on)
 
 set(CMAKE_INCLUDE_CURRENT_DIR on)
 
-find_package(Qt6 COMPONENTS Core Gui Concurrent Widgets OpenGL OpenGLWidgets)
-#find_package(Qt5 COMPONENTS Core Gui Concurrent Widgets OpenGL)
+#find_package(Qt6 COMPONENTS Core Gui Concurrent Widgets OpenGL OpenGLWidgets)
+find_package(Qt5 COMPONENTS Core Gui Concurrent Widgets OpenGL)
 
 if (WIN32)
     set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS on)
@@ -50,6 +50,10 @@ set(SOURCES
         filter/private/DecoderDXVA2_p.h
         filter/DecoderDXVA2.cpp
 
+        filter/DecoderMMAL.h
+        filter/private/DecoderMMAL_p.h
+        filter/DecoderMMAL.cpp
+
         filter/AudioDecoder.h
         filter/private/AudioDecoder_p.h
         filter/private/OpenALErrorHandler.h
@@ -63,6 +67,10 @@ set(SOURCES
         filter/private/EncoderVAAPI_p.h
         filter/EncoderVAAPI.cpp
 
+        output/Muxer.h
+        output/private/Muxer_p.h
+        output/Muxer.cpp
+
         output/RenderClock.h
         output/private/RenderClock_p.h
         output/RenderClock.cpp
@@ -85,6 +93,7 @@ target_compile_options(AVQt PRIVATE
         -Werror=all
         -Werror=extra
         -Werror=pedantic
+        -Wno-float-equal
         -ansi
         -Werror=init-self
         -Werror=old-style-cast
@@ -167,6 +176,7 @@ target_compile_options(AVQtStatic PRIVATE
         -Wdouble-promotion
         )
 
-target_link_libraries(AVQt Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
-target_link_libraries(AVQtStatic Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
-#target_link_libraries(AVQt Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
\ No newline at end of file
+#target_link_libraries(AVQt Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
+#target_link_libraries(AVQtStatic Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGL Qt6::OpenGLWidgets avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
+target_link_libraries(AVQt Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
+target_link_libraries(AVQtStatic Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample GL openal)
\ No newline at end of file
diff --git a/AVQt/filter/AudioDecoder.cpp b/AVQt/filter/AudioDecoder.cpp
index 88dbccb5171de9d27f330b56f81482340951314a..867c7d337c0aaf2f73b8fae4345fbd88ba322808 100644
--- a/AVQt/filter/AudioDecoder.cpp
+++ b/AVQt/filter/AudioDecoder.cpp
@@ -10,22 +10,21 @@
 
 #include <QtCore>
 
-#include <AL/al.h>
-#include <AL/alc.h>
 #include <AL/alext.h>
 
 namespace AVQt {
     AudioDecoder::AudioDecoder(QObject *parent) : QThread(parent), d_ptr(new AudioDecoderPrivate(this)) {
-        Q_D(AVQt::AudioDecoder);
     }
 
     [[maybe_unused]] AudioDecoder::AudioDecoder(AVQt::AudioDecoderPrivate &p) : d_ptr(&p) {
-        Q_D(AVQt::AudioDecoder);
     }
 
-    AudioDecoder::~AudioDecoder() {
-        deinit();
+    AudioDecoder::AudioDecoder(AudioDecoder &&other) noexcept: d_ptr(other.d_ptr) {
+        d_ptr->q_ptr = this;
+        other.d_ptr = nullptr;
+    }
 
+    AudioDecoder::~AudioDecoder() {
         delete d_ptr;
         d_ptr = nullptr;
     }
@@ -128,7 +127,7 @@ namespace AVQt {
         return d->m_paused.load();
     }
 
-    int AudioDecoder::registerCallback(IAudioSink *callback) {
+    qint64 AudioDecoder::registerCallback(IAudioSink *callback) {
         Q_D(AVQt::AudioDecoder);
 
         QMutexLocker lock(&d->m_cbListMutex);
@@ -142,12 +141,13 @@ namespace AVQt {
             if (d->m_running) {
                 callback->start(this);
             }
+            return d->m_cbList.indexOf(callback);
         }
 
-        return 0;
+        return -1;
     }
 
-    int AudioDecoder::unregisterCallback(IAudioSink *callback) {
+    qint64 AudioDecoder::unregisterCallback(IAudioSink *callback) {
         Q_D(AVQt::AudioDecoder);
 
         QMutexLocker lock(&d->m_cbListMutex);
@@ -161,10 +161,10 @@ namespace AVQt {
     void AudioDecoder::init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams,
                             AVCodecParameters *aParams, AVCodecParameters *sParams) {
         Q_D(AVQt::AudioDecoder);
-        Q_UNUSED(source);
-        Q_UNUSED(framerate);
-        Q_UNUSED(vParams);
-        Q_UNUSED(sParams);
+        Q_UNUSED(source)
+        Q_UNUSED(framerate)
+        Q_UNUSED(vParams)
+        Q_UNUSED(sParams)
 
         d->m_timebase = timebase;
         d->m_duration = duration;
@@ -183,29 +183,26 @@ namespace AVQt {
     }
 
     void AudioDecoder::deinit(IPacketSource *source) {
-        Q_D(AVQt::AudioDecoder);
         Q_UNUSED(source)
 
         deinit();
     }
 
     void AudioDecoder::start(IPacketSource *source) {
-        Q_D(AVQt::AudioDecoder);
-        Q_UNUSED(source);
+        Q_UNUSED(source)
 
         start();
     }
 
     void AudioDecoder::stop(IPacketSource *source) {
-        Q_D(AVQt::AudioDecoder);
-        Q_UNUSED(source);
+        Q_UNUSED(source)
 
         stop();
     }
 
     void AudioDecoder::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) {
         Q_D(AVQt::AudioDecoder);
-        Q_UNUSED(source);
+        Q_UNUSED(source)
 
         if (packetType == IPacketSource::CB_AUDIO) {
             AVPacket *queuePacket = av_packet_clone(packet);
@@ -223,9 +220,9 @@ namespace AVQt {
         while (d->m_running.load()) {
             if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) {
                 QTime time1 = QTime::currentTime();
-                qDebug("Audio packet queue size: %lld", d->m_inputQueue.size());
+                qDebug("Audio packet queue size: %d", d->m_inputQueue.size());
 
-                int ret = 0;
+                int ret;
                 constexpr size_t strBufSize = 64;
                 char strBuf[strBufSize];
 
@@ -258,7 +255,7 @@ namespace AVQt {
                         AVPacket *packet = d->m_inputQueue.dequeue();
                         lock.unlock();
 
-                        qDebug("Audio packet queue size: %lld", d->m_inputQueue.size());
+                        qDebug("Audio packet queue size: %d", d->m_inputQueue.size());
 
                         ret = avcodec_send_packet(d->m_pCodecCtx, packet);
                         if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
@@ -288,15 +285,17 @@ namespace AVQt {
                         AVFrame *cbFrame = av_frame_clone(frame);
                         cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase,
                                                     av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing
-                        qDebug("Calling audio frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num,
+                        qDebug("Calling audio frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts),
+                               d->m_timebase.num,
                                d->m_timebase.den);
                         QTime time = QTime::currentTime();
-                        cb->onAudioFrame(this, cbFrame, frame->nb_samples * 1.0 / frame->sample_rate / frame->channels * 1000.0);
+                        cb->onAudioFrame(this, cbFrame,
+                                         static_cast<uint32_t>(frame->nb_samples * 1.0 / frame->sample_rate / frame->channels * 1000.0));
                         qDebug() << "Audio CB time:" << time.msecsTo(QTime::currentTime());
                         av_frame_unref(cbFrame);
                     }
                     int64_t sleepDuration = (frame->nb_samples / frame->sample_rate * 1000) - time1.msecsTo(QTime::currentTime());
-                    msleep(sleepDuration <= 0 ? 0 : sleepDuration);
+                    msleep(sleepDuration <= 0 ? 0 : static_cast<unsigned long>(sleepDuration));
                     time1 = QTime::currentTime();
                     av_frame_unref(frame);
                 }
diff --git a/AVQt/filter/AudioDecoder.h b/AVQt/filter/AudioDecoder.h
index 44487311c0bcdb733dc03bada30031afc37f3e0a..d0dba7fdc3fa2e281019d5781b8b38a2ec451a10 100644
--- a/AVQt/filter/AudioDecoder.h
+++ b/AVQt/filter/AudioDecoder.h
@@ -24,13 +24,19 @@ namespace AVQt {
     public:
         explicit AudioDecoder(QObject *parent = nullptr);
 
+        AudioDecoder(AudioDecoder &&other) noexcept;
+
+        AudioDecoder(const AudioDecoder &) = delete;
+
+        void operator=(const AudioDecoder &) = delete;
+
         ~AudioDecoder() Q_DECL_OVERRIDE;
 
         bool isPaused() Q_DECL_OVERRIDE;
 
-        int registerCallback(IAudioSink *callback) Q_DECL_OVERRIDE;
+        qint64 registerCallback(IAudioSink *callback) Q_DECL_OVERRIDE;
 
-        int unregisterCallback(IAudioSink *callback) Q_DECL_OVERRIDE;
+        qint64 unregisterCallback(IAudioSink *callback) Q_DECL_OVERRIDE;
 
         void run() Q_DECL_OVERRIDE;
 
@@ -73,4 +79,4 @@ namespace AVQt {
     };
 }
 
-#endif //LIBAVQT_AUDIODECODER_H
+#endif //LIBAVQT_AUDIODECODER_H
\ No newline at end of file
diff --git a/AVQt/filter/DecoderDXVA2.cpp b/AVQt/filter/DecoderDXVA2.cpp
index 3362f7a34e45aba3775aae6a5fcbdc2f6c3027c3..41c5c294f37b2a6558dab609c3b126fea351c6d1 100644
--- a/AVQt/filter/DecoderDXVA2.cpp
+++ b/AVQt/filter/DecoderDXVA2.cpp
@@ -9,27 +9,29 @@
 #include "input/IPacketSource.h"
 
 #include <QApplication>
-#include <QtConcurrent>
 #include <QImage>
+//#include <QtConcurrent>
 
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-#define NOW() std::chrono::high_resolution_clock::now()
-#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
-#endif
+//#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//#define NOW() std::chrono::high_resolution_clock::now()
+//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
+//#endif
 
 
 namespace AVQt {
     DecoderDXVA2::DecoderDXVA2(QObject *parent) : QThread(parent), d_ptr(new DecoderDXVA2Private(this)) {
-        Q_D(AVQt::DecoderDXVA2);
     }
 
     [[maybe_unused]] DecoderDXVA2::DecoderDXVA2(DecoderDXVA2Private &p) : d_ptr(&p) {
 
     }
 
-    DecoderDXVA2::~DecoderDXVA2() {
-        deinit();
+    DecoderDXVA2::DecoderDXVA2(DecoderDXVA2 &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
 
+    DecoderDXVA2::~DecoderDXVA2() {
         delete d_ptr;
     }
 
@@ -57,8 +59,6 @@ namespace AVQt {
     }
 
     int DecoderDXVA2::init() {
-        Q_D(AVQt::DecoderDXVA2);
-
         return 0;
     }
 
@@ -158,7 +158,7 @@ namespace AVQt {
         return d->m_paused.load();
     }
 
-    int DecoderDXVA2::registerCallback(IFrameSink *frameSink) {
+    qint64 DecoderDXVA2::registerCallback(IFrameSink *frameSink) {
         Q_D(AVQt::DecoderDXVA2);
 
         QMutexLocker lock(&d->m_cbListMutex);
@@ -173,11 +173,11 @@ namespace AVQt {
         return -1;
     }
 
-    int DecoderDXVA2::unregisterCallback(IFrameSink *frameSink) {
+    qint64 DecoderDXVA2::unregisterCallback(IFrameSink *frameSink) {
         Q_D(AVQt::DecoderDXVA2);
         QMutexLocker lock(&d->m_cbListMutex);
         if (d->m_cbList.contains(frameSink)) {
-            int result = d->m_cbList.indexOf(frameSink);
+            auto result = d->m_cbList.indexOf(frameSink);
             d->m_cbList.removeOne(frameSink);
             frameSink->stop(this);
             frameSink->deinit(this);
@@ -187,7 +187,7 @@ namespace AVQt {
     }
 
     void DecoderDXVA2::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) {
-        Q_UNUSED(source);
+        Q_UNUSED(source)
 
         Q_D(AVQt::DecoderDXVA2);
 
@@ -244,7 +244,7 @@ namespace AVQt {
                         AVPacket *packet = d->m_inputQueue.dequeue();
                         lock.unlock();
 
-                        qDebug("Video packet queue size: %lld", d->m_inputQueue.size());
+                        qDebug("Video packet queue size: %d", d->m_inputQueue.size());
 
                         ret = avcodec_send_packet(d->m_pCodecCtx, packet);
                         if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
@@ -283,7 +283,8 @@ namespace AVQt {
                         AVFrame *cbFrame = av_frame_clone(frame);
                         cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase,
                                                     av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing
-                        qDebug("Calling video frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num,
+                        qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts),
+                               d->m_timebase.num,
                                d->m_timebase.den);
                         QTime time = QTime::currentTime();
                         cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0));
diff --git a/AVQt/filter/DecoderDXVA2.h b/AVQt/filter/DecoderDXVA2.h
index 9079a432095def79cb3ade21224bbdf3d5aa4582..7c4eee29f656c4831c24ce98af6a9609b5632a9f 100644
--- a/AVQt/filter/DecoderDXVA2.h
+++ b/AVQt/filter/DecoderDXVA2.h
@@ -35,10 +35,10 @@ namespace AVQt {
      * that either at least one callback is registered or the decoder is paused, or frames will be dropped
      */
     class DecoderDXVA2 : public QThread, public IDecoder {
-        Q_OBJECT
-            Q_INTERFACES(AVQt::IDecoder)
-    //        Q_INTERFACES(AVQt::IFrameSource)
-    //        Q_INTERFACES(AVQt::IPacketSink)
+    Q_OBJECT
+        Q_INTERFACES(AVQt::IDecoder)
+        //        Q_INTERFACES(AVQt::IFrameSource)
+        //        Q_INTERFACES(AVQt::IPacketSink)
 
         Q_DECLARE_PRIVATE(AVQt::DecoderDXVA2)
 
@@ -52,6 +52,18 @@ namespace AVQt {
          */
         explicit DecoderDXVA2(QObject *parent = nullptr);
 
+        DecoderDXVA2(DecoderDXVA2 &&other) noexcept;
+
+        /*!
+         * \private
+         */
+        DecoderDXVA2(const DecoderDXVA2 &) = delete;
+
+        /*!
+         * \private
+         */
+        void operator=(const DecoderDXVA2 &) = delete;
+
         /*!
          * \private
          */
@@ -69,14 +81,14 @@ namespace AVQt {
          * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE
          * @return Current position in callback list
          */
-        Q_INVOKABLE int registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
 
         /*!
          * \brief Removes frame sink/filter from registry
          * @param frameSink Frame sink/filter to be removed
          * @return Last position in callback list, -1 when not found
          */
-        Q_INVOKABLE int unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
 
     public slots:
         /*!
@@ -160,4 +172,4 @@ namespace AVQt {
 
 }
 
-#endif //TRANSCODE_DECODERDXVA2_H
+#endif //TRANSCODE_DECODERDXVA2_H
\ No newline at end of file
diff --git a/AVQt/filter/DecoderMMAL.cpp b/AVQt/filter/DecoderMMAL.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2ddf4d6df1a95f9e61e51386d57ae98897e5a936
--- /dev/null
+++ b/AVQt/filter/DecoderMMAL.cpp
@@ -0,0 +1,324 @@
+//
+// Created by silas on 3/1/21.
+//
+
+#include "private/DecoderMMAL_p.h"
+#include "DecoderMMAL.h"
+#include "output/IFrameSink.h"
+#include "output/IAudioSink.h"
+#include "input/IPacketSource.h"
+
+#include <QApplication>
+#include <QImage>
+//#include <QtConcurrent>
+
+//#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//#define NOW() std::chrono::high_resolution_clock::now()
+//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
+//#endif
+
+
+namespace AVQt {
+    DecoderMMAL::DecoderMMAL(QObject *parent) : QThread(parent), d_ptr(new DecoderMMALPrivate(this)) {
+    }
+
+    [[maybe_unused]] DecoderMMAL::DecoderMMAL(DecoderMMALPrivate &p) : d_ptr(&p) {
+
+    }
+
+    DecoderMMAL::DecoderMMAL(DecoderMMAL &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
+
+    DecoderMMAL::~DecoderMMAL() {
+        delete d_ptr;
+    }
+
+    void DecoderMMAL::init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams,
+                           AVCodecParameters *aParams, AVCodecParameters *sParams) {
+        Q_UNUSED(aParams)
+        Q_UNUSED(sParams)
+        Q_D(AVQt::DecoderMMAL);
+        Q_UNUSED(source)
+        if (d->m_pCodecParams) {
+            avcodec_parameters_free(&d->m_pCodecParams);
+        }
+        d->m_pCodecParams = avcodec_parameters_alloc();
+        avcodec_parameters_copy(d->m_pCodecParams, vParams);
+        d->m_duration = duration;
+        d->m_framerate = framerate;
+        d->m_timebase = timebase;
+        {
+            QMutexLocker lock(&d->m_cbListMutex);
+            for (const auto &cb: d->m_cbList) {
+                cb->init(this, framerate, duration);
+            }
+        }
+        init();
+    }
+
+    int DecoderMMAL::init() {
+        return 0;
+    }
+
+    void DecoderMMAL::deinit(IPacketSource *source) {
+        Q_UNUSED(source)
+        deinit();
+    }
+
+    int DecoderMMAL::deinit() {
+        Q_D(AVQt::DecoderMMAL);
+
+        stop();
+
+        if (d->m_pCodecParams) {
+            avcodec_parameters_free(&d->m_pCodecParams);
+            d->m_pCodecParams = nullptr;
+        }
+        if (d->m_pCodecCtx) {
+            avcodec_close(d->m_pCodecCtx);
+            avcodec_free_context(&d->m_pCodecCtx);
+            d->m_pCodecCtx = nullptr;
+            d->m_pCodec = nullptr;
+        }
+
+        return 0;
+    }
+
+    void DecoderMMAL::start(IPacketSource *source) {
+        Q_UNUSED(source)
+        start();
+    }
+
+    int DecoderMMAL::start() {
+        Q_D(AVQt::DecoderMMAL);
+
+        bool notRunning = false;
+        if (d->m_running.compare_exchange_strong(notRunning, true)) {
+            d->m_paused.store(false);
+            paused(false);
+
+            QThread::start();
+            return 0;
+        }
+        return -1;
+    }
+
+    void DecoderMMAL::stop(IPacketSource *source) {
+        Q_UNUSED(source)
+        stop();
+    }
+
+    int DecoderMMAL::stop() {
+        Q_D(AVQt::DecoderMMAL);
+
+        bool shouldBeCurrent = true;
+        if (d->m_running.compare_exchange_strong(shouldBeCurrent, false)) {
+            {
+//                QMutexLocker lock(&d->m_cbListMutex);
+                for (const auto &cb: d->m_cbList) {
+                    cb->stop(this);
+                }
+            }
+            {
+                QMutexLocker lock(&d->m_inputQueueMutex);
+                for (auto &packet : d->m_inputQueue) {
+                    av_packet_unref(packet);
+                    av_packet_free(&packet);
+                }
+                d->m_inputQueue.clear();
+            }
+
+            wait();
+
+            return 0;
+        }
+
+        return -1;
+    }
+
+    void DecoderMMAL::pause(bool pause) {
+        Q_D(AVQt::DecoderMMAL);
+        if (d->m_running.load() != pause) {
+            d->m_paused.store(pause);
+
+            for (const auto &cb: d->m_cbList) {
+                cb->pause(this, pause);
+            }
+
+            paused(pause);
+        }
+    }
+
+    bool DecoderMMAL::isPaused() {
+        Q_D(AVQt::DecoderMMAL);
+        return d->m_paused.load();
+    }
+
+    qint64 DecoderMMAL::registerCallback(IFrameSink *frameSink) {
+        Q_D(AVQt::DecoderMMAL);
+
+        QMutexLocker lock(&d->m_cbListMutex);
+        if (!d->m_cbList.contains(frameSink)) {
+            d->m_cbList.append(frameSink);
+            if (d->m_running.load() && d->m_pCodecCtx) {
+                frameSink->init(this, d->m_framerate, d->m_duration);
+                frameSink->start(this);
+            }
+            return d->m_cbList.indexOf(frameSink);
+        }
+        return -1;
+    }
+
+    qint64 DecoderMMAL::unregisterCallback(IFrameSink *frameSink) {
+        Q_D(AVQt::DecoderMMAL);
+        QMutexLocker lock(&d->m_cbListMutex);
+        if (d->m_cbList.contains(frameSink)) {
+            auto result = d->m_cbList.indexOf(frameSink);
+            d->m_cbList.removeOne(frameSink);
+            frameSink->stop(this);
+            frameSink->deinit(this);
+            return result;
+        }
+        return -1;
+    }
+
+    void DecoderMMAL::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) {
+        Q_UNUSED(source)
+
+        Q_D(AVQt::DecoderMMAL);
+
+        if (packetType == IPacketSource::CB_VIDEO) {
+            AVPacket *queuePacket = av_packet_clone(packet);
+            while (d->m_inputQueue.size() >= 50) {
+                QThread::msleep(4);
+            }
+            QMutexLocker lock(&d->m_inputQueueMutex);
+            d->m_inputQueue.append(queuePacket);
+        }
+    }
+
+    void DecoderMMAL::run() {
+        Q_D(AVQt::DecoderMMAL);
+
+        while (d->m_running) {
+            if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) {
+                int ret;
+                constexpr size_t strBufSize = 64;
+                char strBuf[strBufSize];
+                // If m_pCodecParams is nullptr, it is not initialized by packet source, if video codec context is nullptr, this is the first packet
+                if (d->m_pCodecParams && !d->m_pCodecCtx) {
+                    switch (d->m_pCodecParams->codec_id) {
+                        case AV_CODEC_ID_H264:
+                            d->m_pCodec = avcodec_find_decoder_by_name("h264_mmal");
+                            break;
+                        case AV_CODEC_ID_MPEG2VIDEO:
+                            d->m_pCodec = avcodec_find_decoder_by_name("mpeg2_mmal");
+                            break;
+                        case AV_CODEC_ID_MPEG4:
+                            d->m_pCodec = avcodec_find_decoder_by_name("mpeg4_mmal");
+                            break;
+                        case AV_CODEC_ID_VC1:
+                            d->m_pCodec = avcodec_find_decoder_by_name("vc1_mmal");
+                            break;
+                        default:
+                            qFatal("No MMAL decoder found for the specified codec: %s", avcodec_get_name(d->m_pCodecParams->codec_id));
+                            break;
+                    }
+
+                    d->m_pCodecCtx = avcodec_alloc_context3(d->m_pCodec);
+                    if (!d->m_pCodecCtx) {
+                        qFatal("Could not allocate MMAL decoder context, probably out of memory");
+                    }
+
+                    avcodec_parameters_to_context(d->m_pCodecCtx, d->m_pCodecParams);
+                    ret = avcodec_open2(d->m_pCodecCtx, d->m_pCodec, nullptr);
+                    if (ret != 0) {
+                        qFatal("%d: Could not open MMAL decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+                    }
+
+                    for (const auto &cb: d->m_cbList) {
+                        cb->start(this);
+                    }
+                    started();
+                }
+                {
+                    QMutexLocker lock(&d->m_inputQueueMutex);
+                    while (!d->m_inputQueue.isEmpty()) {
+                        AVPacket *packet = d->m_inputQueue.dequeue();
+                        lock.unlock();
+
+                        qDebug("Video packet queue size: %d", d->m_inputQueue.size());
+
+                        ret = avcodec_send_packet(d->m_pCodecCtx, packet);
+                        if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
+                            lock.relock();
+                            d->m_inputQueue.prepend(packet);
+                            break;
+                        } else if (ret < 0) {
+                            qFatal("%d: Error sending packet to MMAL decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+                        }
+                        av_packet_unref(packet);
+                        av_packet_free(&packet);
+                        lock.relock();
+                    }
+                }
+                AVFrame *frame = av_frame_alloc();
+                while (true) {
+                    ret = avcodec_receive_frame(d->m_pCodecCtx, frame);
+                    if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
+                        break;
+                    } else if (ret < 0) {
+                        qFatal("%d: Error receiving frame from MMAL decoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+                    }
+
+//                    auto t1 = NOW();
+//                    AVFrame *swFrame = av_frame_alloc();
+//                    ret = av_hwframe_transfer_data(swFrame, frame, 0);
+//                    if (ret != 0) {
+//                        qFatal("%d: Could not get hw frame: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+//                    }
+//                    auto t3 = NOW();
+
+                    QMutexLocker lock2(&d->m_cbListMutex);
+//                    QList<QFuture<void>> cbFutures;
+                    for (const auto &cb: d->m_cbList) {
+//                        cbFutures.append(QtConcurrent::run([=] {
+                        AVFrame *cbFrame = av_frame_clone(frame);
+                        cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase,
+                                                    av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing
+                        qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts),
+                               d->m_timebase.num,
+                               d->m_timebase.den);
+                        QTime time = QTime::currentTime();
+                        cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0));
+                        qDebug() << "Video CB time:" << time.msecsTo(QTime::currentTime());
+                        av_frame_unref(cbFrame);
+                        av_frame_free(&cbFrame);
+//                        }));
+                    }
+//                    bool cbBusy = true;
+//                    while (cbBusy) {
+//                        cbBusy = false;
+//                        for (const auto &future: cbFutures) {
+//                            if (future.isRunning()) {
+//                                cbBusy = true;
+//                                break;
+//                            }
+//                        }
+//                        usleep(500);
+//                    }
+//                    auto t2 = NOW();
+//                    qDebug("Decoder frame transfer time: %ld us", TIME_US(t1, t3));
+
+//                    av_frame_free(&swFrame);
+                    av_frame_unref(frame);
+                }
+                av_frame_free(&frame);
+            } else {
+                msleep(4);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/AVQt/filter/DecoderMMAL.h b/AVQt/filter/DecoderMMAL.h
new file mode 100644
index 0000000000000000000000000000000000000000..513a1ce81bf15ea5f11297bf22dd6f9da6e91219
--- /dev/null
+++ b/AVQt/filter/DecoderMMAL.h
@@ -0,0 +1,175 @@
+//
+// Created by silas on 3/1/21.
+//
+
+#include "IDecoder.h"
+
+#include <QtCore>
+#include <QtGui>
+
+extern "C" {
+#include <libavutil/avutil.h>
+#include <libavutil/hwcontext.h>
+#include <libavfilter/avfilter.h>
+#include <libavformat/avformat.h>
+#include <libavdevice/avdevice.h>
+#include <libavcodec/avcodec.h>
+#include <libswscale/swscale.h>
+#include <libswresample/swresample.h>
+#include <libavutil/imgutils.h>
+}
+
+
+#ifndef TRANSCODE_DECODERMMAL_H
+#define TRANSCODE_DECODERMMAL_H
+
+namespace AVQt {
+    class DecoderMMALPrivate;
+
+    /*!
+     * \class DecoderMMAL
+     * \brief MMAL accelerated video decoder
+     *
+     * Decodes video from packet source into single frames, that are passed to every via registerCallback registered callback.
+     * It does ***not*** stop when no callbacks are registered, so make sure,
+     * that either at least one callback is registered or the decoder is paused, or frames will be dropped
+     */
+    class DecoderMMAL : public QThread, public IDecoder {
+    Q_OBJECT
+        Q_INTERFACES(AVQt::IDecoder)
+//        Q_INTERFACES(AVQt::IFrameSource)
+//        Q_INTERFACES(AVQt::IPacketSink)
+
+        Q_DECLARE_PRIVATE(AVQt::DecoderMMAL)
+
+    public:
+
+        /*!
+         * Standard public constructor. Creates new instance with given input device. This device can not be changed afterwards,
+         * because the decoder would have to be completely reinitialized thereafter
+         * @param inputDevice QIODevice to read video from
+         * @param parent Pointer to parent QObject for Qt's meta object system
+         */
+        explicit DecoderMMAL(QObject *parent = nullptr);
+
+        DecoderMMAL(DecoderMMAL &&other) noexcept;
+
+        /*!
+         * \private
+         */
+        DecoderMMAL(const DecoderMMAL &) = delete;
+
+        /*!
+         * \private
+         */
+        void operator=(const DecoderMMAL &) = delete;
+
+        /*!
+         * \private
+         */
+        ~DecoderMMAL() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Returns paused state of decoder
+         * @return Paused state
+         */
+        bool isPaused() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Register frame sink/filter with given callback type \c type
+         * @param frameSink Frame sink/filter to be added
+         * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE
+         * @return Current position in callback list
+         */
+        Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Removes frame sink/filter from registry
+         * @param frameSink Frame sink/filter to be removed
+         * @return Last position in callback list, -1 when not found
+         */
+        Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+
+    public slots:
+        /*!
+         * \brief Initialize decoder. Opens input device, creates input contexts.
+         * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message)
+         */
+        Q_INVOKABLE int init() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Clean up decoder. Closes input device, destroys input contexts.
+         * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message)
+         */
+        Q_INVOKABLE int deinit() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Starts decoder. Sets running flag and starts decoding thread.
+         * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message)
+         */
+        Q_INVOKABLE int start() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Stops decoder. Resets running flag and interrupts decoding thread.
+         * @return Status code (0 = Success, LibAV error codes, use av_make_error_string() to get an error message)
+         */
+        Q_INVOKABLE int stop() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Sets paused flag. When set to true, decoder thread will suspend after processing current frame
+         * @param pause New paused flag state
+         */
+        Q_INVOKABLE void pause(bool pause) Q_DECL_OVERRIDE;
+
+
+        Q_INVOKABLE void
+        init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams,
+             AVCodecParameters *aParams, AVCodecParameters *sParams) Q_DECL_OVERRIDE;
+
+        Q_INVOKABLE void deinit(IPacketSource *source) Q_DECL_OVERRIDE;
+
+        Q_INVOKABLE void start(IPacketSource *source) Q_DECL_OVERRIDE;
+
+        Q_INVOKABLE void stop(IPacketSource *source) Q_DECL_OVERRIDE;
+
+        Q_INVOKABLE void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) Q_DECL_OVERRIDE;
+
+    signals:
+
+        /*!
+         * \brief Emitted after start() finished
+         */
+        void started() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Emitted after stop() finished
+         */
+        void stopped() Q_DECL_OVERRIDE;
+
+        /*!
+         * \brief Emitted when paused state changes
+         * @param pause Current paused state
+         */
+        void paused(bool pause) Q_DECL_OVERRIDE;
+
+    protected:
+        /*!
+         * Protected constructor for use in derived classes to initialize private struct
+         * @param p Private struct
+         */
+        [[maybe_unused]] explicit DecoderMMAL(DecoderMMALPrivate &p);
+
+        /*!
+         * \private
+         */
+        void run() Q_DECL_OVERRIDE;
+
+        /*!
+         * \private
+         */
+        DecoderMMALPrivate *d_ptr;
+    };
+
+}
+
+#endif //TRANSCODE_DECODERMMAL_H
\ No newline at end of file
diff --git a/AVQt/filter/DecoderQSV.cpp b/AVQt/filter/DecoderQSV.cpp
index 0ed9cf21bf62752952bdc654054c3228382bf02b..464d43c5ed3d288e1e52208cddb857c708fc65bc 100644
--- a/AVQt/filter/DecoderQSV.cpp
+++ b/AVQt/filter/DecoderQSV.cpp
@@ -9,27 +9,29 @@
 #include "input/IPacketSource.h"
 
 #include <QApplication>
-#include <QtConcurrent>
 #include <QImage>
+//#include <QtConcurrent>
 
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-#define NOW() std::chrono::high_resolution_clock::now()
-#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
-#endif
+//#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//#define NOW() std::chrono::high_resolution_clock::now()
+//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
+//#endif
 
 
 namespace AVQt {
     DecoderQSV::DecoderQSV(QObject *parent) : QThread(parent), d_ptr(new DecoderQSVPrivate(this)) {
-        Q_D(AVQt::DecoderQSV);
     }
 
     [[maybe_unused]] DecoderQSV::DecoderQSV(DecoderQSVPrivate &p) : d_ptr(&p) {
 
     }
 
-    DecoderQSV::~DecoderQSV() {
-        deinit();
+    DecoderQSV::DecoderQSV(DecoderQSV &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
 
+    DecoderQSV::~DecoderQSV() {
         delete d_ptr;
     }
 
@@ -57,8 +59,6 @@ namespace AVQt {
     }
 
     int DecoderQSV::init() {
-        Q_D(AVQt::DecoderQSV);
-
         return 0;
     }
 
@@ -158,7 +158,7 @@ namespace AVQt {
         return d->m_paused.load();
     }
 
-    int DecoderQSV::registerCallback(IFrameSink *frameSink) {
+    qint64 DecoderQSV::registerCallback(IFrameSink *frameSink) {
         Q_D(AVQt::DecoderQSV);
 
         QMutexLocker lock(&d->m_cbListMutex);
@@ -173,11 +173,11 @@ namespace AVQt {
         return -1;
     }
 
-    int DecoderQSV::unregisterCallback(IFrameSink *frameSink) {
+    qint64 DecoderQSV::unregisterCallback(IFrameSink *frameSink) {
         Q_D(AVQt::DecoderQSV);
         QMutexLocker lock(&d->m_cbListMutex);
         if (d->m_cbList.contains(frameSink)) {
-            int result = d->m_cbList.indexOf(frameSink);
+            auto result = d->m_cbList.indexOf(frameSink);
             d->m_cbList.removeOne(frameSink);
             frameSink->stop(this);
             frameSink->deinit(this);
@@ -187,7 +187,7 @@ namespace AVQt {
     }
 
     void DecoderQSV::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) {
-        Q_UNUSED(source);
+        Q_UNUSED(source)
 
         Q_D(AVQt::DecoderQSV);
 
@@ -206,7 +206,7 @@ namespace AVQt {
 
         while (d->m_running) {
             if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) {
-                int ret = 0;
+                int ret;
                 constexpr size_t strBufSize = 64;
                 char strBuf[strBufSize];
                 // If m_pCodecParams is nullptr, it is not initialized by packet source, if video codec context is nullptr, this is the first packet
@@ -244,7 +244,7 @@ namespace AVQt {
                         AVPacket *packet = d->m_inputQueue.dequeue();
                         lock.unlock();
 
-                        qDebug("Video packet queue size: %lld", d->m_inputQueue.size());
+                        qDebug("Video packet queue size: %d", d->m_inputQueue.size());
 
                         ret = avcodec_send_packet(d->m_pCodecCtx, packet);
                         if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
@@ -283,7 +283,8 @@ namespace AVQt {
                         AVFrame *cbFrame = av_frame_clone(frame);
                         cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase,
                                                     av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing
-                        qDebug("Calling video frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num,
+                        qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts),
+                               d->m_timebase.num,
                                d->m_timebase.den);
                         QTime time = QTime::currentTime();
                         cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0));
diff --git a/AVQt/filter/DecoderQSV.h b/AVQt/filter/DecoderQSV.h
index 30a15ac507470295561b7d843e2af3a2b165e623..3d740cf8066c269aef0c21ebf3a2a036359ddd4a 100644
--- a/AVQt/filter/DecoderQSV.h
+++ b/AVQt/filter/DecoderQSV.h
@@ -52,6 +52,18 @@ namespace AVQt {
          */
         explicit DecoderQSV(QObject *parent = nullptr);
 
+        DecoderQSV(DecoderQSV &&other) noexcept;
+
+        /*!
+         * \private
+         */
+        DecoderQSV(const DecoderQSV &) = delete;
+
+        /*!
+         * \private
+         */
+        void operator=(const DecoderQSV &) = delete;
+
         /*!
          * \private
          */
@@ -69,14 +81,14 @@ namespace AVQt {
          * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE
          * @return Current position in callback list
          */
-        Q_INVOKABLE int registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
 
         /*!
          * \brief Removes frame sink/filter from registry
          * @param frameSink Frame sink/filter to be removed
          * @return Last position in callback list, -1 when not found
          */
-        Q_INVOKABLE int unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
 
     public slots:
         /*!
diff --git a/AVQt/filter/DecoderVAAPI.cpp b/AVQt/filter/DecoderVAAPI.cpp
index 7bd0a2e9de25b24eea7a6be0526f9355e1b3918a..a68f3edf229d6faf9bf99d846ac56483e12743b3 100644
--- a/AVQt/filter/DecoderVAAPI.cpp
+++ b/AVQt/filter/DecoderVAAPI.cpp
@@ -9,27 +9,29 @@
 #include "input/IPacketSource.h"
 
 #include <QApplication>
-#include <QtConcurrent>
 #include <QImage>
+//#include <QtConcurrent>
 
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
-#define NOW() std::chrono::high_resolution_clock::now()
-#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
-#endif
+//#ifndef DOXYGEN_SHOULD_SKIP_THIS
+//#define NOW() std::chrono::high_resolution_clock::now()
+//#define TIME_US(t1, t2) std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count()
+//#endif
 
 
 namespace AVQt {
     DecoderVAAPI::DecoderVAAPI(QObject *parent) : QThread(parent), d_ptr(new DecoderVAAPIPrivate(this)) {
-        Q_D(AVQt::DecoderVAAPI);
     }
 
     [[maybe_unused]] DecoderVAAPI::DecoderVAAPI(DecoderVAAPIPrivate &p) : d_ptr(&p) {
 
     }
 
-    DecoderVAAPI::~DecoderVAAPI() {
-        deinit();
+    DecoderVAAPI::DecoderVAAPI(DecoderVAAPI &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
 
+    DecoderVAAPI::~DecoderVAAPI() {
         delete d_ptr;
     }
 
@@ -57,8 +59,6 @@ namespace AVQt {
     }
 
     int DecoderVAAPI::init() {
-        Q_D(AVQt::DecoderVAAPI);
-
         return 0;
     }
 
@@ -158,7 +158,7 @@ namespace AVQt {
         return d->m_paused.load();
     }
 
-    int DecoderVAAPI::registerCallback(IFrameSink *frameSink) {
+    qint64 DecoderVAAPI::registerCallback(IFrameSink *frameSink) {
         Q_D(AVQt::DecoderVAAPI);
 
         QMutexLocker lock(&d->m_cbListMutex);
@@ -173,11 +173,11 @@ namespace AVQt {
         return -1;
     }
 
-    int DecoderVAAPI::unregisterCallback(IFrameSink *frameSink) {
+    qint64 DecoderVAAPI::unregisterCallback(IFrameSink *frameSink) {
         Q_D(AVQt::DecoderVAAPI);
         QMutexLocker lock(&d->m_cbListMutex);
         if (d->m_cbList.contains(frameSink)) {
-            int result = d->m_cbList.indexOf(frameSink);
+            auto result = d->m_cbList.indexOf(frameSink);
             d->m_cbList.removeOne(frameSink);
             frameSink->stop(this);
             frameSink->deinit(this);
@@ -187,7 +187,7 @@ namespace AVQt {
     }
 
     void DecoderVAAPI::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) {
-        Q_UNUSED(source);
+        Q_UNUSED(source)
 
         Q_D(AVQt::DecoderVAAPI);
 
@@ -206,7 +206,7 @@ namespace AVQt {
 
         while (d->m_running) {
             if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) {
-                int ret = 0;
+                int ret;
                 constexpr size_t strBufSize = 64;
                 char strBuf[strBufSize];
                 // If m_pCodecParams is nullptr, it is not initialized by packet source, if video codec context is nullptr, this is the first packet
@@ -244,7 +244,7 @@ namespace AVQt {
                         AVPacket *packet = d->m_inputQueue.dequeue();
                         lock.unlock();
 
-                        qDebug("Video packet queue size: %lld", d->m_inputQueue.size());
+                        qDebug("Video packet queue size: %d", d->m_inputQueue.size());
 
                         ret = avcodec_send_packet(d->m_pCodecCtx, packet);
                         if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
@@ -280,16 +280,17 @@ namespace AVQt {
 //                    QList<QFuture<void>> cbFutures;
                     for (const auto &cb: d->m_cbList) {
 //                        cbFutures.append(QtConcurrent::run([=] {
-                            AVFrame *cbFrame = av_frame_clone(frame);
-                            cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase,
-                                                        av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing
-                            qDebug("Calling video frame callback for PTS: %ld, Timebase: %d/%d", cbFrame->pts, d->m_timebase.num,
-                                   d->m_timebase.den);
-                            QTime time = QTime::currentTime();
-                            cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0));
-                            qDebug() << "Video CB time:" << time.msecsTo(QTime::currentTime());
-                            av_frame_unref(cbFrame);
-                            av_frame_free(&cbFrame);
+                        AVFrame *cbFrame = av_frame_clone(frame);
+                        cbFrame->pts = av_rescale_q(frame->pts, d->m_timebase,
+                                                    av_make_q(1, 1000000)); // Rescale pts to microseconds for easier processing
+                        qDebug("Calling video frame callback for PTS: %lld, Timebase: %d/%d", static_cast<long long>(cbFrame->pts),
+                               d->m_timebase.num,
+                               d->m_timebase.den);
+                        QTime time = QTime::currentTime();
+                        cb->onFrame(this, cbFrame, static_cast<int64_t>(av_q2d(av_inv_q(d->m_framerate)) * 1000.0));
+                        qDebug() << "Video CB time:" << time.msecsTo(QTime::currentTime());
+                        av_frame_unref(cbFrame);
+                        av_frame_free(&cbFrame);
 //                        }));
                     }
 //                    bool cbBusy = true;
diff --git a/AVQt/filter/DecoderVAAPI.h b/AVQt/filter/DecoderVAAPI.h
index 3bb315c57201671f9de1564b70ea3116aece62b2..f9170617791c82edd17f5cbaa0210b101ce30150 100644
--- a/AVQt/filter/DecoderVAAPI.h
+++ b/AVQt/filter/DecoderVAAPI.h
@@ -52,6 +52,18 @@ namespace AVQt {
          */
         explicit DecoderVAAPI(QObject *parent = nullptr);
 
+        DecoderVAAPI(DecoderVAAPI &&other) noexcept;
+
+        /*!
+         * \private
+         */
+        DecoderVAAPI(const DecoderVAAPI &) = delete;
+
+        /*!
+         * \private
+         */
+        void operator=(const DecoderVAAPI &) = delete;
+
         /*!
          * \private
          */
@@ -69,14 +81,14 @@ namespace AVQt {
          * @param type One element or some bitwise or combination of elements of IFrameSource::CB_TYPE
          * @return Current position in callback list
          */
-        Q_INVOKABLE int registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
 
         /*!
          * \brief Removes frame sink/filter from registry
          * @param frameSink Frame sink/filter to be removed
          * @return Last position in callback list, -1 when not found
          */
-        Q_INVOKABLE int unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE;
 
     public slots:
         /*!
@@ -160,4 +172,4 @@ namespace AVQt {
 
 }
 
-#endif //TRANSCODE_DECODERVAAPI_H
+#endif //TRANSCODE_DECODERVAAPI_H
\ No newline at end of file
diff --git a/AVQt/filter/EncoderVAAPI.cpp b/AVQt/filter/EncoderVAAPI.cpp
index 7ce43ec0c972f188684b8bb75920865a5038ae9a..1c76568bd995933b4f4dcbc67934052ee35c141b 100644
--- a/AVQt/filter/EncoderVAAPI.cpp
+++ b/AVQt/filter/EncoderVAAPI.cpp
@@ -5,7 +5,6 @@
 #include "private/EncoderVAAPI_p.h"
 #include "EncoderVAAPI.h"
 
-#include <QtCore>
 #include "output/IPacketSink.h"
 
 namespace AVQt {
@@ -19,6 +18,11 @@ namespace AVQt {
 
     }
 
+    EncoderVAAPI::EncoderVAAPI(EncoderVAAPI &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
+
     EncoderVAAPI::~EncoderVAAPI() {
         delete d_ptr;
     }
@@ -41,6 +45,13 @@ namespace AVQt {
     int EncoderVAAPI::deinit() {
         Q_D(AVQt::EncoderVAAPI);
 
+        {
+            QMutexLocker lock(&d->m_cbListMutex);
+            for (const auto &cb: d->m_cbList) {
+                cb->deinit(this);
+            }
+        }
+
         if (d->m_pDeviceCtx) {
             av_buffer_unref(&d->m_pDeviceCtx);
         }
@@ -67,6 +78,7 @@ namespace AVQt {
         bool shouldBe = false;
         if (d->m_running.compare_exchange_strong(shouldBe, true)) {
             d->m_paused.store(false);
+
             QThread::start();
 
             started();
@@ -79,7 +91,7 @@ namespace AVQt {
         Q_D(AVQt::EncoderVAAPI);
 
         bool shouldBe = true;
-        if (d->m_running.compare_exchange_strong(shouldBe, true)) {
+        if (d->m_running.compare_exchange_strong(shouldBe, false)) {
             d->m_paused.store(false);
 
             {
@@ -120,7 +132,7 @@ namespace AVQt {
     }
 
     int EncoderVAAPI::init(IFrameSource *source, AVRational framerate, int64_t duration) {
-        Q_UNUSED(source);
+        Q_UNUSED(source)
         Q_UNUSED(duration)
         Q_D(AVQt::EncoderVAAPI);
         d->m_framerate = framerate;
@@ -130,32 +142,28 @@ namespace AVQt {
 
     int EncoderVAAPI::deinit(IFrameSource *source) {
         Q_UNUSED(source)
-        Q_D(AVQt::EncoderVAAPI);
         deinit();
         return 0;
     }
 
     int EncoderVAAPI::start(IFrameSource *source) {
         Q_UNUSED(source)
-        Q_D(AVQt::EncoderVAAPI);
         start();
         return 0;
     }
 
     int EncoderVAAPI::stop(IFrameSource *source) {
         Q_UNUSED(source)
-        Q_D(AVQt::EncoderVAAPI);
         stop();
         return 0;
     }
 
     void EncoderVAAPI::pause(IFrameSource *source, bool paused) {
         Q_UNUSED(source)
-        Q_D(AVQt::EncoderVAAPI);
-        this->pause(paused);
+        pause(paused);
     }
 
-    qsizetype EncoderVAAPI::registerCallback(IPacketSink *packetSink, uint8_t type) {
+    qint64 EncoderVAAPI::registerCallback(IPacketSink *packetSink, int8_t type) {
         Q_D(AVQt::EncoderVAAPI);
 
         if (type != IPacketSource::CB_VIDEO) {
@@ -173,7 +181,7 @@ namespace AVQt {
         }
     }
 
-    qsizetype EncoderVAAPI::unregisterCallback(IPacketSink *packetSink) {
+    qint64 EncoderVAAPI::unregisterCallback(IPacketSink *packetSink) {
         Q_D(AVQt::EncoderVAAPI);
 
         {
@@ -184,7 +192,7 @@ namespace AVQt {
     }
 
     void EncoderVAAPI::onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) {
-        Q_UNUSED(source);
+        Q_UNUSED(source)
         Q_D(AVQt::EncoderVAAPI);
 
         QPair<AVFrame *, int64_t> queueFrame{av_frame_alloc(), frameDuration};
@@ -194,6 +202,7 @@ namespace AVQt {
             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");
@@ -213,7 +222,7 @@ namespace AVQt {
     void EncoderVAAPI::run() {
         Q_D(AVQt::EncoderVAAPI);
 
-        int ret{0};
+        int ret;
         constexpr auto strBufSize{64};
         char strBuf[strBufSize];
 
@@ -284,19 +293,28 @@ namespace AVQt {
 //                    }
 
 //                    d->m_pCodecCtx->pix_fmt = AV_PIX_FMT_VAAPI;
-                    d->m_pCodecCtx->time_base = av_inv_q(d->m_framerate);
+                    d->m_pCodecCtx->time_base = av_make_q(1, 1000000);
                     ret = avcodec_open2(d->m_pCodecCtx, d->m_pCodec, nullptr);
                     if (ret < 0) {
                         qFatal("%i: Unable to open VAAPI encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
                     }
+                    qDebug("[AVQt::EncoderVAAPI] Encoder timebase: %d/%d", d->m_pCodecCtx->time_base.num, d->m_pCodecCtx->time_base.den);
                     d->m_pHWFrame = av_frame_alloc();
                     ret = av_hwframe_get_buffer(d->m_pFramesCtx, d->m_pHWFrame, 0);
                     if (ret != 0) {
                         qFatal("%i: Could not allocate HW frame in encoder context: %s", ret,
                                av_make_error_string(strBuf, strBufSize, ret));
                     }
+
+                    QMutexLocker lock(&d->m_cbListMutex);
+                    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);
+                        cb->start(this);
+                    }
                 }
-                while (!d->m_inputQueue.isEmpty()) {
+                if (!d->m_inputQueue.isEmpty()) {
                     QPair<AVFrame *, int64_t> frame;
                     {
                         QMutexLocker lock(&d->m_inputQueueMutex);
@@ -307,6 +325,7 @@ namespace AVQt {
                     } else {
                         av_hwframe_transfer_data(d->m_pHWFrame, frame.first, 0);
                     }
+                    d->m_pHWFrame->pts = av_rescale_q(frame.first->pts, av_make_q(1, 1000000), d->m_pCodecCtx->time_base);
                     av_frame_free(&frame.first);
                     ret = avcodec_send_frame(d->m_pCodecCtx, d->m_pHWFrame);
                     if (ret != 0) {
@@ -316,12 +335,15 @@ namespace AVQt {
                 AVPacket *packet = av_packet_alloc();
                 while (true) {
                     ret = avcodec_receive_packet(d->m_pCodecCtx, packet);
+                    qDebug("[AVQt::EncoderVAAPI] Got packet from encoder with PTS: %lld, timebase: %d/%d",
+                           static_cast<long long>(packet->pts),
+                           d->m_pCodecCtx->time_base.num, d->m_pCodecCtx->time_base.den);
                     if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
                         break;
-                    } else if (ret < 0) {
+                    } else if (ret != 0) {
                         qFatal("%i: Could not receive packet from encoder: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
                     }
-                    {
+                    if (packet->buf) {
                         QMutexLocker lock(&d->m_cbListMutex);
                         for (const auto &cb: d->m_cbList) {
                             AVPacket *cbPacket = av_packet_clone(packet);
@@ -329,6 +351,7 @@ namespace AVQt {
                             av_packet_free(&cbPacket);
                         }
                     }
+                    av_packet_unref(packet);
                 }
             } else {
                 msleep(1);
@@ -340,12 +363,8 @@ namespace AVQt {
         delete d_ptr;
         d_ptr = other.d_ptr;
         other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
 
         return *this;
     }
-
-    EncoderVAAPI::EncoderVAAPI(EncoderVAAPI &&other) {
-        d_ptr = other.d_ptr;
-        other.d_ptr = nullptr;
-    }
 }
\ No newline at end of file
diff --git a/AVQt/filter/EncoderVAAPI.h b/AVQt/filter/EncoderVAAPI.h
index 1b00d8e9ffa0a1fa179504206330a8c4bfc07c6a..617160defbe3ee073a00125e38b0b4cc4d8088e2 100644
--- a/AVQt/filter/EncoderVAAPI.h
+++ b/AVQt/filter/EncoderVAAPI.h
@@ -34,19 +34,19 @@ namespace AVQt {
     public:
         explicit EncoderVAAPI(QString encoder, QObject *parent = nullptr);
 
-        explicit EncoderVAAPI(EncoderVAAPI &other) = delete;
+        EncoderVAAPI(EncoderVAAPI &&other) noexcept;
 
-        explicit EncoderVAAPI(EncoderVAAPI &&other);
+        explicit EncoderVAAPI(EncoderVAAPI &other) = delete;
 
-        ~EncoderVAAPI() override;
+        ~EncoderVAAPI() Q_DECL_OVERRIDE;
 
-        bool isPaused() override;
+        bool isPaused() Q_DECL_OVERRIDE;
 
-        qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) override;
+        qint64 registerCallback(IPacketSink *packetSink, int8_t type) Q_DECL_OVERRIDE;
 
-        qsizetype unregisterCallback(IPacketSink *packetSink) override;
+        qint64 unregisterCallback(IPacketSink *packetSink) Q_DECL_OVERRIDE;
 
-        void run() override;
+        void run() Q_DECL_OVERRIDE;
 
         EncoderVAAPI &operator=(const EncoderVAAPI &other) = delete;
 
@@ -54,35 +54,35 @@ namespace AVQt {
 
     public slots:
 
-        Q_INVOKABLE int init() override;
+        Q_INVOKABLE int init() Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int deinit() override;
+        Q_INVOKABLE int deinit() Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int start() override;
+        Q_INVOKABLE int start() Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int stop() override;
+        Q_INVOKABLE int stop() Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE void pause(bool pause) override;
+        Q_INVOKABLE void pause(bool pause) Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int init(IFrameSource *source, AVRational framerate, int64_t duration) override;
+        Q_INVOKABLE int init(IFrameSource *source, AVRational framerate, int64_t duration) Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int deinit(IFrameSource *source) override;
+        Q_INVOKABLE int deinit(IFrameSource *source) Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int start(IFrameSource *source) override;
+        Q_INVOKABLE int start(IFrameSource *source) Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE int stop(IFrameSource *source) override;
+        Q_INVOKABLE int stop(IFrameSource *source) Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE void pause(IFrameSource *source, bool paused) override;
+        Q_INVOKABLE void pause(IFrameSource *source, bool paused) Q_DECL_OVERRIDE;
 
-        Q_INVOKABLE void onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) override;
+        Q_INVOKABLE void onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) Q_DECL_OVERRIDE;
 
     signals:
 
-        void started() override;
+        void started() Q_DECL_OVERRIDE;
 
-        void stopped() override;
+        void stopped() Q_DECL_OVERRIDE;
 
-        void paused(bool pause) override;
+        void paused(bool pause) Q_DECL_OVERRIDE;
 
     protected:
         [[maybe_unused]] explicit EncoderVAAPI(EncoderVAAPIPrivate &p);
diff --git a/AVQt/filter/IDecoder.h b/AVQt/filter/IDecoder.h
index 2751e39d00f9b157f3468901178c73a616e0ad77..f0dfb84aa4c3d619955c3520ac73a3627656aa28 100644
--- a/AVQt/filter/IDecoder.h
+++ b/AVQt/filter/IDecoder.h
@@ -9,38 +9,54 @@
 #define LIBAVQT_DECODER_H
 
 namespace AVQt {
-    class IDecoder: public IFrameSource, public IPacketSink {
+    class IDecoder : public IFrameSource, public IPacketSink {
 
 
         // IPacketSink interface
     public:
-        ~IDecoder() override {};
-        virtual bool isPaused() override = 0;
-        virtual void init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams, AVCodecParameters *aParams, AVCodecParameters *sParams) override = 0;
-        virtual void deinit(IPacketSource *source) override = 0;
-        virtual void start(IPacketSource *source) override = 0;
-        virtual void stop(IPacketSource *source) override = 0;
-        virtual void pause(bool paused) override = 0;
-        virtual void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) override = 0;
+        ~IDecoder() Q_DECL_OVERRIDE {};
+
+        virtual bool isPaused() Q_DECL_OVERRIDE = 0;
+
+        virtual void init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams,
+                          AVCodecParameters *aParams, AVCodecParameters *sParams) Q_DECL_OVERRIDE = 0;
+
+        virtual void deinit(IPacketSource *source) Q_DECL_OVERRIDE = 0;
+
+        virtual void start(IPacketSource *source) Q_DECL_OVERRIDE = 0;
+
+        virtual void stop(IPacketSource *source) Q_DECL_OVERRIDE = 0;
+
+        virtual void pause(bool paused) Q_DECL_OVERRIDE = 0;
+
+        virtual void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) Q_DECL_OVERRIDE = 0;
 
     signals:
-        virtual void started() override = 0;
-        virtual void stopped() override = 0;
+
+        virtual void started() Q_DECL_OVERRIDE = 0;
+
+        virtual void stopped() Q_DECL_OVERRIDE = 0;
 
         // IFrameSource interface
     public:
-        virtual int registerCallback(IFrameSink *frameSink) override = 0;
-        virtual int unregisterCallback(IFrameSink *frameSink) override = 0;
-        virtual int init() override = 0;
-        virtual int deinit() override = 0;
-        virtual int start() override = 0;
-        virtual int stop() override = 0;
+        virtual qint64 registerCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE = 0;
+
+        virtual qint64 unregisterCallback(IFrameSink *frameSink) Q_DECL_OVERRIDE = 0;
+
+        virtual int init() Q_DECL_OVERRIDE = 0;
+
+        virtual int deinit() Q_DECL_OVERRIDE = 0;
+
+        virtual int start() Q_DECL_OVERRIDE = 0;
+
+        virtual int stop() Q_DECL_OVERRIDE = 0;
 
     signals:
-        virtual void paused(bool pause) override = 0;
+
+        virtual void paused(bool pause) Q_DECL_OVERRIDE = 0;
     };
 }
 
 Q_DECLARE_INTERFACE(AVQt::IDecoder, "AVQt::IDecoder")
 
-#endif //LIBAVQT_DECODER_H
+#endif //LIBAVQT_DECODER_H
\ No newline at end of file
diff --git a/AVQt/filter/IEncoder.h b/AVQt/filter/IEncoder.h
index a262a025e7777991a3b0afdf49759c2d797a04d3..0554f17f0783899c26f1420fff0cae824e2b1e66 100644
--- a/AVQt/filter/IEncoder.h
+++ b/AVQt/filter/IEncoder.h
@@ -46,9 +46,9 @@ namespace AVQt {
 
         Q_INVOKABLE virtual void pause(bool pause) = 0;
 
-        Q_INVOKABLE virtual qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) = 0;
+        Q_INVOKABLE virtual qint64 registerCallback(IPacketSink *packetSink, int8_t type) = 0;
 
-        Q_INVOKABLE virtual qsizetype unregisterCallback(IPacketSink *packetSink) = 0;
+        Q_INVOKABLE virtual qint64 unregisterCallback(IPacketSink *packetSink) = 0;
 
     signals:
 
diff --git a/AVQt/filter/private/AudioDecoder_p.h b/AVQt/filter/private/AudioDecoder_p.h
index b766659c69a94e49902c1e37341165971a64ed80..9e11a3e02456cb3c2e08b3c17efa0fe88d282585 100644
--- a/AVQt/filter/private/AudioDecoder_p.h
+++ b/AVQt/filter/private/AudioDecoder_p.h
@@ -20,29 +20,35 @@ extern "C" {
 
 namespace AVQt {
     class AudioDecoderPrivate {
+    public:
+        AudioDecoderPrivate(const AudioDecoderPrivate &) = delete;
+
+        void operator=(const AudioDecoderPrivate &) = delete;
+
+    private:
         explicit AudioDecoderPrivate(AudioDecoder *q) : q_ptr(q) {};
 
         AudioDecoder *q_ptr;
 
-        QMutex m_inputQueueMutex;
-        QQueue<AVPacket *> m_inputQueue;
-        int64_t m_duration {0};
+        QMutex m_inputQueueMutex{};
+        QQueue<AVPacket *> m_inputQueue{};
+        int64_t m_duration{0};
 
-        AVCodecParameters *m_pCodecParams {nullptr};
-        AVCodec *m_pCodec {nullptr};
-        AVCodecContext *m_pCodecCtx {nullptr};
-        AVRational m_timebase;
+        AVCodecParameters *m_pCodecParams{nullptr};
+        AVCodec *m_pCodec{nullptr};
+        AVCodecContext *m_pCodecCtx{nullptr};
+        AVRational m_timebase{0, 1};
 
         // Callback stuff
-        QMutex m_cbListMutex;
-        QList<IAudioSink *> m_cbList;
+        QMutex m_cbListMutex{};
+        QList<IAudioSink *> m_cbList{};
 
         // Threading stuff
-        std::atomic_bool m_running  {false};
-        std::atomic_bool m_paused  {false};
+        std::atomic_bool m_running{false};
+        std::atomic_bool m_paused{false};
 
         friend class AudioDecoder;
     };
 }
 
-#endif //LIBAVQT_AUDIODECODER_P_H
+#endif //LIBAVQT_AUDIODECODER_P_H
\ No newline at end of file
diff --git a/AVQt/filter/private/DecoderDXVA2_p.h b/AVQt/filter/private/DecoderDXVA2_p.h
index c532b1ca1cb7d7ce8541406dd5dd8f2d50293c9f..87c8cc3f23d40035984c762d8002c4db11fb1138 100644
--- a/AVQt/filter/private/DecoderDXVA2_p.h
+++ b/AVQt/filter/private/DecoderDXVA2_p.h
@@ -16,6 +16,13 @@ namespace AVQt {
     * \private
     */
     class DecoderDXVA2Private {
+    public:
+        DecoderDXVA2Private(const DecoderDXVA2Private &) = delete;
+
+        void operator=(const DecoderDXVA2Private &) = delete;
+
+    private:
+
         explicit DecoderDXVA2Private(DecoderDXVA2 *q) : q_ptr(q) {};
 
         DecoderDXVA2 *q_ptr;
diff --git a/AVQt/filter/private/DecoderMMAL_p.h b/AVQt/filter/private/DecoderMMAL_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..02028a6437621451bd96dd7033737d267c820d33
--- /dev/null
+++ b/AVQt/filter/private/DecoderMMAL_p.h
@@ -0,0 +1,54 @@
+/*!
+ * \private
+ * \internal
+ */
+
+#include "../DecoderMMAL.h"
+
+extern "C" {
+#include <libavutil/rational.h>
+#include <libavutil/frame.h>
+}
+
+
+#ifndef TRANSCODE_DECODERMMAL_P_H
+#define TRANSCODE_DECODERMMAL_P_H
+
+namespace AVQt {
+    /*!
+     * \private
+     */
+    class DecoderMMALPrivate {
+    public:
+        DecoderMMALPrivate(const DecoderMMALPrivate &) = delete;
+
+        void operator=(const DecoderMMALPrivate &) = delete;
+
+    private:
+        explicit DecoderMMALPrivate(DecoderMMAL *q) : q_ptr(q) {};
+
+        DecoderMMAL *q_ptr;
+
+        QMutex m_inputQueueMutex{};
+        QQueue<AVPacket *> m_inputQueue{};
+        int64_t m_duration{0};
+        AVRational m_framerate{};
+        AVRational m_timebase{};
+
+        AVCodec *m_pCodec{nullptr};
+        AVCodecParameters *m_pCodecParams{nullptr};
+        AVCodecContext *m_pCodecCtx{nullptr};
+
+        // Callback stuff
+        QMutex m_cbListMutex{};
+        QList<IFrameSink *> m_cbList{};
+
+        // Threading stuff
+        std::atomic_bool m_running{false};
+        std::atomic_bool m_paused{false};
+
+        friend class DecoderMMAL;
+    };
+}
+
+#endif //TRANSCODE_DECODERMMAL_P_H
\ No newline at end of file
diff --git a/AVQt/filter/private/DecoderQSV_p.h b/AVQt/filter/private/DecoderQSV_p.h
index 0e4112760821a0f900aa6b1b922802cddcc4985d..ef82bc0524eda150b17de6eb0fc7d0a05b6761be 100644
--- a/AVQt/filter/private/DecoderQSV_p.h
+++ b/AVQt/filter/private/DecoderQSV_p.h
@@ -18,15 +18,21 @@ namespace AVQt {
      * \private
      */
     class DecoderQSVPrivate {
+    public:
+        DecoderQSVPrivate(const DecoderQSVPrivate &) = delete;
+
+        void operator=(const DecoderQSVPrivate &) = delete;
+
+    private:
         explicit DecoderQSVPrivate(DecoderQSV *q) : q_ptr(q) {};
 
         DecoderQSV *q_ptr;
 
-        QMutex m_inputQueueMutex;
-        QQueue<AVPacket *> m_inputQueue;
+        QMutex m_inputQueueMutex{};
+        QQueue<AVPacket *> m_inputQueue{};
         int64_t m_duration{0};
-        AVRational m_framerate;
-        AVRational m_timebase;
+        AVRational m_framerate{};
+        AVRational m_timebase{};
 
         AVCodec *m_pCodec{nullptr};
         AVCodecParameters *m_pCodecParams{nullptr};
@@ -34,8 +40,8 @@ namespace AVQt {
         AVBufferRef *m_pDeviceCtx{nullptr};
 
         // Callback stuff
-        QMutex m_cbListMutex;
-        QList<IFrameSink *> m_cbList;
+        QMutex m_cbListMutex{};
+        QList<IFrameSink *> m_cbList{};
 
         // Threading stuff
         std::atomic_bool m_running{false};
diff --git a/AVQt/filter/private/DecoderVAAPI_p.h b/AVQt/filter/private/DecoderVAAPI_p.h
index 12cf4c882a9aabe536cbbcdaebbb4c435cc8f494..c247ecbc04cb2b2636ed6e61f67dd36e33dbf847 100644
--- a/AVQt/filter/private/DecoderVAAPI_p.h
+++ b/AVQt/filter/private/DecoderVAAPI_p.h
@@ -7,6 +7,7 @@
 
 extern "C" {
 #include <libavutil/rational.h>
+#include <libavutil/frame.h>
 }
 
 
@@ -18,15 +19,21 @@ namespace AVQt {
      * \private
      */
     class DecoderVAAPIPrivate {
+    public:
+        DecoderVAAPIPrivate(const DecoderVAAPIPrivate &) = delete;
+
+        void operator=(const DecoderVAAPIPrivate &) = delete;
+
+    private:
         explicit DecoderVAAPIPrivate(DecoderVAAPI *q) : q_ptr(q) {};
 
         DecoderVAAPI *q_ptr;
 
-        QMutex m_inputQueueMutex;
-        QQueue<AVPacket *> m_inputQueue;
+        QMutex m_inputQueueMutex{};
+        QQueue<AVPacket *> m_inputQueue{};
         int64_t m_duration{0};
-        AVRational m_framerate;
-        AVRational m_timebase;
+        AVRational m_framerate{};
+        AVRational m_timebase{};
 
         AVCodec *m_pCodec{nullptr};
         AVCodecParameters *m_pCodecParams{nullptr};
@@ -34,8 +41,8 @@ namespace AVQt {
         AVBufferRef *m_pDeviceCtx{nullptr};
 
         // Callback stuff
-        QMutex m_cbListMutex;
-        QList<IFrameSink *> m_cbList;
+        QMutex m_cbListMutex{};
+        QList<IFrameSink *> m_cbList{};
 
         // Threading stuff
         std::atomic_bool m_running{false};
@@ -45,4 +52,4 @@ namespace AVQt {
     };
 }
 
-#endif //TRANSCODE_DECODERVAAPI_P_H
\ No newline at end of file
+#endif //TRANSCODE_DECODERVAAPI_P_H
diff --git a/AVQt/filter/private/EncoderVAAPI_p.h b/AVQt/filter/private/EncoderVAAPI_p.h
index d7be7f1186a6113d8c3a5887f39fb4ea5fd59c77..239bd63c8fde9d4705cdf6cfab0dc1cf98660e51 100644
--- a/AVQt/filter/private/EncoderVAAPI_p.h
+++ b/AVQt/filter/private/EncoderVAAPI_p.h
@@ -11,6 +11,12 @@
 
 namespace AVQt {
     class EncoderVAAPIPrivate {
+    public:
+        EncoderVAAPIPrivate(const EncoderVAAPIPrivate &) = delete;
+
+        void operator=(const EncoderVAAPIPrivate &) = delete;
+
+    private:
         explicit EncoderVAAPIPrivate(EncoderVAAPI *q) : q_ptr(q) {};
 
         EncoderVAAPI *q_ptr;
@@ -23,11 +29,11 @@ namespace AVQt {
         AVBufferRef *m_pDeviceCtx{nullptr}, *m_pFramesCtx{nullptr};
         AVFrame *m_pHWFrame{nullptr};
 
-        QMutex m_inputQueueMutex;
-        QQueue<QPair<AVFrame *, int64_t>> m_inputQueue;
+        QMutex m_inputQueueMutex{};
+        QQueue<QPair<AVFrame *, int64_t>> m_inputQueue{};
 
-        QMutex m_cbListMutex;
-        QList<IPacketSink *> m_cbList;
+        QMutex m_cbListMutex{};
+        QList<IPacketSink *> m_cbList{};
 
         std::atomic_bool m_running{false}, m_paused{false}, m_firstFrame{true};
 
diff --git a/AVQt/input/Demuxer.cpp b/AVQt/input/Demuxer.cpp
index a4fc9030b7141c2a48661b3f524ef5120e970f06..d99e134901427e16e18385b13a8bdb749967497e 100644
--- a/AVQt/input/Demuxer.cpp
+++ b/AVQt/input/Demuxer.cpp
@@ -15,8 +15,6 @@ namespace AVQt {
     }
 
     Demuxer::Demuxer(DemuxerPrivate &p) : d_ptr(&p) {
-        Q_D(AVQt::Demuxer);
-
     }
 
     void Demuxer::pause(bool pause) {
@@ -34,7 +32,7 @@ namespace AVQt {
         return d->m_paused.load();
     }
 
-    qsizetype Demuxer::registerCallback(IPacketSink *packetSink, uint8_t type) {
+    qint64 Demuxer::registerCallback(IPacketSink *packetSink, int8_t type) {
         Q_D(AVQt::Demuxer);
 
         QMutexLocker lock(&d->m_cbMutex);
@@ -59,8 +57,9 @@ namespace AVQt {
                 sParams = avcodec_parameters_alloc();
                 avcodec_parameters_copy(sParams, d->m_pFormatCtx->streams[d->m_subtitleStream]->codecpar);
             }
-            packetSink->init(this, d->m_pFormatCtx->streams[d->m_videoStream]->avg_frame_rate, AVRational(),
-                             d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, vParams, aParams, sParams);
+            packetSink->init(this, d->m_pFormatCtx->streams[d->m_videoStream]->avg_frame_rate, av_make_q(1, AV_TIME_BASE),
+                             static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), vParams, aParams,
+                             sParams);
             if (vParams) {
                 avcodec_parameters_free(&vParams);
             }
@@ -78,7 +77,7 @@ namespace AVQt {
         return 0;
     }
 
-    qsizetype Demuxer::unregisterCallback(IPacketSink *packetSink) {
+    qint64 Demuxer::unregisterCallback(IPacketSink *packetSink) {
         Q_D(AVQt::Demuxer);
 
         QMutexLocker lock(&d->m_cbMutex);
@@ -118,7 +117,7 @@ namespace AVQt {
             avformat_find_stream_info(d->m_pFormatCtx, nullptr);
 //            }
 
-            for (size_t si = 0; si < d->m_pFormatCtx->nb_streams; ++si) {
+            for (int64_t si = 0; si < d->m_pFormatCtx->nb_streams; ++si) {
                 switch (d->m_pFormatCtx->streams[si]->codecpar->codec_type) {
                     case AVMEDIA_TYPE_VIDEO:
                         d->m_videoStreams.append(si);
@@ -163,17 +162,20 @@ namespace AVQt {
             if (vParams) {
                 cb->init(this, d->m_pFormatCtx->streams[d->m_videoStream]->avg_frame_rate,
                          d->m_pFormatCtx->streams[d->m_videoStream]->time_base,
-                         d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, vParams, nullptr, nullptr);
+                         static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), vParams, nullptr,
+                         nullptr);
             }
             if (aParams) {
                 cb->init(this, d->m_pFormatCtx->streams[d->m_audioStream]->avg_frame_rate,
                          d->m_pFormatCtx->streams[d->m_audioStream]->time_base,
-                         d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, nullptr, aParams, nullptr);
+                         static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), nullptr, aParams,
+                         nullptr);
             }
             if (sParams) {
                 cb->init(this, d->m_pFormatCtx->streams[d->m_subtitleStream]->avg_frame_rate,
                          d->m_pFormatCtx->streams[d->m_subtitleStream]->time_base,
-                         d->m_pFormatCtx->duration * 1000.0 / AV_TIME_BASE, nullptr, nullptr, sParams);
+                         static_cast<int64_t>(static_cast<double>(d->m_pFormatCtx->duration) * 1000.0 / AV_TIME_BASE), nullptr, nullptr,
+                         sParams);
             }
             if (vParams) {
                 avcodec_parameters_free(&vParams);
@@ -200,7 +202,6 @@ namespace AVQt {
 
 //        avio_closep(&d->m_pIOCtx);
         avformat_close_input(&d->m_pFormatCtx);
-        delete[] d->m_pBuffer;
 
         d->m_videoStreams.clear();
         d->m_audioStreams.clear();
@@ -300,9 +301,12 @@ namespace AVQt {
                     aP = QString("%1").arg(audioPackets, 12).toLocal8Bit();
                     vP = QString("%1").arg(videoPackets, 12).toLocal8Bit();
                     sP = QString("%1").arg(sttPackets, 12).toLocal8Bit();
-                    aR = QString("%1").arg((audioPackets * 1.0 / packetCount) * 100.0, 10).toLocal8Bit();
-                    vR = QString("%1").arg((videoPackets * 1.0 / packetCount) * 100.0, 10).toLocal8Bit();
-                    sR = QString("%1").arg((sttPackets * 1.0 / packetCount) * 100.0, 10).toLocal8Bit();
+                    aR = QString("%1").arg((static_cast<double>(audioPackets) * 1.0 / static_cast<double>(packetCount)) * 100.0,
+                                           10).toLocal8Bit();
+                    vR = QString("%1").arg((static_cast<double>(videoPackets) * 1.0 / static_cast<double>(packetCount)) * 100.0,
+                                           10).toLocal8Bit();
+                    sR = QString("%1").arg((static_cast<double>(sttPackets) * 1.0 / static_cast<double>(packetCount)) * 100.0,
+                                           10).toLocal8Bit();
 
                     qDebug() << "Packet statistics";
                     qDebug() << "| Packet type | Packet count |   Percentage |";
@@ -318,19 +322,9 @@ namespace AVQt {
         }
     }
 
-    Demuxer::Demuxer(Demuxer &&other) {
-        d_ptr = other.d_ptr;
-        d_ptr->q_ptr = this;
+    Demuxer::Demuxer(Demuxer &&other) noexcept: d_ptr(other.d_ptr) {
         other.d_ptr = nullptr;
-    }
-
-    Demuxer &Demuxer::operator=(Demuxer &&other) noexcept {
-        delete d_ptr;
-        d_ptr = other.d_ptr;
         d_ptr->q_ptr = this;
-        other.d_ptr = nullptr;
-
-        return *this;
     }
 
     int DemuxerPrivate::readFromIO(void *opaque, uint8_t *buf, int bufSize) {
@@ -340,7 +334,7 @@ namespace AVQt {
         if (bytesRead == 0) {
             return AVERROR_EOF;
         } else {
-            return bytesRead;
+            return static_cast<int>(bytesRead);
         }
     }
 
diff --git a/AVQt/input/Demuxer.h b/AVQt/input/Demuxer.h
index 9ab24afa9987055adbf73ea7dc81b2b9df7e9351..313c9bed4e5e1c22748c00c5fec91687b457c30e 100644
--- a/AVQt/input/Demuxer.h
+++ b/AVQt/input/Demuxer.h
@@ -20,11 +20,13 @@ namespace AVQt {
         Q_DECLARE_PRIVATE(AVQt::Demuxer)
 
     public:
-        [[maybe_unused]] explicit Demuxer(QIODevice *inputDevice, QObject *parent = nullptr);
+        explicit Demuxer(QIODevice *inputDevice, QObject *parent = nullptr);
 
         explicit Demuxer(Demuxer &other) = delete;
 
-        explicit Demuxer(Demuxer &&other);
+        Demuxer &operator=(const Demuxer &other) = delete;
+
+        Demuxer(Demuxer &&other) noexcept;
 
         /*!
          * \private
@@ -43,18 +45,14 @@ namespace AVQt {
          * @param type Callback type, can be linked with bitwise or to set multiple options
          * @return
          */
-        Q_INVOKABLE qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) Q_DECL_OVERRIDE;
+        Q_INVOKABLE qint64 registerCallback(IPacketSink *packetSink, int8_t type) Q_DECL_OVERRIDE;
 
         /*!
          * \brief Removes packet callback \c packetSink from registry
          * @param packetSink Packet sink/filter to be removed
          * @return Previous position of the item, is -1 when not in registry
          */
-        Q_INVOKABLE qsizetype unregisterCallback(IPacketSink *packetSink) Q_DECL_OVERRIDE;
-
-        Demuxer &operator=(const Demuxer &other) = delete;
-
-        Demuxer &operator=(Demuxer &&other) noexcept;
+        Q_INVOKABLE qint64 unregisterCallback(IPacketSink *packetSink) Q_DECL_OVERRIDE;
 
     public slots:
         /*!
@@ -79,7 +77,7 @@ namespace AVQt {
          * \brief Stops packet source (e.g. Interrupt processing thread, free camera).
          * @return Status code (0 = Success)
          */
-        Q_INVOKABLE int stop();
+        Q_INVOKABLE int stop() Q_DECL_OVERRIDE;
 
         /*!
          * \brief Sets paused flag of packet source
diff --git a/AVQt/input/IAudioSource.h b/AVQt/input/IAudioSource.h
index 76e30c1e18a3366b7fa1f7ebb817b6cff4861186..9d563e134a105deaf3531e306bad3f71ec8989eb 100644
--- a/AVQt/input/IAudioSource.h
+++ b/AVQt/input/IAudioSource.h
@@ -18,9 +18,9 @@ namespace AVQt {
 
         virtual bool isPaused() = 0;
 
-        Q_INVOKABLE virtual int registerCallback(IAudioSink *callback) = 0;
+        Q_INVOKABLE virtual qint64 registerCallback(IAudioSink *callback) = 0;
 
-        Q_INVOKABLE virtual int unregisterCallback(IAudioSink *callback) = 0;
+        Q_INVOKABLE virtual qint64 unregisterCallback(IAudioSink *callback) = 0;
 
     public slots:
         Q_INVOKABLE virtual int init() = 0;
diff --git a/AVQt/input/IFrameSource.h b/AVQt/input/IFrameSource.h
index fcea49dff7b4c6bd393d2f63c29144ea89a12d8a..2e3c88a44dfe3b0534b003ec3df72cbb972dac7f 100644
--- a/AVQt/input/IFrameSource.h
+++ b/AVQt/input/IFrameSource.h
@@ -63,14 +63,14 @@ namespace AVQt {
          * @param type Callback type, can be linked with bitwise or to set multiple options
          * @return
          */
-        Q_INVOKABLE virtual int registerCallback(IFrameSink *frameSink) = 0;
+        Q_INVOKABLE virtual qint64 registerCallback(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 int unregisterCallback(IFrameSink *frameSink) = 0;
+        Q_INVOKABLE virtual qint64 unregisterCallback(IFrameSink *frameSink) = 0;
 
     public slots:
         /*!
diff --git a/AVQt/input/IPacketSource.h b/AVQt/input/IPacketSource.h
index de6ef2c1b1f09c5c842c5b85b90b97f857395f51..e092baf597c99fa3b7252798103921b379a3ca66 100644
--- a/AVQt/input/IPacketSource.h
+++ b/AVQt/input/IPacketSource.h
@@ -57,14 +57,14 @@ namespace AVQt {
          * @param type Callback type, can be linked with bitwise or to set multiple options
          * @return
          */
-        Q_INVOKABLE virtual qsizetype registerCallback(IPacketSink *packetSink, uint8_t type) = 0;
+        Q_INVOKABLE virtual qint64 registerCallback(IPacketSink *packetSink, int8_t type) = 0;
 
         /*!
          * \brief Removes packet callback \c packetSink from registry
          * @param packetSink Packet sink/filter to be removed
          * @return Previous position of the item, is -1 when not in registry
          */
-        Q_INVOKABLE virtual qsizetype unregisterCallback(IPacketSink *packetSink) = 0;
+        Q_INVOKABLE virtual qint64 unregisterCallback(IPacketSink *packetSink) = 0;
 
     public slots:
         /*!
diff --git a/AVQt/input/private/Demuxer_p.h b/AVQt/input/private/Demuxer_p.h
index ff733d2a4c22c0ffc201764bf32a0905959d4d07..aa6cc518b355539e34baecb91b801520db1bc449 100644
--- a/AVQt/input/private/Demuxer_p.h
+++ b/AVQt/input/private/Demuxer_p.h
@@ -16,6 +16,12 @@ extern "C" {
 
 namespace AVQt {
     class DemuxerPrivate {
+    public:
+        DemuxerPrivate(const DemuxerPrivate &) = delete;
+
+        void operator=(const DemuxerPrivate &) = delete;
+
+    private:
         explicit DemuxerPrivate(Demuxer *q) : q_ptr(q) {};
 
         static int readFromIO(void *opaque, uint8_t *buf, int bufSize);
@@ -24,18 +30,18 @@ namespace AVQt {
 
         Demuxer *q_ptr;
 
-        std::atomic_bool m_running {false}, m_paused {false}, m_initialized  {false};
-        QMutex m_cbMutex;
-        QMap<IPacketSink *, int8_t> m_cbMap;
+        std::atomic_bool m_running{false}, m_paused{false}, m_initialized{false};
+        QMutex m_cbMutex{};
+        QMap<IPacketSink *, int8_t> m_cbMap{};
 
-        QList<int> m_videoStreams, m_audioStreams, m_subtitleStreams;
-        int m_videoStream = -1, m_audioStream = -1, m_subtitleStream = -1;
+        QList<int64_t> m_videoStreams{}, m_audioStreams{}, m_subtitleStreams{};
+        int64_t m_videoStream{-1}, m_audioStream{-1}, m_subtitleStream{-1};
 
-        static constexpr size_t BUFFER_SIZE = 1024;
-        uint8_t *m_pBuffer {nullptr};
-        AVFormatContext *m_pFormatCtx {nullptr};
-        AVIOContext *m_pIOCtx {nullptr};
-        QIODevice *m_inputDevice {nullptr};
+        static constexpr size_t BUFFER_SIZE{1024};
+        uint8_t *m_pBuffer{nullptr};
+        AVFormatContext *m_pFormatCtx{nullptr};
+        AVIOContext *m_pIOCtx{nullptr};
+        QIODevice *m_inputDevice{nullptr};
 
         friend class Demuxer;
     };
diff --git a/AVQt/output/IPacketSink.h b/AVQt/output/IPacketSink.h
index 5e9ee3c0ee2378bf0fb7c10f6e4b76d88dbf1339..d922961aa0a66c3018223bc61e134156da4a8056 100644
--- a/AVQt/output/IPacketSink.h
+++ b/AVQt/output/IPacketSink.h
@@ -44,7 +44,7 @@ namespace AVQt {
 
         Q_INVOKABLE virtual void stop(IPacketSource *source) = 0;
 
-        Q_INVOKABLE virtual void pause(bool paused) = 0;
+        Q_INVOKABLE virtual void pause(bool p) = 0;
 
         /*!
          * \brief Callback method, is called for every registered packet type.
@@ -61,6 +61,8 @@ namespace AVQt {
         virtual void started() = 0;
 
         virtual void stopped() = 0;
+
+        virtual void paused(bool pause) = 0;
     };
 }
 
diff --git a/AVQt/output/Muxer.cpp b/AVQt/output/Muxer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..935ff345d5ff7b3d7e615227e9d1b953608c5550
--- /dev/null
+++ b/AVQt/output/Muxer.cpp
@@ -0,0 +1,267 @@
+//
+// Created by silas on 5/24/21.
+//
+
+#include <input/IPacketSource.h>
+#include "private/Muxer_p.h"
+#include "Muxer.h"
+
+namespace AVQt {
+    Muxer::Muxer(QIODevice *outputDevice, QObject *parent) : QThread(parent), d_ptr(new MuxerPrivate(this)) {
+        Q_D(AVQt::Muxer);
+        d->m_outputDevice = outputDevice;
+    }
+
+    Muxer::Muxer(AVQt::MuxerPrivate &p) : d_ptr(&p) {
+
+    }
+
+    AVQt::Muxer::Muxer(Muxer &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+    }
+
+    bool Muxer::isPaused() {
+        Q_D(AVQt::Muxer);
+        return d->m_paused.load();
+    }
+
+    void Muxer::init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams,
+                     AVCodecParameters *aParams, AVCodecParameters *sParams) {
+        Q_UNUSED(framerate)
+        Q_UNUSED(duration)
+        Q_D(AVQt::Muxer);
+
+        if ((vParams && aParams) || (vParams && sParams) || (aParams && sParams)) {
+            qWarning("[AVQt::Muxer] init() called for multiple stream types at once. Ignoring call");
+            return;
+        }
+
+        if (d->m_sourceStreamMap.contains(source)) {
+            bool alreadyCalled = false;
+            for (const auto &stream: d->m_sourceStreamMap[source].keys()) {
+                if ((vParams && stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ||
+                    (aParams && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ||
+                    (sParams && stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
+                    alreadyCalled = true;
+                    break;
+                }
+            }
+            if (alreadyCalled) {
+                qWarning("[AVQt::Muxer] init() called multiple times for the same stream type by the same source. Ignoring call");
+                return;
+            }
+        }
+        QMutexLocker lock(&d->m_initMutex);
+        if (!d->m_pFormatContext) {
+            if (!d->m_outputDevice->isOpen()) {
+                if (!d->m_outputDevice->open((d->m_outputDevice->isSequential() ? QIODevice::WriteOnly : QIODevice::ReadWrite))) {
+                    qFatal("[AVQt::Muxer] Could not open output device");
+                }
+            } else if (!d->m_outputDevice->isWritable()) {
+                qFatal("[AVQt::Muxer] Output device is not writable");
+            }
+            d->m_pFormatContext = avformat_alloc_context();
+            d->m_pFormatContext->oformat = av_guess_format("mpegts", "", 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,
+                                                 &MuxerPrivate::writeToIO, &MuxerPrivate::seekIO);
+            d->m_pIOContext->seekable = !d->m_outputDevice->isSequential();
+            d->m_pFormatContext->pb = d->m_pIOContext;
+            d->m_pFormatContext->flags |= AVFMT_FLAG_CUSTOM_IO;
+        }
+
+        if (!d->m_sourceStreamMap.contains(source)) {
+            d->m_sourceStreamMap[source] = QMap<AVStream *, AVRational>();
+        }
+        if (vParams) {
+            AVStream *videoStream = avformat_new_stream(d->m_pFormatContext, avcodec_find_encoder(vParams->codec_id));
+            videoStream->codecpar = avcodec_parameters_alloc();
+            avcodec_parameters_copy(videoStream->codecpar, vParams);
+            videoStream->time_base = timebase;
+            d->m_sourceStreamMap[source].insert(videoStream, timebase);
+        } else if (aParams) {
+            AVStream *audioStream = avformat_new_stream(d->m_pFormatContext, avcodec_find_encoder(aParams->codec_id));
+            audioStream->codecpar = avcodec_parameters_alloc();
+            avcodec_parameters_copy(audioStream->codecpar, aParams);
+            audioStream->time_base = timebase;
+            d->m_sourceStreamMap[source].insert(audioStream, timebase);
+        } else if (sParams) {
+            AVStream *subtitleStream = avformat_new_stream(d->m_pFormatContext, avcodec_find_encoder(sParams->codec_id));
+            subtitleStream->codecpar = avcodec_parameters_alloc();
+            avcodec_parameters_copy(subtitleStream->codecpar, sParams);
+            subtitleStream->time_base = timebase;
+            d->m_sourceStreamMap[source].insert(subtitleStream, timebase);
+        }
+        int ret = avformat_init_output(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));
+        }
+    }
+
+    void Muxer::deinit(IPacketSource *source) {
+        Q_D(AVQt::Muxer);
+
+        stop(nullptr);
+
+        if (d->m_sourceStreamMap.contains(source)) {
+            d->m_sourceStreamMap.remove(source);
+        } else {
+            qWarning("[AVQt::Muxer] deinit() called without preceding init() from source. Ignoring call");
+            return;
+        }
+
+        if (d->m_sourceStreamMap.isEmpty()) {
+            QMutexLocker lock(&d->m_initMutex);
+            if (d->m_pFormatContext) {
+                if (d->m_headerWritten.load()) {
+                    av_write_trailer(d->m_pFormatContext);
+                }
+                int ret = av_interleaved_write_frame(d->m_pFormatContext, nullptr);
+                if (ret != 0) {
+                    constexpr auto strBufSize = 32;
+                    char strBuf[strBufSize];
+                    qWarning("%d: Couldn't flush AVFormatContext packet queue: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+                }
+                avio_flush(d->m_pFormatContext->pb);
+                avio_closep(&d->m_pIOContext);
+                avformat_free_context(d->m_pFormatContext);
+            }
+        }
+
+    }
+
+    void Muxer::start(IPacketSource *source) {
+        Q_UNUSED(source)
+        Q_D(AVQt::Muxer);
+
+        bool shouldBe = false;
+        if (d->m_running.compare_exchange_strong(shouldBe, true)) {
+            shouldBe = false;
+//            if (d->m_headerWritten.compare_exchange_strong(shouldBe, true)) {
+//
+//            }
+            d->m_paused.store(false);
+            QThread::start();
+            started();
+        }
+    }
+
+    void Muxer::stop(IPacketSource *source) {
+        Q_UNUSED(source)
+        Q_D(AVQt::Muxer);
+
+        bool shouldBe = true;
+        if (d->m_running.compare_exchange_strong(shouldBe, false)) {
+            d->m_paused.store(false);
+            wait();
+            {
+                QMutexLocker lock{&d->m_inputQueueMutex};
+                while (!d->m_inputQueue.isEmpty()) {
+                    auto packet = d->m_inputQueue.dequeue();
+                    av_packet_free(&packet.first);
+                }
+            }
+            stopped();
+        }
+    }
+
+    void Muxer::pause(bool p) {
+        Q_D(AVQt::Muxer);
+
+        bool shouldBe = !p;
+        if (d->m_paused.compare_exchange_strong(shouldBe, p)) {
+            paused(p);
+        }
+    }
+
+    void Muxer::onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) {
+        Q_D(AVQt::Muxer);
+
+        bool unknownSource = !d->m_sourceStreamMap.contains(source);
+        bool initStream = false;
+        AVStream *addStream;
+        if (!unknownSource) {
+            for (const auto &stream : d->m_sourceStreamMap[source].keys()) {
+                if ((packetType == IPacketSource::CB_VIDEO && stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ||
+                    (packetType == IPacketSource::CB_AUDIO && stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ||
+                    (packetType == IPacketSource::CB_SUBTITLE && stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)) {
+                    addStream = stream;
+                    initStream = true;
+                    break;
+                }
+            }
+        }
+        if (unknownSource || !initStream) {
+            qWarning("[AVQt::Muxer] onPacket() called without preceding call to init() for stream type. Ignoring packet");
+            return;
+        }
+
+        QPair<AVPacket *, AVStream *> queuePacket{av_packet_clone(packet), addStream};
+        queuePacket.first->stream_index = addStream->index;
+        qDebug("[AVQt::Muxer] Getting packet with PTS: %lld", static_cast<long long>(packet->pts));
+        av_packet_rescale_ts(queuePacket.first, d->m_sourceStreamMap[source][addStream], addStream->time_base);
+
+        QMutexLocker lock(&d->m_inputQueueMutex);
+        d->m_inputQueue.enqueue(queuePacket);
+    }
+
+    void Muxer::run() {
+        Q_D(AVQt::Muxer);
+
+        while (d->m_running.load()) {
+            if (!d->m_paused.load() && !d->m_inputQueue.isEmpty()) {
+                QPair<AVPacket *, AVStream *> packet;
+                {
+                    QMutexLocker lock(&d->m_inputQueueMutex);
+                    packet = d->m_inputQueue.dequeue();
+                }
+//                av_packet_rescale_ts(packet.first, packet.second->time_base, av_make_q(1, AV_TIME_BASE));
+                int ret = av_interleaved_write_frame(d->m_pFormatContext, packet.first);
+                qDebug("Written packet");
+                if (ret != 0) {
+                    constexpr auto strBufSize = 32;
+                    char strBuf[strBufSize];
+                    qWarning("%d: Couldn't write packet to AVFormatContext: %s", ret, av_make_error_string(strBuf, strBufSize, ret));
+                }
+            } else {
+                msleep(4);
+            }
+        }
+    }
+
+    int MuxerPrivate::writeToIO(void *opaque, uint8_t *buf, int buf_size) {
+        auto *outputDevice = reinterpret_cast<QIODevice *>(opaque);
+
+        auto bytesWritten = 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);
+
+        if (outputDevice->isSequential()) {
+            return AVERROR_UNKNOWN;
+        }
+
+        bool result;
+        switch (whence) {
+            case SEEK_SET:
+                result = outputDevice->seek(offset);
+                break;
+            case SEEK_CUR:
+                result = outputDevice->seek(outputDevice->pos() + offset);
+                break;
+            case SEEK_END:
+                result = outputDevice->seek(outputDevice->size() - offset);
+                break;
+            case AVSEEK_SIZE:
+                return outputDevice->size();
+            default:
+                return AVERROR_UNKNOWN;
+        }
+
+        return result ? outputDevice->pos() : AVERROR_UNKNOWN;
+    }
+}
\ No newline at end of file
diff --git a/AVQt/output/Muxer.h b/AVQt/output/Muxer.h
new file mode 100644
index 0000000000000000000000000000000000000000..a0ddeeda02cdf1b994ed35483680c2924d0fb902
--- /dev/null
+++ b/AVQt/output/Muxer.h
@@ -0,0 +1,64 @@
+//
+// Created by silas on 5/24/21.
+//
+
+#include "IPacketSink.h"
+
+#include <QThread>
+#include <QIODevice>
+
+#ifndef LIBAVQT_MUXER_H
+#define LIBAVQT_MUXER_H
+
+namespace AVQt {
+    class MuxerPrivate;
+
+    class Muxer : public QThread, public IPacketSink {
+    Q_OBJECT
+        Q_INTERFACES(AVQt::IPacketSink)
+
+        Q_DECLARE_PRIVATE(AVQt::Muxer)
+
+    protected:
+        void run() Q_DECL_OVERRIDE;
+
+    public:
+        explicit Muxer(QIODevice *outputDevice, QObject *parent = nullptr);
+
+        Muxer(Muxer &) = delete;
+
+        Muxer(Muxer &&other) noexcept;
+
+        bool isPaused() Q_DECL_OVERRIDE;
+
+        void init(IPacketSource *source, AVRational framerate, AVRational timebase, int64_t duration, AVCodecParameters *vParams,
+                  AVCodecParameters *aParams, AVCodecParameters *sParams) Q_DECL_OVERRIDE;
+
+        void deinit(IPacketSource *source) Q_DECL_OVERRIDE;
+
+        void start(IPacketSource *source) Q_DECL_OVERRIDE;
+
+        void stop(IPacketSource *source) Q_DECL_OVERRIDE;
+
+        void pause(bool p) Q_DECL_OVERRIDE;
+
+        void onPacket(IPacketSource *source, AVPacket *packet, int8_t packetType) Q_DECL_OVERRIDE;
+
+        void operator=(const Muxer &) = delete;
+
+    signals:
+
+        void started() Q_DECL_OVERRIDE;
+
+        void stopped() Q_DECL_OVERRIDE;
+
+        void paused(bool pause) Q_DECL_OVERRIDE;
+
+    protected:
+        [[maybe_unused]] explicit Muxer(MuxerPrivate &p);
+
+        MuxerPrivate *d_ptr;
+    };
+}
+
+#endif //LIBAVQT_MUXER_H
diff --git a/AVQt/output/OpenALAudioOutput.cpp b/AVQt/output/OpenALAudioOutput.cpp
index 93afef3b12b3258921e92867470fb8fa61343055..bd6d57881a48a09ec569c2a57ac03e5e6f73fc75 100644
--- a/AVQt/output/OpenALAudioOutput.cpp
+++ b/AVQt/output/OpenALAudioOutput.cpp
@@ -8,18 +8,16 @@
 #include "filter/private/OpenALErrorHandler.h"
 #include "OpenGLRenderer.h"
 
-#include <QtCore>
-#include <QtConcurrent>
-
 namespace AVQt {
     OpenALAudioOutput::OpenALAudioOutput(QObject *parent) : QThread(parent), d_ptr(new OpenALAudioOutputPrivate(this)) {
-        Q_D(AVQt::OpenALAudioOutput);
-
     }
 
     [[maybe_unused]] OpenALAudioOutput::OpenALAudioOutput(OpenALAudioOutputPrivate &p) : d_ptr(&p) {
-        Q_D(AVQt::OpenALAudioOutput);
+    }
 
+    OpenALAudioOutput::OpenALAudioOutput(OpenALAudioOutput &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
     }
 
     int OpenALAudioOutput::init(IAudioSource *source, int64_t duration, int sampleRate) {
@@ -54,7 +52,7 @@ namespace AVQt {
             qFatal("Could not make ALC context current");
         }
 
-        d->m_ALBufferCount = static_cast<int>(std::ceil((1000 * 1.0 / 60) * static_cast<double>(d->m_sampleRate / 1536)));
+        d->m_ALBufferCount = static_cast<int>(std::ceil((1000 * 1.0 / 60) * (static_cast<double>(d->m_sampleRate) / 1536.0)));
         qDebug("Using %d OpenAL buffers", d->m_ALBufferCount);
 
         d->m_ALBuffers.resize(d->m_ALBufferCount);
@@ -62,10 +60,10 @@ namespace AVQt {
         alCall(alGenBuffers, d->m_ALBufferCount, d->m_ALBuffers.data());
 
         alCall(alGenSources, 1, &d->m_ALSource);
-        alCall(alSourcef, d->m_ALSource, AL_PITCH, 1);
+        alCall(alSourcef, d->m_ALSource, AL_PITCH, 1.0f);
         alCall(alSourcef, d->m_ALSource, AL_GAIN, 1.0f);
-        alCall(alSource3f, d->m_ALSource, AL_POSITION, 0, 0, 0);
-        alCall(alSource3f, d->m_ALSource, AL_VELOCITY, 0, 0, 0);
+        alCall(alSource3f, d->m_ALSource, AL_POSITION, 0.f, 0.f, 0.f);
+        alCall(alSource3f, d->m_ALSource, AL_VELOCITY, 0.f, 0.f, 0.f);
         alCall(alSourcei, d->m_ALSource, AL_LOOPING, AL_FALSE);
         alCall(alSourcei, d->m_ALSource, AL_BUFFER, 0);
 
@@ -114,7 +112,7 @@ namespace AVQt {
         alCall(alDeleteSources, 1, &d->m_ALSource);
         alCall(alDeleteBuffers, d->m_ALBufferCount, d->m_ALBuffers.data());
         d->m_ALBuffers.clear();
-        d->m_ALBuffers.shrink_to_fit();
+        d->m_ALBuffers.squeeze();
 
         alcCall(alcMakeContextCurrent, contextCurrent, d->m_ALCDevice, nullptr);
         alcCall(alcDestroyContext, d->m_ALCDevice, d->m_ALCContext);
@@ -192,7 +190,7 @@ namespace AVQt {
 
 //            pause(nullptr, true);
             wait();
-            qDebug("OpenALAudioOutput at %#lx stopped", reinterpret_cast<int64_t>(this));
+            qDebug("OpenALAudioOutput at %#llx stopped", static_cast<long long>(reinterpret_cast<int64_t>(this)));
 
             stopped();
 
@@ -244,8 +242,10 @@ namespace AVQt {
         {
             QMutexLocker lock(&d->m_swrContextMutex);
             if (!d->m_pSwrContext && frame->format != AV_SAMPLE_FMT_S16) {
-                d->m_pSwrContext = swr_alloc_set_opts(nullptr, frame->channel_layout, AV_SAMPLE_FMT_S16, frame->sample_rate,
-                                                      frame->channel_layout, static_cast<AVSampleFormat>(frame->format), frame->sample_rate,
+                d->m_pSwrContext = swr_alloc_set_opts(nullptr, static_cast<int64_t>(frame->channel_layout), AV_SAMPLE_FMT_S16,
+                                                      frame->sample_rate,
+                                                      static_cast<int64_t>(frame->channel_layout),
+                                                      static_cast<AVSampleFormat>(frame->format), frame->sample_rate,
                                                       0, nullptr);
                 swr_init(d->m_pSwrContext);
             }
@@ -307,16 +307,17 @@ namespace AVQt {
 
             d->m_clock.store(renderer->getClock());
             clockIntervalChanged(d->m_clock.load()->getInterval());
-            int newBufferCount = static_cast<int>(std::ceil((1000 * 1.0 / static_cast<double>(d->m_clockInterval) * static_cast<double>(d->m_sampleRate) / 1536)));
+            int newBufferCount = static_cast<int>(std::ceil(
+                    (1000 * 1.0 / static_cast<double>(d->m_clockInterval) * static_cast<double>(d->m_sampleRate) / 1536)));
             if (d->m_ALBuffers.size() != newBufferCount) {
                 ALCboolean contextCurrent = ALC_FALSE;
                 alcCall(alcMakeContextCurrent, contextCurrent, d->m_ALCDevice, d->m_ALCContext);
                 if (d->m_ALBuffers.size() > newBufferCount) {
-                    auto overhead = d->m_ALBuffers.size() - newBufferCount;
+                    int overhead = static_cast<int>(d->m_ALBuffers.size() - newBufferCount);
                     alCall(alDeleteBuffers, overhead, d->m_ALBuffers.data() + d->m_ALBuffers.size() - overhead);
                     d->m_ALBuffers.resize(newBufferCount);
                 } else if (d->m_ALBuffers.size() < newBufferCount) {
-                    auto extraBuffers = newBufferCount - d->m_ALBuffers.size();
+                    int extraBuffers = static_cast<int>(newBufferCount - d->m_ALBuffers.size());
                     d->m_ALBuffers.resize(newBufferCount);
                     alCall(alGenBuffers, extraBuffers, d->m_ALBuffers.data() + d->m_ALBuffers.size() - extraBuffers);
                 }
@@ -369,24 +370,27 @@ namespace AVQt {
             }
             if (!d->m_ALBufferQueue.isEmpty()) {
                 QMutexLocker lock3(&d->m_ALBufferQueueMutex);
-                alCall(alSourceQueueBuffers, d->m_ALSource, std::min(d->m_ALBufferQueue.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), d->m_ALBufferQueue.toVector().data());
-                for (const auto &buf: QList(d->m_ALBufferQueue.end() - std::min(d->m_ALBufferQueue.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), d->m_ALBufferQueue.end())) {
+                alCall(alSourceQueueBuffers, d->m_ALSource, static_cast<int>(qMin(d->m_ALBufferQueue.size(), 4)),
+                       d->m_ALBufferQueue.toVector().data());
+                for (const auto &buf: d->m_ALBufferQueue.mid(d->m_ALBufferQueue.size() - static_cast<int>(qMin(d->m_ALBufferQueue.size(), 4)))) {
                     d->m_queuedSamples += d->m_ALBufferSampleMap[buf];
                 }
                 if (d->m_ALBufferQueue.size() < 4) {
-                    alCall(alSourceQueueBuffers, d->m_ALSource, std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()), buffers.data());
-                    for (qint64 i = buffers.size() - std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()); i < buffers.size(); ++i) {
+                    alCall(alSourceQueueBuffers, d->m_ALSource, static_cast<int>(qMin(buffers.size(), 4 - d->m_ALBufferQueue.size())),
+                           buffers.data());
+                    for (qint64 i = static_cast<int>(buffers.size() - qMin(buffers.size(), 4 - d->m_ALBufferQueue.size()));
+                         i < buffers.size(); ++i) {
                         d->m_queuedSamples += d->m_ALBufferSampleMap[buffers[i]];
                     }
-                    buffers.remove(buffers.size() - std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()),
-                                   std::min(buffers.size(), 4 - d->m_ALBufferQueue.size()));
+                    buffers.remove(buffers.size() - qMin(buffers.size(), 4 - d->m_ALBufferQueue.size()),
+                                   qMin(buffers.size(), 4 - d->m_ALBufferQueue.size()));
                     d->m_ALBufferQueue.append(buffers.toList());
                 }
                 d->m_ALBufferQueue.clear();
             } else {
                 QMutexLocker lock3(&d->m_ALBufferQueueMutex);
-                alCall(alSourceQueueBuffers, d->m_ALSource, std::min(buffers.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), buffers.data());
-                buffers.remove(buffers.size() - std::min(buffers.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)), std::min(buffers.size(), static_cast<typeof (d->m_ALBufferQueue.size())>(4)));
+                alCall(alSourceQueueBuffers, d->m_ALSource, static_cast<int>(qMin(buffers.size(), 4)), buffers.data());
+                buffers.remove(buffers.size() - qMin(buffers.size(), 4), qMin(buffers.size(), 4));
                 d->m_ALBufferQueue.append(buffers.toList());
             }
         } else {
@@ -433,7 +437,7 @@ namespace AVQt {
             if (d->m_inputQueue.isEmpty() || d->m_paused.load()) {
                 return;
             }
-            qDebug("Size of output queue: %lld", d->m_inputQueue.size());
+            qDebug("Size of output queue: %d", d->m_inputQueue.size());
             QMutexLocker lock(&d->m_ALBufferQueueMutex);
             if (!d->m_ALBufferQueue.isEmpty()) {
                 lock.unlock();
@@ -454,14 +458,15 @@ namespace AVQt {
                             break;
                         }
                         frame = d->m_inputQueue.dequeue();
-                        qDebug("Discarding audio frame at PTS: %ld < PTS: %lld", frame.first->pts, timestamp);
+                        qDebug("Discarding audio frame at PTS: %lld < PTS: %lld", static_cast<long long>(frame.first->pts), timestamp);
                         av_frame_unref(frame.first);
                         av_frame_free(&frame.first);
                     }
                 }
                 while (!d->m_inputQueue.isEmpty() &&
                        !d->m_ALBufferQueue.isEmpty()) {
-                    if (d->m_queuedSamples * 1.0 / d->m_inputQueue.front().first->sample_rate < d->m_clockInterval * 1.5) {
+                    if (static_cast<double>(d->m_queuedSamples) * 1.0 / d->m_inputQueue.front().first->sample_rate <
+                        static_cast<double>(d->m_clockInterval) * 1.5) {
                         lock.relock();
                         QMutexLocker lock1(&d->m_inputQueueMutex);
                         QMutexLocker lock2(&d->m_ALBufferSampleMapMutex);
@@ -482,7 +487,7 @@ namespace AVQt {
 //                    } else {
                         frame = d->m_inputQueue.dequeue();
 //                    }
-                        duration += frame.first->nb_samples * 1000.0 / frame.first->sample_rate;
+                        duration += static_cast<int64_t>(std::round(frame.first->nb_samples * 1000.0 / frame.first->sample_rate));
                         lock1.unlock();
                         auto buf = d->m_ALBufferQueue.dequeue();
                         lock.unlock();
diff --git a/AVQt/output/OpenALAudioOutput.h b/AVQt/output/OpenALAudioOutput.h
index 476bcf076196d722070f2b05d96f04f3482e2593..2e05a19dbfce87d2e65f7b3e9e1626adfafc9afd 100644
--- a/AVQt/output/OpenALAudioOutput.h
+++ b/AVQt/output/OpenALAudioOutput.h
@@ -24,6 +24,12 @@ namespace AVQt {
     public:
         explicit OpenALAudioOutput(QObject *parent = nullptr);
 
+        OpenALAudioOutput(OpenALAudioOutput &&other) noexcept;
+
+        OpenALAudioOutput(const OpenALAudioOutput &) = delete;
+
+        void operator=(const OpenALAudioOutput &) = delete;
+
         Q_INVOKABLE bool isPaused() Q_DECL_OVERRIDE;
 
         void run() Q_DECL_OVERRIDE;
@@ -66,4 +72,4 @@ namespace AVQt {
 }
 
 
-#endif //LIBAVQT_OPENALAUDIOOUTPUT_H
+#endif //LIBAVQT_OPENALAUDIOOUTPUT_H
\ No newline at end of file
diff --git a/AVQt/output/OpenGLRenderer.cpp b/AVQt/output/OpenGLRenderer.cpp
index 1a696ba9f72d46779734620cd1ea6eaad4e46d88..8ce839be0e36325e412df022e94d4ed8f2a58982 100644
--- a/AVQt/output/OpenGLRenderer.cpp
+++ b/AVQt/output/OpenGLRenderer.cpp
@@ -6,7 +6,6 @@
 #include "OpenGLRenderer.h"
 
 #include <QtGui>
-#include <iostream>
 
 extern "C" {
 #include <libavutil/frame.h>
@@ -27,9 +26,12 @@ namespace AVQt {
     [[maybe_unused]] [[maybe_unused]] OpenGLRenderer::OpenGLRenderer(OpenGLRendererPrivate &p) : d_ptr(&p) {
     }
 
-    OpenGLRenderer::~OpenGLRenderer() noexcept {
-        Q_D(AVQt::OpenGLRenderer);
+    OpenGLRenderer::OpenGLRenderer(OpenGLRenderer &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
 
+    OpenGLRenderer::~OpenGLRenderer() noexcept {
         delete d_ptr;
     }
 
@@ -37,14 +39,9 @@ namespace AVQt {
         Q_UNUSED(framerate) // Don't use framerate, because it is not always specified in stream information
         Q_UNUSED(source)
         Q_D(AVQt::OpenGLRenderer);
-        int64_t h = duration / 3600000;
-        int64_t m = (duration - h * 3600000) / 60000;
-        int64_t s = (duration - h * 3600000 - m * 60000) / 1000;
-        int64_t ms = duration - h * 3600000 - m * 60000 - s * 1000;
 
-        d->m_duration = QTime(static_cast<int>(h), static_cast<int>(m), static_cast<int>(s), static_cast<int>(ms));
-        d->m_position = QTime(0, 0, 0, 0);
-        qDebug() << "Duration:" << d->m_duration.toString("hh:mm:ss");
+        d->m_duration = OpenGLRendererPrivate::timeFromMillis(duration);
+        qDebug() << "Duration:" << d->m_duration.toString("hh:mm:ss.zzz");
 
         d->m_clock = new RenderClock;
         d->m_clock->setInterval(16);
@@ -104,20 +101,18 @@ namespace AVQt {
             d->m_vbo.destroy();
             d->m_vao.destroy();
 
-            if (d->m_yTexture) {
 //                d->m_yTexture->destroy();
-                delete d->m_yTexture;
-            }
+            delete d->m_yTexture;
+
 
-            if (d->m_uTexture) {
 //                d->m_uTexture->destroy();
-                delete d->m_uTexture;
-            }
+            delete d->m_uTexture;
+
+
 
-            if (d->m_vTexture) {
 //                d->m_yTexture->destroy();
-                delete d->m_vTexture;
-            }
+            delete d->m_vTexture;
+
 
             stopped();
             return 0;
@@ -192,15 +187,26 @@ namespace AVQt {
 
         loadResources();
 
-        d->m_program = new QOpenGLShaderProgram();
+        QByteArray shaderVersionString;
 
-        if (!d->m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shaders/texture.vsh")) {
-            qDebug() << "Vertex shader errors:\n" << d->m_program->log();
+        if (context()->isOpenGLES()) {
+            shaderVersionString = "#version 300 es\n";
+        } else {
+            shaderVersionString = "#version 330 core\n";
         }
 
-        if (!d->m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/shaders/texture.fsh")) {
-            qDebug() << "Fragment shader errors:\n" << d->m_program->log();
-        }
+        QFile vsh{":/shaders/texture.vsh"};
+        QFile fsh{":/shaders/texture.fsh"};
+        vsh.open(QIODevice::ReadOnly);
+        fsh.open(QIODevice::ReadOnly);
+        QByteArray vertexShader = vsh.readAll().prepend(shaderVersionString);
+        QByteArray fragmentShader = fsh.readAll().prepend(shaderVersionString);
+        vsh.close();
+        fsh.close();
+
+        d->m_program = new QOpenGLShaderProgram();
+        d->m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexShader);
+        d->m_program->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShader);
 
         d->m_program->bindAttributeLocation("vertex", OpenGLRendererPrivate::PROGRAM_VERTEX_ATTRIBUTE);
         d->m_program->bindAttributeLocation("texCoord", OpenGLRendererPrivate::PROGRAM_TEXCOORD_ATTRIBUTE);
@@ -333,7 +339,8 @@ namespace AVQt {
                             if (d->m_renderQueue.first().first->pts >= d->m_updateTimestamp.load()) {
                                 break;
                             } else {
-                                qDebug("Discarding video frame at PTS: %ld < PTS: %lld", frame.first->pts, d->m_updateTimestamp.load());
+                                qDebug("Discarding video frame at PTS: %lld < PTS: %lld", static_cast<long long>(frame.first->pts),
+                                       d->m_updateTimestamp.load());
                                 av_frame_unref(frame.first);
                                 av_frame_free(&frame.first);
                             }
@@ -371,34 +378,89 @@ namespace AVQt {
                     }
 
                     if (firstFrame) {
+                        bool VTexActive = false, UTexActive = false;
+                        QSize YSize, USize, VSize;
+                        QOpenGLTexture::TextureFormat textureFormatY, textureFormatU, textureFormatV;
+                        QOpenGLTexture::PixelFormat pixelFormatY, pixelFormatU, pixelFormatV;
+                        QOpenGLTexture::PixelType pixelType;
+                        switch (static_cast<AVPixelFormat>(d->m_currentFrame->format)) {
+                            case AV_PIX_FMT_BGRA:
+                                YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height);
+                                textureFormatY = QOpenGLTexture::RGBA8_UNorm;
+                                pixelFormatY = QOpenGLTexture::BGRA;
+                                pixelType = QOpenGLTexture::UInt8;
+                                break;
+                            case AV_PIX_FMT_YUV420P:
+                                YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height);
+                                USize = VSize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2);
+                                textureFormatY = textureFormatU = textureFormatV = QOpenGLTexture::R8_UNorm;
+                                pixelFormatY = pixelFormatU = pixelFormatV = QOpenGLTexture::Red;
+                                pixelType = QOpenGLTexture::UInt8;
+                                UTexActive = VTexActive = true;
+                                break;
+                            case AV_PIX_FMT_YUV420P10:
+                                YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height);
+                                USize = VSize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2);
+                                textureFormatY = textureFormatU = textureFormatV = QOpenGLTexture::R16_UNorm;
+                                pixelFormatY = pixelFormatU = pixelFormatV = QOpenGLTexture::Red;
+                                pixelType = QOpenGLTexture::UInt16;
+                                UTexActive = VTexActive = true;
+                                break;
+                            case AV_PIX_FMT_NV12:
+                                YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height);
+                                USize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2);
+                                textureFormatY = QOpenGLTexture::R8_UNorm;
+                                textureFormatU = QOpenGLTexture::RG8_UNorm;
+                                pixelFormatY = QOpenGLTexture::Red;
+                                pixelFormatU = QOpenGLTexture::RG;
+                                pixelType = QOpenGLTexture::UInt8;
+                                UTexActive = true;
+                                break;
+                            case AV_PIX_FMT_P010:
+                                YSize = QSize(d->m_currentFrame->width, d->m_currentFrame->height);
+                                USize = QSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2);
+                                textureFormatY = QOpenGLTexture::R16_UNorm;
+                                textureFormatU = QOpenGLTexture::RG16_UNorm;
+                                pixelFormatY = QOpenGLTexture::Red;
+                                pixelFormatU = QOpenGLTexture::RG;
+                                pixelType = QOpenGLTexture::UInt16;
+                                UTexActive = true;
+                                break;
+                            default:
+                                qFatal("[AVQt::OpenGLRenderer] Unsupported pixel format");
+                                break;
+                        }
                         d->m_yTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
-                        d->m_uTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
-                        d->m_vTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
-                        d->m_yTexture->setSize(d->m_currentFrame->width, d->m_currentFrame->height);
-                        d->m_uTexture->setSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2);
-                        d->m_vTexture->setSize(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2);
-                        d->m_yTexture->setFormat(QOpenGLTexture::RGBA16_UNorm);
-                        d->m_uTexture->setFormat(QOpenGLTexture::RGBA16_UNorm);
-                        d->m_vTexture->setFormat(QOpenGLTexture::RGBA16_UNorm);
-                        d->m_yTexture->allocateStorage(QOpenGLTexture::Red, QOpenGLTexture::UInt16);
-                        d->m_uTexture->allocateStorage(QOpenGLTexture::RG, QOpenGLTexture::UInt16);
-                        d->m_vTexture->allocateStorage(QOpenGLTexture::Red, QOpenGLTexture::UInt16);
-//                            d->m_uTexture = new QOpenGLTexture(
-//                                    QImage(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2, QImage::Format_RGBX64));
-//                            d->m_vTexture = new QOpenGLTexture(
-//                                    QImage(d->m_currentFrame->width / 2, d->m_currentFrame->height / 2, QImage::Format_RGBX64));
-
+                        d->m_yTexture->setSize(YSize.width(), YSize.height());
+                        d->m_yTexture->setFormat(textureFormatY);
                         d->m_yTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
-                        d->m_uTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
-                        d->m_vTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
+                        d->m_yTexture->allocateStorage(pixelFormatY, pixelType);
+                        if (UTexActive) {
+                            d->m_uTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+                            d->m_uTexture->setSize(USize.width(), USize.height());
+                            d->m_uTexture->setFormat(textureFormatU);
+                            d->m_uTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
+                            d->m_uTexture->allocateStorage(pixelFormatU, pixelType);
+                        }
+                        if (VTexActive) {
+                            d->m_vTexture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+                            d->m_vTexture->setSize(VSize.width(), VSize.height());
+                            d->m_vTexture->setFormat(textureFormatV);
+                            d->m_vTexture->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);
+                            d->m_vTexture->allocateStorage(pixelFormatV, pixelType);
+                        }
                     }
 //                    qDebug("Frame duration: %ld ms", d->m_currentFrameTimeout);
                     if (differentPixFmt) {
                         d->m_program->bind();
                     }
                     d->m_yTexture->bind(0);
-                    d->m_uTexture->bind(1);
-                    d->m_vTexture->bind(2);
+                    if (d->m_uTexture) {
+                        d->m_uTexture->bind(1);
+                    }
+                    if (d->m_vTexture) {
+                        d->m_vTexture->bind(2);
+                    }
                     switch (d->m_currentFrame->format) {
                         case AV_PIX_FMT_BGRA:
                             d->m_yTexture->setData(QOpenGLTexture::PixelFormat::BGRA, QOpenGLTexture::UInt8,
@@ -461,12 +523,16 @@ namespace AVQt {
 //            }
 
         if (d->m_currentFrame) {
-            qDebug("Drawing frame with PTS: %ld", d->m_currentFrame->pts);
+            qDebug("Drawing frame with PTS: %lld", static_cast<long long>(d->m_currentFrame->pts));
             d->m_program->bind();
             if (!d->m_yTexture->isBound(0)) {
                 d->m_yTexture->bind(0);
-                d->m_uTexture->bind(1);
-                d->m_vTexture->bind(2);
+                if (d->m_uTexture) {
+                    d->m_uTexture->bind(1);
+                }
+                if (d->m_vTexture) {
+                    d->m_vTexture->bind(2);
+                }
             }
 
             d->m_vao.bind();
@@ -482,23 +548,27 @@ namespace AVQt {
             d->m_vbo.release();
             d->m_program->release();
             d->m_yTexture->release(0);
-            d->m_uTexture->release(1);
-            d->m_vTexture->release(2);
+            if (d->m_uTexture) {
+                d->m_uTexture->release(1);
+            }
+            if (d->m_vTexture) {
+                d->m_vTexture->release(2);
+            }
         }
 
-        int height = this->height();
+//        int height = this->height();
 
-        GLdouble model[4][4], proj[4][4];
-        GLint view[4];
-        glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
-        glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
-        glGetIntegerv(GL_VIEWPORT, &view[0]);
-        GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;
+//        GLdouble model[4][4], proj[4][4];
+//        GLint view[4];
+//        glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]);
+//        glGetDoublev(GL_PROJECTION_MATRIX, &proj[0][0]);
+//        glGetIntegerv(GL_VIEWPORT, &view[0]);
+//        GLdouble textPosX = 0, textPosY = 0, textPosZ = 0;
 
-        OpenGLRendererPrivate::project(-0.9, 0.8, 0.0, &model[0][0], &proj[0][0], &view[0],
-                                       &textPosX, &textPosY, &textPosZ);
+//        OpenGLRendererPrivate::project(-0.9, 0.8, 0.0, &model[0][0], &proj[0][0], &view[0],
+//                                       &textPosX, &textPosY, &textPosZ);
 
-        textPosY = height - textPosY; // y is inverted
+//        textPosY = height - textPosY; // y is inverted
 
         QPainter p(this);
 
@@ -510,11 +580,16 @@ namespace AVQt {
 
 //            qDebug() << "Current timestamp:" << d->m_position.toString("hh:mm:ss.zzz");
 
-        QString overlay(d->m_position.toString("hh:mm:ss") + "/" + d->m_duration.toString("hh:mm:ss"));
+        QTime position{0, 0, 0, 0};
+        if (d->m_currentFrame) {
+            position = OpenGLRendererPrivate::timeFromMillis(d->m_currentFrame->pts / 1000);
+        }
+
+        QString overlay(position.toString("hh:mm:ss.zzz") + "/" + d->m_duration.toString("hh:mm:ss.zzz"));
         QFontMetrics fm(roboto);
-        p.fillRect(fm.boundingRect(overlay).translated(static_cast<int>(textPosX), static_cast<int>(textPosY)).adjusted(-5, -5, 5, 5),
+        p.fillRect(fm.boundingRect(overlay).translated(static_cast<int>(20), static_cast<int>(20)).adjusted(-5, -5, 5, 5),
                    QColor(0xFF, 0xFF, 0xFF, 0x48));
-        p.drawText(fm.boundingRect(overlay).translated(static_cast<int>(textPosX), static_cast<int>(textPosY)), overlay);
+        p.drawText(fm.boundingRect(overlay).translated(static_cast<int>(20), static_cast<int>(20)), overlay);
         p.end();
         qDebug() << "Paused:" << (d->m_paused.load() ? "true" : "false");
         if (!d->m_paused.load()) {
@@ -528,14 +603,6 @@ namespace AVQt {
         }
     }
 
-    void OpenGLRenderer::triggerUpdate(qint64 timestamp) {
-        Q_UNUSED(timestamp)
-        Q_D(AVQt::OpenGLRenderer);
-
-//        d->m_updateRequired.store(true);
-//        d->m_updateTimestamp.store(timestamp);
-    }
-
     RenderClock *OpenGLRenderer::getClock() {
         Q_D(AVQt::OpenGLRenderer);
         return d->m_clock;
@@ -552,7 +619,7 @@ namespace AVQt {
         transformPoint(out, model, in);
         transformPoint(in, proj, out);
 
-        if (in[3] == 0.0)
+        if (in[3] < 0.0001)
             return GL_FALSE;
 
         in[0] /= in[3];
@@ -575,4 +642,11 @@ namespace AVQt {
 #undef M
     }
 
+    QTime OpenGLRendererPrivate::timeFromMillis(int64_t ts) {
+        int ms = static_cast<int>(ts % 1000);
+        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);
+    }
 }
\ No newline at end of file
diff --git a/AVQt/output/OpenGLRenderer.h b/AVQt/output/OpenGLRenderer.h
index 96216b70d6e902930edc78c59b1ddee910841f8e..a872577af3fb93025e832149b43dc68b52218841 100644
--- a/AVQt/output/OpenGLRenderer.h
+++ b/AVQt/output/OpenGLRenderer.h
@@ -29,6 +29,12 @@ namespace AVQt {
     public:
         explicit OpenGLRenderer(QWindow *parent = nullptr);
 
+        OpenGLRenderer(OpenGLRenderer &&other) noexcept;
+
+        OpenGLRenderer(const OpenGLRenderer &) = delete;
+
+        void operator=(const OpenGLRenderer &) = delete;
+
         ~OpenGLRenderer() noexcept;
 
         bool isPaused() Q_DECL_OVERRIDE;
@@ -88,11 +94,6 @@ namespace AVQt {
          */
         Q_INVOKABLE void onFrame(IFrameSource *source, AVFrame *frame, int64_t frameDuration) Q_DECL_OVERRIDE;
 
-        /*!
-         * \private
-         */
-        void triggerUpdate(qint64 timestamp);
-
     signals:
 
         void started() Q_DECL_OVERRIDE;
@@ -115,4 +116,4 @@ namespace AVQt {
 }
 
 
-#endif //LIBAVQT_OPENGLRENDERER_H
+#endif //LIBAVQT_OPENGLRENDERER_H
\ No newline at end of file
diff --git a/AVQt/output/RenderClock.cpp b/AVQt/output/RenderClock.cpp
index 723b01d6e8a30b26537ea60df2ca9ce2fe8158fe..2d19d3edea4d93af669e2a22dff2158f7e8a7e06 100644
--- a/AVQt/output/RenderClock.cpp
+++ b/AVQt/output/RenderClock.cpp
@@ -24,6 +24,11 @@ namespace AVQt {
         connect(d->m_timer, &QTimer::timeout, this, &RenderClock::timerTimeout);
     }
 
+    RenderClock::RenderClock(RenderClock &&other) noexcept: d_ptr(other.d_ptr) {
+        other.d_ptr = nullptr;
+        d_ptr->q_ptr = this;
+    }
+
     RenderClock::~RenderClock() {
         Q_D(AVQt::RenderClock);
 
@@ -78,13 +83,13 @@ namespace AVQt {
         }
     }
 
-    void RenderClock::setInterval(int64_t interval) {
+    void RenderClock::setInterval(int interval) {
         Q_D(AVQt::RenderClock);
 
-        if (interval > 0 && interval != d->m_interval.load()) {
+        if (interval > 0 && interval != d->m_interval) {
             d->m_interval.store(interval);
 
-            d->m_timer->setInterval(d->m_interval.load());
+            d->m_timer->setInterval(d->m_interval);
             d->m_timer->stop();
             d->m_timer->start();
 
diff --git a/AVQt/output/RenderClock.h b/AVQt/output/RenderClock.h
index 6e1ee39d1ddda44555ea7db5519717fc26aa0c7f..3cb95b65c0d25fa3348e511ad2a1e134365b9360 100644
--- a/AVQt/output/RenderClock.h
+++ b/AVQt/output/RenderClock.h
@@ -18,9 +18,15 @@ namespace AVQt {
     public:
         explicit RenderClock(QObject *parent = nullptr);
 
+        RenderClock(RenderClock &&other) noexcept;
+
+        RenderClock(const RenderClock &) = delete;
+
+        void operator=(const RenderClock &) = delete;
+
         ~RenderClock();
 
-        void setInterval(int64_t interval);
+        void setInterval(int interval);
 
         int64_t getInterval();
 
@@ -55,6 +61,7 @@ namespace AVQt {
         RenderClockPrivate *d_ptr;
 
     private slots:
+
         void timerTimeout();
 
     };
diff --git a/AVQt/output/private/Muxer_p.h b/AVQt/output/private/Muxer_p.h
new file mode 100644
index 0000000000000000000000000000000000000000..cbe1523645755427e74bf816403d36950e46039d
--- /dev/null
+++ b/AVQt/output/private/Muxer_p.h
@@ -0,0 +1,53 @@
+//
+// Created by silas on 5/24/21.
+//
+
+#include "../Muxer.h"
+
+extern "C" {
+#include <libavformat/avformat.h>
+#include <libavcodec/avcodec.h>
+}
+
+#include <QtCore>
+
+#ifndef LIBAVQT_MUXER_P_H
+#define LIBAVQT_MUXER_P_H
+
+namespace AVQt {
+    class MuxerPrivate {
+    public:
+        MuxerPrivate(const MuxerPrivate &) = delete;
+
+        void operator=(const MuxerPrivate &) = delete;
+
+    private:
+        explicit MuxerPrivate(Muxer *q) : q_ptr(q) {};
+
+        Muxer *q_ptr;
+
+        QIODevice *m_outputDevice{nullptr};
+
+        QMutex m_initMutex{};
+        static constexpr size_t IOBUF_SIZE{4 * 1024};  // 4 KB
+        uint8_t *m_pIOBuffer{nullptr};
+        AVIOContext *m_pIOContext{nullptr};
+        AVFormatContext *m_pFormatContext{nullptr};
+        QMap<IPacketSource *, QMap<AVStream *, AVRational>> m_sourceStreamMap{};
+
+        QMutex m_inputQueueMutex{};
+        QQueue<QPair<AVPacket *, AVStream *>> m_inputQueue{};
+
+        std::atomic_bool m_running{false};
+        std::atomic_bool m_paused{false};
+        std::atomic_bool m_headerWritten{false};
+
+        friend class Muxer;
+
+        static int64_t seekIO(void *opaque, int64_t offset, int whence);
+
+        static int writeToIO(void *opaque, uint8_t *buf, int buf_size);
+    };
+}
+
+#endif //LIBAVQT_MUXER_P_H
diff --git a/AVQt/output/private/OpenALAudioOutput_p.h b/AVQt/output/private/OpenALAudioOutput_p.h
index 04b678b8fdd56cff826e5571793fa7cbf9480a8b..5ebc8169d9ee4f149f6063b12e10ec986828cc4e 100644
--- a/AVQt/output/private/OpenALAudioOutput_p.h
+++ b/AVQt/output/private/OpenALAudioOutput_p.h
@@ -22,46 +22,52 @@ namespace AVQt {
     class OpenALAudioOutput;
 
     class OpenALAudioOutputPrivate {
+    public:
+        OpenALAudioOutputPrivate(const OpenALAudioOutputPrivate &) = delete;
+
+        void operator=(const OpenALAudioOutputPrivate &) = delete;
+
+    private:
         explicit OpenALAudioOutputPrivate(OpenALAudioOutput *q) : q_ptr(q) {};
 
-        [[maybe_unused]] OpenALAudioOutput *q_ptr;
+        OpenALAudioOutput *q_ptr;
 
-        QMutex m_inputQueueMutex;
-        QQueue<QPair<AVFrame *, int64_t>> m_inputQueue;
+        QMutex m_inputQueueMutex{};
+        QQueue<QPair<AVFrame *, int64_t>> m_inputQueue{};
 //        AVFrame *m_partialFrame {nullptr};
 //        size_t m_partialFrameOffset {0};
-        QMutex m_outputQueueMutex;
-        QQueue<QPair<AVFrame *, int64_t>> m_outputQueue;
-        std::atomic_bool m_outputSliceDurationChanged  {false};
-        int64_t m_duration = 0, m_clockInterval = 0, m_sampleRate {0};
-
-        std::atomic<RenderClock *> m_clock {nullptr};
-
-        QMutex m_swrContextMutex;
-        SwrContext *m_pSwrContext {nullptr};
-
-        int m_ALBufferCount = 5;
-        std::atomic_size_t m_playingBuffers {0};
-        QVector<ALuint> m_ALBuffers;
-        QRecursiveMutex m_ALBufferQueueMutex;
-        QQueue<ALuint> m_ALBufferQueue;
-        QRecursiveMutex m_ALBufferSampleMapMutex;
-        QMap<ALuint, int64_t> m_ALBufferSampleMap;
-        int64_t m_queuedSamples {0};
-        ALCdevice *m_ALCDevice {nullptr};
-        ALCcontext *m_ALCContext {nullptr};
-
-        ALuint m_ALSource {0};
+        QMutex m_outputQueueMutex{};
+        QQueue<QPair<AVFrame *, int64_t>> m_outputQueue{};
+        std::atomic_bool m_outputSliceDurationChanged{false};
+        int64_t m_duration{0}, m_clockInterval{0}, m_sampleRate{0};
+
+        std::atomic<RenderClock *> m_clock{nullptr};
+
+        QMutex m_swrContextMutex{};
+        SwrContext *m_pSwrContext{nullptr};
+
+        int m_ALBufferCount{5};
+        std::atomic_int64_t m_playingBuffers{0};
+        QVector<ALuint> m_ALBuffers{};
+        QMutex m_ALBufferQueueMutex{};
+        QQueue<ALuint> m_ALBufferQueue{};
+        QMutex m_ALBufferSampleMapMutex{};
+        QMap<ALuint, int64_t> m_ALBufferSampleMap{};
+        int64_t m_queuedSamples{0};
+        ALCdevice *m_ALCDevice{nullptr};
+        ALCcontext *m_ALCContext{nullptr};
+
+        ALuint m_ALSource{0};
         // Threading stuff
-        std::atomic_bool m_running  {false};
-        std::atomic_bool m_paused  {false};
+        std::atomic_bool m_running{false};
+        std::atomic_bool m_paused{false};
 
-        std::atomic_bool m_wasPaused  {false};
-        std::atomic_int64_t m_lastUpdate {0};
-        std::atomic_size_t m_audioFrame {0};
+        std::atomic_bool m_wasPaused{false};
+        std::atomic_int64_t m_lastUpdate{0};
+        std::atomic_size_t m_audioFrame{0};
 
         friend class OpenALAudioOutput;
     };
 }
 
-#endif //LIBAVQT_OPENALAUDIOOUTPUT_P_H
\ No newline at end of file
+#endif //LIBAVQT_OPENALAUDIOOUTPUT_P_H
diff --git a/AVQt/output/private/OpenGLRenderer_p.h b/AVQt/output/private/OpenGLRenderer_p.h
index c5a00e6385b256621adcef570ba54c42575366ab..90a33dbb94c47b4376c6b5261452abcb876190b9 100644
--- a/AVQt/output/private/OpenGLRenderer_p.h
+++ b/AVQt/output/private/OpenGLRenderer_p.h
@@ -23,7 +23,13 @@ namespace AVQt {
      * \private
      * \internal
      */
-    class OpenGLRendererPrivate {
+    class OpenGLRendererPrivate : public QObject {
+    public:
+        OpenGLRendererPrivate(const OpenGLRendererPrivate &) = delete;
+
+        void operator=(const OpenGLRendererPrivate &) = delete;
+
+    private:
         explicit OpenGLRendererPrivate(OpenGLRenderer *q) : q_ptr(q) {};
 
         static GLint
@@ -32,30 +38,31 @@ namespace AVQt {
 
         static inline void transformPoint(GLdouble out[4], const GLdouble m[16], const GLdouble in[4]);
 
+        static QTime timeFromMillis(int64_t ts);
 
-        OpenGLRenderer *q_ptr {nullptr};
+        OpenGLRenderer *q_ptr{nullptr};
 
-        QMutex m_renderQueueMutex;
-        QQueue<QPair<AVFrame *, int64_t>> m_renderQueue;
+        QMutex m_renderQueueMutex{};
+        QQueue<QPair<AVFrame *, int64_t>> m_renderQueue{};
 
-        RenderClock *m_clock {nullptr};
-        int64_t m_currentFrameTimeout = 1;
-        QTime m_duration;
-        QTime m_position;
-        std::atomic_bool m_updateRequired {true}, m_paused {false}, m_running {false}, m_firstFrame {true};
-        std::atomic<qint64> m_updateTimestamp {0};
-        std::chrono::time_point<std::chrono::high_resolution_clock> m_lastFrame;
+        RenderClock *m_clock{nullptr};
+        int64_t m_currentFrameTimeout{1};
+        QTime m_duration{};
+        QTime m_position{};
+        std::atomic_bool m_updateRequired{true}, m_paused{false}, m_running{false}, m_firstFrame{true};
+        std::atomic<qint64> m_updateTimestamp{0};
+        std::chrono::time_point<std::chrono::high_resolution_clock> m_lastFrame{};
 
-        QMutex m_currentFrameMutex;
-        AVFrame *m_currentFrame {nullptr};
+        QMutex m_currentFrameMutex{};
+        AVFrame *m_currentFrame{nullptr};
 
-        QOpenGLVertexArrayObject m_vao;
-        QOpenGLBuffer m_vbo, m_ibo;
-        QOpenGLShaderProgram *m_program {nullptr};
-        QOpenGLTexture *m_yTexture {nullptr}, *m_uTexture {nullptr}, *m_vTexture {nullptr};
+        QOpenGLVertexArrayObject m_vao{};
+        QOpenGLBuffer m_vbo{}, m_ibo{};
+        QOpenGLShaderProgram *m_program{nullptr};
+        QOpenGLTexture *m_yTexture{nullptr}, *m_uTexture{nullptr}, *m_vTexture{nullptr};
 
-        static constexpr uint PROGRAM_VERTEX_ATTRIBUTE {0};
-        static constexpr uint PROGRAM_TEXCOORD_ATTRIBUTE {1};
+        static constexpr uint PROGRAM_VERTEX_ATTRIBUTE{0};
+        static constexpr uint PROGRAM_TEXCOORD_ATTRIBUTE{1};
 
         friend class OpenGLRenderer;
     };
diff --git a/AVQt/output/private/RenderClock_p.h b/AVQt/output/private/RenderClock_p.h
index de66bdc9fcb5f6225787723e8556e4dea51de646..71ff96a97a5d9006b64bd2b44718e3b50b7158ed 100644
--- a/AVQt/output/private/RenderClock_p.h
+++ b/AVQt/output/private/RenderClock_p.h
@@ -9,15 +9,21 @@
 
 namespace AVQt {
     class RenderClockPrivate {
+    public:
+        RenderClockPrivate(const RenderClockPrivate &) = delete;
+
+        void operator=(const RenderClockPrivate &) = delete;
+
+    private:
         explicit RenderClockPrivate(RenderClock *q) : q_ptr(q) {};
         RenderClock *q_ptr;
 
-        QTimer *m_timer {nullptr};
-        std::atomic_int64_t m_interval {1};
-        std::atomic_bool m_active  {false};
-        std::atomic_bool m_paused  {false};
-        QElapsedTimer *m_elapsedTime {nullptr};
-        qint64 m_lastPauseTimestamp {0};
+        QTimer *m_timer{nullptr};
+        std::atomic_int m_interval{1};
+        std::atomic_bool m_active{false};
+        std::atomic_bool m_paused{false};
+        QElapsedTimer *m_elapsedTime{nullptr};
+        qint64 m_lastPauseTimestamp{0};
 
         friend class RenderClock;
     };
diff --git a/AVQt/resources.qrc b/AVQt/resources.qrc
index d76d75d131fa716b2b0cb26a08398b6267ab9349..dba686671f093444f81869b7722fa628f4a36b7c 100644
--- a/AVQt/resources.qrc
+++ b/AVQt/resources.qrc
@@ -2,11 +2,11 @@
     <qresource prefix="/shaders">
         <file>texture.vsh</file>
         <file>texture.fsh</file>
-        <file>textureNV12.fsh</file>
-        <file>textureP010.fsh</file>
+        <file>textureES.vsh</file>
+        <file>textureES.fsh</file>
     </qresource>
     <qresource prefix="/images">
         <file>frame.bmp</file>
         <file>frame.yuv</file>
     </qresource>
-</RCC>
+</RCC>
\ No newline at end of file
diff --git a/AVQt/texture.fsh b/AVQt/texture.fsh
index 43c3f19edf4087503aa078f586f2d1090a80d0f2..6a1fdcd7757aacaa7dd1c9f09b9bd154f8c8aa2b 100644
--- a/AVQt/texture.fsh
+++ b/AVQt/texture.fsh
@@ -1,7 +1,11 @@
-#version 330 core
-
 // fragment shader
 
+#ifdef GL_ES
+// Set default precision to medium
+precision mediump int;
+precision mediump float;
+#endif
+
 in vec4 fragColor;// input: interpolated color as rgba-value
 in vec2 texCoord;// input: texture coordinate (xy-coordinates)
 out vec4 finalColor;// output: final color value as rgba-value
@@ -15,37 +19,37 @@ void main() {
     vec2 newTexCoord = vec2(1.0f - texCoord.x, texCoord.y);
 
     if (inputFormat == 0) { // RGB(A)
-        vec3 rgb = texture(textureY, newTexCoord).rgb;
+        vec3 rgb = texture2D(textureY, newTexCoord).rgb;
         finalColor = vec4(rgb, 1.0);
     } else if (inputFormat == 1) { // YUV 4:2:0 with Y plane interleaved UV plane (e.g. NV12 or P010)
         vec3 yuv;
 
         // We had put the Y values of each pixel to the R component by using GL_RED
-        yuv.x = texture(textureY, newTexCoord.st).r - 0.0625;
+        yuv.x = texture2D(textureY, newTexCoord.st).r - 0.0625;
         // We had put the U and V values of each pixel to the R and G components of the
         // texture respectively using GL_RG. Since U, V bytes are interspread
         // in the texture, this is probably the fastest way to use them in the shader
-        yuv.y = texture(textureU, newTexCoord.st).r - 0.5;
-        yuv.z = texture(textureU, newTexCoord.st).g - 0.5;
+        yuv.y = texture2D(textureU, newTexCoord.st).r - 0.5;
+        yuv.z = texture2D(textureU, newTexCoord.st).g - 0.5;
         // The numbers are just YUV to RGB conversion constants
-        vec3 rgb = mat3(1, 1, 1,
-        0, -0.39465, 2.03211,
-        1.13983, -0.58060, 0) * yuv;
+        vec3 rgb = mat3(1.0, 1.0, 1.0,
+        0.0, -0.39465, 2.03211,
+        1.13983, -0.58060, 0.0) * yuv;
         finalColor = vec4(rgb, 1.0f);
     } else if (inputFormat == 2) { // YUV420P
         vec3 yuv;
 
         // We had put the Y values of each pixel to the R component by using GL_RED
-        yuv.x = texture(textureY, newTexCoord.st).r - 0.0625;
+        yuv.x = texture2D(textureY, newTexCoord.st).r - 0.0625;
         // We had put the U and V values of each pixel to the R and G components of the
         // texture respectively using GL_RG. Since U, V bytes are interspread
         // in the texture, this is probably the fastest way to use them in the shader
-        yuv.y = texture(textureU, newTexCoord.st).r - 0.5;
-        yuv.z = texture(textureV, newTexCoord.st).r - 0.5;
+        yuv.y = texture2D(textureU, newTexCoord.st).r - 0.5;
+        yuv.z = texture2D(textureV, newTexCoord.st).r - 0.5;
         // The numbers are just YUV to RGB conversion constants
-        vec3 rgb = mat3(1, 1, 1,
-        0, -0.39465, 2.03211,
-        1.13983, -0.58060, 0) * yuv;
+        vec3 rgb = mat3(1.0, 1.0, 1.0,
+        0.0, -0.39465, 2.03211,
+        1.13983, -0.58060, 0.0) * yuv;
         finalColor = vec4(rgb, 1.0f);
     } else if (inputFormat == 3) { // YUV420P10
         vec3 yuv;
@@ -53,13 +57,13 @@ void main() {
         // Multiply every value with 2^6 = 64, because the values are stored in the lower 10 bits,
         // but have to be in the upper 10 bits for correct calculation
         // We had put the Y values of each pixel to the R component of every texture by using GL_RED
-        yuv.x = texture(textureY, newTexCoord.st).r * 64 - 0.0625;
-        yuv.y = texture(textureU, newTexCoord.st).r * 64 - 0.5;
-        yuv.z = texture(textureV, newTexCoord.st).r * 64 - 0.5;
+        yuv.x = texture2D(textureY, newTexCoord.st).r * 64.0 - 0.0625;
+        yuv.y = texture2D(textureU, newTexCoord.st).r * 64.0 - 0.5;
+        yuv.z = texture2D(textureV, newTexCoord.st).r * 64.0 - 0.5;
         // The numbers are just YUV to RGB conversion constants
-        vec3 rgb = mat3(1, 1, 1,
-        0, -0.39465, 2.03211,
-        1.13983, -0.58060, 0) * yuv;
+        vec3 rgb = mat3(1.0, 1.0, 1.0,
+        0.0, -0.39465, 2.03211,
+        1.13983, -0.58060, 0.0) * yuv;
 
         finalColor = vec4(rgb, 1.0);
     }
diff --git a/AVQt/texture.vsh b/AVQt/texture.vsh
index 1bd6500697ee58eab971e4852131da04cb1739ee..83caa39e01b930166a2641b00198de9457550ed2 100644
--- a/AVQt/texture.vsh
+++ b/AVQt/texture.vsh
@@ -1,15 +1,18 @@
-#version 330
-
-// GLSL version 3.3
 // vertex shader
 
-layout(location = 0) in vec3 position;   // input:  attribute with index '0' with 3 elements per vertex
+#ifdef GL_ES
+// Set default precision to medium
+precision mediump int;
+precision mediump float;
+#endif
+
+layout(location = 0) in vec3 position;// input:  attribute with index '0' with 3 elements per vertex
 //layout(location = 1) in vec3 color;      // input:  attribute with index '1' with 3 elements (=rgb) per vertex
-layout(location = 1) in vec2 texcoords;  // input:  attribute with index '2' with 2 elements per vertex
+layout(location = 1) in vec2 texcoords;// input:  attribute with index '2' with 2 elements per vertex
 //layout(location = 2) in float texnr;     // input:  attribute with index '3' with 1 float per vertex
 
-out vec4 fragColor;                      // output: computed fragmentation color
-out vec2 texCoord;                       // output: computed texture coordinates
+out vec4 fragColor;// output: computed fragmentation color
+out vec2 texCoord;// output: computed texture coordinates
 //flat out float texID;                    // output: texture ID - mind the 'flat' attribute!
 
 void main() {
@@ -18,4 +21,4 @@ void main() {
   fragColor = vec4(0.5, 1.0, 0.3, 1.0);
   texCoord = texcoords;
 //  texID = texnr;
-}
+}
\ No newline at end of file
diff --git a/AVQt/textureES.fsh b/AVQt/textureES.fsh
new file mode 100644
index 0000000000000000000000000000000000000000..83a502131e21411c2bafd935c6e0b2dbc2035912
--- /dev/null
+++ b/AVQt/textureES.fsh
@@ -0,0 +1,62 @@
+#version 300 es
+
+// fragment shader
+
+in mediump vec4 fragColor;// input: interpolated color as rgba-value
+in mediump vec2 texCoord;// input: texture coordinate (xy-coordinates)
+out mediump vec4 finalColor;// output: final color value as rgba-value
+
+uniform sampler2D textureY;
+uniform sampler2D textureU;
+uniform sampler2D textureV;
+uniform int inputFormat;
+
+void main() {
+    mediump vec2 newTexCoord = vec2(1.0f - texCoord.x, texCoord.y);
+
+    if (inputFormat == 0) { // RGB(A)
+        mediump vec3 rgb = texture(textureY, newTexCoord).rgb;
+        finalColor = vec4(rgb, 1.0);
+    } else if (inputFormat == 1) { // YUV 4:2:0 with Y plane and interleaved UV plane (e.g. NV12 or P010)
+        mediump vec3 yuv;
+
+        // We had put the Y values of each pixel to the R component by using GL_RED
+        yuv.x = texture(textureY, newTexCoord.st).r - 0.0625;
+        // We had put the U and V values of each pixel to the R and G components of the
+        // texture respectively using GL_RG. Since U, V bytes are interspread
+        // in the texture, this is probably the fastest way to use them in the shader
+        yuv.y = texture(textureU, newTexCoord.st).r - 0.5;
+        yuv.z = texture(textureU, newTexCoord.st).g - 0.5;
+        // The numbers are just YUV to RGB conversion constants
+        mediump vec3 rgb = mat3(1.0, 1.0, 1.0,
+        0.0, -0.39465, 2.03211,
+        1.13983, -0.58060, 0.0) * yuv;
+        finalColor = vec4(rgb, 1.0f);
+    } else if (inputFormat == 2) { // YUV420P
+        mediump vec3 yuv;
+        yuv.x = texture(textureY, newTexCoord.st).r * 4.0;
+        //        yuv.x = texture(textureY, newTexCoord.st).r - 0.0625;
+        yuv.y = texture(textureU, newTexCoord.st).r - 0.5;
+        yuv.z = texture(textureV, newTexCoord.st).r - 0.5;
+        // The numbers are just YUV to RGB conversion constants
+        //        mediump vec3 rgb = mat3(1.0, 1.0, 1.0,
+        //        0.0, -0.39465, 2.03211,
+        //        1.13983, -0.58060, 0.0) * yuv;
+        finalColor = vec4(yuv.x, yuv.x, yuv.x, 1.0f);
+    } else if (inputFormat == 3) { // YUV420P10
+        mediump vec3 yuv;
+
+        // Multiply every value with 2^6 = 64, because the values are stored in the lower 10 bits,
+        // but have to be in the upper 10 bits for correct calculation
+        // We had put the Y values of each pixel to the R component of every texture by using GL_RED
+        yuv.x = texture(textureY, newTexCoord.st).r * 64.0 - 0.0625;
+        yuv.y = texture(textureU, newTexCoord.st).r * 64.0 - 0.5;
+        yuv.z = texture(textureV, newTexCoord.st).r * 64.0 - 0.5;
+        // The numbers are just YUV to RGB conversion constants
+        mediump vec3 rgb = mat3(1.0, 1.0, 1.0,
+        0.0, -0.39465, 2.03211,
+        1.13983, -0.58060, 0.0) * yuv;
+
+        finalColor = vec4(rgb, 1.0);
+    }
+}
\ No newline at end of file
diff --git a/AVQt/textureES.vsh b/AVQt/textureES.vsh
new file mode 100644
index 0000000000000000000000000000000000000000..936f2a993a50d8536251fd4da89886db19c180a3
--- /dev/null
+++ b/AVQt/textureES.vsh
@@ -0,0 +1,16 @@
+#version 300 es
+
+// GLSL version 3.3
+// vertex shader
+
+layout(location = 0) in mediump vec3 position;// input:  attribute with index '0' with 3 elements per vertex
+layout(location = 1) in mediump vec2 texcoords;// input:  attribute with index '2' with 2 elements per vertex
+
+out mediump vec4 fragColor;// output: computed fragmentation color
+out mediump vec2 texCoord;// output: computed texture coordinates
+
+void main() {
+    gl_Position = vec4(position, 1.0);
+    fragColor = vec4(0.5, 1.0, 0.3, 1.0);
+    texCoord = texcoords;
+}
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e3ff5081ca97ee4dba87b198e3c9fca5db601de..9a4414e8199c6c64be30ee0a20db1450da0d4bd2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.17)
+cmake_minimum_required(VERSION 3.16)
 project(LibAVQt)
 
 set(CMAKE_CXX_STANDARD 20)
@@ -9,4 +9,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.pro.user b/Player.pro.user
index be6e13f22ce1231dee0e3945ef97de4c1140f736..f83227fa715eaf8098ae427188c8cc4f03cc6810 100644
--- a/Player.pro.user
+++ b/Player.pro.user
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE QtCreatorProject>
-<!-- Written by QtCreator 4.14.1, 2021-05-08T11:26:40. -->
+<!-- Written by QtCreator 4.14.1, 2021-05-28T17:40:00. -->
 <qtcreator>
  <data>
   <variable>EnvironmentId</variable>
@@ -8,7 +8,7 @@
  </data>
  <data>
   <variable>ProjectExplorer.Project.ActiveTarget</variable>
-  <value type="int">0</value>
+  <value type="int">1</value>
  </data>
  <data>
   <variable>ProjectExplorer.Project.EditorSettings</variable>
@@ -109,8 +109,8 @@
       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
     </valuemap>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
@@ -120,8 +120,8 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
@@ -151,8 +151,8 @@
       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
     </valuemap>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
@@ -162,8 +162,8 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
@@ -193,8 +193,8 @@
       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
     </valuemap>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
@@ -204,8 +204,8 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
@@ -223,8 +223,8 @@
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
@@ -311,15 +311,16 @@
   <variable>ProjectExplorer.Project.Target.1</variable>
   <valuemap type="QVariantMap">
    <value type="QString" key="DeviceType">Desktop</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">MinGW x86_64</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">MinGW x86_64</value>
-   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{d1a101cb-333a-40d5-afd0-8c544cd86c51}</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
+   <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{ff2c954c-33d9-44cd-91b8-591dd542e6e9}</value>
    <value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
    <value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Debug</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Debug</value>
+    <value type="int" key="EnableQmlDebugging">0</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-Desktop-Debug</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-Desktop-Debug</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -333,8 +334,8 @@
       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
     </valuemap>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
@@ -344,8 +345,8 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
@@ -355,12 +356,12 @@
     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
     <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
     <value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
-    <value type="int" key="QtQuickCompiler">0</value>
+    <value type="int" key="RunSystemFunction">0</value>
    </valuemap>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
     <value type="int" key="EnableQmlDebugging">0</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Release</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-Desktop-Release</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-Desktop-Release</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -374,8 +375,8 @@
       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
     </valuemap>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
@@ -385,8 +386,8 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
@@ -401,8 +402,8 @@
    </valuemap>
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
     <value type="int" key="EnableQmlDebugging">0</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Profile</value>
-    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-MinGW_x86_64-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/silas/CLionProjects/build-Player-Desktop-Profile</value>
+    <value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory.shadowDir">/home/silas/CLionProjects/build-Player-Desktop-Profile</value>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
       <value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
@@ -416,8 +417,8 @@
       <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Erstellen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Erstellen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Build</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
     </valuemap>
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
@@ -427,8 +428,8 @@
       <value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
      </valuemap>
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Bereinigen</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Bereinigen</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Clean</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
@@ -446,8 +447,8 @@
    <valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
     <valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
      <value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deployment</value>
-     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deployment</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
+     <value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
      <value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
     </valuemap>
     <value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
@@ -524,7 +525,7 @@
     <value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
     <value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
     <value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
-    <value type="bool" key="RunConfiguration.UseTerminal">true</value>
+    <value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/silas/CLionProjects/build-Player-Desktop-Debug/Player</value>
    </valuemap>
    <value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
   </valuemap>
diff --git a/Player/CMakeLists.txt b/Player/CMakeLists.txt
index 684765b0169160633af05a4c1670e2cfd912d249..7b5e0e2d7235b717ebc42b1edd318b4aa02de322 100644
--- a/Player/CMakeLists.txt
+++ b/Player/CMakeLists.txt
@@ -1,19 +1,22 @@
 #SET(CMAKE_INSTALL_RPATH "$ORIGIN/../AVQt")
 #SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH on)
 
-find_package(Qt6 COMPONENTS Core Gui Widgets OpenGL OpenGLWidgets Concurrent)
-#find_package(Qt5 COMPONENTS Core Gui Widgets OpenGL Concurrent)
+#find_package(Qt6 COMPONENTS Core Gui Widgets OpenGL OpenGLWidgets Concurrent)
+
+set(CMAKE_CXX_STANDARD 20)
+
+find_package(Qt5 COMPONENTS Core Gui Widgets OpenGL Concurrent)
 find_package(OpenAL)
 
 #add_compile_options(-O4)
 
 add_executable(Player main.cpp)
 
-target_link_libraries(Player Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGLWidgets Qt6::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt)
-#target_link_libraries(Player Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt)
+#target_link_libraries(Player Qt6::Core Qt6::Gui Qt6::Concurrent Qt6::Widgets Qt6::OpenGLWidgets Qt6::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt)
+target_link_libraries(Player Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Widgets Qt5::OpenGL avformat avfilter avutil avcodec avdevice swscale swresample AVQt)
 
 if (LINUX)
     target_link_libraries(Player openal GL)
 elseif (WIN32)
     target_link_libraries(Player OpenAL32 opengl32)
-endif ()
\ No newline at end of file
+endif ()
diff --git a/Player/main.cpp b/Player/main.cpp
index 5d6937800afc33a297a0c6beef2e75ceb0a0cc18..7a4cef9d521e602b0ce52c88599296c90e85133a 100644
--- a/Player/main.cpp
+++ b/Player/main.cpp
@@ -4,6 +4,7 @@
 #include <QFileDialog>
 #include <csignal>
 #include <iostream>
+#include <qglobal.h>
 
 constexpr auto LOGFILE_LOCATION = "libAVQt.log";
 
@@ -28,7 +29,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
     auto now = QDateTime::currentDateTime();
 
     os << now.toString(Qt::ISODateWithMs) << ": ";
-    os << qPrintable(qFormatLogMessage(type, context, message)) << Qt::endl;
+    os << qPrintable(qFormatLogMessage(type, context, message)) << "\n";
 
     std::cerr << output.toStdString();
 
@@ -59,15 +60,15 @@ int main(int argc, char *argv[]) {
     inputFile->open(QIODevice::ReadOnly);
 
     AVQt::Demuxer demuxer(inputFile);
-    AVQt::AudioDecoder decoder;
-    AVQt::OpenALAudioOutput output;
+//    AVQt::AudioDecoder decoder;
+//    AVQt::OpenALAudioOutput output;
 
-    demuxer.registerCallback(&decoder, AVQt::IPacketSource::CB_AUDIO);
-    decoder.registerCallback(&output);
+//    demuxer.registerCallback(&decoder, AVQt::IPacketSource::CB_AUDIO);
+//    decoder.registerCallback(&output);
 
     AVQt::IDecoder *videoDecoder;
 #ifdef Q_OS_LINUX
-    videoDecoder = new AVQt::DecoderQSV;
+    videoDecoder = new AVQt::DecoderVAAPI;
 #elif defined(Q_OS_WINDOWS)
     videoDecoder = new AVQt::DecoderDXVA2();
 #else
@@ -79,6 +80,12 @@ int main(int argc, char *argv[]) {
 
     demuxer.registerCallback(videoDecoder, AVQt::IPacketSource::CB_VIDEO);
 //    videoDecoder->registerCallback(encoder);
+
+//    QFile outputFile("output.ts");
+//    outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate);
+//    AVQt::Muxer muxer(&outputFile);
+
+//    encoder->registerCallback(&muxer, AVQt::IPacketSource::CB_VIDEO);
     videoDecoder->registerCallback(&renderer);
 
     renderer.setMinimumSize(QSize(360, 240));
@@ -92,7 +99,7 @@ int main(int argc, char *argv[]) {
 
     demuxer.init();
 
-    output.syncToOutput(&renderer);
+//    output.syncToOutput(&renderer);
 
     demuxer.start();