From 45faaa25d61303e9415464ed0c00662af4fc0321 Mon Sep 17 00:00:00 2001 From: philippegorley Date: Tue, 8 Nov 2016 10:23:19 -0500 Subject: [PATCH] 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 --- configure.ac | 19 +++- contrib/src/ffmpeg/clock_gettime.patch | 8 +- contrib/src/ffmpeg/rules.mak | 11 ++- src/media/video/accel.cpp | 11 ++- src/media/video/v4l2/Makefile.am | 8 +- src/media/video/v4l2/vdpau.cpp | 119 +++++++++++++++++++++++++ src/media/video/v4l2/vdpau.h | 64 +++++++++++++ 7 files changed, 226 insertions(+), 14 deletions(-) create mode 100644 src/media/video/v4l2/vdpau.cpp create mode 100644 src/media/video/v4l2/vdpau.h diff --git a/configure.ac b/configure.ac index 927869254..9b3689ef4 100644 --- a/configure.ac +++ b/configure.ac @@ -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]) diff --git a/contrib/src/ffmpeg/clock_gettime.patch b/contrib/src/ffmpeg/clock_gettime.patch index 26df8f291..f280b41aa 100644 --- a/contrib/src/ffmpeg/clock_gettime.patch +++ b/contrib/src/ffmpeg/clock_gettime.patch @@ -1,9 +1,9 @@ ---- 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 diff --git a/contrib/src/ffmpeg/rules.mak b/contrib/src/ffmpeg/rules.mak index be78ff85f..96b3e7ee5 100644 --- a/contrib/src/ffmpeg/rules.mak +++ b/contrib/src/ffmpeg/rules.mak @@ -1,4 +1,4 @@ -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 diff --git a/src/media/video/accel.cpp b/src/media/video/accel.cpp index 2146ca774..8ca385dc1 100644 --- a/src/media/video/accel.cpp +++ b/src/media/video/accel.cpp @@ -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 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 }, +#endif +#ifdef RING_VDPAU + { AccelID::Vdpau, "vdpau", AV_PIX_FMT_VDPAU, makeHardwareAccel }, #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: diff --git a/src/media/video/v4l2/Makefile.am b/src/media/video/v4l2/Makefile.am index f18e20484..05ff5a226 100644 --- a/src/media/video/v4l2/Makefile.am +++ b/src/media/video/v4l2/Makefile.am @@ -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@ diff --git a/src/media/video/v4l2/vdpau.cpp b/src/media/video/v4l2/vdpau.cpp new file mode 100644 index 000000000..cef1880be --- /dev/null +++ b/src/media/video/v4l2/vdpau.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2017 Savoir-faire Linux Inc. + * + * Author: Philippe Gorley + * + * 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 +#include +#include +#include +#include + +#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(deviceBufferRef_->data); + auto hardwareContext = static_cast(device->hwctx); + + framesBufferRef_.reset(av_hwframe_ctx_alloc(deviceBufferRef_.get())); + auto frames = reinterpret_cast(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 diff --git a/src/media/video/v4l2/vdpau.h b/src/media/video/v4l2/vdpau.h new file mode 100644 index 000000000..1b318fbf0 --- /dev/null +++ b/src/media/video/v4l2/vdpau.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Savoir-faire Linux Inc. + * + * Author: Philippe Gorley + * + * 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 + +#include +#include +#include +#include +#include +} + +#include "video/accel.h" + +#include +#include + +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>; + AVBufferRefPtr deviceBufferRef_; + AVBufferRefPtr framesBufferRef_; +}; + +}} // namespace ring::video + +#endif // RING_VDPAU