diff --git a/playback/player/qt/extension/extension.pro b/playback/player/qt/extension/extension.pro
new file mode 100644
index 0000000000..58c65b0ea4
--- /dev/null
+++ b/playback/player/qt/extension/extension.pro
@@ -0,0 +1,39 @@
+TEMPLATE = lib
+CONFIG += qt plugin
+QT += qml quick
+CONFIG += c++11
+DEFINES += GST_USE_UNSTABLE_API
+TARGET = qmlplayerextension
+
+# Additional import path used to resolve QML modules in Qt Creator's code model
+# QML_IMPORT_PATH =
+
+HEADERS += qplayerextension.h \
+    qgstplayer.h \
+    player.h \
+    quickrenderer.h \
+    imagesample.h
+
+SOURCES += qplayerextension.cpp \
+    qgstplayer.cpp \
+    player.cpp \
+    quickrenderer.cpp \
+    imagesample.cpp
+
+unix:!macx {
+QT_CONFIG -= no-pkg-config
+CONFIG += link_pkgconfig
+PKGCONFIG = \
+    gstreamer-1.0 \
+    gstreamer-player-1.0 \
+    gstreamer-tag-1.0
+}
+
+macx {
+    QMAKE_MAC_SDK = macosx10.9
+    INCLUDEPATH += /Library/Frameworks/GStreamer.framework/Headers
+
+    LIBS += \
+        -framework AppKit \
+        -F/Library/Frameworks -framework GStreamer
+}
diff --git a/playback/player/qt/imagesample.cpp b/playback/player/qt/extension/imagesample.cpp
similarity index 100%
rename from playback/player/qt/imagesample.cpp
rename to playback/player/qt/extension/imagesample.cpp
diff --git a/playback/player/qt/imagesample.h b/playback/player/qt/extension/imagesample.h
similarity index 100%
rename from playback/player/qt/imagesample.h
rename to playback/player/qt/extension/imagesample.h
diff --git a/playback/player/qt/player.cpp b/playback/player/qt/extension/player.cpp
similarity index 88%
rename from playback/player/qt/player.cpp
rename to playback/player/qt/extension/player.cpp
index 3e2cade32f..20cf0e47de 100644
--- a/playback/player/qt/player.cpp
+++ b/playback/player/qt/extension/player.cpp
@@ -35,7 +35,9 @@ Player::Player(QObject *parent, QuickRenderer *renderer)
     renderer_->setParent(this);
 }
 
-void Player::setVideoOutput(QQuickItem *output)
+void Player::setVideoOutput(QVariant output)
 {
-    renderer_->setVideoItem(output);
+    QQuickItem *item = qvariant_cast<QQuickItem*>(output);
+
+    renderer_->setVideoItem(item);
 }
diff --git a/playback/player/qt/player.h b/playback/player/qt/extension/player.h
similarity index 92%
rename from playback/player/qt/player.h
rename to playback/player/qt/extension/player.h
index 34a00028f9..583fba7b5d 100644
--- a/playback/player/qt/player.h
+++ b/playback/player/qt/extension/player.h
@@ -32,10 +32,13 @@ class Player : public QGstPlayer::Player
     Q_OBJECT
 public:
     Player(QObject *parent = 0);
-    void setVideoOutput(QQuickItem *output);
+    Q_PROPERTY(QVariant videoOutput WRITE setVideoOutput)
 
 private:
     Player(QObject *parent, QuickRenderer *renderer);
+
+    void setVideoOutput(QVariant output);
+
     QuickRenderer *renderer_;
 };
 
diff --git a/playback/player/qt/qgstplayer.cpp b/playback/player/qt/extension/qgstplayer.cpp
similarity index 100%
rename from playback/player/qt/qgstplayer.cpp
rename to playback/player/qt/extension/qgstplayer.cpp
diff --git a/playback/player/qt/qgstplayer.h b/playback/player/qt/extension/qgstplayer.h
similarity index 100%
rename from playback/player/qt/qgstplayer.h
rename to playback/player/qt/extension/qgstplayer.h
diff --git a/playback/player/qt/extension/qmldir b/playback/player/qt/extension/qmldir
new file mode 100644
index 0000000000..e0b72cfd04
--- /dev/null
+++ b/playback/player/qt/extension/qmldir
@@ -0,0 +1,2 @@
+module extension
+plugin qmlplayerextension
diff --git a/playback/player/qt/extension/qplayerextension.cpp b/playback/player/qt/extension/qplayerextension.cpp
new file mode 100644
index 0000000000..edec6a06fd
--- /dev/null
+++ b/playback/player/qt/extension/qplayerextension.cpp
@@ -0,0 +1,33 @@
+/* GStreamer
+ *
+ * Copyright (C) 2018 Matthew Waters <matthew@cenricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "qplayerextension.h"
+#include "qgstplayer.h"
+#include "imagesample.h"
+
+#include <iostream>
+
+void QGstPlayerPlayerExtension::registerTypes(const char *uri)
+{
+        Q_ASSERT(uri == QLatin1String("extension"));
+        std::cout << "register uri: " << uri << std::endl;
+        qmlRegisterType<Player>(uri, 1, 0, "Player");
+        qmlRegisterType<ImageSample>("extension", 1, 0, "ImageSample");
+}
diff --git a/playback/player/qt/extension/qplayerextension.h b/playback/player/qt/extension/qplayerextension.h
new file mode 100644
index 0000000000..41961b1900
--- /dev/null
+++ b/playback/player/qt/extension/qplayerextension.h
@@ -0,0 +1,30 @@
+/* GStreamer
+ *
+ * Copyright (C) 2018 Matthew Waters <matthew@cenricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <QQmlExtensionPlugin>
+
+class QGstPlayerPlayerExtension : public QQmlExtensionPlugin
+{
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+    void registerTypes(const char *uri) override;
+};
diff --git a/playback/player/qt/quickrenderer.cpp b/playback/player/qt/extension/quickrenderer.cpp
similarity index 100%
rename from playback/player/qt/quickrenderer.cpp
rename to playback/player/qt/extension/quickrenderer.cpp
diff --git a/playback/player/qt/quickrenderer.h b/playback/player/qt/extension/quickrenderer.h
similarity index 100%
rename from playback/player/qt/quickrenderer.h
rename to playback/player/qt/extension/quickrenderer.h
diff --git a/playback/player/qt/main.cpp b/playback/player/qt/main.cpp
index b4495225cb..41938f991f 100644
--- a/playback/player/qt/main.cpp
+++ b/playback/player/qt/main.cpp
@@ -20,12 +20,12 @@
 
 #include <QApplication>
 #include <QQmlApplicationEngine>
+#include <QQmlProperty>
+#include <QQuickItem>
 #include <QCommandLineParser>
 #include <QStringList>
 #include <QUrl>
-
-#include "player.h"
-#include "imagesample.h"
+#include <gst/gst.h>
 
 int main(int argc, char *argv[])
 {
@@ -48,9 +48,6 @@ int main(int argc, char *argv[])
         media_files << QUrl::fromUserInput(file);
     }
 
-    qmlRegisterType<Player>("Player", 1, 0, "Player");
-    qmlRegisterType<ImageSample>("ImageSample", 1, 0, "ImageSample");
-
     /* the plugin must be loaded before loading the qml file to register the
      * GstGLVideoItem qml item
      * FIXME Add a QQmlExtensionPlugin into qmlglsink to register GstGLVideoItem
@@ -65,13 +62,17 @@ int main(int argc, char *argv[])
 
     QObject *rootObject = engine.rootObjects().first();
 
-    Player *player = rootObject->findChild<Player*>("player");
+    QObject *player = rootObject->findChild<QObject*>("player");
+    QObject *videoItem = rootObject->findChild<QObject*>("videoItem");
+    QVariant v;
+    v.setValue<QObject*>(videoItem);
+    QQmlProperty(player, "videoOutput").write(v);
 
-    QQuickItem *videoItem = rootObject->findChild<QQuickItem*>("videoItem");
-    player->setVideoOutput(videoItem);
-
-    if (!media_files.isEmpty())
-        player->setPlaylist(media_files);
+    if (!media_files.isEmpty()) {
+        QVariant v;
+        v.setValue<QList<QUrl>>(media_files);
+        QQmlProperty(player, "playlist").write(QVariant (v));
+    }
 
     result = app.exec();
 
diff --git a/playback/player/qt/main.qml b/playback/player/qt/main.qml
index c71ffabc0b..222dba7f1e 100644
--- a/playback/player/qt/main.qml
+++ b/playback/player/qt/main.qml
@@ -23,9 +23,8 @@ import QtQuick.Controls 1.1
 import QtQuick.Controls.Styles 1.3
 import QtQuick.Dialogs 1.2
 import QtQuick.Window 2.1
-import Player 1.0
+import extension 1.0
 import org.freedesktop.gstreamer.GLVideoItem 1.0
-import ImageSample 1.0
 
 import "fontawesome.js" as FontAwesome
 
diff --git a/playback/player/qt/play.pro b/playback/player/qt/play.pro
index 87a14902d4..d12143e7bb 100644
--- a/playback/player/qt/play.pro
+++ b/playback/player/qt/play.pro
@@ -33,16 +33,8 @@ macx {
         -F/Library/Frameworks -framework GStreamer
 }
 
-HEADERS += \
-    qgstplayer.h \
-    player.h \
-    quickrenderer.h \
-    imagesample.h
+HEADERS +=
 
-SOURCES += main.cpp \
-    qgstplayer.cpp \
-    player.cpp \
-    quickrenderer.cpp \
-    imagesample.cpp
+SOURCES += main.cpp
 
 DISTFILES +=
diff --git a/playback/player/qt/qt.pro b/playback/player/qt/qt.pro
new file mode 100644
index 0000000000..b11fea1d18
--- /dev/null
+++ b/playback/player/qt/qt.pro
@@ -0,0 +1,5 @@
+TEMPLATE = subdirs
+SUBDIRS = extension play
+
+play.depends = extension
+play.file = play.pro