diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index 18f146bb9d84069b13aca933899f932bb8f20f1e..7813353f12e8c72fc0104b565260a4b3a6197c96 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -48,6 +48,8 @@ namespace ring {
 const unsigned jitterBufferMaxSize_ {1500};
 // maximum time a packet can be queued
 const constexpr auto jitterBufferMaxDelay_ = std::chrono::milliseconds(50);
+// maximum number of times accelerated decoding can fail in a row before falling back to software
+const constexpr unsigned MAX_ACCEL_FAILURES { 5 };
 
 MediaDecoder::MediaDecoder() :
     inputCtx_(avformat_alloc_context()),
@@ -57,6 +59,8 @@ MediaDecoder::MediaDecoder() :
 
 MediaDecoder::~MediaDecoder()
 {
+    if (decoderCtx_->hw_device_ctx)
+        av_buffer_unref(&decoderCtx_->hw_device_ctx);
     if (decoderCtx_)
         avcodec_close(decoderCtx_);
     if (inputCtx_)
@@ -106,7 +110,7 @@ int MediaDecoder::openInput(const DeviceParams& params)
 
 #ifdef RING_ACCEL
     // if there was a fallback to software decoding, do not enable accel
-    // it has been disabled already by the video_receive_thread
+    // it has been disabled already by the video_receive_thread/video_input
     enableAccel_ &= Manager::instance().getDecodingAccelerated();
 #endif
 
@@ -301,21 +305,21 @@ int MediaDecoder::setupFromVideoData()
 
     decoderCtx_->thread_count = std::max(1u, std::min(8u, std::thread::hardware_concurrency()/2));
 
+    if (emulateRate_) {
+        RING_DBG("Using framerate emulation");
+        startTime_ = av_gettime();
+    }
+
 #ifdef RING_ACCEL
     if (enableAccel_) {
-        accel_ = video::makeHardwareAccel(decoderCtx_);
-        decoderCtx_->opaque = accel_.get();
+        accel_ = video::setupHardwareDecoding(decoderCtx_);
+        decoderCtx_->opaque = &accel_;
     } else if (Manager::instance().getDecodingAccelerated()) {
         RING_WARN("Hardware accelerated decoding disabled because of previous failure");
     } else {
         RING_WARN("Hardware accelerated decoding disabled by user preference");
     }
-#endif // RING_ACCEL
-
-    if (emulateRate_) {
-        RING_DBG("Using framerate emulation");
-        startTime_ = av_gettime();
-    }
+#endif
 
     ret = avcodec_open2(decoderCtx_, inputDecoder_, NULL);
     if (ret) {
@@ -353,18 +357,10 @@ MediaDecoder::decode(VideoFrame& result)
     int frameFinished = 0;
     ret = avcodec_send_packet(decoderCtx_, &inpacket);
     if (ret < 0) {
-#ifdef RING_ACCEL
-        if (accel_ && accel_->hasFailed())
-            return Status::RestartRequired;
-#endif
         return ret == AVERROR_EOF ? Status::Success : Status::DecodeError;
     }
     ret = avcodec_receive_frame(decoderCtx_, frame);
     if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
-#ifdef RING_ACCEL
-        if (accel_ && accel_->hasFailed())
-            return Status::RestartRequired;
-#endif
         return Status::DecodeError;
     }
     if (ret >= 0)
@@ -375,13 +371,18 @@ MediaDecoder::decode(VideoFrame& result)
     if (frameFinished) {
         frame->format = (AVPixelFormat) correctPixFmt(frame->format);
 #ifdef RING_ACCEL
-        if (accel_) {
-            if (!accel_->hasFailed())
-                accel_->extractData(result);
-            else
-                return Status::RestartRequired;
+        if (!accel_.name.empty()) {
+            ret = video::transferFrameData(accel_, decoderCtx_, result);
+            if (ret < 0) {
+                ++accelFailures_;
+                if (accelFailures_ >= MAX_ACCEL_FAILURES) {
+                    RING_ERR("Hardware decoding failure");
+                    accelFailures_ = 0; // reset error count for next time
+                    return Status::RestartRequired;
+                }
+            }
         }
-#endif // RING_ACCEL
+#endif
         if (emulateRate_ and frame->pts != AV_NOPTS_VALUE) {
             auto frame_time = getTimeBase()*(frame->pts - avStream_->start_time);
             auto target = startTime_ + static_cast<std::int64_t>(frame_time.real() * 1e6);
@@ -457,7 +458,9 @@ MediaDecoder::enableAccel(bool enableAccel)
 {
     enableAccel_ = enableAccel;
     if (!enableAccel) {
-        accel_.reset();
+        accel_ = {};
+        if (decoderCtx_->hw_device_ctx)
+            av_buffer_unref(&decoderCtx_->hw_device_ctx);
         if (decoderCtx_)
             decoderCtx_->opaque = nullptr;
     }
@@ -487,9 +490,9 @@ MediaDecoder::flush(VideoFrame& result)
 #ifdef RING_ACCEL
         // flush is called when closing the stream
         // so don't restart the media decoder
-        if (accel_ && !accel_->hasFailed())
-            accel_->extractData(result);
-#endif // RING_ACCEL
+        if (!accel_.name.empty() && accelFailures_ < MAX_ACCEL_FAILURES)
+            video::transferFrameData(accel_, decoderCtx_, result);
+#endif
         return Status::FrameFinished;
     }
 
diff --git a/src/media/media_decoder.h b/src/media/media_decoder.h
index c2bb87db99fe8d688ece51546bb2aa90b95e23d9..7835d67ee6a5e428510ba33a98c6eb4b0c8aeccb 100644
--- a/src/media/media_decoder.h
+++ b/src/media/media_decoder.h
@@ -26,6 +26,10 @@
 #include "video/video_scaler.h"
 #endif // RING_VIDEO
 
+#ifdef RING_ACCEL
+#include "video/accel.h"
+#endif
+
 #include "audio/audiobuffer.h"
 
 #include "rational.h"
@@ -44,12 +48,6 @@ class AVCodec;
 
 namespace ring {
 
-#ifdef RING_ACCEL
-namespace video {
-class HardwareAccel;
-}
-#endif
-
 struct AudioFrame;
 class AudioFormat;
 class RingBuffer;
@@ -120,7 +118,8 @@ class MediaDecoder {
 
 #ifdef RING_ACCEL
         bool enableAccel_ = true;
-        std::unique_ptr<video::HardwareAccel> accel_;
+        video::HardwareAccel accel_;
+        unsigned short accelFailures_ = 0;
 #endif
 
     protected:
diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp
index d91ae52eed847eed142fb89a8f3c5f9c134ed666..45b60a1a95ebd256e81fdb7b75183e0e82df4bff 100644
--- a/src/media/video/accel.cpp
+++ b/src/media/video/accel.cpp
@@ -18,232 +18,129 @@
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
  */
 
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-#include "media_buffer.h"
-
-#include "accel.h"
-
-#ifdef RING_VAAPI
-#include "v4l2/vaapi.h"
-#endif
-
-#ifdef RING_VDPAU
-#include "v4l2/vdpau.h"
-#endif
+extern "C" {
+#include <libavutil/hwcontext.h>
+}
 
-#ifdef RING_VIDEOTOOLBOX
-#include "osxvideo/videotoolbox.h"
-#endif
+#include <algorithm>
 
+#include "media_buffer.h"
 #include "string_utils.h"
+#include "fileutils.h"
 #include "logger.h"
-
-#include <sstream>
-#include <algorithm>
+#include "accel.h"
+#include "config.h"
 
 namespace ring { namespace video {
 
-static constexpr const unsigned MAX_ACCEL_FAILURES { 5 };
-
 static AVPixelFormat
 getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats)
 {
     auto accel = static_cast<HardwareAccel*>(codecCtx->opaque);
-    if (!accel) {
-        // invalid state, try to recover
-        return avcodec_default_get_format(codecCtx, formats);
-    }
 
     AVPixelFormat fallback = AV_PIX_FMT_NONE;
     for (int i = 0; formats[i] != AV_PIX_FMT_NONE; ++i) {
         fallback = formats[i];
-        if (formats[i] == accel->format()) {
-            accel->setWidth(codecCtx->coded_width);
-            accel->setHeight(codecCtx->coded_height);
-            accel->setProfile(codecCtx->profile);
-            accel->setCodecCtx(codecCtx);
-            if (accel->init())
-                return accel->format();
+        if (formats[i] == accel->format) {
+            return formats[i];
         }
     }
 
-    accel->fail(true);
-    RING_WARN("Falling back to software decoding");
-    codecCtx->get_format = avcodec_default_get_format;
-    codecCtx->get_buffer2 = avcodec_default_get_buffer2;
+    RING_WARN("'%s' acceleration not supported, falling back to software decoding", accel->name.c_str());
+    accel->name = {}; // don't use accel
     return fallback;
 }
 
-static int
-allocateBufferCb(AVCodecContext* codecCtx, AVFrame* frame, int flags)
+int
+transferFrameData(HardwareAccel accel, AVCodecContext* codecCtx, VideoFrame& frame)
 {
-    if (auto accel = static_cast<HardwareAccel*>(codecCtx->opaque)) {
-        if (!accel->hasFailed() && accel->allocateBuffer(frame, flags) == 0) {
-            accel->succeedAllocation();
-            return 0;
-        }
-
-        accel->failAllocation();
+    if (accel.name.empty())
+        return -1;
+
+    auto input = frame.pointer();
+    if (input->format != accel.format) {
+        RING_ERR("Frame format mismatch: expected %s, got %s",
+                 av_get_pix_fmt_name(static_cast<AVPixelFormat>(accel.format)),
+                 av_get_pix_fmt_name(static_cast<AVPixelFormat>(input->format)));
+        return -1;
     }
 
-    return avcodec_default_get_buffer2(codecCtx, frame, flags);
-}
+    // FFmpeg requires a second frame in which to transfer the data from the GPU buffer to the main memory
+    auto container = std::unique_ptr<VideoFrame>(new VideoFrame());
+    auto output = container->pointer();
 
-HardwareAccel::HardwareAccel(const std::string& name, const AVPixelFormat format)
-    : name_(name)
-    , format_(format)
-{}
+    // most hardware accelerations output NV12, so skip extra conversions
+    output->format = AV_PIX_FMT_NV12;
+    int ret = av_hwframe_transfer_data(output, input, 0);
 
-void
-HardwareAccel::failAllocation()
-{
-    ++allocationFails_;
-    fail(false);
-}
+    // move output into input so the caller receives extracted image data
+    // but we have to delete input's data first
+    av_frame_unref(input);
+    av_frame_move_ref(input, output);
 
-void
-HardwareAccel::failExtraction()
-{
-    ++extractionFails_;
-    fail(false);
-}
-
-void
-HardwareAccel::fail(bool forceFallback)
-{
-    if (allocationFails_ >= MAX_ACCEL_FAILURES || extractionFails_ >= MAX_ACCEL_FAILURES || forceFallback) {
-        RING_ERR("Hardware acceleration failure");
-        fallback_ = true;
-        allocationFails_ = 0;
-        extractionFails_ = 0;
-        if (codecCtx_) {
-            codecCtx_->get_format = avcodec_default_get_format;
-            codecCtx_->get_buffer2 = avcodec_default_get_buffer2;
-        }
-    }
+    return ret;
 }
 
-bool
-HardwareAccel::extractData(VideoFrame& input)
+static int
+openDevice(HardwareAccel accel, AVBufferRef** hardwareDeviceCtx)
 {
-    try {
-        auto inFrame = input.pointer();
-
-        if (inFrame->format != format_) {
-            std::stringstream buf;
-            buf << "Frame format mismatch: expected " << av_get_pix_fmt_name(format_);
-            buf << ", got " << av_get_pix_fmt_name((AVPixelFormat)inFrame->format);
-            throw std::runtime_error(buf.str());
+    int ret;
+    auto hwType = av_hwdevice_find_type_by_name(accel.name.c_str());
+#ifdef HAVE_VAAPI_ACCEL_DRM
+    // default DRM device may not work on multi GPU computers, so check all possible values
+    if (accel.name == "vaapi") {
+        const std::string path = "/dev/dri/";
+        auto files = ring::fileutils::readDirectory(path);
+        // renderD* is preferred over card*
+        std::sort(files.rbegin(), files.rend());
+        for (auto& entry : files) {
+            std::string deviceName = path + entry;
+            if ((ret = av_hwdevice_ctx_create(hardwareDeviceCtx, hwType, deviceName.c_str(), nullptr, 0)) >= 0) {
+                RING_DBG("Using '%s' hardware acceleration with device '%s'", accel.name.c_str(), deviceName.c_str());
+                return ret;
+            }
         }
-
-        // FFmpeg requires a second frame in which to transfer the data
-        // from the GPU buffer to the main memory
-        auto output = std::unique_ptr<VideoFrame>(new VideoFrame());
-        auto outFrame = output->pointer();
-        outFrame->format = AV_PIX_FMT_YUV420P;
-
-        extractData(input, *output);
-
-        // move outFrame into inFrame so the caller receives extracted image data
-        // but we have to delete inFrame first
-        av_frame_unref(inFrame);
-        av_frame_move_ref(inFrame, outFrame);
-    } catch (const std::runtime_error& e) {
-        failExtraction();
-        RING_ERR("%s", e.what());
-        return false;
     }
+#endif
+    // default device (nullptr) works for most cases
+    if ((ret = av_hwdevice_ctx_create(hardwareDeviceCtx, hwType, nullptr, nullptr, 0)) >= 0)
+        RING_DBG("Using '%s' hardware acceleration", accel.name.c_str());
 
-    succeedExtraction();
-    return true;
-}
-
-template <class T>
-static std::unique_ptr<HardwareAccel>
-makeHardwareAccel(const std::string name, const AVPixelFormat format) {
-    return std::unique_ptr<HardwareAccel>(new T(name, format));
+    return ret;
 }
 
-std::unique_ptr<HardwareAccel>
-makeHardwareAccel(AVCodecContext* codecCtx)
+const HardwareAccel
+setupHardwareDecoding(AVCodecContext* codecCtx)
 {
-    enum class AccelID {
-        NoAccel,
-        Vdpau,
-        Vaapi,
-        VideoToolbox,
-    };
-
-    struct AccelInfo {
-        AccelID type;
-        std::string name;
-        AVPixelFormat format;
-        std::unique_ptr<HardwareAccel> (*create)(const std::string name, const AVPixelFormat format);
-    };
-
-    /* Each item in this array reprensents a fully implemented hardware acceleration in Ring.
-     * Each item should be enclosed in an #ifdef to prevent its compilation on an
-     * unsupported platform (VAAPI for Linux Intel won't compile on a Mac).
-     * A new item should be added when support for an acceleration has been added to Ring,
-     * which is also supported by FFmpeg.
-     * Steps to add an acceleration (after its implementation):
-     * - Create an AccelID and add it to the switch statement
-     * - Give it a name (this is used for the daemon logs)
-     * - Specify its AVPixelFormat (the one used by FFmpeg: check pixfmt.h)
-     * - Add a function pointer that returns an instance (makeHardwareAccel<> does this already)
-     * Note: the include of the acceleration's header file must be guarded by the same #ifdef as
-     * in this array.
+    /**
+     * This array represents FFmpeg's hwaccels, along with their pixel format
+     * and their potentially supported codecs. Each item contains:
+     * - Name (must match the name used in FFmpeg)
+     * - Pixel format (tells FFmpeg which hwaccel to use)
+     * - Array of AVCodecID (potential codecs that can be accelerated by the hwaccel)
+     * Note: an empty name means the video isn't accelerated
      */
-    const AccelInfo accels[] = {
-#ifdef RING_VAAPI
-        { AccelID::Vaapi, "vaapi", AV_PIX_FMT_VAAPI, makeHardwareAccel<VaapiAccel> },
-#endif
-#ifdef RING_VDPAU
-        { AccelID::Vdpau, "vdpau", AV_PIX_FMT_VDPAU, makeHardwareAccel<VdpauAccel> },
-#endif
-#ifdef RING_VIDEOTOOLBOX
-        { AccelID::VideoToolbox, "videotoolbox", AV_PIX_FMT_VIDEOTOOLBOX, makeHardwareAccel<VideoToolboxAccel> },
-#endif
-        { AccelID::NoAccel, "none", AV_PIX_FMT_NONE, nullptr },
+    const HardwareAccel accels[] = {
+        { "vaapi", AV_PIX_FMT_VAAPI, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_H263 } },
+        { "vdpau", AV_PIX_FMT_VDPAU, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_H263 } },
+        { "videotoolbox", AV_PIX_FMT_VIDEOTOOLBOX, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_H263 } },
     };
 
-    std::vector<AccelID> possibleAccels = {};
-    switch (codecCtx->codec_id) {
-        case AV_CODEC_ID_H264:
-            possibleAccels.push_back(AccelID::Vdpau);
-            possibleAccels.push_back(AccelID::Vaapi);
-            possibleAccels.push_back(AccelID::VideoToolbox);
-            break;
-        case AV_CODEC_ID_MPEG4:
-        case AV_CODEC_ID_H263P:
-            possibleAccels.push_back(AccelID::Vdpau);
-            possibleAccels.push_back(AccelID::Vaapi);
-            possibleAccels.push_back(AccelID::VideoToolbox);
-            break;
-        case AV_CODEC_ID_VP8:
-            break;
-        default:
-            break;
-    }
-
-    for (auto& info : accels) {
-        for (auto& pa : possibleAccels) {
-            if (info.type == pa) {
-                auto accel = info.create(info.name, info.format);
-                // don't break if the check fails, we want to check every possibility
-                if (accel->checkAvailability()) {
-                    codecCtx->get_format = getFormatCb;
-                    codecCtx->get_buffer2 = allocateBufferCb;
-                    codecCtx->thread_safe_callbacks = 1;
-                    RING_DBG("Attempting to use '%s' hardware acceleration", accel->name().c_str());
-                    return accel;
-                }
+    AVBufferRef* hardwareDeviceCtx = nullptr;
+    for (auto accel : accels) {
+        if (std::find(accel.supportedCodecs.begin(), accel.supportedCodecs.end(),
+                static_cast<AVCodecID>(codecCtx->codec_id)) != accel.supportedCodecs.end()) {
+            if (openDevice(accel, &hardwareDeviceCtx) >= 0) {
+                codecCtx->hw_device_ctx = av_buffer_ref(hardwareDeviceCtx);
+                codecCtx->get_format = getFormatCb;
+                codecCtx->thread_safe_callbacks = 1;
+                return accel;
             }
         }
     }
 
-    RING_WARN("Not using hardware acceleration");
-    return nullptr;
+    RING_WARN("Not using hardware accelerated decoding");
+    return {};
 }
 
 }} // namespace ring::video
diff --git a/src/media/video/accel.h b/src/media/video/accel.h
index 265f6d379eefb3eb28c56e8fd63cc050179a6531..f438e8f8cf024d9959df07cb83b0502e98891714 100644
--- a/src/media/video/accel.h
+++ b/src/media/video/accel.h
@@ -21,57 +21,19 @@
 #pragma once
 
 #include "libav_deps.h"
-#include "media_buffer.h"
-#include "config.h"
 
 #include <string>
-#include <memory>
+#include <vector>
 
 namespace ring { namespace video {
 
-class HardwareAccel {
-    public:
-        HardwareAccel(const std::string& name, const AVPixelFormat format);
-        virtual ~HardwareAccel() {};
-
-        AVPixelFormat format() const { return format_; }
-        std::string name() const { return name_; }
-        bool hasFailed() const { return fallback_; }
-
-        void setCodecCtx(AVCodecContext* codecCtx) { codecCtx_ = codecCtx; }
-        void setWidth(int width) { width_ = width; }
-        void setHeight(int height) { height_ = height; }
-        void setProfile(int profile) { profile_ = profile; }
-
-        void failAllocation();
-        void failExtraction();
-        void fail(bool forceFallback);
-        void succeedAllocation() { allocationFails_ = 0; }
-        void succeedExtraction() { extractionFails_ = 0; }
-
-        // wrapper to take care of boilerplate before calling the derived class's implementation
-        bool extractData(VideoFrame& input);
-
-    public: // must be implemented by derived classes
-        virtual bool checkAvailability() = 0;
-        virtual bool init() = 0;
-        virtual int allocateBuffer(AVFrame* frame, int flags) = 0;
-        virtual void extractData(VideoFrame& input, VideoFrame& output) = 0;
-
-    protected:
-        AVCodecContext* codecCtx_ = nullptr;
-        std::string name_;
-        AVPixelFormat format_;
-        unsigned allocationFails_ = 0; // how many times in a row allocateBuffer has failed
-        unsigned extractionFails_ = 0; // how many times in a row extractData has failed
-        bool fallback_ = false; // set to true when successive failures exceeds MAX_ACCEL_FAILURES
-        int width_ = -1;
-        int height_ = -1;
-        int profile_ = -1;
+struct HardwareAccel {
+        std::string name;
+        AVPixelFormat format;
+        std::vector<AVCodecID> supportedCodecs;
 };
 
-// HardwareAccel factory
-// Checks if codec acceleration is possible
-std::unique_ptr<HardwareAccel> makeHardwareAccel(AVCodecContext* codecCtx);
+const HardwareAccel setupHardwareDecoding(AVCodecContext* codecCtx);
+int transferFrameData(HardwareAccel accel, AVCodecContext* codecCtx, VideoFrame& frame);
 
 }} // namespace ring::video
diff --git a/src/media/video/osxvideo/Makefile.am b/src/media/video/osxvideo/Makefile.am
index d228e21e7ea59d532403a1d3c9a88ad64595c8b4..f471e28a58e26389ddc9deb961778f040789d7d5 100644
--- a/src/media/video/osxvideo/Makefile.am
+++ b/src/media/video/osxvideo/Makefile.am
@@ -6,8 +6,4 @@ libosxvideo_la_SOURCES = \
 	video_device_impl.mm \
 	video_device_monitor_impl.mm
 
-if RING_ACCEL
-libosxvideo_la_SOURCES += videotoolbox.h videotoolbox.mm
-endif
-
 AM_OBJCXXFLAGS = -std=c++11
diff --git a/src/media/video/osxvideo/videotoolbox.h b/src/media/video/osxvideo/videotoolbox.h
deleted file mode 100644
index 90ea36031050aff04d0d4e5c269a1b0501694789..0000000000000000000000000000000000000000
--- a/src/media/video/osxvideo/videotoolbox.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- *  Copyright (C) 2016-2017 Savoir-faire Linux Inc.
- *
- *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#pragma once
-
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-
-#include "config.h"
-
-#ifdef RING_VIDEOTOOLBOX
-
-extern "C" {
-#include <libavcodec/avcodec.h>
-#include <libavutil/hwcontext.h>
-#include <libavcodec/videotoolbox.h>
-#include <libavutil/imgutils.h>
-}
-
-#include "video/accel.h"
-
-#include <memory>
-#include <functional>
-
-namespace ring { namespace video {
-
-class VideoToolboxAccel : public HardwareAccel {
-    public:
-        VideoToolboxAccel(const std::string name, const AVPixelFormat format);
-        ~VideoToolboxAccel();
-
-        bool checkAvailability() override;
-        bool init() override;
-        int allocateBuffer(AVFrame* frame, int flags) override;
-        void extractData(VideoFrame& input, VideoFrame& output) override;
-
-    private:
-        using AVBufferRefPtr = std::unique_ptr<AVBufferRef, std::function<void(AVBufferRef*)>>;
-        AVBufferRefPtr deviceBufferRef_;
-};
-
-}} // namespace ring::video
-
-#endif // RING_VIDEOTOOLBOX
diff --git a/src/media/video/osxvideo/videotoolbox.mm b/src/media/video/osxvideo/videotoolbox.mm
deleted file mode 100644
index ab058d772a9a315b759254ad955c85d82fae452c..0000000000000000000000000000000000000000
--- a/src/media/video/osxvideo/videotoolbox.mm
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- *  Copyright (C) 2016-2017 Savoir-faire Linux Inc.
- *
- *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-
-#include "config.h"
-
-#ifdef RING_VIDEOTOOLBOX
-
-#include <string>
-#include <sstream>
-#include <array>
-
-#include "video/osxvideo/videotoolbox.h"
-#include "video/accel.h"
-
-#include "logger.h"
-
-namespace ring { namespace video {
-
-static auto avBufferRefDeleter = [](AVBufferRef* buf){ av_buffer_unref(&buf); };
-
-VideoToolboxAccel::VideoToolboxAccel(const std::string name, const AVPixelFormat format)
-    : HardwareAccel(name, format)
-    , deviceBufferRef_(nullptr, avBufferRefDeleter)
-{
-}
-
-VideoToolboxAccel::~VideoToolboxAccel()
-{
-    if (codecCtx_)
-        av_videotoolbox_default_free(codecCtx_);
-}
-
-int
-VideoToolboxAccel::allocateBuffer(AVFrame* frame, int flags)
-{
-    // do nothing, as this is done during extractData
-    (void) frame; // unused
-    (void) flags; // unused
-    return 0;
-}
-
-void
-VideoToolboxAccel::extractData(VideoFrame& input, VideoFrame& output)
-{
-    auto inFrame = input.pointer();
-    auto outFrame = output.pointer();
-    auto pixelBuffer = reinterpret_cast<CVPixelBufferRef>(inFrame->data[3]);
-    auto pixelFormat = CVPixelBufferGetPixelFormatType(pixelBuffer);
-
-    switch (pixelFormat) {
-        case kCVPixelFormatType_420YpCbCr8Planar:
-            outFrame->format = AV_PIX_FMT_YUV420P;
-            break;
-        case kCVPixelFormatType_32BGRA:
-            outFrame->format = AV_PIX_FMT_BGRA;
-            break;
-        case kCVPixelFormatType_422YpCbCr8:
-            outFrame->format = AV_PIX_FMT_UYVY422;
-            break;
-        case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: // OS X 10.7+
-            outFrame->format = AV_PIX_FMT_NV12;
-            break;
-        default:
-            char codecTag[32];
-            av_get_codec_tag_string(codecTag, sizeof(codecTag), codecCtx_->codec_tag);
-            std::stringstream buf;
-            buf << "VideoToolbox (" << codecTag << "): unsupported pixel format (";
-            buf << av_get_pix_fmt_name(format_) << ")";
-            throw std::runtime_error(buf.str());
-    }
-
-    outFrame->width = inFrame->width;
-    outFrame->height = inFrame->height;
-    // align on 32 bytes
-    if (av_frame_get_buffer(outFrame, 32) < 0) {
-        std::stringstream buf;
-        buf << "Could not allocate a buffer for VideoToolbox";
-        throw std::runtime_error(buf.str());
-    }
-
-    if (CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly) != kCVReturnSuccess) {
-        throw std::runtime_error("Could not lock the pixel buffer");
-    }
-
-    // av_image_copy function takes a 4 element array (according to its signature)
-    std::array<uint8_t*, 4> buffer = {};
-    std::array<int, 4> lineSize = {};
-    if (CVPixelBufferIsPlanar(pixelBuffer)) {
-        int planeCount = CVPixelBufferGetPlaneCount(pixelBuffer);
-        for (int i = 0; i < planeCount; i++) {
-            buffer[i] = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, i));
-            lineSize[i] = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, i);
-        }
-    } else {
-        buffer[0] = static_cast<uint8_t*>(CVPixelBufferGetBaseAddress(pixelBuffer));
-        lineSize[0] = CVPixelBufferGetBytesPerRow(pixelBuffer);
-    }
-
-    av_image_copy(outFrame->data, outFrame->linesize,
-        const_cast<const uint8_t**>(static_cast<uint8_t**>(buffer.data())),
-        lineSize.data(), static_cast<AVPixelFormat>(outFrame->format),
-        inFrame->width, inFrame->height);
-
-    if (av_frame_copy_props(outFrame, inFrame) < 0) {
-        av_frame_unref(outFrame);
-    }
-
-    CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
-}
-
-bool
-VideoToolboxAccel::checkAvailability()
-{
-    AVBufferRef* hardwareDeviceCtx;
-    if (av_hwdevice_ctx_create(&hardwareDeviceCtx, AV_HWDEVICE_TYPE_VIDEOTOOLBOX, nullptr, nullptr, 0) == 0) {
-        deviceBufferRef_.reset(hardwareDeviceCtx);
-        return true;
-    }
-
-    av_buffer_unref(&hardwareDeviceCtx);
-    return false;
-}
-
-bool
-VideoToolboxAccel::init()
-{
-    if (av_videotoolbox_default_init(codecCtx_) >= 0) {
-        RING_DBG("VideoToolbox decoder initialized");
-        codecCtx_->hw_device_ctx = av_buffer_ref(deviceBufferRef_.get());
-        return true;
-    } else {
-        RING_ERR("Failed to initialize VideoToolbox decoder");
-        return false;
-    }
-}
-
-}}
-
-#endif // RING_VIDEOTOOLBOX
diff --git a/src/media/video/v4l2/Makefile.am b/src/media/video/v4l2/Makefile.am
index 05ff5a226175f08871c987202b559fffed544603..faaa97fe15d7dd8807c13980eeff8a4e0e457594 100644
--- a/src/media/video/v4l2/Makefile.am
+++ b/src/media/video/v4l2/Makefile.am
@@ -6,15 +6,7 @@ libv4l2_la_SOURCES = \
 	video_device_impl.cpp \
 	video_device_monitor_impl.cpp
 
-if RING_VDPAU
-libv4l2_la_SOURCES += vdpau.h vdpau.cpp
-endif
-
-if RING_VAAPI
-libv4l2_la_SOURCES += vaapi.h vaapi.cpp
-endif
-
-AM_CXXFLAGS = @LIBAVCODEC_CFLAGS@ @LIBAVFORMAT_CFLAGS@ @LIBAVDEVICE_CFLAGS@ @LIBSWSCALE_CFLAGS@
+AM_CXXFLAGS = @LIBAVCODEC_CFLAGS@ @LIBAVFORMAT_CFLAGS@ @LIBAVDEVICE_CFLAGS@ @LIBSWSCALE_CFLAGS@ @LIBAVUTIL_CFLAGS@
 AM_CXXFLAGS += @UDEV_CFLAGS@ @VDPAU_CFLAGS@ @LIBVA_CFLAGS@ @LIBVA_DRM_CFLAGS@ @LIBVA_X11_CFLAGS@
 
 libv4l2_la_LIBADD = @LIBAVCODEC_LIBS@ @LIBAVFORMAT_LIBS@ @LIBAVDEVICE_LIBS@ @LIBSWSCALE_LIBS@ @LIBAVUTIL_LIBS@
diff --git a/src/media/video/v4l2/vaapi.cpp b/src/media/video/v4l2/vaapi.cpp
deleted file mode 100644
index b222e8959a6ff9acc367f04f2588419a1880f1af..0000000000000000000000000000000000000000
--- a/src/media/video/v4l2/vaapi.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- *  Copyright (C) 2016-2017 Savoir-faire Linux Inc.
- *
- *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-
-#include "config.h"
-
-#ifdef RING_VAAPI
-
-#include "video/v4l2/vaapi.h"
-#include "video/accel.h"
-
-#include "fileutils.h"
-
-#include <sstream>
-#include <stdexcept>
-#include <map>
-#include <algorithm>
-#include <vector>
-
-#include "logger.h"
-
-namespace ring { namespace video {
-
-static auto avBufferRefDeleter = [](AVBufferRef* buf){ av_buffer_unref(&buf); };
-
-VaapiAccel::VaapiAccel(const std::string name, const AVPixelFormat format)
-    : HardwareAccel(name, format)
-    , deviceBufferRef_(nullptr, avBufferRefDeleter)
-    , framesBufferRef_(nullptr, avBufferRefDeleter)
-{
-}
-
-VaapiAccel::~VaapiAccel()
-{
-}
-
-int
-VaapiAccel::allocateBuffer(AVFrame* frame, int flags)
-{
-    (void) flags; // unused
-    return av_hwframe_get_buffer(framesBufferRef_.get(), frame, 0);
-}
-
-void
-VaapiAccel::extractData(VideoFrame& input, VideoFrame& output)
-{
-    auto inFrame = input.pointer();
-    auto outFrame = output.pointer();
-
-    if (av_hwframe_transfer_data(outFrame, inFrame, 0) < 0) {
-        throw std::runtime_error("Unable to extract data from VAAPI frame");
-    }
-
-    if (av_frame_copy_props(outFrame, inFrame) < 0 ) {
-        av_frame_unref(outFrame);
-    }
-}
-
-bool
-VaapiAccel::checkAvailability()
-{
-    AVBufferRef* hardwareDeviceCtx = nullptr;
-#ifdef HAVE_VAAPI_ACCEL_DRM
-    const std::string path = "/dev/dri/";
-    auto files = ring::fileutils::readDirectory(path);
-    // renderD* is preferred over card*
-    std::sort(files.rbegin(), files.rend());
-    for (auto& entry : files) {
-        std::string deviceName = path + entry;
-        if (av_hwdevice_ctx_create(&hardwareDeviceCtx, AV_HWDEVICE_TYPE_VAAPI, deviceName.c_str(), nullptr, 0) >= 0) {
-            deviceName_ = deviceName;
-            break;
-        }
-    }
-    if (hardwareDeviceCtx == nullptr)
-        return false;
-#elif HAVE_VAAPI_ACCEL_X11
-    if (av_hwdevice_ctx_create(&hardwareDeviceCtx, AV_HWDEVICE_TYPE_VAAPI, nullptr, nullptr, 0) < 0) {
-        return false;
-    }
-#endif
-
-    deviceBufferRef_.reset(hardwareDeviceCtx);
-    return true;
-}
-
-bool
-VaapiAccel::init()
-{
-    int numSurfaces = 16; // based on codec instead?
-    if (codecCtx_->active_thread_type & FF_THREAD_FRAME)
-        numSurfaces += codecCtx_->thread_count; // need extra surface per thread
-
-    framesBufferRef_.reset(av_hwframe_ctx_alloc(deviceBufferRef_.get()));
-    auto frames = reinterpret_cast<AVHWFramesContext*>(framesBufferRef_->data);
-    frames->format = format_;
-    frames->sw_format = AV_PIX_FMT_YUV420P;
-    frames->width = width_;
-    frames->height = height_;
-    frames->initial_pool_size = numSurfaces;
-
-    if (av_hwframe_ctx_init(framesBufferRef_.get()) < 0) {
-        RING_ERR("Failed to initialize VAAPI frame context");
-        return false;
-    }
-
-    codecCtx_->hw_frames_ctx = av_buffer_ref(framesBufferRef_.get());
-
-    if (!deviceName_.empty())
-        RING_DBG("VAAPI decoder initialized via device: %s", deviceName_.c_str());
-    else
-        RING_DBG("VAAPI decoder initialized");
-    return true;
-}
-
-}}
-
-#endif // RING_VAAPI
diff --git a/src/media/video/v4l2/vaapi.h b/src/media/video/v4l2/vaapi.h
deleted file mode 100644
index db061eefcb3443bad76a291ef7bde090704983d6..0000000000000000000000000000000000000000
--- a/src/media/video/v4l2/vaapi.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- *  Copyright (C) 2016-2017 Savoir-faire Linux Inc.
- *
- *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#pragma once
-
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-
-#include "config.h"
-
-#ifdef RING_VAAPI
-
-extern "C" {
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <va/va.h>
-#ifdef HAVE_VAAPI_ACCEL_DRM
-#   include <va/va_drm.h>
-#endif
-#ifdef HAVE_VAAPI_ACCEL_X11
-#   include <va/va_x11.h>
-#endif
-
-#include <libavutil/avconfig.h>
-#include <libavutil/buffer.h>
-#include <libavutil/frame.h>
-#include <libavutil/hwcontext.h>
-#include <libavutil/hwcontext_vaapi.h>
-
-#include <libavcodec/vaapi.h>
-}
-
-#include "video/accel.h"
-
-#include <memory>
-#include <functional>
-
-namespace ring { namespace video {
-
-class VaapiAccel : public HardwareAccel {
-    public:
-        VaapiAccel(const std::string name, const AVPixelFormat format);
-        ~VaapiAccel();
-
-        bool checkAvailability() override;
-        bool init() override;
-        int allocateBuffer(AVFrame* frame, int flags) override;
-        void extractData(VideoFrame& input, VideoFrame& output) override;
-
-    private:
-        using AVBufferRefPtr = std::unique_ptr<AVBufferRef, std::function<void(AVBufferRef*)>>;
-        AVBufferRefPtr deviceBufferRef_;
-        AVBufferRefPtr framesBufferRef_;
-
-        std::string deviceName_;
-};
-
-}} // namespace ring::video
-
-#endif // RING_VAAPI
diff --git a/src/media/video/v4l2/vdpau.cpp b/src/media/video/v4l2/vdpau.cpp
deleted file mode 100644
index dbb5c21fe25b0d1aee7e9ba7dc375b06d4cc5cf6..0000000000000000000000000000000000000000
--- a/src/media/video/v4l2/vdpau.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- *  Copyright (C) 2017 Savoir-faire Linux Inc.
- *
- *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-
-#include "config.h"
-
-#ifdef RING_VDPAU
-
-#include "video/v4l2/vdpau.h"
-#include "video/accel.h"
-
-#include "fileutils.h"
-
-#include <sstream>
-#include <stdexcept>
-#include <map>
-#include <algorithm>
-#include <vector>
-
-#include "logger.h"
-
-namespace ring { namespace video {
-
-static auto avBufferRefDeleter = [](AVBufferRef* buf){ av_buffer_unref(&buf); };
-
-VdpauAccel::VdpauAccel(const std::string name, const AVPixelFormat format)
-    : HardwareAccel(name, format)
-    , deviceBufferRef_(nullptr, avBufferRefDeleter)
-    , framesBufferRef_(nullptr, avBufferRefDeleter)
-{
-}
-
-VdpauAccel::~VdpauAccel()
-{
-}
-
-int
-VdpauAccel::allocateBuffer(AVFrame* frame, int flags)
-{
-    (void) flags;
-    return av_hwframe_get_buffer(framesBufferRef_.get(), frame, 0);
-}
-
-void
-VdpauAccel::extractData(VideoFrame& input, VideoFrame& output)
-{
-    auto inFrame = input.pointer();
-    auto outFrame = output.pointer();
-
-    if (av_hwframe_transfer_data(outFrame, inFrame, 0) < 0) {
-        throw std::runtime_error("Unable to extract data from VDPAU frame");
-    }
-
-    if (av_frame_copy_props(outFrame, inFrame) < 0 ) {
-        av_frame_unref(outFrame);
-    }
-}
-
-bool
-VdpauAccel::checkAvailability()
-{
-    AVBufferRef* hardwareDeviceCtx;
-    if (av_hwdevice_ctx_create(&hardwareDeviceCtx, AV_HWDEVICE_TYPE_VDPAU, nullptr, nullptr, 0) == 0) {
-        deviceBufferRef_.reset(hardwareDeviceCtx);
-        return true;
-    }
-
-    av_buffer_unref(&hardwareDeviceCtx);
-    return false;
-}
-
-bool
-VdpauAccel::init()
-{
-    auto device = reinterpret_cast<AVHWDeviceContext*>(deviceBufferRef_->data);
-    auto hardwareContext = static_cast<AVVDPAUDeviceContext*>(device->hwctx);
-
-    framesBufferRef_.reset(av_hwframe_ctx_alloc(deviceBufferRef_.get()));
-    auto frames = reinterpret_cast<AVHWFramesContext*>(framesBufferRef_->data);
-    frames->format = AV_PIX_FMT_VDPAU;
-    frames->sw_format = AV_PIX_FMT_YUV420P;
-    frames->width = width_;
-    frames->height = height_;
-
-    if (av_hwframe_ctx_init(framesBufferRef_.get()) < 0) {
-        RING_ERR("Failed to initialize VDPAU frame context");
-        return false;
-    }
-
-    if (av_vdpau_bind_context(codecCtx_, hardwareContext->device, hardwareContext->get_proc_address, 0)) {
-        RING_ERR("Could not bind VDPAU context");
-        return false;
-    }
-
-    RING_DBG("VDPAU decoder initialized");
-
-    return true;
-}
-
-}} // namespace ring::video
-
-#endif // RING_VDPAU
diff --git a/src/media/video/v4l2/vdpau.h b/src/media/video/v4l2/vdpau.h
deleted file mode 100644
index d3ba0a0bf31b342aa67daad2a57e78280774eb42..0000000000000000000000000000000000000000
--- a/src/media/video/v4l2/vdpau.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- *  Copyright (C) 2017 Savoir-faire Linux Inc.
- *
- *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program 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 General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
- */
-
-#pragma once
-
-#include "libav_deps.h" // MUST BE INCLUDED FIRST
-
-#include "config.h"
-
-#ifdef RING_VDPAU
-
-extern "C" {
-#include <stdint.h>
-
-#include <libavcodec/vdpau.h>
-#include <libavutil/buffer.h>
-#include <libavutil/frame.h>
-#include <libavutil/hwcontext.h>
-#include <libavutil/hwcontext_vdpau.h>
-}
-
-#include "video/accel.h"
-
-#include <memory>
-#include <functional>
-
-namespace ring { namespace video {
-
-class VdpauAccel : public HardwareAccel {
-    public:
-        VdpauAccel(const std::string name, const AVPixelFormat format);
-        ~VdpauAccel();
-
-        bool checkAvailability() override;
-        bool init() override;
-        int allocateBuffer(AVFrame* frame, int flags) override;
-        void extractData(VideoFrame& input, VideoFrame& output) override;
-
-    private:
-        using AVBufferRefPtr = std::unique_ptr<AVBufferRef, std::function<void(AVBufferRef*)>>;
-        AVBufferRefPtr deviceBufferRef_;
-        AVBufferRefPtr framesBufferRef_;
-};
-
-}} // namespace ring::video
-
-#endif // RING_VDPAU