diff --git a/src/media/media_decoder.cpp b/src/media/media_decoder.cpp
index 673721ebb07f47fa3504063da915ebe7ca95a947..e7455c07f6b38bf2b53ebd402e016076e738470a 100644
--- a/src/media/media_decoder.cpp
+++ b/src/media/media_decoder.cpp
@@ -271,12 +271,69 @@ MediaDecoder::setupStream()
     int ret = 0;
     avcodec_free_context(&decoderCtx_);
 
+    if(prepareDecoderContext() < 0)
+        return -1; // failed
+
 #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/video_input
     enableAccel_ &= Manager::instance().videoPreferences.getDecodingAccelerated();
+
+    if (enableAccel_) {
+        auto APIs = video::HardwareAccel::getCompatibleAccel(decoderCtx_->codec_id,
+                    decoderCtx_->width, decoderCtx_->height, CODEC_DECODER);
+        if (!APIs.empty()) {
+            for (const auto& it : APIs) {
+                accel_ = std::make_unique<video::HardwareAccel>(it);    // save accel
+                auto ret = accel_->initAPI(false, nullptr);
+                if (ret < 0) {
+                    accel_ = nullptr;
+                    continue;
+                }
+                if(prepareDecoderContext() < 0)
+                    return -1; // failed
+                accel_->setDetails(decoderCtx_);
+                decoderCtx_->opaque = accel_.get();
+                decoderCtx_->pix_fmt = accel_->getFormat();
+                if (avcodec_open2(decoderCtx_, inputDecoder_, &options_) < 0) {
+                    // Failed to open codec
+                    JAMI_WARN("Fail to open hardware decoder for %s with %s ", avcodec_get_name(decoderCtx_->codec_id), it.getName().c_str());
+                    avcodec_free_context(&decoderCtx_);
+                    decoderCtx_ = nullptr;
+                    accel_.reset();
+                    continue;
+                } else {
+                    // Succeed to open codec
+                    JAMI_WARN("Using hardware decoding for %s with %s ", avcodec_get_name(decoderCtx_->codec_id), it.getName().c_str());
+                    break;
+                }
+            }
+        }
+    }
 #endif
 
+    JAMI_DBG() << "Decoding " << av_get_media_type_string(avStream_->codecpar->codec_type) << " using " << inputDecoder_->long_name << " (" << inputDecoder_->name << ")";
+
+    decoderCtx_->thread_count = std::max(1u, std::min(8u, std::thread::hardware_concurrency()/2));
+    if (emulateRate_)
+        JAMI_DBG() << "Using framerate emulation";
+    startTime_ = av_gettime(); // used to set pts after decoding, and for rate emulation
+
+    if(!accel_) {
+        JAMI_WARN("Not using hardware decoding for %s",  avcodec_get_name(decoderCtx_->codec_id));
+        ret = avcodec_open2(decoderCtx_, inputDecoder_, nullptr);
+    }
+    if (ret < 0) {
+        JAMI_ERR() << "Could not open codec: " << libav_utils::getError(ret);
+        return -1;
+    }
+
+    return 0;
+}
+
+int
+MediaDecoder::prepareDecoderContext()
+{
     inputDecoder_ = findDecoder(avStream_->codecpar->codec_id);
     if (!inputDecoder_) {
         JAMI_ERR() << "Unsupported codec";
@@ -297,36 +354,7 @@ MediaDecoder::setupStream()
             decoderCtx_->framerate = av_inv_q(decoderCtx_->time_base);
         if (decoderCtx_->framerate.num == 0 || decoderCtx_->framerate.den == 0)
             decoderCtx_->framerate = {30, 1};
-
-#ifdef RING_ACCEL
-        if (enableAccel_) {
-            accel_ = video::HardwareAccel::setupDecoder(decoderCtx_->codec_id,
-                decoderCtx_->width, decoderCtx_->height);
-            if (accel_) {
-                accel_->setDetails(decoderCtx_);
-                decoderCtx_->opaque = accel_.get();
-            }
-        } else if (Manager::instance().videoPreferences.getDecodingAccelerated()) {
-            JAMI_WARN() << "Hardware decoding disabled because of previous failure";
-        } else {
-            JAMI_WARN() << "Hardware decoding disabled by user preference";
-        }
-#endif
-    }
-
-    JAMI_DBG() << "Decoding " << av_get_media_type_string(avStream_->codecpar->codec_type) << " using " << inputDecoder_->long_name << " (" << inputDecoder_->name << ")";
-
-    decoderCtx_->thread_count = std::max(1u, std::min(8u, std::thread::hardware_concurrency()/2));
-    if (emulateRate_)
-        JAMI_DBG() << "Using framerate emulation";
-    startTime_ = av_gettime(); // used to set pts after decoding, and for rate emulation
-
-    ret = avcodec_open2(decoderCtx_, inputDecoder_, nullptr);
-    if (ret < 0) {
-        JAMI_ERR() << "Could not open codec: " << libav_utils::getError(ret);
-        return -1;
     }
-
     return 0;
 }
 
diff --git a/src/media/media_decoder.h b/src/media/media_decoder.h
index a85df45e18faa23e839c172b6691a7c0cc5d91ff..b2687ae9a51a9abc91b89d7878ac86f34da5729c 100644
--- a/src/media/media_decoder.h
+++ b/src/media/media_decoder.h
@@ -184,6 +184,7 @@ private:
     unsigned short accelFailures_ = 0;
 #endif
     MediaObserver callback_;
+    int prepareDecoderContext();
 
 protected:
     AVDictionary *options_ = nullptr;
diff --git a/src/media/media_encoder.cpp b/src/media/media_encoder.cpp
index 182ebea4555e6a8626438a19e39f36bc03695f6d..2d3b15816458ccffe6efe3c497415d7613afe06c 100644
--- a/src/media/media_encoder.cpp
+++ b/src/media/media_encoder.cpp
@@ -198,7 +198,6 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr
     else if(systemCodecInfo.mediaType == MEDIA_AUDIO)
         mediaType = AVMEDIA_TYPE_AUDIO;
 
-    encoderCtx = initCodec(mediaType, static_cast<AVCodecID>(systemCodecInfo.avcodecId), framesCtx, SystemCodecInfo::DEFAULT_VIDEO_BITRATE);
 
     // add video stream to outputformat context
     AVStream* stream = avformat_new_stream(outputCtx_, outputCodec_);
@@ -206,10 +205,59 @@ MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* fr
         throw MediaEncoderException("Could not allocate stream");
 
     currentStreamIdx_ = stream->index;
+#ifdef RING_ACCEL
+    // Get compatible list of Hardware API
+    if (enableAccel_ && mediaType == AVMEDIA_TYPE_VIDEO) {
+        auto APIs = video::HardwareAccel::getCompatibleAccel(static_cast<AVCodecID>(systemCodecInfo.avcodecId),
+                videoOpts_.width, videoOpts_.height, CODEC_ENCODER);
+
+        if (APIs.size() > 0) {
+            for (const auto& it : APIs) {
+                accel_ = std::make_unique<video::HardwareAccel>(it);    // save accel
+                // Init codec need accel_ to init encoderCtx accelerated
+                encoderCtx = initCodec(mediaType, static_cast<AVCodecID>(systemCodecInfo.avcodecId), SystemCodecInfo::DEFAULT_VIDEO_BITRATE);
+                encoderCtx->opaque = accel_.get();
+                // Check if pixel format from encoder match pixel format from decoder frame context
+                // if it mismatch, it means that we are using two different hardware API (nvenc and vaapi for example)
+                // in this case we don't want link the APIs
+                if (framesCtx) {
+                    auto hw = reinterpret_cast<AVHWFramesContext*>(framesCtx->data);
+                    if (encoderCtx->pix_fmt != hw->format)
+                        linkableHW_ = false;
+                }
+                auto ret = accel_->initAPI(linkableHW_, framesCtx);
+                if (ret < 0) {
+                    accel_.reset();
+                    encoderCtx = nullptr;
+                    continue;
+                }
+                accel_->setDetails(encoderCtx);
+                if (avcodec_open2(encoderCtx, outputCodec_, &options_) < 0) {
+                    // Fail opening codec
+                    JAMI_WARN("Fail to open hardware encoder %s with %s ", avcodec_get_name(static_cast<AVCodecID>(systemCodecInfo.avcodecId)), it.getName().c_str());
+                    avcodec_free_context(&encoderCtx);
+                    encoderCtx = nullptr;
+                    accel_ = nullptr;
+                    continue;
+                } else {
+                    // Success opening codec
+                    JAMI_WARN("Using hardware encoding for %s with %s ", avcodec_get_name(static_cast<AVCodecID>(systemCodecInfo.avcodecId)), it.getName().c_str());
+                    encoders_.push_back(encoderCtx);
+                    break;
+                }
+            }
+        }
+    }
+#endif
 
-    readConfig(encoderCtx);
-    if (avcodec_open2(encoderCtx, outputCodec_, &options_) < 0)
-        throw MediaEncoderException("Could not open encoder");
+    if (!encoderCtx) {
+        JAMI_WARN("Not using hardware encoding for %s", avcodec_get_name(static_cast<AVCodecID>(systemCodecInfo.avcodecId)));
+        encoderCtx = initCodec(mediaType, static_cast<AVCodecID>(systemCodecInfo.avcodecId), SystemCodecInfo::DEFAULT_VIDEO_BITRATE);
+        readConfig(encoderCtx);
+        encoders_.push_back(encoderCtx);
+        if (avcodec_open2(encoderCtx, outputCodec_, &options_) < 0)
+            throw MediaEncoderException("Could not open encoder");
+    }
 
 #ifndef _WIN32
     avcodec_parameters_from_context(stream->codecpar, encoderCtx);
@@ -651,15 +699,13 @@ MediaEncoder::getStream(const std::string& name, int streamIdx) const
 }
 
 AVCodecContext*
-MediaEncoder::initCodec(AVMediaType mediaType, AVCodecID avcodecId, AVBufferRef* framesCtx, uint64_t br)
+MediaEncoder::initCodec(AVMediaType mediaType, AVCodecID avcodecId, uint64_t br)
 {
     outputCodec_ = nullptr;
 #ifdef RING_ACCEL
     if (mediaType == AVMEDIA_TYPE_VIDEO) {
         if (enableAccel_) {
-            if (accel_ = video::HardwareAccel::setupEncoder(
-                static_cast<AVCodecID>(avcodecId),
-                videoOpts_.width, videoOpts_.height, linkableHW_, framesCtx)) {
+            if (accel_) {
                 outputCodec_ = avcodec_find_encoder_by_name(accel_->getCodecName().c_str());
             }
         } else {
@@ -683,14 +729,6 @@ MediaEncoder::initCodec(AVMediaType mediaType, AVCodecID avcodecId, AVBufferRef*
     }
 
     AVCodecContext* encoderCtx = prepareEncoderContext(outputCodec_, mediaType == AVMEDIA_TYPE_VIDEO);
-    encoders_.push_back(encoderCtx);
-
-#ifdef RING_ACCEL
-    if (accel_) {
-        accel_->setDetails(encoderCtx);
-        encoderCtx->opaque = accel_.get();
-    }
-#endif
 
     // Only clamp video bitrate
     if (mediaType == AVMEDIA_TYPE_VIDEO && br > 0) {
@@ -743,7 +781,7 @@ MediaEncoder::setBitrate(uint64_t br)
     else {
         // restart encoder on runtime doesn't work for VP8
         // stopEncoder();
-        // encoderCtx = initCodec(codecType, codecId, NULL, br);
+        // encoderCtx = initCodec(codecType, codecId, br);
         // if (avcodec_open2(encoderCtx, outputCodec_, &options_) < 0)
         //     throw MediaEncoderException("Could not open encoder");
     }
diff --git a/src/media/media_encoder.h b/src/media/media_encoder.h
index 35ead17d993c3a6e0bebf0ebdd8425e4c10f69cf..b223afea036452edf60645af665cd9c5ca780c1c 100644
--- a/src/media/media_encoder.h
+++ b/src/media/media_encoder.h
@@ -120,7 +120,7 @@ private:
     void startIO();
     AVCodecContext* getCurrentVideoAVCtx();
     void stopEncoder();
-    AVCodecContext* initCodec(AVMediaType mediaType, AVCodecID avcodecId, AVBufferRef* framesCtx, uint64_t br);
+    AVCodecContext* initCodec(AVMediaType mediaType, AVCodecID avcodecId, uint64_t br);
 
     std::vector<AVCodecContext*> encoders_;
     AVFormatContext *outputCtx_ = nullptr;
diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp
index 3894d8124424033fdd25b589c0b76a5fbe1b2114..906d52b6355b0565c933e9f9f90eac3e0c9c8361 100644
--- a/src/media/video/accel.cpp
+++ b/src/media/video/accel.cpp
@@ -2,6 +2,7 @@
  *  Copyright (C) 2004-2020 Savoir-faire Linux Inc.
  *
  *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
+ *  Author: Pierre Lespagnol <pierre.lespagnol@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
@@ -19,6 +20,7 @@
  */
 
 #include <algorithm>
+#include <thread> // hardware_concurrency
 
 #include "media_buffer.h"
 #include "string_utils.h"
@@ -29,15 +31,38 @@
 
 namespace jami { namespace video {
 
-struct HardwareAPI
-{
-    std::string name;
-    AVHWDeviceType hwType;
-    AVPixelFormat format;
-    AVPixelFormat swFormat;
-    std::vector<AVCodecID> supportedCodecs;
+static const std::list<HardwareAPI> apiListDec = {
+    { "nvdec", AV_HWDEVICE_TYPE_CUDA, AV_PIX_FMT_CUDA, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_VP8, AV_CODEC_ID_MJPEG }, { "0", "1", "2" } },
+    { "vaapi", AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VP8, AV_CODEC_ID_MJPEG }, { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },
+    { "vdpau", AV_HWDEVICE_TYPE_VDPAU, AV_PIX_FMT_VDPAU, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 }, { } },
+    { "videotoolbox", AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 }, { } },
+    { "qsv", AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8, AV_CODEC_ID_VP9 }, { } },
+};
+
+static const std::list<HardwareAPI> apiListEnc = {
+    { "nvenc", AV_HWDEVICE_TYPE_CUDA, AV_PIX_FMT_CUDA, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265 }, { "0", "1", "2" } },
+    { "vaapi", AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 }, { "/dev/dri/renderD128", "/dev/dri/renderD129", ":0" } },
+    { "videotoolbox", AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264 }, { } },
+    { "qsv", AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 }, { } },
 };
 
+HardwareAccel::HardwareAccel(AVCodecID id, const std::string& name, AVHWDeviceType hwType, AVPixelFormat format, AVPixelFormat swFormat, CodecType type)
+    : id_(id)
+    , name_(name)
+    , hwType_(hwType)
+    , format_(format)
+    , swFormat_(swFormat)
+    , type_(type)
+{}
+
+HardwareAccel::~HardwareAccel()
+{
+    if (deviceCtx_)
+        av_buffer_unref(&deviceCtx_);
+    if (framesCtx_)
+        av_buffer_unref(&framesCtx_);
+}
+
 static AVPixelFormat
 getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats)
 {
@@ -59,23 +84,81 @@ getFormatCb(AVCodecContext* codecCtx, const AVPixelFormat* formats)
     return fallback;
 }
 
-HardwareAccel::HardwareAccel(AVCodecID id, const std::string& name, AVHWDeviceType hwType, AVPixelFormat format, AVPixelFormat swFormat, CodecType type)
-    : id_(id)
-    , name_(name)
-    , hwType_(hwType)
-    , format_(format)
-    , swFormat_(swFormat)
-    , type_(type)
-{}
-
-HardwareAccel::~HardwareAccel()
+int
+HardwareAccel::test_device(const char* name,
+                        const char* device, int flags)
 {
-    if (deviceCtx_)
+    const AVHWDeviceContext* dev = nullptr;
+
+    // Create device ctx
+    int err;
+    err = av_hwdevice_ctx_create(&deviceCtx_, hwType_, device, NULL, flags);
+    if (err < 0) {
+        JAMI_DBG("Failed to create %s device: %d.\n", name, err);
+        return 1;
+    }
+
+    // Verify that the device create correspond to api
+    dev = (AVHWDeviceContext*)deviceCtx_->data;
+    if (dev->type != hwType_) {
+        JAMI_DBG("Device created as type %d has type %d.",
+                hwType_, dev->type);
         av_buffer_unref(&deviceCtx_);
-    if (framesCtx_)
-        av_buffer_unref(&framesCtx_);
+        return -1;
+    }
+    JAMI_DBG("Device type %s successfully created.", name);
+
+    return 0;
 }
 
+int
+HardwareAccel::test_device_type(std::string& dev)
+{
+    AVHWDeviceType check;
+    const char* name;
+    int err;
+
+    name = av_hwdevice_get_type_name(hwType_);
+    if (!name) {
+        JAMI_DBG("No name available for device type %d.", hwType_);
+        return -1;
+    }
+
+    check = av_hwdevice_find_type_by_name(name);
+    if (check != hwType_) {
+        JAMI_DBG("Type %d maps to name %s maps to type %d.",
+               hwType_, name, check);
+        return -1;
+    }
+
+    JAMI_WARN("-- Starting %s test for %s with default device.", (type_ == CODEC_ENCODER) ? "encoding" : "decoding", name);
+    err = test_device(name, nullptr, 0);
+    if (err == 0) {
+        JAMI_DBG("-- Test passed for %s with default device.", name);
+        dev = "default";
+        return 0;
+    } else {
+        JAMI_DBG("-- Test failed for %s with default device.", name);
+    }
+
+    for (const auto& device : possible_devices_) {
+        JAMI_WARN("-- Starting %s test for %s with device %s.", (type_ == CODEC_ENCODER) ? "encoding" : "decoding", name, device.c_str());
+        err = test_device(name, device.c_str(), 0);
+        if (err == 0) {
+            JAMI_DBG("-- Test passed for %s with device %s.",
+                    name, device.c_str());
+            dev = device;
+            return 0;
+        }
+        else {
+            JAMI_DBG("-- Test failed for %s with device %s.",
+                    name, device.c_str());
+        }
+    }
+    return -1;
+}
+
+
 std::string
 HardwareAccel::getCodecName() const
 {
@@ -116,7 +199,7 @@ HardwareAccel::transfer(const VideoFrame& frame)
         auto hwFrame = framePtr->pointer();
 
         if ((ret = av_hwframe_get_buffer(framesCtx_, hwFrame, 0)) < 0) {
-            JAMI_ERR() << "Failed to allocate hardware buffer: " << libav_utils::getError(ret);
+            JAMI_ERR() << "Failed to allocate hardware buffer: " << libav_utils::getError(ret).c_str();
             return nullptr;
         }
 
@@ -126,7 +209,7 @@ HardwareAccel::transfer(const VideoFrame& frame)
         }
 
         if ((ret = av_hwframe_transfer_data(hwFrame, input, 0)) < 0) {
-            JAMI_ERR() << "Failed to push frame to GPU: " << libav_utils::getError(ret);
+            JAMI_ERR() << "Failed to push frame to GPU: " << libav_utils::getError(ret).c_str();
             return nullptr;
         }
 
@@ -153,16 +236,7 @@ HardwareAccel::setDetails(AVCodecContext* codecCtx)
 }
 
 bool
-HardwareAccel::initDevice()
-{
-    int ret = av_hwdevice_ctx_create(&deviceCtx_, hwType_, nullptr, nullptr, 0);
-    if (ret < 0)
-        JAMI_ERR("Creating hardware device context failed: %s (%d)", libav_utils::getError(ret).c_str(), ret);
-    return ret >= 0;
-}
-
-bool
-HardwareAccel::initFrame(int width, int height)
+HardwareAccel::initFrame()
 {
     int ret = 0;
     if (!deviceCtx_) {
@@ -177,8 +251,8 @@ HardwareAccel::initFrame(int width, int height)
     auto ctx = reinterpret_cast<AVHWFramesContext*>(framesCtx_->data);
     ctx->format = format_;
     ctx->sw_format = swFormat_;
-    ctx->width = width;
-    ctx->height = height;
+    ctx->width = width_;
+    ctx->height = height_;
     ctx->initial_pool_size = 20; // TODO try other values
 
     if ((ret = av_hwframe_ctx_init(framesCtx_)) < 0) {
@@ -238,67 +312,40 @@ HardwareAccel::transferToMainMemory(const VideoFrame& frame, AVPixelFormat desir
     return out;
 }
 
-std::unique_ptr<HardwareAccel>
-HardwareAccel::setupDecoder(AVCodecID id, int width, int height)
+int
+HardwareAccel::initAPI(bool linkable, AVBufferRef* framesCtx)
 {
-    static const HardwareAPI apiList[] = {
-        { "nvdec", AV_HWDEVICE_TYPE_CUDA, AV_PIX_FMT_CUDA, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_VP8, AV_CODEC_ID_MJPEG } },
-        { "vaapi", AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4, AV_CODEC_ID_VP8, AV_CODEC_ID_MJPEG } },
-        { "vdpau", AV_HWDEVICE_TYPE_VDPAU, AV_PIX_FMT_VDPAU, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 } },
-        { "videotoolbox", AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MPEG4 } },
-        { "qsv", AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8, AV_CODEC_ID_VP9 } },
-    };
-
-    for (const auto& api : apiList) {
-        if (std::find(api.supportedCodecs.begin(), api.supportedCodecs.end(), id) != api.supportedCodecs.end()) {
-            auto accel = std::make_unique<HardwareAccel>(id, api.name, api.hwType, api.format, api.swFormat, CODEC_DECODER);
-            if (accel->initDevice()) {
-                 // we don't need frame context for videotoolbox
-                if (api.format == AV_PIX_FMT_VIDEOTOOLBOX ||
-                    accel->initFrame(width, height))  {
-                    JAMI_DBG() << "Attempting to use hardware decoder " << accel->getCodecName() << " with " << api.name;
-                    return accel;
-                }
-            }
+    const auto& codecName = getCodecName();
+    std::string device;
+    auto ret = test_device_type(device);
+    if(ret == 0) {
+        bool link = false;
+        if (linkable && framesCtx)
+            link = linkHardware(framesCtx);
+        // we don't need frame context for videotoolbox
+        if (format_ == AV_PIX_FMT_VIDEOTOOLBOX || link || initFrame()) {
+            return 0;
         }
     }
-
-    return nullptr;
+    return -1;
 }
 
-std::unique_ptr<HardwareAccel>
-HardwareAccel::setupEncoder(AVCodecID id, int width, int height, bool linkable, AVBufferRef* framesCtx)
+std::list<HardwareAccel>
+HardwareAccel::getCompatibleAccel(AVCodecID id, int width, int height, CodecType type)
 {
-    static const HardwareAPI apiList[] = {
-        { "nvenc", AV_HWDEVICE_TYPE_CUDA, AV_PIX_FMT_CUDA, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265 } },
-        { "vaapi", AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 } },
-        { "videotoolbox", AV_HWDEVICE_TYPE_VIDEOTOOLBOX, AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264 } },
-        { "qsv", AV_HWDEVICE_TYPE_QSV, AV_PIX_FMT_QSV, AV_PIX_FMT_NV12, { AV_CODEC_ID_H264, AV_CODEC_ID_H265, AV_CODEC_ID_MJPEG, AV_CODEC_ID_VP8 } },
-    };
-
-    for (auto api : apiList) {
+    std::list<HardwareAccel> l;
+    const auto& list = (type == CODEC_ENCODER) ? &apiListEnc : &apiListDec;
+    for (auto api : *list) {
         const auto& it = std::find(api.supportedCodecs.begin(), api.supportedCodecs.end(), id);
         if (it != api.supportedCodecs.end()) {
-            auto accel = std::make_unique<HardwareAccel>(id, api.name, api.hwType, api.format, api.swFormat, CODEC_ENCODER);
-            const auto& codecName = accel->getCodecName();
-            if (avcodec_find_encoder_by_name(codecName.c_str())) {
-                if (accel->initDevice()) {
-                    bool link = false;
-                    if (linkable)
-                        link = accel->linkHardware(framesCtx);
-                    // we don't need frame context for videotoolbox
-                    if (api.format == AV_PIX_FMT_VIDEOTOOLBOX ||
-                        link || accel->initFrame(width, height)) {
-                        JAMI_DBG() << "Attempting to use hardware encoder " << codecName << " with " << api.name;
-                        return accel;
-                    }
-                }
-            }
+            auto accel = HardwareAccel(id, api.name, api.hwType, api.format, api.swFormat, type);
+            accel.height_ = height;
+            accel.width_ = width;
+            accel.possible_devices_= api.possible_devices;
+            l.emplace_back(std::move(accel));
         }
     }
-
-    JAMI_WARN() << "Not using hardware encoding";
-    return nullptr;
+    return l;
 }
 
 }} // namespace jami::video
diff --git a/src/media/video/accel.h b/src/media/video/accel.h
index 099a20603cfeb94f070c8f4d2f405748e724c41e..8776b77598f51959c145bcbdac72ad2266495bed 100644
--- a/src/media/video/accel.h
+++ b/src/media/video/accel.h
@@ -2,6 +2,7 @@
  *  Copyright (C) 2004-2020 Savoir-faire Linux Inc.
  *
  *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
+ *  Author: Pierre Lespagnol <pierre.lespagnol@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
@@ -26,6 +27,7 @@
 #include <memory>
 #include <string>
 #include <vector>
+#include <list>
 
 extern "C" {
 #include <libavutil/hwcontext.h>
@@ -33,22 +35,21 @@ extern "C" {
 
 namespace jami { namespace video {
 
+struct HardwareAPI
+{
+    std::string name;
+    AVHWDeviceType hwType;
+    AVPixelFormat format;
+    AVPixelFormat swFormat;
+    std::vector<AVCodecID> supportedCodecs;
+    std::set<std::string> possible_devices;
+};
+
 /**
  * @brief Provides an abstraction layer to the hardware acceleration APIs in FFmpeg.
  */
 class HardwareAccel {
 public:
-    /**
-     * @brief Static factory method for hardware decoding.
-     */
-    static std::unique_ptr<HardwareAccel> setupDecoder(AVCodecID id, int width, int height);
-
-    /**
-     * @brief Static factory method for hardware encoding.
-     */
-    static std::unique_ptr<HardwareAccel> setupEncoder(AVCodecID id, int width, int height, bool linkable,
-        AVBufferRef* framesCtx = nullptr);
-
     /**
      * @brief Transfers hardware frame to main memory.
      *
@@ -146,9 +147,13 @@ public:
      */
     bool linkHardware(AVBufferRef* framesCtx);
 
+
+    static std::list<HardwareAccel> getCompatibleAccel(AVCodecID id, int width, int height, CodecType type);
+    int initAPI(bool linkable, AVBufferRef* framesCtx);
+
 private:
-    bool initDevice();
-    bool initFrame(int width, int height);
+    bool initDevice(const std::string& device);
+    bool initFrame();
 
     AVCodecID id_ {AV_CODEC_ID_NONE};
     std::string name_;
@@ -157,9 +162,16 @@ private:
     AVPixelFormat swFormat_ {AV_PIX_FMT_NONE};
     CodecType type_ {CODEC_NONE};
     bool linked_ {false};
+    int width_ {0};
+    int height_ {0};
 
     AVBufferRef* deviceCtx_ {nullptr};
     AVBufferRef* framesCtx_ {nullptr};
+
+    int test_device(const char* name, const char* device, int flags);
+    int test_device_type(std::string& dev);
+
+    std::set<std::string> possible_devices_;
 };
 
 }} // namespace jami::video