Skip to content
Snippets Groups Projects
Select Git revision
  • master default protected
  • release/202005
  • release/202001
  • release/201912
  • release/201911
  • release/releaseWindowsTestOne
  • release/windowsReleaseTest
  • release/releaseTest
  • release/releaseWindowsTest
  • release/201910
  • release/qt/201910
  • release/windows-test/201910
  • release/201908
  • release/201906
  • release/201905
  • release/201904
  • release/201903
  • release/201902
  • release/201901
  • release/201812
  • 4.0.0
  • 2.2.0
  • 2.1.0
  • 2.0.1
  • 2.0.0
  • 1.4.1
  • 1.4.0
  • 1.3.0
  • 1.2.0
  • 1.1.0
30 results

vaapi.cpp

Blame
    • Philippe Gorley's avatar
      274049bc
      video: fix hardware acceleration bugs · 274049bc
      Philippe Gorley authored
      Now correctly takes into account user setting. Will no longer try
      to retrieve data from the GPU buffer if acceleration has failed.
      Uses hardware acceleration when flushing the video stream.
      
      Change-Id: Id7787a181b3822e8c7da0e8c2ce2cdfa302a3ddd
      (cherry picked from commit c49f1368)
      274049bc
      History
      video: fix hardware acceleration bugs
      Philippe Gorley authored
      Now correctly takes into account user setting. Will no longer try
      to retrieve data from the GPU buffer if acceleration has failed.
      Uses hardware acceleration when flushing the video stream.
      
      Change-Id: Id7787a181b3822e8c7da0e8c2ce2cdfa302a3ddd
      (cherry picked from commit c49f1368)
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    vaapi.cpp 7.54 KiB
    /*
     *  Copyright (C) 2016 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_VIDEO) && defined(RING_ACCEL)
    
    #include "video/v4l2/vaapi.h"
    #include "video/accel.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(AccelInfo info) : HardwareAccel(info)
        , deviceBufferRef_(nullptr, avBufferRefDeleter)
        , framesBufferRef_(nullptr, avBufferRefDeleter)
    {
    }
    
    VaapiAccel::~VaapiAccel()
    {
    }
    
    int
    VaapiAccel::allocateBuffer(AVCodecContext* codecCtx, AVFrame* frame, int flags)
    {
        return av_hwframe_get_buffer(framesBufferRef_.get(), frame, 0);
    }
    
    bool
    VaapiAccel::extractData(AVCodecContext* codecCtx, VideoFrame& container)
    {
        try {
            auto input = container.pointer();
    
            if (input->format != format_) {
                std::stringstream buf;
                buf << "Frame format mismatch: expected " << av_get_pix_fmt_name(format_);
                buf << ", got " << av_get_pix_fmt_name((AVPixelFormat)input->format);
                throw std::runtime_error(buf.str());
            }
    
            auto outContainer = new VideoFrame();
            auto output = outContainer->pointer();
            output->format = AV_PIX_FMT_YUV420P;
    
            if (av_hwframe_transfer_data(output, input, 0) < 0) {
                throw std::runtime_error("Unable to extract data from VAAPI frame");
            }
    
            if (av_frame_copy_props(output, input) < 0 ) {
                av_frame_unref(output);
            }
    
            av_frame_unref(input);
            av_frame_move_ref(input, output);
        } catch (const std::runtime_error& e) {
            fail(codecCtx, false);
            RING_ERR("%s", e.what());
            return false;
        }
    
        succeed();
        return true;
    }
    
    bool
    VaapiAccel::init(AVCodecContext* codecCtx)
    {
        vaProfile_ = VAProfileNone;
        vaEntryPoint_ = VAEntrypointVLD;
        using ProfileMap = std::map<int, VAProfile>;
        ProfileMap h264 = {
            { FF_PROFILE_H264_CONSTRAINED_BASELINE, VAProfileH264ConstrainedBaseline },
            { FF_PROFILE_H264_BASELINE, VAProfileH264Baseline },
            { FF_PROFILE_H264_MAIN, VAProfileH264Main },
            { FF_PROFILE_H264_HIGH, VAProfileH264High }
        };
        ProfileMap mpeg4 = {
            { FF_PROFILE_MPEG4_SIMPLE, VAProfileMPEG4Simple },
            { FF_PROFILE_MPEG4_ADVANCED_SIMPLE, VAProfileMPEG4AdvancedSimple },
            { FF_PROFILE_MPEG4_MAIN, VAProfileMPEG4Main }
        };
        ProfileMap h263 = {
            { FF_PROFILE_UNKNOWN, VAProfileH263Baseline }
        };
    
        std::map<int, ProfileMap> profileMap = {
            { AV_CODEC_ID_H264, h264 },
            { AV_CODEC_ID_MPEG4, mpeg4 },
            { AV_CODEC_ID_H263, h263 },
            { AV_CODEC_ID_H263P, h263 }
        };
    
        VAStatus status;
    
    #ifdef HAVE_VAAPI_ACCEL_DRM
        const char* deviceName = "/dev/dri/card0"; // check for renderDX first?
    #else
        const char* deviceName = nullptr; // use default device
    #endif
    
        AVBufferRef* hardwareDeviceCtx;
        if (av_hwdevice_ctx_create(&hardwareDeviceCtx, AV_HWDEVICE_TYPE_VAAPI, deviceName, nullptr, 0) < 0) {
            RING_ERR("Failed to create VAAPI device");
            av_buffer_unref(&hardwareDeviceCtx);
            return false;
        }
    
        deviceBufferRef_.reset(av_buffer_ref(hardwareDeviceCtx));
    
        auto device = reinterpret_cast<AVHWDeviceContext*>(deviceBufferRef_->data);
        vaConfig_ = VA_INVALID_ID;
        vaContext_ = VA_INVALID_ID;
        auto hardwareContext = static_cast<AVVAAPIDeviceContext*>(device->hwctx);
    
        int numProfiles = vaMaxNumProfiles(hardwareContext->display);
        auto profiles = std::vector<VAProfile>(numProfiles);
        status = vaQueryConfigProfiles(hardwareContext->display, profiles.data(), &numProfiles);
        if (status != VA_STATUS_SUCCESS) {
            RING_ERR("Failed to query profiles: %s", vaErrorStr(status));
            return false;
        }
    
        VAProfile codecProfile;
        auto itOuter = profileMap.find(codecCtx->codec_id);
        if (itOuter != profileMap.end()) {
            auto innerMap = itOuter->second;
            auto itInner = innerMap.find(codecCtx->profile);
            if (itInner != innerMap.end()) {
                codecProfile = itInner->second;
            }
        }
    
        auto iter = std::find_if(std::begin(profiles),
                                 std::end(profiles),
                                 [codecProfile](const VAProfile& p){ return p == codecProfile; });
    
        if (iter == std::end(profiles)) {
            RING_ERR("VAAPI does not support selected codec");
            return false;
        }
    
        vaProfile_ = *iter;
    
        status = vaCreateConfig(hardwareContext->display, vaProfile_, vaEntryPoint_, 0, 0, &vaConfig_);
        if (status != VA_STATUS_SUCCESS) {
            RING_ERR("Failed to create VAAPI configuration: %s", vaErrorStr(status));
            return false;
        }
    
        auto hardwareConfig = static_cast<AVVAAPIHWConfig*>(av_hwdevice_hwconfig_alloc(deviceBufferRef_.get()));
        hardwareConfig->config_id = vaConfig_;
    
        auto constraints = av_hwdevice_get_hwframe_constraints(deviceBufferRef_.get(), hardwareConfig);
        if (width_ < constraints->min_width
            || width_ > constraints->max_width
            || height_ < constraints->min_height
            || height_ > constraints->max_height) {
            av_hwframe_constraints_free(&constraints);
            av_freep(&hardwareConfig);
            RING_ERR("Hardware does not support image size with VAAPI: %dx%d", width_, height_);
            return false;
        }
    
        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 = AV_PIX_FMT_VAAPI;
        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;
        }
    
        auto framesContext = static_cast<AVVAAPIFramesContext*>(frames->hwctx);
        status = vaCreateContext(hardwareContext->display, vaConfig_, width_, height_,
            VA_PROGRESSIVE, framesContext->surface_ids, framesContext->nb_surfaces, &vaContext_);
        if (status != VA_STATUS_SUCCESS) {
            RING_ERR("Failed to create VAAPI context: %s", vaErrorStr(status));
            return false;
        }
    
        RING_DBG("VAAPI decoder initialized");
    
        ffmpegAccelCtx_.display = hardwareContext->display;
        ffmpegAccelCtx_.config_id = vaConfig_;
        ffmpegAccelCtx_.context_id = vaContext_;
        codecCtx->hwaccel_context = (void*)&ffmpegAccelCtx_;
        return true;
    }
    
    }}
    
    #endif // defined(RING_VIDEO) && defined(RING_ACCEL)