diff --git a/av/__init__.py b/av/__init__.py
index 7713215b7cfe4536632473521f34591264e0b7f3..870cdf15999a52a3e5184c27df9a76ebc49a408b 100644
--- a/av/__init__.py
+++ b/av/__init__.py
@@ -19,6 +19,7 @@ from av.audio.layout import AudioLayout
 from av.audio.resampler import AudioResampler
 from av.codec.codec import Codec, codecs_available
 from av.codec.context import CodecContext
+from av.codec.hwaccel import HWConfig
 from av.container import open
 from av.format import ContainerFormat, formats_available
 from av.packet import Packet
diff --git a/av/__main__.py b/av/__main__.py
index 09ace3c4bba8c114d96e50af9383de0c878605cd..6f11d3d8ce375d6ccdee2538d64f4641775d6dc0 100644
--- a/av/__main__.py
+++ b/av/__main__.py
@@ -7,6 +7,8 @@ def main():
 
     parser = argparse.ArgumentParser()
     parser.add_argument('--codecs', action='store_true')
+    parser.add_argument('--hwdevices', action='store_true')
+    parser.add_argument('--hwconfigs', action='store_true')
     parser.add_argument('--version', action='store_true')
     args = parser.parse_args()
 
@@ -39,6 +41,14 @@ def main():
         from av.codec.codec import dump_codecs
         dump_codecs()
 
+    if args.hwdevices:
+        from av.codec.hwaccel import dump_hwdevices
+        dump_hwdevices()
+
+    if args.hwconfigs:
+        from av.codec.codec import dump_hwconfigs
+        dump_hwconfigs()
+
 
 if __name__ == '__main__':
     main()
diff --git a/av/audio/codeccontext.pyx b/av/audio/codeccontext.pyx
index b66064f1230b1eb2a9a7f5a5d918e4fce69f14ae..7d5439d0bd479b3e8808dd45e083e76f21070c87 100644
--- a/av/audio/codeccontext.pyx
+++ b/av/audio/codeccontext.pyx
@@ -3,6 +3,7 @@ cimport libav as lib
 from av.audio.format cimport AudioFormat, get_audio_format
 from av.audio.frame cimport AudioFrame, alloc_audio_frame
 from av.audio.layout cimport AudioLayout, get_audio_layout
+from av.codec.hwaccel cimport HWAccel
 from av.error cimport err_check
 from av.frame cimport Frame
 from av.packet cimport Packet
@@ -10,8 +11,8 @@ from av.packet cimport Packet
 
 cdef class AudioCodecContext(CodecContext):
 
-    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec):
-        CodecContext._init(self, ptr, codec)
+    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec, HWAccel hwaccel):
+        CodecContext._init(self, ptr, codec, hwaccel)
 
         # Sometimes there isn't a layout set, but there are a number of
         # channels. Assume it is the default layout.
diff --git a/av/codec/codec.pxd b/av/codec/codec.pxd
index 173f0ef182497e359b35f8ebfba0cf41dd84edac..1713180afafde556e4ffb5baef439bbc7cb5f879 100644
--- a/av/codec/codec.pxd
+++ b/av/codec/codec.pxd
@@ -7,6 +7,8 @@ cdef class Codec(object):
     cdef const lib.AVCodecDescriptor *desc
     cdef readonly bint is_encoder
 
+    cdef tuple _hardware_configs
+
     cdef _init(self, name=?)
 
 
diff --git a/av/codec/codec.pyx b/av/codec/codec.pyx
index ed9762d3014885a7e16218d0b979e13ce9cfc5b1..7846a234528941c7ce36d37e3d2e28a1345825cc 100644
--- a/av/codec/codec.pyx
+++ b/av/codec/codec.pyx
@@ -1,4 +1,7 @@
+from __future__ import print_function
+
 from av.audio.format cimport get_audio_format
+from av.codec.hwaccel cimport wrap_hwconfig
 from av.descriptor cimport wrap_avclass
 from av.utils cimport avrational_to_fraction, flag_in_bitfield
 from av.video.format cimport get_video_format
@@ -86,14 +89,21 @@ cdef class Codec(object):
         if self.is_encoder and lib.av_codec_is_decoder(self.ptr):
             raise RuntimeError('%s is both encoder and decoder.')
 
-    def create(self):
+    def __repr__(self):
+        return f'<av.{self.__class__.__name__}({self.name!r}, {self.mode!r})>'
+
+    def create(self, *args, **kwargs):
         from .context import CodecContext
-        return CodecContext.create(self)
+        return CodecContext.create(self, *args, **kwargs)
 
     property is_decoder:
         def __get__(self):
             return not self.is_encoder
 
+    @property
+    def mode(self):
+        return 'w' if self.is_encoder else 'r'
+
     property descriptor:
         def __get__(self): return wrap_avclass(self.ptr.priv_class)
 
@@ -184,6 +194,26 @@ cdef class Codec(object):
             i += 1
         return ret
 
+    @property
+    def hardware_configs(self):
+
+        if self._hardware_configs:
+            return self._hardware_configs
+
+        ret = []
+        cdef int i = 0
+        cdef lib.AVCodecHWConfig *ptr
+        while True:
+            ptr = lib.avcodec_get_hw_config(self.ptr, i)
+            if not ptr:
+                break
+            ret.append(wrap_hwconfig(ptr))
+            i += 1
+
+        ret = tuple(ret)
+        self._hardware_configs = ret
+        return ret
+
     # Capabilities.
     property draw_horiz_band:
         def __get__(self): return flag_in_bitfield(self.ptr.capabilities, lib.CODEC_CAP_DRAW_HORIZ_BAND)
@@ -266,16 +296,16 @@ codec_descriptor = wrap_avclass(lib.avcodec_get_class())
 def dump_codecs():
     """Print information about availible codecs."""
 
-    print '''Codecs:
- D..... = Decoding supported
- .E.... = Encoding supported
- ..V... = Video codec
- ..A... = Audio codec
- ..S... = Subtitle codec
- ...I.. = Intra frame-only codec
- ....L. = Lossy compression
- .....S = Lossless compression
- ------'''
+    print('''Codecs:
+    D....  = Decoding supported
+    .E...  = Encoding supported
+    ..V..  = Video codec
+    ..A..  = Audio codec
+    ..S..  = Subtitle codec
+    ...I.  = Intra frame-only codec
+    ....L  = Lossless compression
+    .....H = Hardware decoding supported
+    ------''')
 
     for name in sorted(codecs_available):
 
@@ -292,13 +322,33 @@ def dump_codecs():
         # TODO: Assert these always have the same properties.
         codec = e_codec or d_codec
 
-        print ' %s%s%s%s%s%s %-18s %s' % (
+        print('    %s%s%s%s%s%s %-18s %s' % (
             '.D'[bool(d_codec)],
             '.E'[bool(e_codec)],
             codec.type[0].upper(),
             '.I'[codec.intra_only],
-            'L.'[codec.lossless],
-            '.S'[codec.lossless],
+            '.L'[codec.lossless],
+            '.H'[bool((d_codec or codec).hardware_configs)],
             codec.name,
             codec.long_name
-        )
+        ))
+
+
+def dump_hwconfigs():
+
+    print('Hardware configs:')
+
+    for name in sorted(codecs_available):
+
+        try:
+            codec = Codec(name, 'r')
+        except ValueError:
+            continue
+
+        configs = codec.hardware_configs
+        if not configs:
+            continue
+
+        print('   ', codec.name)
+        for config in configs:
+            print('       ', config)
diff --git a/av/codec/context.pxd b/av/codec/context.pxd
index 446cef8e9dcc2cf3cac9af7b4350d801e3eb56bf..7163fe73c3948936f93608fc213a7ccfdc0ea66e 100644
--- a/av/codec/context.pxd
+++ b/av/codec/context.pxd
@@ -2,10 +2,11 @@ from libc.stdint cimport int64_t
 
 cimport libav as lib
 
+from av.bytesource cimport ByteSource
 from av.codec.codec cimport Codec
+from av.codec.hwaccel cimport HWAccel, HWAccelContext
 from av.frame cimport Frame
 from av.packet cimport Packet
-from av.bytesource cimport ByteSource
 
 
 cdef class CodecContext(object):
@@ -25,10 +26,12 @@ cdef class CodecContext(object):
     # To hold a reference to passed extradata.
     cdef ByteSource extradata_source
 
-    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec)
+    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec, HWAccel hwaccel)
 
     cdef readonly Codec codec
 
+    cdef readonly HWAccelContext hwaccel
+    
     cdef public dict options
 
     # Public API.
@@ -59,10 +62,12 @@ cdef class CodecContext(object):
     cdef _send_packet_and_recv(self, Packet packet)
     cdef _recv_frame(self)
 
+    cdef _transfer_hwframe(self, Frame frame)
+
     # Implemented by children for the generic send/recv API, so we have the
     # correct subclass of Frame.
     cdef Frame _next_frame
     cdef Frame _alloc_next_frame(self)
 
 
-cdef CodecContext wrap_codec_context(lib.AVCodecContext*, const lib.AVCodec*, bint allocated)
+cdef CodecContext wrap_codec_context(lib.AVCodecContext*, const lib.AVCodec*, bint allocated, HWAccel hwaccel)
diff --git a/av/codec/context.pyx b/av/codec/context.pyx
index d983829ee7ca2386895f6cb184d29200c9f41efb..7a527254985de1687bdbc7cc55a23d84e00b6acf 100644
--- a/av/codec/context.pyx
+++ b/av/codec/context.pyx
@@ -7,6 +7,7 @@ cimport libav as lib
 
 from av.bytesource cimport ByteSource, bytesource
 from av.codec.codec cimport Codec, wrap_codec
+from av.codec.hwaccel cimport HWAccel
 from av.dictionary cimport _Dictionary
 from av.dictionary import Dictionary
 from av.enums cimport define_enum
@@ -18,7 +19,7 @@ from av.utils cimport avdict_to_dict, avrational_to_fraction, to_avrational
 cdef object _cinit_sentinel = object()
 
 
-cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCodec *c_codec, bint allocated):
+cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCodec *c_codec, bint allocated, HWAccel hwaccel):
     """Build an av.CodecContext for an existing AVCodecContext."""
 
     cdef CodecContext py_ctx
@@ -37,7 +38,7 @@ cdef CodecContext wrap_codec_context(lib.AVCodecContext *c_ctx, const lib.AVCode
         py_ctx = CodecContext(_cinit_sentinel)
 
     py_ctx.allocated = allocated
-    py_ctx._init(c_ctx, c_codec)
+    py_ctx._init(c_ctx, c_codec, hwaccel)
 
     return py_ctx
 
@@ -62,10 +63,10 @@ SkipType = define_enum('SkipType', (
 cdef class CodecContext(object):
 
     @staticmethod
-    def create(codec, mode=None):
+    def create(codec, mode=None, hwaccel=None):
         cdef Codec cy_codec = codec if isinstance(codec, Codec) else Codec(codec, mode)
         cdef lib.AVCodecContext *c_ctx = lib.avcodec_alloc_context3(cy_codec.ptr)
-        return wrap_codec_context(c_ctx, cy_codec.ptr, True)
+        return wrap_codec_context(c_ctx, cy_codec.ptr, True, hwaccel)
 
     def __cinit__(self, sentinel=None, *args, **kwargs):
         if sentinel is not _cinit_sentinel:
@@ -74,7 +75,7 @@ cdef class CodecContext(object):
         self.options = {}
         self.stream_index = -1  # This is set by the container immediately.
 
-    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec):
+    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec, HWAccel hwaccel):
 
         self.ptr = ptr
         if self.ptr.codec and codec and self.ptr.codec != codec:
@@ -301,10 +302,15 @@ cdef class CodecContext(object):
             return
         err_check(res)
 
+        frame = self._transfer_hwframe(frame)
+
         if not res:
             self._next_frame = None
             return frame
 
+    cdef _transfer_hwframe(self, Frame frame):
+        return frame
+
     cdef _recv_packet(self):
 
         cdef Packet packet = Packet()
diff --git a/av/codec/hwaccel.pxd b/av/codec/hwaccel.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..d0c874b3a4580e974e901b9df05bab9ced2bec92
--- /dev/null
+++ b/av/codec/hwaccel.pxd
@@ -0,0 +1,33 @@
+
+cimport libav as lib
+
+from av.codec.codec cimport Codec
+
+
+cdef class HWConfig(object):
+
+    cdef object __weakref__
+
+    cdef lib.AVCodecHWConfig *ptr
+
+    cdef void _init(self, lib.AVCodecHWConfig *ptr)
+
+
+cdef HWConfig wrap_hwconfig(lib.AVCodecHWConfig *ptr)
+
+
+cdef class HWAccel(object):
+
+    #cdef lib.AVHWAccel *ptr
+
+    cdef str _device_type
+    cdef str _device
+    cdef public dict options
+
+cdef class HWAccelContext(HWAccel):
+
+    cdef readonly Codec codec
+    cdef readonly HWConfig config
+
+    cdef lib.AVBufferRef *ptr
+
diff --git a/av/codec/hwaccel.pyx b/av/codec/hwaccel.pyx
new file mode 100644
index 0000000000000000000000000000000000000000..206ee746591dada8826bf60dd753e960b5f34021
--- /dev/null
+++ b/av/codec/hwaccel.pyx
@@ -0,0 +1,176 @@
+from __future__ import print_function
+
+import weakref
+
+cimport libav as lib
+
+from av.codec.codec cimport Codec
+from av.dictionary cimport _Dictionary
+from av.enums cimport define_enum
+from av.error cimport err_check
+from av.video.format cimport get_video_format
+
+from av.dictionary import Dictionary
+
+
+HWDeviceType = define_enum('HWDeviceType', (
+    # ('NONE', lib.AV_HWDEVICE_TYPE_NONE),
+    ('VDPAU', lib.AV_HWDEVICE_TYPE_VDPAU),
+    ('CUDA', lib.AV_HWDEVICE_TYPE_CUDA),
+    ('VAAPI', lib.AV_HWDEVICE_TYPE_VAAPI),
+    ('DXVA2', lib.AV_HWDEVICE_TYPE_DXVA2),
+    ('QSV', lib.AV_HWDEVICE_TYPE_QSV),
+    ('VIDEOTOOLBOX', lib.AV_HWDEVICE_TYPE_VIDEOTOOLBOX),
+    ('D3D11VA', lib.AV_HWDEVICE_TYPE_D3D11VA),
+    ('DRM', lib.AV_HWDEVICE_TYPE_DRM),
+    ('OPENCL', lib.AV_HWDEVICE_TYPE_OPENCL),
+    ('MEDIACODEC', lib.AV_HWDEVICE_TYPE_MEDIACODEC),
+))
+
+
+HWConfigMethod = define_enum('HWConfigMethod', (
+    ('NONE', 0),
+    ('HW_DEVICE_CTX', lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX),  # This is the only one we support.
+    ('HW_FRAMES_CTX', lib.AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX),
+    ('INTERNAL', lib.AV_CODEC_HW_CONFIG_METHOD_INTERNAL),
+    ('AD_HOC', lib.AV_CODEC_HW_CONFIG_METHOD_AD_HOC),
+), is_flags=True, allow_multi_flags=True)
+
+
+cdef object _cinit_sentinel = object()
+cdef object _singletons = weakref.WeakValueDictionary()
+
+cdef HWConfig wrap_hwconfig(lib.AVCodecHWConfig *ptr):
+    try:
+        return _singletons[<int>ptr]
+    except KeyError:
+        pass
+    cdef HWConfig config = HWConfig(_cinit_sentinel)
+    config._init(ptr)
+    _singletons[<int>ptr] = config
+    return config
+
+
+cdef class HWConfig(object):
+
+    def __init__(self, sentinel):
+        if sentinel is not _cinit_sentinel:
+            raise RuntimeError('Cannot instantiate CodecContext')
+
+    cdef void _init(self, lib.AVCodecHWConfig *ptr):
+        self.ptr = ptr
+
+    def __repr__(self):
+        return (
+            f'<av.{self.__class__.__name__} '
+            f'device={self.device_type} '
+            f'format={self.format.name if self.format else None} '
+            f'is_supported={self.is_supported} '
+            f'at 0x{<int>self.ptr:x}>'
+        )
+
+    @property
+    def device_type(self):
+        return HWDeviceType.get(self.ptr.device_type)
+
+    @property
+    def format(self):
+        return get_video_format(self.ptr.pix_fmt, 0, 0)
+
+    @property
+    def methods(self):
+        return HWConfigMethod.get(self.ptr.methods)
+
+    @property
+    def is_supported(self):
+        return bool(self.ptr.methods & lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX)
+
+
+hwdevices_availible = set()
+
+cdef lib.AVHWDeviceType x = lib.AV_HWDEVICE_TYPE_NONE
+while True:
+    x = lib.av_hwdevice_iterate_types(x)
+    if x == lib.AV_HWDEVICE_TYPE_NONE:
+        break
+    hwdevices_availible.add(HWDeviceType.get(x))
+
+
+def dump_hwdevices():
+    print('Hardware devices:')
+    for x in hwdevices_availible:
+        print('   ', x)
+
+
+cdef class HWAccel(object):
+
+    @classmethod
+    def adapt(cls, input_):
+        if input_ is True:
+            return cls()
+        if isinstance(input_, cls):
+            return input_
+        if isinstance(input_, (str, HWDeviceType)):
+            return cls(input_)
+        if isinstance(input_, (list, tuple)):
+            return cls(*input_)
+        if isinstance(input_, dict):
+            return cls(**input_)
+        raise TypeError(f"can't adapt to HWAccel; {input_!r}")
+
+    def __init__(self, device_type=None, device=None, options=None, **kwargs):
+
+        self._device_type = HWDeviceType(device_type) if device_type else None
+        self._device = device
+
+        if options and kwargs:
+            raise ValueError("accepts only one of options arg or kwargs")
+        self.options = dict(options or kwargs)
+
+    def create(self, Codec codec):
+        return HWAccelContext(self._device_type, self._device, self.options, codec)
+
+
+cdef class HWAccelContext(HWAccel):
+
+    def __init__(self, device_type=None, device=None, options=None, codec=None, **kwargs):
+        super().__init__(device_type, device, options, **kwargs)
+
+        if not codec:
+            raise ValueError("codec is required")
+        self.codec = codec
+
+        cdef HWConfig config
+        for config in codec.hardware_configs:
+
+            if not (config.ptr.methods & lib.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX):
+                continue
+
+            if self._device_type and config.device_type != self._device_type:
+                continue
+
+            break
+
+        else:
+            raise ValueError(f"no supported hardware config for {codec}")
+
+        self.config = config
+
+        cdef char *c_device = NULL
+        if self._device:
+            device_bytes = self._device.encode()
+            c_device = device_bytes
+
+        cdef _Dictionary c_options = Dictionary(self.options)
+
+        err_check(lib.av_hwdevice_ctx_create(&self.ptr, config.ptr.device_type, c_device, c_options.ptr, 0))
+
+    def __dealloc__(self):
+        if self.ptr:
+            lib.av_buffer_unref(&self.ptr)
+
+    def create(self, *args, **kwargs):
+        raise ValueError("cannot call HWAccelContext.create")
+
+
+
diff --git a/av/container/core.pxd b/av/container/core.pxd
index 01a9dfa0d0673748199fa0afb90c68e51c6c1bf1..0cb234de6c0b1b6845502139f5043a4d4e32e6f0 100644
--- a/av/container/core.pxd
+++ b/av/container/core.pxd
@@ -1,5 +1,6 @@
 cimport libav as lib
 
+from av.codec.hwaccel cimport HWAccel
 from av.container.streams cimport StreamContainer
 from av.dictionary cimport _Dictionary
 from av.format cimport ContainerFormat
@@ -41,6 +42,8 @@ cdef class Container(object):
     cdef readonly dict container_options
     cdef readonly list stream_options
 
+    cdef HWAccel hwaccel
+
     cdef readonly StreamContainer streams
     cdef readonly dict metadata
 
diff --git a/av/container/core.pyx b/av/container/core.pyx
index 65b20d8b0e73f2b4242767de45ac8353e5570f2b..422392981f59ecf9bb85f8e5c4859ccd3d60ca89 100755
--- a/av/container/core.pyx
+++ b/av/container/core.pyx
@@ -7,6 +7,7 @@ import time
 
 cimport libav as lib
 
+from av.codec.hwaccel cimport HWAccel
 from av.container.core cimport timeout_info
 from av.container.input cimport InputContainer
 from av.container.output cimport OutputContainer
@@ -64,7 +65,7 @@ cdef class Container(object):
     def __cinit__(self, sentinel, file_, format_name, options,
                   container_options, stream_options,
                   metadata_encoding, metadata_errors,
-                  buffer_size, open_timeout, read_timeout):
+                  buffer_size, open_timeout, read_timeout, hwaccel):
 
         if sentinel is not _cinit_sentinel:
             raise RuntimeError('cannot construct base Container')
@@ -91,6 +92,8 @@ cdef class Container(object):
         self.open_timeout = open_timeout
         self.read_timeout = read_timeout
 
+        self.hwaccel = hwaccel
+
         if format_name is not None:
             self.format = ContainerFormat(format_name)
 
@@ -238,7 +241,7 @@ cdef class Container(object):
 def open(file, mode=None, format=None, options=None,
          container_options=None, stream_options=None,
          metadata_encoding=None, metadata_errors='strict',
-         buffer_size=32768, timeout=None):
+         buffer_size=32768, timeout=None, hwaccel=None):
     """open(file, mode='r', format=None, options=None, metadata_encoding=None, metadata_errors='strict')
 
     Main entrypoint to opening files/streams.
@@ -259,6 +262,8 @@ def open(file, mode=None, format=None, options=None,
     :param timeout: How many seconds to wait for data before giving up, as a float, or a
         :ref:`(open timeout, read timeout) <timeouts>` tuple.
     :type timeout: float or tuple
+    :param dict hwaccel: The desired device parameters to use for hardware acceleration
+        including device_type_name (e.x. cuda) and optional device (e.x. '/dev/dri/renderD128').
 
     For devices (via ``libavdevice``), pass the name of the device to ``format``,
     e.g.::
@@ -282,12 +287,16 @@ def open(file, mode=None, format=None, options=None,
         open_timeout = timeout
         read_timeout = timeout
 
+    if hwaccel is not None:
+        hwaccel = HWAccel.adapt(hwaccel)
+
     if mode.startswith('r'):
         return InputContainer(
             _cinit_sentinel, file, format, options,
             container_options, stream_options,
             metadata_encoding, metadata_errors,
-            buffer_size, open_timeout, read_timeout
+            buffer_size, open_timeout, read_timeout,
+            hwaccel,
         )
     if mode.startswith('w'):
         if stream_options:
@@ -296,6 +305,7 @@ def open(file, mode=None, format=None, options=None,
             _cinit_sentinel, file, format, options,
             container_options, stream_options,
             metadata_encoding, metadata_errors,
-            buffer_size, open_timeout, read_timeout
+            buffer_size, open_timeout, read_timeout,
+            hwaccel,
         )
     raise ValueError("mode must be 'r' or 'w'; got %r" % mode)
diff --git a/av/stream.pyx b/av/stream.pyx
index 3a1f13579074d610577ed59413c7bcacd9351256..cb051b5394284c7a1f66a5434feb4bdfc781e6b6 100644
--- a/av/stream.pyx
+++ b/av/stream.pyx
@@ -85,7 +85,7 @@ cdef class Stream(object):
         else:
             self._codec = self._codec_context.codec
 
-        self.codec_context = wrap_codec_context(self._codec_context, self._codec, False)
+        self.codec_context = wrap_codec_context(self._codec_context, self._codec, False, container.hwaccel)
         self.codec_context.stream_index = stream.index
 
     def __repr__(self):
diff --git a/av/video/codeccontext.pxd b/av/video/codeccontext.pxd
index 9693caa9bb91e111c0f0e1f44ec485665648a7b2..31e7ff187bed28e92d39a50b89cbfe2bdbd3fea0 100644
--- a/av/video/codeccontext.pxd
+++ b/av/video/codeccontext.pxd
@@ -1,4 +1,6 @@
 
+cimport libav as lib
+
 from av.codec.context cimport CodecContext
 from av.video.format cimport VideoFormat
 from av.video.frame cimport VideoFrame
diff --git a/av/video/codeccontext.pyx b/av/video/codeccontext.pyx
index 162413302e93df050a5e99c306ff9fa6d2cf038c..caa9e2b5586b566d42e92122f367631be89d6ec3 100644
--- a/av/video/codeccontext.pyx
+++ b/av/video/codeccontext.pyx
@@ -3,6 +3,7 @@ from libc.stdint cimport int64_t
 cimport libav as lib
 
 from av.codec.context cimport CodecContext
+from av.codec.hwaccel cimport HWAccel, HWConfig
 from av.frame cimport Frame
 from av.packet cimport Packet
 from av.utils cimport avrational_to_fraction, to_avrational
@@ -12,14 +13,31 @@ from av.video.frame cimport VideoFrame, alloc_video_frame
 from av.video.reformatter cimport VideoReformatter
 
 
+cdef lib.AVPixelFormat _get_hw_format(lib.AVCodecContext *ctx, lib.AVPixelFormat *pix_fmts):
+    i = 0
+    while pix_fmts[i] != -1:
+        if pix_fmts[i] == ctx.pix_fmt:
+            return pix_fmts[i]
+        i += 1
+
+    return lib.AV_PIX_FMT_NONE
+
+
 cdef class VideoCodecContext(CodecContext):
 
     def __cinit__(self, *args, **kwargs):
         self.last_w = 0
         self.last_h = 0
 
-    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec):
-        CodecContext._init(self, ptr, codec)  # TODO: Can this be `super`?
+    cdef _init(self, lib.AVCodecContext *ptr, const lib.AVCodec *codec, HWAccel hwaccel):
+        CodecContext._init(self, ptr, codec, hwaccel)  # TODO: Can this be `super`?
+
+        if hwaccel is not None:
+            self.hwaccel = hwaccel.create(self.codec)
+            self.ptr.hw_device_ctx = lib.av_buffer_ref(self.hwaccel.ptr)
+            self.ptr.pix_fmt = self.hwaccel.config.ptr.pix_fmt
+            self.ptr.get_format = _get_hw_format
+
         self._build_format()
         self.encoded_frame_count = 0
 
@@ -68,6 +86,24 @@ cdef class VideoCodecContext(CodecContext):
         cdef VideoFrame vframe = frame
         vframe._init_user_attributes()
 
+    cdef _transfer_hwframe(self, Frame frame):
+        cdef Frame frame_sw
+
+        # TODO: What is up with the format check?!
+        # retrieve data from GPU to CPU
+        if self.hwaccel is not None and frame.ptr.format == self.hwaccel.config.ptr.pix_fmt:
+            frame_sw = self._alloc_next_frame()
+
+            err_check(lib.av_hwframe_transfer_data(frame_sw.ptr, frame.ptr, 0))
+
+            # TODO: Is there anything else to transfer?!
+            frame_sw.pts = frame.pts
+
+            return frame_sw
+
+        else:
+            return frame
+
     cdef _build_format(self):
         self._format = get_video_format(<lib.AVPixelFormat>self.ptr.pix_fmt, self.ptr.width, self.ptr.height)
 
diff --git a/include/libav.pxd b/include/libav.pxd
index 5598ce143e5e1cacaa4854a5743bb844a17db4f0..6da830d5c30eade14641a6fa59f88299097930fe 100644
--- a/include/libav.pxd
+++ b/include/libav.pxd
@@ -12,8 +12,12 @@ include "libavutil/dict.pxd"
 include "libavutil/error.pxd"
 include "libavutil/frame.pxd"
 include "libavutil/samplefmt.pxd"
+include "libavutil/buffer.pxd"
+include "libavutil/hwcontext.pxd"
 
 include "libavcodec/avcodec.pxd"
+include "libavcodec/hwaccel.pxd"
+
 include "libavdevice/avdevice.pxd"
 include "libavformat/avformat.pxd"
 include "libswresample/swresample.pxd"
diff --git a/include/libavcodec/avcodec.pxd b/include/libavcodec/avcodec.pxd
index 99cb27e5cfc4445c845a43b2556db7399f2be098..7f99a764d924e0b28cc162dbd18baba752940700 100644
--- a/include/libavcodec/avcodec.pxd
+++ b/include/libavcodec/avcodec.pxd
@@ -176,6 +176,11 @@ cdef extern from "libavcodec/avcodec.pyav.h" nogil:
         int get_buffer(AVCodecContext *ctx, AVFrame *frame)
         void release_buffer(AVCodecContext *ctx, AVFrame *frame)
 
+        # Hardware acceleration
+        AVHWAccel *hwaccel
+        AVBufferRef *hw_device_ctx
+        AVPixelFormat (*get_format)(AVCodecContext *s, const AVPixelFormat * fmt)
+
         # User Data
         void *opaque
 
diff --git a/include/libavcodec/hwaccel.pxd b/include/libavcodec/hwaccel.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..ab1290c7b50a483adea4c37e5234279f13818fbd
--- /dev/null
+++ b/include/libavcodec/hwaccel.pxd
@@ -0,0 +1,24 @@
+cdef extern from "libavcodec/avcodec.h" nogil:
+
+    cdef enum:
+        AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX,
+        AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX,
+        AV_CODEC_HW_CONFIG_METHOD_INTERNAL,
+        AV_CODEC_HW_CONFIG_METHOD_AD_HOC,
+
+    cdef struct AVCodecHWConfig:
+        AVPixelFormat pix_fmt
+        int methods
+        AVHWDeviceType device_type
+
+    cdef const AVCodecHWConfig* avcodec_get_hw_config(const AVCodec *codec, int index)
+
+    cdef enum:
+        AV_HWACCEL_CODEC_CAP_EXPERIMENTAL
+
+    cdef struct AVHWAccel:
+        char *name
+        AVMediaType type
+        AVCodecID id
+        AVPixelFormat pix_fmt
+        int capabilities
diff --git a/include/libavutil/buffer.pxd b/include/libavutil/buffer.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..5445b563f8505576061d241bf9ea92ff7c8aaa43
--- /dev/null
+++ b/include/libavutil/buffer.pxd
@@ -0,0 +1,21 @@
+from libc.stdint cimport intptr_t, uint8_t
+
+
+cdef extern from "libavutil/buffer.h" nogil:
+
+    cdef struct AVBuffer:
+        uint8_t *data
+        int size
+        intptr_t refcount
+        void (*free)(void *opaque, uint8_t *data)
+        void *opaque
+        int flags
+
+    cdef struct AVBufferRef:
+        AVBuffer *buffer
+        uint8_t *data
+        int size
+
+
+    cdef AVBufferRef* av_buffer_ref(AVBufferRef *buf)
+    cdef void av_buffer_unref(AVBufferRef **buf)
diff --git a/include/libavutil/hwcontext.pxd b/include/libavutil/hwcontext.pxd
new file mode 100644
index 0000000000000000000000000000000000000000..fb76f6dfd71fdce7f9489bb68ab20e9219ce7708
--- /dev/null
+++ b/include/libavutil/hwcontext.pxd
@@ -0,0 +1,24 @@
+cdef extern from "libavutil/hwcontext.h" nogil:
+
+    enum AVHWDeviceType:
+        AV_HWDEVICE_TYPE_NONE
+        AV_HWDEVICE_TYPE_VDPAU
+        AV_HWDEVICE_TYPE_CUDA
+        AV_HWDEVICE_TYPE_VAAPI
+        AV_HWDEVICE_TYPE_DXVA2
+        AV_HWDEVICE_TYPE_QSV
+        AV_HWDEVICE_TYPE_VIDEOTOOLBOX
+        AV_HWDEVICE_TYPE_D3D11VA
+        AV_HWDEVICE_TYPE_DRM
+        AV_HWDEVICE_TYPE_OPENCL
+        AV_HWDEVICE_TYPE_MEDIACODEC
+
+    cdef AVHWDeviceType av_hwdevice_iterate_types(AVHWDeviceType prev)
+
+    cdef int av_hwdevice_ctx_create(AVBufferRef **device_ctx, AVHWDeviceType type, const char *device, AVDictionary *opts, int flags)
+
+    cdef AVHWDeviceType av_hwdevice_find_type_by_name(const char *name)
+    cdef const char *av_hwdevice_get_type_name(AVHWDeviceType type)
+
+    cdef int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags)
+
diff --git a/scratchpad/decode.py b/scratchpad/decode.py
index 0dfbf2df931feb5a0a9b7c5d685a9852ae8b5acf..398a542be801621a16c56ce2ce542937dd9134fc 100644
--- a/scratchpad/decode.py
+++ b/scratchpad/decode.py
@@ -27,6 +27,7 @@ arg_parser.add_argument('-a', '--audio', action='store_true')
 arg_parser.add_argument('-v', '--video', action='store_true')
 arg_parser.add_argument('-s', '--subs', action='store_true')
 arg_parser.add_argument('-d', '--data', action='store_true')
+arg_parser.add_argument('-H', '--hwaccel', action='store_true')
 arg_parser.add_argument('--dump-packets', action='store_true')
 arg_parser.add_argument('--dump-planes', action='store_true')
 arg_parser.add_argument('-p', '--play', action='store_true')
@@ -39,7 +40,7 @@ args = arg_parser.parse_args()
 proc = None
 
 options = dict(x.split('=') for x in args.option)
-container = open(args.path, format=args.format, options=options)
+container = open(args.path, format=args.format, options=options, hwaccel=args.hwaccel or None)
 
 print('container:', container)
 print('\tformat:', container.format)
diff --git a/scripts/build-deps b/scripts/build-deps
index a33ce197d255630c608ce3b663cc62b670d54413..bc650169584a14891482e6792ed0d1f41320913c 100755
--- a/scripts/build-deps
+++ b/scripts/build-deps
@@ -25,6 +25,32 @@ fi
 
 mkdir -p "$PYAV_LIBRARY_ROOT"
 mkdir -p "$PYAV_LIBRARY_PREFIX"
+
+# Nvidia build
+CONFFLAGS_NVIDIA=""
+if [[ -e /usr/local/cuda ]]; then
+    # Get Nvidia headers for ffmpeg
+    cd $PYAV_LIBRARY_ROOT
+    if [[ ! -e "$PYAV_LIBRARY_ROOT/nv-codec-headers" ]]; then
+        git clone https://github.com/FFmpeg/nv-codec-headers.git
+        cd nv-codec-headers
+        make -j4
+        make PREFIX="$PYAV_LIBRARY_PREFIX" install
+    fi
+
+    PKG_CONFIG_PATH="/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH"
+    CONFFLAGS_NVIDIA="--enable-cuda \
+                      --enable-cuvid \
+                      --enable-nvenc \
+                      --enable-nonfree \
+                      --enable-libnpp \
+                      --extra-cflags=-I/usr/local/cuda/include \
+                      --extra-ldflags=-L/usr/local/cuda/lib64"
+else
+    echo "WARNING: Did not find cuda libraries in /usr/local/cuda..."
+    echo "         Building without hardware acceleration support"
+fi
+
 cd "$PYAV_LIBRARY_ROOT"
 
 
@@ -52,6 +78,7 @@ fi
     --enable-debug=3 \
     --enable-gpl \
     --enable-libx264 \
+    $CONFFLAGS_NVIDIA \
     $CONFFLAGS \
     --prefix="$PYAV_LIBRARY_PREFIX" \
     || exit 2