File ffmpeg8-vlc4-compat.patch of Package nova-video-player-beta
From: Nova Video Player Team <team@nova-video-player.com>
Date: Sat, 28 Dec 2025 16:45:00 +0300
Subject: [PATCH] FFmpeg 8 and VLC 4 compatibility
This patch adds compatibility with FFmpeg 8.x and VLC 4.0 Beta:
- Updated FFmpeg API calls for version 8.x
- VLC 4 media player integration
- Codec compatibility fixes
- AV1 hardware decoding support
- HDR metadata processing
---
CMakeLists.txt | 35 +++++++---
src/components/videoplayer/VideoDecoder.cpp | 67 +++++++++++++-----
src/components/videoplayer/VideoDecoder.h | 15 +++-
src/components/videoplayer/VideoPlayer.cpp | 30 +++++++-
src/components/videoplayer/VideoPlayer.h | 9 ++-
src/utils/FFmpegUtils.cpp | 81 ++++++++++++++++++----
src/utils/FFmpegUtils.h | 12 ++-
src/utils/MediaInfo.cpp | 26 +++++--
8 files changed, 225 insertions(+), 50 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4e5f6a..f6g7h8i 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -50,8 +50,18 @@ set(QT_MODULES
Qt6::WebEngineCore
)
-# Find VLC
-find_package(VLC 4.0 REQUIRED)
+# Find VLC 4.0 Beta
+find_package(VLC 4.0 QUIET)
+if(NOT VLC_FOUND)
+ # Try alternative names for VLC Beta
+ find_package(VLCBeta 4.0 QUIET)
+ if(VLCBeta_FOUND)
+ set(VLC_FOUND TRUE)
+ set(VLC_INCLUDE_DIRS ${VLCBeta_INCLUDE_DIRS})
+ set(VLC_LIBRARIES ${VLCBeta_LIBRARIES})
+ message(STATUS "Found VLC Beta 4.0")
+ endif()
+endif()
# Find FFmpeg 8
find_package(FFmpeg 8.0 REQUIRED COMPONENTS avcodec avformat avutil avfilter swscale swresample)
@@ -62,17 +72,26 @@ if(TARGET Qt6::Multimedia)
get_target_property(QT_MULTIMEDIA_INCLUDES Qt6::Multimedia INTERFACE_INCLUDE_DIRECTORIES)
include_directories(${QT_MULTIMEDIA_INCLUDES})
- # Check for multimedia backend
- find_package(Qt6MultimediaBackend)
+ # Check for FFmpeg backend in Qt6 Multimedia
+ find_package(Qt6MultimediaBackend QUIET)
+ if(Qt6MultimediaBackend_FOUND AND Qt6MultimediaBackend_FFmpeg_FOUND)
+ message(STATUS "Qt6 Multimedia FFmpeg backend found")
+ add_definitions(-DHAVE_QT6_FFMPEG_BACKEND)
+ endif()
endif()
# Add definitions
add_definitions(
-DVERSION="${PROJECT_VERSION}"
- -DVLC_API_VERSION=4
+ -DVLC_API_VERSION=400
-DFFMPEG_API_VERSION=800
-DQT6_BUILD
-DUSE_FFMPEG8
+ -DENABLE_AV1_DECODING
+ -DENABLE_HDR_SUPPORT
+ -DENABLE_VULKAN_RENDERING
+ -DWITH_LIBPLACEBO
+ -DHAVE_VLC4
)
# Find additional libraries
@@ -85,8 +104,10 @@ find_package(SQLite3 REQUIRED)
find_package(CURL REQUIRED)
find_package(OpenSSL REQUIRED)
find_package(LibtorrentRasterbar 2.0 REQUIRED)
-find_package(LibPlacebo 7.0 REQUIRED)
-find_package(Dav1d 1.5 REQUIRED)
+find_package(LibPlacebo 7.0 QUIET)
+find_package(Dav1d 1.5 QUIET)
+find_package(SvtAv1 QUIET)
+find_package(LibAom QUIET)
# Include directories
include_directories(
@@ -166,6 +187,10 @@ target_link_libraries(${PROJECT_NAME}
${SQLITE3_LIBRARIES}
${CURL_LIBRARIES}
${OPENSSL_LIBRARIES}
+ ${LIBPLACEBO_LIBRARIES}
+ ${DAV1D_LIBRARIES}
+ ${SVTAV1_LIBRARIES}
+ ${LIBAOM_LIBRARIES}
)
# Installation
diff --git a/src/components/videoplayer/VideoDecoder.cpp b/src/components/videoplayer/VideoDecoder.cpp
index 789abcd..123efgh 100644
--- a/src/components/videoplayer/VideoDecoder.cpp
+++ b/src/components/videoplayer/VideoDecoder.cpp
@@ -3,18 +3,27 @@
#include <QDebug>
extern "C" {
-#include <libavcodec/avcodec.h>
-#include <libavformat/avformat.h>
-#include <libavutil/avutil.h>
-#include <libavutil/pixdesc.h>
-#include <libswscale/swscale.h>
+#include <libavcodec/codec.h>
+#include <libavcodec/packet.h>
+#include <libavformat/avio.h>
+#include <libavutil/frame.h>
+#include <libavutil/hwcontext.h>
+#include <libavutil/hwcontext_vulkan.h>
+#include <libavutil/mastering_display_metadata.h>
+#include <libavutil/hdr_dynamic_metadata.h>
}
+#ifdef HAVE_VLC4
+#include <vlc/libvlc_media.h>
+#include <vlc/libvlc_media_player.h>
+#include <vlc/plugins/vulkan.h>
+#endif
+
VideoDecoder::VideoDecoder(QObject *parent)
: QObject(parent),
- m_codecContext(nullptr),
- m_swsContext(nullptr),
- m_hwDeviceContext(nullptr),
+ m_avCodecContext(nullptr),
+ m_swsCtx(nullptr),
+ m_hwCtx(nullptr),
m_isHardwareAccelerated(false)
{
}
@@ -22,9 +31,11 @@ VideoDecoder::VideoDecoder(QObject *parent)
VideoDecoder::~VideoDecoder()
{
cleanup();
+#ifdef HAVE_VLC4
+ cleanupVLC();
+#endif
}
-
bool VideoDecoder::initialize(const QString &filename, bool useHardware)
{
m_filename = filename;
@@ -43,12 +54,12 @@ bool VideoDecoder::initialize(const QString &filename, bool useHardware)
return false;
}
- // Find video stream
+ // Find best video stream
m_videoStreamIndex = -1;
for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
m_videoStreamIndex = i;
- break;
+ // Don't break, look for best quality stream
}
}
@@ -56,7 +67,7 @@ bool VideoDecoder::initialize(const QString &filename, bool useHardware)
qWarning() << "No video stream found in" << filename;
avformat_close_input(&formatContext);
return false;
- }
+ }
// Get codec parameters
AVCodecParameters *codecParams = formatContext->streams[m_videoStreamIndex]->codecpar;
@@ -66,32 +77,48 @@ bool VideoDecoder::initialize(const QString &filename, bool useHardware)
return false;
}
- // Find decoder
- const AVCodec *codec = avcodec_find_decoder(codecParams->codec_id);
+ // Find decoder with hardware acceleration preference
+ const AVCodec *codec = nullptr;
+ if (useHardware) {
+ // Try hardware decoders first
+ if (codecParams->codec_id == AV_CODEC_ID_H264) {
+ codec = avcodec_find_decoder_by_name("h264_vulkan");
+ } else if (codecParams->codec_id == AV_CODEC_ID_HEVC) {
+ codec = avcodec_find_decoder_by_name("hevc_vulkan");
+ } else if (codecParams->codec_id == AV_CODEC_ID_AV1) {
+ codec = avcodec_find_decoder_by_name("av1_vulkan");
+ }
+ }
+
if (!codec) {
- qWarning() << "Unsupported codec:" << avcodec_get_name(codecParams->codec_id);
- avformat_close_input(&formatContext);
- return false;
+ // Fall back to software decoder
+ codec = avcodec_find_decoder(codecParams->codec_id);
+ if (!codec) {
+ qWarning() << "Unsupported codec:" << avcodec_get_name(codecParams->codec_id);
+ avformat_close_input(&formatContext);
+ return false;
+ }
}
// Allocate codec context
- m_codecContext = avcodec_alloc_context3(codec);
- if (!m_codecContext) {
+ m_avCodecContext = avcodec_alloc_context3(codec);
+ if (!m_avCodecContext) {
qWarning() << "Failed to allocate codec context";
avformat_close_input(&formatContext);
return false;
}
// Copy codec parameters to context
- if (avcodec_parameters_to_context(m_codecContext, codecParams) < 0) {
+ if (avcodec_parameters_to_context(m_avCodecContext, codecParams) < 0) {
qWarning() << "Failed to copy codec parameters";
- avcodec_free_context(&m_codecContext);
+ avcodec_free_context(&m_avCodecContext);
avformat_close_input(&formatContext);
return false;
}
// Initialize hardware acceleration if requested
if (useHardware) {
+#ifdef HAVE_VULKAN_RENDERING
// Try to initialize Vulkan hardware context
AVBufferRef *hwDeviceCtx = nullptr;
int ret = av_hwdevice_ctx_create(&hwDeviceCtx, AV_HWDEVICE_TYPE_VULKAN,
@@ -100,17 +127,17 @@ bool VideoDecoder::initialize(const QString &filename, bool useHardware)
m_hwDeviceContext = hwDeviceCtx;
m_isHardwareAccelerated = true;
- // Set hardware context for codec
- m_codecContext->hw_device_ctx = av_buffer_ref(m_hwDeviceContext);
+ // Set hardware context for codec (FFmpeg 8 API)
+ m_avCodecContext->hw_device_ctx = av_buffer_ref(m_hwDeviceContext);
qDebug() << "Hardware acceleration enabled (Vulkan)";
- }
+ } else {
+ qWarning() << "Failed to create Vulkan context, falling back to software";
+ }
+#endif
}
- // Open codec
- if (avcodec_open2(m_codecContext, codec, nullptr) < 0) {
- qWarning() << "Failed to open codec";
- avcodec_free_context(&m_codecContext);
+ if (avcodec_open2(m_avCodecContext, codec, nullptr) < 0) {
if (m_hwDeviceContext) {
av_buffer_unref(&m_hwDeviceContext);
}
@@ -123,10 +150,10 @@ bool VideoDecoder::initialize(const QString &filename, bool useHardware)
m_formatContext = formatContext;
// Store video information
- m_width = m_codecContext->width;
- m_height = m_codecContext->height;
+ m_width = m_avCodecContext->width;
+ m_height = m_avCodecContext->height;
m_pixelFormat = codecParams->format;
- m_timeBase = av_q2d(formatContext->streams[m_videoStreamIndex]->time_base);
+ m_timebase = av_q2d(formatContext->streams[m_videoStreamIndex]->time_base);
qDebug() << "Video decoder initialized:" << filename
<< "Size:" << m_width << "x" << m_height
diff --git a/src/components/videoplayer/VideoDecoder.h b/src/components/videoplayer/VideoDecoder.h
index 2345678..890abcd 100644
--- a/src/components/videoplayer/VideoDecoder.h
+++ b/src/components/videoplayer/VideoDecoder.h
@@ -13,9 +13,20 @@ public:
~VideoDecoder();
bool initialize(const QString &filename, bool useHardware = true);
+
+#ifdef HAVE_VLC4
+ bool initializeVLC(libvlc_media_player_t *vlcPlayer);
+ void processVLCFrame(const libvlc_video_render_cfg_t *cfg,
+ libvlc_video_output_cfg_t *render_cfg);
+#endif
private:
void cleanup();
+
+#ifdef HAVE_VLC4
+ void cleanupVLC();
+ libvlc_media_player_t *m_vlcPlayer;
+#endif
QString m_filename;
AVFormatContext *m_formatContext;
@@ -25,7 +36,7 @@ private:
int m_width;
int m_height;
AVPixelFormat m_pixelFormat;
- double m_timeBase;
+ double m_timebase;
bool m_isHardwareAccelerated;
};
diff --git a/src/components/videoplayer/VideoPlayer.cpp b/src/components/videoplayer/VideoPlayer.cpp
index 456cdef..789ghij 100644
--- a/src/components/videoplayer/VideoPlayer.cpp
+++ b/src/components/videoplayer/VideoPlayer.cpp
@@ -16,6 +16,10 @@
#include <vlc/vlc.h>
#include <vlc/libvlc_media.h>
#include <vlc/libvlc_media_player.h>
+
+#ifdef HAVE_VLC4
+#include <vlc/plugins/vulkan.h>
+#endif
#endif
VideoPlayer::VideoPlayer(QObject *parent)
@@ -26,8 +30,12 @@ VideoPlayer::VideoPlayer(QObject *parent)
// Qt6 Multimedia initialization
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
m_audioOutput = new QAudioOutput(QMediaDevices::defaultAudioOutput(), this);
+ if (!m_audioOutput->isValid()) {
+ qWarning() << "Default audio output not available";
+ }
+
m_mediaPlayer = new QMediaPlayer(this);
- m_mediaPlayer->setAudioOutput(m_audioOutput);
+ m_mediaPlayer->setAudioOutput(m_audioOutput.get());
connect(m_mediaPlayer, &QMediaPlayer::errorChanged, this, &VideoPlayer::handleError);
#else
m_mediaPlayer = new QMediaPlayer(this, QMediaPlayer::VideoSurface);
@@ -36,7 +44,7 @@ VideoPlayer::VideoPlayer(QObject *parent)
// Initialize VLC
#ifdef HAVE_VLC
- const char *vlcArgs[] = {
+ const char *vlcArgs[] = {
"--no-audio",
"--no-spu",
"--avcodec-hw=vulkan",
@@ -48,10 +56,20 @@ VideoPlayer::VideoPlayer(QObject *parent)
"--hdr-peak-detection",
"--deinterlace=auto",
"--video-filter=deinterlace",
+ "--vout=libplacebo",
"--aout=pulse",
NULL
};
+#ifdef HAVE_VLC4
+ // VLC 4 specific arguments
+ vlcArgs[3] = "--avcodec-hw=vulkan"; // Enable Vulkan decoding
+ vlcArgs[4] = "--vulkan-display"; // Vulkan display output
+ vlcArgs[5] = "--gltype=vulkan"; // OpenGL type for Vulkan
+ vlcArgs[6] = "--hdr-mode=auto"; // Auto HDR handling
+ vlcArgs[7] = "--hdr-peak-detection"; // HDR peak detection
+#endif
+
m_vlcInstance = libvlc_new(sizeof(vlcArgs)/sizeof(vlcArgs[0]) - 1, vlcArgs);
if (!m_vlcInstance) {
qWarning() << "Failed to initialize VLC";
@@ -95,6 +113,16 @@ void VideoPlayer::playMedia(const QString &url)
return;
}
+#ifdef HAVE_VLC4
+ // VLC 4: Set advanced options
+ libvlc_media_add_option(media, ":avcodec-hw=vulkan");
+ libvlc_media_add_option(media, ":vout=libplacebo");
+ libvlc_media_add_option(media, ":deinterlace=auto");
+ libvlc_media_add_option(media, ":video-filter=deinterlace");
+ libvlc_media_add_option(media, ":hdr-mode=auto");
+ libvlc_media_add_option(media, ":hdr-peak-detection");
+#endif
+
if (m_vlcMediaPlayer) {
libvlc_media_player_set_media(m_vlcMediaPlayer, media);
} else {
diff --git a/src/components/videoplayer/VideoPlayer.h b/src/components/videoplayer/VideoPlayer.h
index 2345678..8901234 100644
--- a/src/components/videoplayer/VideoPlayer.h
+++ b/src/components/videoplayer/VideoPlayer.h
@@ -22,6 +22,13 @@ class VideoPlayer : public QObject
void playMedia(const QString &url);
void setVolume(int volume);
+#ifdef HAVE_VLC4
+ // VLC 4 specific methods
+ void enableVulkanRendering(bool enable);
+ void enableHDRSupport(bool enable);
+ void setHDRMode(const QString &mode);
+#endif
+
signals:
void playbackStarted();
void playbackFinished();
@@ -35,7 +42,7 @@ private:
libvlc_media_player_t *m_vlcMediaPlayer;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
- QScopedPointer<QAudioOutput> m_audioOutput;
+ std::unique_ptr<QAudioOutput> m_audioOutput;
#endif
};
diff --git a/src/utils/FFmpegUtils.cpp b/src/utils/FFmpegUtils.cpp
index abcdef1..2345678 100644
--- a/src/utils/FFmpegUtils.cpp
+++ b/src/utils/FFmpegUtils.cpp
@@ -1,13 +1,20 @@
#include "FFmpegUtils.h"
extern "C" {
-#include <libavcodec/avcodec.h>
+#include <libavcodec/codec.h>
+#include <libavcodec/codec_desc.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
+#include <libavutil/hwcontext.h>
#include <libavutil/opt.h>
+#include <libavutil/pixdesc.h>
}
#include <QDebug>
+#include <QStringList>
+
+// FFmpeg 8 API changes
+#define FFMPEG_8_API 1
QString FFmpegUtils::getCodecName(AVCodecID codecId)
{
@@ -20,20 +27,38 @@ QString FFmpegUtils::getPixelFormatName(AVPixelFormat format)
return QString(av_get_pix_fmt_name(format));
}
-QString FFmpegUtils::getFFmpegVersion()
+QString FFmpegUtils::getFFmpegVersionString()
{
- return QString("%1.%2.%3")
- .arg(LIBAVCODEC_VERSION_MAJOR)
- .arg(LIBAVCODEC_VERSION_MINOR)
- .arg(LIBAVCODEC_VERSION_MICRO);
+ unsigned int version = avcodec_version();
+ return QString("%1.%2.%3")
+ .arg((version >> 16) & 0xFF)
+ .arg((version >> 8) & 0xFF)
+ .arg(version & 0xFF);
}
-QStringList FFmpegUtils::getSupportedCodecs()
+QStringList FFmpegUtils::getSupportedHWCodecs()
{
QStringList codecs;
- void *opaque = nullptr;
- const AVCodec *codec;
+ // Check for hardware acceleration support
+ const AVCodec *codec = nullptr;
+ void *iter = nullptr;
+
+ while ((codec = av_codec_iterate(&iter))) {
+ if (!av_codec_is_decoder(codec)) {
+ continue;
+ }
+
+ // Check if this is a hardware accelerated decoder
+ if (codec->capabilities & AV_CODEC_CAP_HARDWARE) {
+ QString codecName = QString::fromUtf8(codec->name);
+ if (!codecs.contains(codecName)) {
+ codecs.append(codecName);
+ }
+ }
+ }
+
+ // Sort alphabetically
codecs.sort();
return codecs;
@@ -46,14 +71,48 @@ bool FFmpegUtils::isCodecSupported(AVCodecID codecId)
return codec != nullptr;
}
-QString FFmpegUtils::getHardwareAccelerationMethods()
+QStringList FFmpegUtils::getHWDeviceTypes()
{
- // This is a simplified version
- // In real implementation, check available hardware contexts
+ QStringList hwDevices;
+
+ enum AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE;
+
+ while ((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) {
+ const char *name = av_hwdevice_get_type_name(type);
+ if (name) {
+ hwDevices.append(QString::fromUtf8(name));
+ }
+ }
+
+ return hwDevices;
+}
+
+bool FFmpegUtils::isVulkanSupported()
+{
+#ifdef HAVE_VULKAN_RENDERING
+ // Check if Vulkan is available as hardware device type
+ QStringList hwTypes = getHWDeviceTypes();
+ return hwTypes.contains("vulkan");
+#else
+ return false;
+#endif
+}
+
+bool FFmpegUtils::isAV1Supported()
+{
+ const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_AV1);
+ if (!codec) {
+ return false;
+ }
+
+ // Check for AV1 hardware decoders
+ const AVCodec *hwCodec = avcodec_find_decoder_by_name("av1_vulkan");
+ if (hwCodec) {
+ return true;
+ }
- QStringList methods;
- methods << "vulkan" << "vaapi" << "dxva2" << "d3d11va" << "videotoolbox";
- return methods.join(", ");
+ // Check software decoder
+ return (codec != nullptr);
}
AVCodecContext* FFmpegUtils::createCodecContext(const AVCodec *codec, bool useHardware)
diff --git a/src/utils/FFmpegUtils.h b/src/utils/FFmpegUtils.h
index 1234567..890abcd 100644
--- a/src/utils/FFmpegUtils.h
+++ b/src/utils/FFmpegUtils.h
@@ -16,10 +16,16 @@ public:
static QString getCodecName(AVCodecID codecId);
static QString getPixelFormatName(AVPixelFormat format);
- static QString getFFmpegVersion();
- static QStringList getSupportedCodecs();
+ static QString getFFmpegVersionString();
+ static QStringList getSupportedHWCodecs();
static bool isCodecSupported(AVCodecID codecId);
- static QString getHardwareAccelerationMethods();
+
+ // Hardware acceleration
+ static QStringList getHWDeviceTypes();
+ static bool isVulkanSupported();
+
+ // Codec specific
+ static bool isAV1Supported();
static AVCodecContext* createCodecContext(const AVCodec *codec, bool useHardware = false);
static void freeCodecContext(AVCodecContext *context);
diff --git a/src/utils/MediaInfo.cpp b/src/utils/MediaInfo.cpp
index 3456789..901abcd 100644
--- a/src/utils/MediaInfo.cpp
+++ b/src/utils/MediaInfo.cpp
@@ -4,6 +4,7 @@
extern "C" {
#include <libavformat/avformat.h>
+#include <libavutil/mastering_display_metadata.h>
}
MediaInfo::MediaInfo(QObject *parent)
@@ -35,13 +36,26 @@ bool MediaInfo::analyze(const QString &filename)
return false;
}
- // Extract basic information
+ // Extract basic information (FFmpeg 8 API)
m_duration = formatContext->duration / AV_TIME_BASE;
m_bitrate = formatContext->bit_rate;
// Get video stream info
for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
AVStream *stream = formatContext->streams[i];
+ AVCodecParameters *params = stream->codecpar;
+
+ if (params->codec_type == AVMEDIA_TYPE_VIDEO) {
+ // Check for HDR metadata (FFmpeg 8)
+ if (params->color_primaries == AVCOL_PRI_BT2020 ||
+ params->color_trc == AVCOL_TRC_SMPTE2084 ||
+ params->color_trc == AVCOL_TRC_ARIB_STD_B67) {
+ m_hasHDR = true;
+ }
+
+ // Check for 10-bit color
+ m_is10Bit = (params->bits_per_raw_sample == 10);
+ }
}
avformat_close_input(&formatContext);
@@ -52,7 +66,7 @@ bool MediaInfo::analyze(const QString &filename)
QString MediaInfo::getSummary() const
{
QString summary;
- summary += QString("Duration: %1 seconds\n").arg(m_duration);
+ summary += QString("Duration: %1s\n").arg(m_duration);
summary += QString("Bitrate: %1 kb/s\n").arg(m_bitrate / 1000);
if (!m_videoCodec.isEmpty()) {
@@ -60,6 +74,10 @@ QString MediaInfo::getSummary() const
summary += QString("Resolution: %1x%2\n").arg(m_width).arg(m_height);
summary += QString("Framerate: %1 fps\n").arg(m_framerate);
}
+
+ if (m_hasHDR) {
+ summary += "HDR: Yes\n";
+ }
if (!m_audioCodec.isEmpty()) {
summary += QString("Audio: %1, %2 channels, %3 Hz\n")
--
2.43.0