Commit 45faaa25 authored by Philippe Gorley's avatar Philippe Gorley Committed by Guillaume Roguez

video: add vdpau acceleration support

Adds the VDPAU acceleration for GNU/Linux systems and
enable it by devault. Can be disabled using ./configure --disable-vdpau

Support for H.264, H.263 and MPEG4, as long as the hardware supports
them.

Requires libvdpau and ffmpeg 3.3 (contrib bump included by this patch)

[guillaume roguez: re-word ci msg to be more explicit]

Change-Id: I7479f4d7e3d51caf702be8c85284a4fca01605b3
Reviewed-by: Guillaume Roguez's avatarGuillaume Roguez <guillaume.roguez@savoirfairelinux.com>
parent f62dccac
......@@ -442,7 +442,8 @@ dnl Ring video acceleration compile-time dependencies
AS_IF([test "${SYS}" = linux && test -z "${HAVE_ANDROID_FALSE}"], [
x11_available="no"
vaapi_available="no"
dnl ffmpeg's vaapi implementation depends on x11
vdpau_available="no"
dnl ffmpeg's vaapi and vdpau implementations depend on x11
PKG_CHECK_MODULES([X11], [x11], [x11_available="yes"], [])
AS_IF([test "${x11_available}" = "yes"], [
PKG_CHECK_MODULES([LIBVA], [libva], [
......@@ -454,13 +455,18 @@ AS_IF([test "${SYS}" = linux && test -z "${HAVE_ANDROID_FALSE}"], [
])
AC_CHECK_HEADERS([libavcodec/vaapi.h], [vaapi_available="yes"])
], [vaapi_available=no])
PKG_CHECK_MODULES([VDPAU], [vdpau], [
AC_CHECK_HEADERS([libavcodec/vdpau.h], [
AC_DEFINE([HAVE_VDPAU_ACCEL], [1], [Vdpau found])
vdpau_available="yes"
])
], [vdpau_available="no"])
])
])
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]))
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])
......@@ -470,9 +476,16 @@ AS_IF([test "x$enable_video" != "xno" -a "x$enable_accel" != "xno"], [
AC_DEFINE([RING_VAAPI], [1], [VAAPI is available in Ring])
])
])
AS_IF([test "x$enable_vdpau" != "xno"], [
AS_IF([test "x${vdpau_available}" = "xyes"], [
ring_vdpau="yes"
AC_DEFINE([RING_VDPAU], [1], [VDPAU 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"])
dnl check for GnuTLS
PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.4.14], [HAVE_GNUTLS=1], [HAVE_GNUTLS=0])
......
--- a/configure 2016-08-17 10:38:51.000000000 -0400
+++ b/configure 2016-08-17 10:38:53.000000000 -0400
@@ -5293,7 +5293,7 @@
--- a/configure 2017-05-17 11:54:39.000000000 -0500
+++ b/configure 2017-05-17 11:55:14.000000000 -0500
@@ -5549,7 +5549,7 @@
check_func access
check_func arc4random
check_func_headers stdlib.h arc4random
-check_func_headers time.h clock_gettime || { check_func_headers time.h clock_gettime -lrt && add_extralibs -lrt && LIBRT="-lrt"; }
+#check_func_headers time.h clock_gettime || { check_func_headers time.h clock_gettime -lrt && add_extralibs -lrt && LIBRT="-lrt"; }
check_func fcntl
......
FFMPEG_HASH := c46d22a4a58467bdc7885685b06a2114dd181c43
FFMPEG_HASH := f7e9275f83ec116fc859367d61998eae8af438fc
FFMPEG_URL := https://git.ffmpeg.org/gitweb/ffmpeg.git/snapshot/$(FFMPEG_HASH).tar.gz
ifdef HAVE_WIN32
......@@ -93,13 +93,17 @@ FFMPEGCONF += \
endif
ifdef HAVE_LINUX
ifndef HAVE_ANDROID
FFMPEGCONF += \
--disable-vdpau \
--enable-vdpau \
--enable-hwaccel=h264_vdpau \
--enable-hwaccel=mpeg4_vdpau \
--enable-vaapi \
--enable-hwaccel=h264_vaapi \
--enable-hwaccel=mpeg4_vaapi \
--enable-hwaccel=h263_vaapi
endif
endif
ifdef HAVE_MACOSX
FFMPEGCONF += \
......@@ -173,7 +177,7 @@ FFMPEGCONF += --target-os=mingw32 --enable-memalign-hack
FFMPEGCONF += --enable-w32threads --disable-decoder=dca
endif
ifeq ($(call need_pkg,"libavcodec >= 57.48.101 libavformat >= 57.41.100 libswscale >= 4.1.100 libavdevice >= 57.0.101 libavutil >= 55.28.100"),)
ifeq ($(call need_pkg,"libavcodec >= 57.89.100 libavformat >= 57.71.100 libswscale >= 4.6.100 libavdevice >= 57.6.100 libavutil >= 55.58.100"),)
PKGS_FOUND += ffmpeg
endif
......@@ -189,7 +193,6 @@ ffmpeg: ffmpeg-$(FFMPEG_HASH).tar.xz .sum-ffmpeg
mkdir -p $@-$(FFMPEG_HASH)
(cd $@-$(FFMPEG_HASH) && tar x $(if ${BATCH_MODE},,-v) --strip-components=1 -f ../$<)
$(UPDATE_AUTOCONFIG)
$(APPLY) $(SRC)/ffmpeg/0004-avformat-fix-find_stream_info-not-considering-extradata.patch
ifdef HAVE_IOS
$(APPLY) $(SRC)/ffmpeg/clock_gettime.patch
endif
......
......@@ -27,6 +27,10 @@
#include "v4l2/vaapi.h"
#endif
#ifdef RING_VDPAU
#include "v4l2/vdpau.h"
#endif
#include "string_utils.h"
#include "logger.h"
......@@ -150,6 +154,7 @@ std::unique_ptr<HardwareAccel>
makeHardwareAccel(AVCodecContext* codecCtx)
{
enum class AccelID {
Vdpau,
Vaapi,
};
......@@ -174,8 +179,11 @@ makeHardwareAccel(AVCodecContext* codecCtx)
* in this array.
*/
const AccelInfo accels[] = {
#if RING_VAAPI
#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
};
......@@ -184,6 +192,7 @@ makeHardwareAccel(AVCodecContext* codecCtx)
case AV_CODEC_ID_H264:
case AV_CODEC_ID_MPEG4:
case AV_CODEC_ID_H263P:
possibleAccels.push_back(AccelID::Vdpau);
possibleAccels.push_back(AccelID::Vaapi);
break;
case AV_CODEC_ID_VP8:
......
......@@ -6,12 +6,16 @@ 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 += @UDEV_CFLAGS@ @LIBVA_CFLAGS@ @LIBVA_DRM_CFLAGS@ @LIBVA_X11_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@
libv4l2_la_LIBADD += @UDEV_LIBS@ @X11_LIBS@ @LIBVA_LIBS@ @LIBVA_DRM_LIBS@ @LIBVA_X11_LIBS@
libv4l2_la_LIBADD += @UDEV_LIBS@ @X11_LIBS@ @VDPAU_LIBS@ @LIBVA_LIBS@ @LIBVA_DRM_LIBS@ @LIBVA_X11_LIBS@
/*
* 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)
{
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::check()
{
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
/*
* 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 check() 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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment