diff --git a/configure.ac b/configure.ac
index 9b3689ef412ad1ea4280f9e0738f7eaebe09023a..70e6de1e4c44b78da973344891cffec46391d36b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -463,10 +463,30 @@ AS_IF([test "${SYS}" = linux && test -z "${HAVE_ANDROID_FALSE}"], [
     ], [vdpau_available="no"])
   ])
 ])
+AS_IF([test "${SYS}" = darwin], [
+  vt_available="no"
+  vda_available="no"
+  AC_CHECK_HEADER([VideoToolbox/VideoToolbox.h], [
+    AC_CHECK_HEADER([libavcodec/videotoolbox.h], [
+      AC_DEFINE([HAVE_VIDEOTOOLBOX_ACCEL], [1], [VideoToolbox found])
+      vt_available="yes"
+    ], [])
+  ], [])
+  AC_CHECK_HEADER([VideoDecodeAcceleration/VDADecoder.h], [
+    AC_CHECK_HEADER([libavcodec/vda.h], [
+      AC_DEFINE([HAVE_VDA_ACCEL], [1], [VDA found])
+      vda_available="yes"
+    ], [])
+  ], [])
+])
 
 AC_ARG_ENABLE([accel], AS_HELP_STRING([--disable-accel], [Disable all hardware accelerations]))
 AC_ARG_ENABLE([vdpau], AS_HELP_STRING([--disable-vdpau], [Disable VDPAU hardware acceleration]))
 AC_ARG_ENABLE([vaapi], AS_HELP_STRING([--disable-vaapi], [Disable VAAPI hardware acceleration]))
+AC_ARG_ENABLE([videotoolbox], AS_HELP_STRING([--disable-videotoolbox], [Disable VideoToolbox hardware acceleration]))
+AC_ARG_ENABLE([vda], AS_HELP_STRING([--disable-vda], [Disable VDA hardware acceleration]))
+
+dnl video acceleration only works if there's video
 AS_IF([test "x$enable_video" != "xno" -a "x$enable_accel" != "xno"], [
   ring_accel="yes"
   AC_DEFINE([RING_ACCEL], [1], [Hardware acceleration is enabled in Ring])
@@ -482,10 +502,24 @@ AS_IF([test "x$enable_video" != "xno" -a "x$enable_accel" != "xno"], [
       AC_DEFINE([RING_VDPAU], [1], [VDPAU is available in Ring])
     ])
   ])
+  AS_IF([test "x$enable_videotoolbox" != "xno"], [
+    AS_IF([test "x${vt_available}" = "xyes"], [
+      ring_vt="yes"
+      AC_DEFINE([RING_VIDEOTOOLBOX], [1], [VideoToolbox is available in Ring])
+    ])
+  ])
+  AS_IF([test "x$enable_vda" != "xno"], [
+    AS_IF([test "x${vda_available}" = "xyes"], [
+      ring_vda="yes"
+      AC_DEFINE([RING_VDA], [1], [VDA is available in Ring])
+    ])
+  ])
 ])
 AM_CONDITIONAL([RING_ACCEL], [test "x${ring_accel}" = "xyes"])
 AM_CONDITIONAL([RING_VAAPI], [test "x${ring_vaapi}" = "xyes"])
 AM_CONDITIONAL([RING_VDPAU], [test "x${ring_vdpau}" = "xyes"])
+AM_CONDITIONAL([RING_VIDEOTOOLBOX], [test "x${ring_vt}" = "xyes"])
+AM_CONDITIONAL([RING_VDA], [test "x${ring_vda}" = "xyes"])
 
 dnl check for GnuTLS
 PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.4.14], [HAVE_GNUTLS=1], [HAVE_GNUTLS=0])
diff --git a/contrib/src/ffmpeg/rules.mak b/contrib/src/ffmpeg/rules.mak
index 7c6c90b5b919039b39bd9bedd84b78814fa7a7ce..a3807731f496ffa3164c145d91b98f5aba1d0be1 100644
--- a/contrib/src/ffmpeg/rules.mak
+++ b/contrib/src/ffmpeg/rules.mak
@@ -142,7 +142,13 @@ endif
 ifdef HAVE_MACOSX
 FFMPEGCONF += \
 	--enable-indev=avfcapture \
-	--enable-indev=avfgrab
+	--enable-indev=avfgrab \
+	--enable-videotoolbox \
+	--enable-hwaccel=h263_videotoolbox \
+	--enable-hwaccel=h264_videotoolbox \
+	--enable-hwaccel=mpeg4_videotoolbox \
+	--enable-vda \
+	--enable-hwaccel=h264_vda
 endif
 
 ifdef HAVE_IOS
diff --git a/contrib/src/libav/rules.mak b/contrib/src/libav/rules.mak
index 9f51348681994dbd37c46a7e4ca89b7e7927d935..307ff9f95f4d6a9460cfb5c0748d8ccd65047037 100644
--- a/contrib/src/libav/rules.mak
+++ b/contrib/src/libav/rules.mak
@@ -137,9 +137,9 @@ ifdef HAVE_NEON
 LIBAVCONF += --as="$(AS)"
 endif
 endif
-#ifdef HAVE_MACOSX
-#LIBAVCONF += --enable-vda
-#endif
+ifdef HAVE_MACOSX
+LIBAVCONF += --enable-vda --enable-hwaccel=h264_vda
+endif
 
 # Linux
 ifdef HAVE_LINUX
diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp
index e17f49f776c33245e8e126ec15de9fb7b1210c8d..b074292f2f4554ca8cadbdc0a4b8b91a98000595 100644
--- a/src/media/video/accel.cpp
+++ b/src/media/video/accel.cpp
@@ -31,6 +31,10 @@
 #include "v4l2/vdpau.h"
 #endif
 
+#if defined(RING_VIDEOTOOLBOX) || defined(RING_VDA)
+#include "osxvideo/videotoolbox.h"
+#endif
+
 #include "string_utils.h"
 #include "logger.h"
 
@@ -170,6 +174,8 @@ makeHardwareAccel(AVCodecContext* codecCtx)
     enum class AccelID {
         Vdpau,
         Vaapi,
+        VideoToolbox,
+        Vda,
     };
 
     struct AccelInfo {
@@ -198,16 +204,28 @@ makeHardwareAccel(AVCodecContext* codecCtx)
 #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
+#ifdef RING_VDA
+        { AccelID::Vda, "vda", AV_PIX_FMT_VDA, makeHardwareAccel<VideoToolboxAccel> },
 #endif
     };
 
     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);
+            possibleAccels.push_back(AccelID::Vda);
+            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;
diff --git a/src/media/video/osxvideo/Makefile.am b/src/media/video/osxvideo/Makefile.am
index f471e28a58e26389ddc9deb961778f040789d7d5..d228e21e7ea59d532403a1d3c9a88ad64595c8b4 100644
--- a/src/media/video/osxvideo/Makefile.am
+++ b/src/media/video/osxvideo/Makefile.am
@@ -6,4 +6,8 @@ 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
new file mode 100644
index 0000000000000000000000000000000000000000..587311b4b4de25663f4df64c9dc8ed3a4864cd1c
--- /dev/null
+++ b/src/media/video/osxvideo/videotoolbox.h
@@ -0,0 +1,64 @@
+/*
+ *  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"
+
+#if defined(RING_VIDEOTOOLBOX) || defined(RING_VDA)
+
+extern "C" {
+#include <libavcodec/avcodec.h>
+#ifdef RING_VIDEOTOOLBOX
+#include <libavcodec/videotoolbox.h>
+#endif
+#ifdef RING_VDA
+#include <libavcodec/vda.h>
+#endif
+#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:
+        bool usingVT_ = false;
+        std::string decoderName_;
+};
+
+}} // namespace ring::video
+
+#endif // defined(RING_VIDEOTOOLBOX) || defined(RING_VDA)
diff --git a/src/media/video/osxvideo/videotoolbox.mm b/src/media/video/osxvideo/videotoolbox.mm
new file mode 100644
index 0000000000000000000000000000000000000000..d6af30672404f7fd5905125c41c09ae16664b0a6
--- /dev/null
+++ b/src/media/video/osxvideo/videotoolbox.mm
@@ -0,0 +1,176 @@
+/*
+ *  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"
+
+#if defined(RING_VIDEOTOOLBOX) || defined(RING_VDA)
+
+#include <string>
+#include <sstream>
+#include <array>
+
+#include "video/osxvideo/videotoolbox.h"
+#include "video/accel.h"
+
+#include "logger.h"
+
+namespace ring { namespace video {
+
+VideoToolboxAccel::VideoToolboxAccel(const std::string name, const AVPixelFormat format)
+    : HardwareAccel(name, format)
+{
+}
+
+VideoToolboxAccel::~VideoToolboxAccel()
+{
+    if (codecCtx_) {
+        if (usingVT_) {
+#ifdef RING_VIDEOTOOLBOX
+            av_videotoolbox_default_free(codecCtx_);
+#endif
+        } else {
+#ifdef RING_VDA
+            av_vda_default_free(codecCtx_);
+#endif
+        }
+    }
+}
+
+int
+VideoToolboxAccel::allocateBuffer(AVFrame* frame, int flags)
+{
+    // do nothing, as this is done during extractData for VideoT and VDA
+    (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 << decoderName_ << " (" << 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 " << decoderName_;
+        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()
+{
+    // VideoToolbox is always present on Mac 10.8+ and iOS 8+
+    // VDA is always present on Mac 10.6.3+
+    return true;
+}
+
+bool
+VideoToolboxAccel::init()
+{
+    decoderName_ = "";
+    bool success = false;
+#ifdef RING_VIDEOTOOLBOX
+    if (int ret = av_videotoolbox_default_init(codecCtx_) == 0) {
+        success = true;
+        usingVT_ = true;
+        decoderName_ = "VideoToolbox";
+    }
+#endif
+#ifdef RING_VDA
+    if (!success) {
+        if (int ret = av_vda_default_init(codecCtx_) == 0) {
+            success = true;
+            usingVT_ = false;
+            decoderName_ = "VDA";
+        }
+    }
+#endif
+
+    if (success)
+        RING_DBG("%s decoder initialized", decoderName_.c_str());
+    else
+        RING_ERR("Failed to initialize Mac hardware accelerator");
+
+    return success;
+}
+
+}}
+
+#endif // defined(RING_VIDEOTOOLBOX) || defined(RING_VDA)