From b51ff044511307f60c9d089caa170e4e50a90c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=AE=E7=94=9F=E8=8B=A5=E6=A2=A6?= <1070753498@qq.com> Date: Tue, 21 Nov 2023 18:42:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=89=B2=E5=9F=9F=E8=BD=AC=E6=8D=A2=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 +- examples/player/colorspacedialog.hpp | 2 +- examples/player/mainwindow.cpp | 77 ++++-- examples/player/mainwindow.h | 3 + ffmpeg/CMakeLists.txt | 4 +- ffmpeg/colorspace.cc | 117 -------- ffmpeg/colorspace.hpp | 25 -- ffmpeg/colorutils.cc | 314 ++++++++++++++++++++++ ffmpeg/colorutils.hpp | 63 +++++ ffmpeg/ffmpeg.pro | 4 +- ffmpeg/ffmpegutils.hpp | 6 +- ffmpeg/hdrmetadata.hpp | 9 +- ffmpeg/videorender/openglrender.cc | 16 +- ffmpeg/videorender/openglshader.cc | 37 ++- ffmpeg/videorender/openglshader.hpp | 12 +- ffmpeg/videorender/openglshaderprogram.cc | 12 +- ffmpeg/videorender/shaderutils.cc | 28 ++ ffmpeg/videorender/shaderutils.hpp | 9 + ffmpeg/videorender/tonemap.cc | 4 - ffmpeg/videorender/tonemap.hpp | 2 +- ffmpeg/videorender/videorender.hpp | 8 + 21 files changed, 567 insertions(+), 206 deletions(-) delete mode 100644 ffmpeg/colorspace.cc delete mode 100644 ffmpeg/colorspace.hpp create mode 100644 ffmpeg/colorutils.cc create mode 100644 ffmpeg/colorutils.hpp diff --git a/README.md b/README.md index b5afebd..f3ff6b2 100644 --- a/README.md +++ b/README.md @@ -13,23 +13,16 @@ 2. 在WidgetRender中,尽可能使用QImage::Format_RGB32和QImage::Format_ARGB32_Premultiplied图像格式。如下原因: 1. Avoid most rendering directly to most of these formats using QPainter. Rendering is best optimized to the Format_RGB32 and Format_ARGB32_Premultiplied formats, and secondarily for rendering to the Format_RGB16, Format_RGBX8888, Format_RGBA8888_Premultiplied, Format_RGBX64 and Format_RGBA64_Premultiplied formats. -### 如何根据AVColorPrimaries、AVColorTransferCharacteristic、AVColorSpace调整图像? +### AVFrame 图像调整 + +1. 根据`AVColorSpace`进行色彩空间转换; +2. 根据`AVColorTransferCharacteristic`进行gamma、PQ、HLG等调整; +3. 根据`AVColorPrimaries`进行色域转换; +4. 根据`AVColorRange`进行色彩范围调整; #### 1. opengl 渲染的情况下,该怎么样修改shader? -1. 参考[MPV video_shaders](https://github.com/mpv-player/mpv/blob/master/video/out/gpu/video_shaders.c),效果也不是很好;应该是哪里有遗漏。 -2. HDR metadata获取 - - ```cpp - AVFrameSideData *mdm = av_frame_get_side_data(src, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); - AVFrameSideData *clm = av_frame_get_side_data(src, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); - AVFrameSideData *dhp = av_frame_get_side_data(src, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); - pl_map_hdr_metadata(&dst->params.color.hdr, &(struct pl_av_hdr_metadata) { - .mdm = (void *)(mdm ? mdm->data : NULL), - .clm = (void *)(clm ? clm->data : NULL), - .dhp = (void *)(dhp ? dhp->data : NULL), - }); - ``` +1. 参考[MPV video_shaders](https://github.com/mpv-player/mpv/blob/master/video/out/gpu/video_shaders.c); #### 2. 非opengl渲染的情况下,又该怎么样添加filter实现图像补偿? diff --git a/examples/player/colorspacedialog.hpp b/examples/player/colorspacedialog.hpp index dd95442..b9b8a81 100644 --- a/examples/player/colorspacedialog.hpp +++ b/examples/player/colorspacedialog.hpp @@ -13,7 +13,7 @@ class ColorSpaceDialog : public QDialog ~ColorSpaceDialog() override; void setColorSpace(const Ffmpeg::ColorSpaceTrc &colorTrc); - [[nodiscard]] Ffmpeg::ColorSpaceTrc colorSpace() const; + [[nodiscard]] auto colorSpace() const -> Ffmpeg::ColorSpaceTrc; signals: void colorSpaceChanged(); diff --git a/examples/player/mainwindow.cpp b/examples/player/mainwindow.cpp index 43a37e9..1eb20ba 100644 --- a/examples/player/mainwindow.cpp +++ b/examples/player/mainwindow.cpp @@ -137,7 +137,7 @@ class MainWindow::MainWindowPrivate controlWidget->setVisible(visible); } - bool setTitleWidgetVisible(bool visible) + auto setTitleWidgetVisible(bool visible) -> bool { if (videoRender.isNull()) { return false; @@ -203,6 +203,7 @@ class MainWindow::MainWindowPrivate QSplitter *splitter; Ffmpeg::Tonemap::Type tonemapType = Ffmpeg::Tonemap::Type::AUTO; + Ffmpeg::ColorUtils::Primaries::Type primarisType = Ffmpeg::ColorUtils::Primaries::Type::AUTO; }; MainWindow::MainWindow(QWidget *parent) @@ -325,6 +326,7 @@ void MainWindow::onRenderChanged(QAction *action) // 为什么切换成widget还是有使用GPU 0-3D,而且使用量是切换为opengl的两倍!!! d_ptr->videoRender.reset(videoRender); d_ptr->videoRender->setTonemapType(d_ptr->tonemapType); + d_ptr->videoRender->setDestPrimaries(d_ptr->primarisType); } void MainWindow::playlistPositionChanged(int currentItem) @@ -453,7 +455,7 @@ void MainWindow::onProcessEvents() } } -bool MainWindow::eventFilter(QObject *watched, QEvent *event) +auto MainWindow::eventFilter(QObject *watched, QEvent *event) -> bool { if (!d_ptr->videoRender.isNull() && watched == d_ptr->videoRender->widget()) { switch (event->type()) { @@ -621,6 +623,29 @@ void MainWindow::initMenu() connect(colorSpaceAction, &QAction::triggered, this, &MainWindow::onShowColorSpace); d_ptr->menu->addAction(colorSpaceAction); + tonemapMenu(); + destPrimarisMenu(); + renderMenu(); + + connect(d_ptr->audioTracksGroup, &QActionGroup::triggered, this, [this](QAction *action) { + d_ptr->playerPtr->addEvent( + Ffmpeg::EventPtr(new Ffmpeg::SelectedMediaTrackEvent(action->property("index").toInt(), + Ffmpeg::Event::AudioTarck))); + }); + connect(d_ptr->videoTracksGroup, &QActionGroup::triggered, this, [this](QAction *action) { + d_ptr->playerPtr->addEvent( + Ffmpeg::EventPtr(new Ffmpeg::SelectedMediaTrackEvent(action->property("index").toInt(), + Ffmpeg::Event::VideoTrack))); + }); + connect(d_ptr->subTracksGroup, &QActionGroup::triggered, this, [this](QAction *action) { + d_ptr->playerPtr->addEvent( + Ffmpeg::EventPtr(new Ffmpeg::SelectedMediaTrackEvent(action->property("index").toInt(), + Ffmpeg::Event::SubtitleTrack))); + }); +} + +void MainWindow::tonemapMenu() +{ auto *tonemapGroup = new QActionGroup(this); tonemapGroup->setExclusive(true); auto *tonemapMenu = new QMenu(tr("Tonemap"), this); @@ -645,7 +670,39 @@ void MainWindow::initMenu() d_ptr->videoRender->setTonemapType(d_ptr->tonemapType); }); tonemapGroup->checkedAction()->trigger(); +} +void MainWindow::destPrimarisMenu() +{ + auto *destPrimarisGroup = new QActionGroup(this); + destPrimarisGroup->setExclusive(true); + auto *destPrimarisMenu = new QMenu(tr("Dest Primaris"), this); + auto destPrimaris = QMetaEnum::fromType(); + for (int i = 0; i < destPrimaris.keyCount(); ++i) { + auto value = destPrimaris.value(i); + auto *action = new QAction(destPrimaris.key(i), this); + action->setCheckable(true); + action->setData(value); + destPrimarisGroup->addAction(action); + destPrimarisMenu->addAction(action); + if (value == Ffmpeg::ColorUtils::Primaries::Type::AUTO) { + action->setChecked(true); + } + } + d_ptr->menu->addMenu(destPrimarisMenu); + connect(destPrimarisGroup, &QActionGroup::triggered, this, [this](QAction *action) { + d_ptr->primarisType = static_cast( + action->data().toInt()); + if (d_ptr->videoRender.isNull()) { + return; + } + d_ptr->videoRender->setDestPrimaries(d_ptr->primarisType); + }); + destPrimarisGroup->checkedAction()->trigger(); +} + +void MainWindow::renderMenu() +{ auto *widgetAction = new QAction(tr("Widget"), this); widgetAction->setCheckable(true); widgetAction->setData(Ffmpeg::VideoRenderCreate::Widget); @@ -668,22 +725,6 @@ void MainWindow::initMenu() renderMenu->addAction(widgetAction); renderMenu->addAction(openglAction); d_ptr->menu->addMenu(renderMenu); - - connect(d_ptr->audioTracksGroup, &QActionGroup::triggered, this, [this](QAction *action) { - d_ptr->playerPtr->addEvent( - Ffmpeg::EventPtr(new Ffmpeg::SelectedMediaTrackEvent(action->property("index").toInt(), - Ffmpeg::Event::AudioTarck))); - }); - connect(d_ptr->videoTracksGroup, &QActionGroup::triggered, this, [this](QAction *action) { - d_ptr->playerPtr->addEvent( - Ffmpeg::EventPtr(new Ffmpeg::SelectedMediaTrackEvent(action->property("index").toInt(), - Ffmpeg::Event::VideoTrack))); - }); - connect(d_ptr->subTracksGroup, &QActionGroup::triggered, this, [this](QAction *action) { - d_ptr->playerPtr->addEvent( - Ffmpeg::EventPtr(new Ffmpeg::SelectedMediaTrackEvent(action->property("index").toInt(), - Ffmpeg::Event::SubtitleTrack))); - }); } void MainWindow::initPlayListMenu() diff --git a/examples/player/mainwindow.h b/examples/player/mainwindow.h index 8e0a8dd..191c9cf 100644 --- a/examples/player/mainwindow.h +++ b/examples/player/mainwindow.h @@ -38,6 +38,9 @@ private slots: void setupUI(); void buildConnect(); void initMenu(); + void tonemapMenu(); + void destPrimarisMenu(); + void renderMenu(); void initPlayListMenu(); void addToPlaylist(const QList &urls); diff --git a/ffmpeg/CMakeLists.txt b/ffmpeg/CMakeLists.txt index 0812c59..d7a4fc6 100644 --- a/ffmpeg/CMakeLists.txt +++ b/ffmpeg/CMakeLists.txt @@ -60,8 +60,8 @@ set(PROJECT_SOURCES clock.hpp codeccontext.cpp codeccontext.h - colorspace.cc - colorspace.hpp + colorutils.cc + colorutils.hpp decoder.cc decoder.h ffmepg_global.h diff --git a/ffmpeg/colorspace.cc b/ffmpeg/colorspace.cc deleted file mode 100644 index 9669648..0000000 --- a/ffmpeg/colorspace.cc +++ /dev/null @@ -1,117 +0,0 @@ -#include "colorspace.hpp" -#include "frame.hpp" - -extern "C" { -#include -#include -} - -namespace Ffmpeg::ColorSpace { - -static constexpr QVector3D kJPEG_full_offset = {0.000000F, -0.501961F, -0.501961F}; -static constexpr QVector3D kBT601_limited_offset = {-0.062745F, -0.501961F, -0.501961F}; -static constexpr QVector3D kBT709_full_offset = {0.000000F, -0.501961F, -0.501961F}; -static constexpr QVector3D kBT709_limited_offset = {-0.062745F, -0.501961F, -0.501961F}; -static constexpr QVector3D kBT2020_8bit_full_offset = {0.000000F, -0.501961F, -0.501961F}; -static constexpr QVector3D kBT2020_8bit_limited_offset = {-0.062745F, -0.501961F, -0.501961F}; -static constexpr QVector3D kBT2020_10bit_full_offset = {0.000000F, -0.500489F, -0.500489F}; -static constexpr QVector3D kBT2020_10bit_limited_offset = {-0.062561F, -0.500489F, -0.500489F}; -static constexpr QVector3D kBT2020_12bit_full_offset = {0.000000F, -0.500122F, -0.500122F}; -static constexpr QVector3D kBT2020_12bit_limited_offset = {-0.062515F, -0.500122F, -0.500122F}; - -static constexpr float kJPEG_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, - {-0.000000F, -0.344136F, 1.772000F}, - {1.402000F, -0.714136F, 0.000000F}}; -static constexpr float kRec601_limited_yuv_to_rgb[3][3] = {{1.164384F, 1.164384F, 1.164384F}, - {-0.000000F, -0.391762F, 2.017232F}, - {1.596027F, -0.812968F, 0.000000F}}; -static constexpr float kRec709_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, - {-0.000000F, -0.187324F, 1.855600F}, - {1.574800F, -0.468124F, -0.000000F}}; -static constexpr float kRec709_limited_yuv_to_rgb[3][3] = {{1.164384F, 1.164384F, 1.164384F}, - {-0.000000F, -0.213249F, 2.112402F}, - {1.792741F, -0.532909F, -0.000000F}}; -static constexpr float kBT2020_8bit_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, - {-0.000000F, -0.164553F, 1.881400F}, - {1.474600F, -0.571353F, -0.000000F}}; -static constexpr float kBT2020_8bit_limited_yuv_to_rgb[3][3] = {{1.164384F, 1.164384F, 1.164384F}, - {-0.000000F, -0.187326F, 2.141772F}, - {1.678674F, -0.650424F, -0.000000F}}; -static constexpr float kBT2020_10bit_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, - {-0.000000F, -0.164553F, 1.881400F}, - {1.474600F, -0.571353F, -0.000000F}}; -static constexpr float kBT2020_10bit_limited_yuv_to_rgb[3][3] - = {{1.167808F, 1.167808F, 1.167808F}, - {-0.000000F, -0.187877F, 2.148072F}, - {1.683611F, -0.652337F, -0.000000F}}; -static constexpr float kBT2020_12bit_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, - {-0.000000F, -0.164553F, 1.881400F}, - {1.474600F, -0.571353F, -0.000000F}}; -static constexpr float kBT2020_12bit_limited_yuv_to_rgb[3][3] - = {{1.168664F, 1.168664F, 1.168664F}, - {-0.000000F, -0.188015F, 2.149647F}, - {1.684846F, -0.652816F, -0.000000F}}; - -auto getYuvToRgbParam(Frame *frame) -> YuvToRgbParam -{ - auto *avFrame = frame->avFrame(); - bool isFullRange = avFrame->color_range == AVCOL_RANGE_JPEG; - YuvToRgbParam param; - switch (avFrame->colorspace) { - case AVCOL_SPC_BT709: - if (isFullRange) { - param.offset = kBT709_full_offset; - param.matrix = QMatrix3x3(&kRec709_full_yuv_to_rgb[0][0]); - } else { - param.offset = kBT709_limited_offset; - param.matrix = QMatrix3x3(&kRec709_limited_yuv_to_rgb[0][0]); - } - break; - case AVCOL_SPC_BT2020_NCL: { - int bitsPerPixel = av_get_bits_per_pixel( - av_pix_fmt_desc_get(static_cast(avFrame->format))); - switch (bitsPerPixel) { - case 8: - if (isFullRange) { - param.offset = kBT2020_8bit_full_offset; - param.matrix = QMatrix3x3(&kBT2020_8bit_full_yuv_to_rgb[0][0]); - } else { - param.offset = kBT2020_8bit_limited_offset; - param.matrix = QMatrix3x3(&kBT2020_8bit_limited_yuv_to_rgb[0][0]); - } - break; - case 12: - if (isFullRange) { - param.offset = kBT2020_12bit_full_offset; - param.matrix = QMatrix3x3(&kBT2020_12bit_full_yuv_to_rgb[0][0]); - } else { - param.offset = kBT2020_12bit_limited_offset; - param.matrix = QMatrix3x3(&kBT2020_12bit_limited_yuv_to_rgb[0][0]); - } - break; - default: - if (isFullRange) { - param.offset = kBT2020_10bit_full_offset; - param.matrix = QMatrix3x3(&kBT2020_10bit_full_yuv_to_rgb[0][0]); - } else { - param.offset = kBT2020_10bit_limited_offset; - param.matrix = QMatrix3x3(&kBT2020_10bit_limited_yuv_to_rgb[0][0]); - } - break; - } - break; - } - default: - if (isFullRange) { - param.offset = kJPEG_full_offset; - param.matrix = QMatrix3x3(&kJPEG_full_yuv_to_rgb[0][0]); - } else { - param.offset = kBT601_limited_offset; - param.matrix = QMatrix3x3(&kRec601_limited_yuv_to_rgb[0][0]); - } - break; - }; - return param; -} - -} // namespace Ffmpeg::ColorSpace diff --git a/ffmpeg/colorspace.hpp b/ffmpeg/colorspace.hpp deleted file mode 100644 index 295021b..0000000 --- a/ffmpeg/colorspace.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef COLORSPACE_HPP -#define COLORSPACE_HPP - -#include -#include - -namespace Ffmpeg { - -class Frame; - -namespace ColorSpace { - -struct YuvToRgbParam -{ - QVector3D offset; - QMatrix3x3 matrix; -}; - -auto getYuvToRgbParam(Frame *frame) -> YuvToRgbParam; - -} // namespace ColorSpace - -} // namespace Ffmpeg - -#endif // COLORSPACE_HPP diff --git a/ffmpeg/colorutils.cc b/ffmpeg/colorutils.cc new file mode 100644 index 0000000..94b6cfb --- /dev/null +++ b/ffmpeg/colorutils.cc @@ -0,0 +1,314 @@ +// Most of this code comes from mpv + +#include "colorutils.hpp" +#include "frame.hpp" + +extern "C" { +#include +#include +} + +namespace Ffmpeg { + +namespace ColorUtils { + +static constexpr QVector3D kJPEG_full_offset = {0.000000F, -0.501961F, -0.501961F}; +static constexpr QVector3D kBT601_limited_offset = {-0.062745F, -0.501961F, -0.501961F}; +static constexpr QVector3D kBT709_full_offset = {0.000000F, -0.501961F, -0.501961F}; +static constexpr QVector3D kBT709_limited_offset = {-0.062745F, -0.501961F, -0.501961F}; +static constexpr QVector3D kBT2020_8bit_full_offset = {0.000000F, -0.501961F, -0.501961F}; +static constexpr QVector3D kBT2020_8bit_limited_offset = {-0.062745F, -0.501961F, -0.501961F}; +static constexpr QVector3D kBT2020_10bit_full_offset = {0.000000F, -0.500489F, -0.500489F}; +static constexpr QVector3D kBT2020_10bit_limited_offset = {-0.062561F, -0.500489F, -0.500489F}; +static constexpr QVector3D kBT2020_12bit_full_offset = {0.000000F, -0.500122F, -0.500122F}; +static constexpr QVector3D kBT2020_12bit_limited_offset = {-0.062515F, -0.500122F, -0.500122F}; + +static constexpr float kJPEG_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, + {-0.000000F, -0.344136F, 1.772000F}, + {1.402000F, -0.714136F, 0.000000F}}; +static constexpr float kRec601_limited_yuv_to_rgb[3][3] = {{1.164384F, 1.164384F, 1.164384F}, + {-0.000000F, -0.391762F, 2.017232F}, + {1.596027F, -0.812968F, 0.000000F}}; +static constexpr float kRec709_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, + {-0.000000F, -0.187324F, 1.855600F}, + {1.574800F, -0.468124F, -0.000000F}}; +static constexpr float kRec709_limited_yuv_to_rgb[3][3] = {{1.164384F, 1.164384F, 1.164384F}, + {-0.000000F, -0.213249F, 2.112402F}, + {1.792741F, -0.532909F, -0.000000F}}; +static constexpr float kBT2020_8bit_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, + {-0.000000F, -0.164553F, 1.881400F}, + {1.474600F, -0.571353F, -0.000000F}}; +static constexpr float kBT2020_8bit_limited_yuv_to_rgb[3][3] = {{1.164384F, 1.164384F, 1.164384F}, + {-0.000000F, -0.187326F, 2.141772F}, + {1.678674F, -0.650424F, -0.000000F}}; +static constexpr float kBT2020_10bit_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, + {-0.000000F, -0.164553F, 1.881400F}, + {1.474600F, -0.571353F, -0.000000F}}; +static constexpr float kBT2020_10bit_limited_yuv_to_rgb[3][3] + = {{1.167808F, 1.167808F, 1.167808F}, + {-0.000000F, -0.187877F, 2.148072F}, + {1.683611F, -0.652337F, -0.000000F}}; +static constexpr float kBT2020_12bit_full_yuv_to_rgb[3][3] = {{1.000000F, 1.000000F, 1.000000F}, + {-0.000000F, -0.164553F, 1.881400F}, + {1.474600F, -0.571353F, -0.000000F}}; +static constexpr float kBT2020_12bit_limited_yuv_to_rgb[3][3] + = {{1.168664F, 1.168664F, 1.168664F}, + {-0.000000F, -0.188015F, 2.149647F}, + {1.684846F, -0.652816F, -0.000000F}}; + +auto getYuvToRgbParam(Frame *frame) -> YuvToRgbParam +{ + auto *avFrame = frame->avFrame(); + bool isFullRange = avFrame->color_range == AVCOL_RANGE_JPEG; + YuvToRgbParam param; + switch (avFrame->colorspace) { + case AVCOL_SPC_BT709: + if (isFullRange) { + param.offset = kBT709_full_offset; + param.matrix = QMatrix3x3(&kRec709_full_yuv_to_rgb[0][0]); + } else { + param.offset = kBT709_limited_offset; + param.matrix = QMatrix3x3(&kRec709_limited_yuv_to_rgb[0][0]); + } + break; + case AVCOL_SPC_BT2020_NCL: { + int bitsPerPixel = av_get_bits_per_pixel( + av_pix_fmt_desc_get(static_cast(avFrame->format))); + switch (bitsPerPixel) { + case 8: + if (isFullRange) { + param.offset = kBT2020_8bit_full_offset; + param.matrix = QMatrix3x3(&kBT2020_8bit_full_yuv_to_rgb[0][0]); + } else { + param.offset = kBT2020_8bit_limited_offset; + param.matrix = QMatrix3x3(&kBT2020_8bit_limited_yuv_to_rgb[0][0]); + } + break; + case 12: + if (isFullRange) { + param.offset = kBT2020_12bit_full_offset; + param.matrix = QMatrix3x3(&kBT2020_12bit_full_yuv_to_rgb[0][0]); + } else { + param.offset = kBT2020_12bit_limited_offset; + param.matrix = QMatrix3x3(&kBT2020_12bit_limited_yuv_to_rgb[0][0]); + } + break; + default: + if (isFullRange) { + param.offset = kBT2020_10bit_full_offset; + param.matrix = QMatrix3x3(&kBT2020_10bit_full_yuv_to_rgb[0][0]); + } else { + param.offset = kBT2020_10bit_limited_offset; + param.matrix = QMatrix3x3(&kBT2020_10bit_limited_yuv_to_rgb[0][0]); + } + break; + } + break; + } + default: + if (isFullRange) { + param.offset = kJPEG_full_offset; + param.matrix = QMatrix3x3(&kJPEG_full_yuv_to_rgb[0][0]); + } else { + param.offset = kBT601_limited_offset; + param.matrix = QMatrix3x3(&kRec601_limited_yuv_to_rgb[0][0]); + } + break; + }; + return param; +} + +auto getRawPrimaries(AVColorPrimaries color_primaries) -> RawPrimaries +{ + /* + Values from: ITU-R Recommendations BT.470-6, BT.601-7, BT.709-5, BT.2020-0 + + https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.470-6-199811-S!!PDF-E.pdf + https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.601-7-201103-I!!PDF-E.pdf + https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.709-5-200204-I!!PDF-E.pdf + https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2020-0-201208-I!!PDF-E.pdf + + Other colorspaces from https://en.wikipedia.org/wiki/RGB_color_space#Specifications + */ + + // CIE standard illuminant series + static constexpr QPointF d50 = {0.34577, 0.35850}, d65 = {0.31271, 0.32902}, + c = {0.31006, 0.31616}, dci = {0.31400, 0.35100}, + e = {1.0 / 3.0, 1.0 / 3.0}; + + RawPrimaries primaries; + switch (color_primaries) { + case AVCOL_PRI_BT709: + primaries.red = {0.640, 0.330}; + primaries.green = {0.300, 0.600}; + primaries.blue = {0.150, 0.060}; + primaries.white = d65; + break; + case AVCOL_PRI_BT470M: + primaries.red = {0.670, 0.330}; + primaries.green = {0.210, 0.710}; + primaries.blue = {0.140, 0.080}; + primaries.white = d65; + break; + case AVCOL_PRI_BT470BG: + primaries.red = {0.640, 0.330}; + primaries.green = {0.290, 0.600}; + primaries.blue = {0.150, 0.060}; + primaries.white = d65; + break; + case AVCOL_PRI_SMPTE170M: + case AVCOL_PRI_SMPTE240M: + primaries.red = {0.630, 0.340}; + primaries.green = {0.310, 0.595}; + primaries.blue = {0.155, 0.070}; + primaries.white = d65; + break; + case AVCOL_PRI_BT2020: + primaries.red = {0.708, 0.292}; + primaries.green = {0.170, 0.797}; + primaries.blue = {0.131, 0.046}; + primaries.white = d65; + break; + case AVCOL_PRI_SMPTE431: + case AVCOL_PRI_SMPTE432: + primaries.red = {0.680, 0.320}; + primaries.green = {0.265, 0.690}; + primaries.blue = {0.150, 0.060}; + primaries.white = color_primaries == AVCOL_PRI_SMPTE431 ? dci : d65; + break; + default: break; + } + return primaries; +} + +auto supportConvertColorPrimaries(AVColorPrimaries color_primaries) -> bool +{ + switch (color_primaries) { + case AVCOL_PRI_BT709: + case AVCOL_PRI_BT470M: + case AVCOL_PRI_BT470BG: + case AVCOL_PRI_SMPTE170M: + case AVCOL_PRI_SMPTE240M: + case AVCOL_PRI_BT2020: + case AVCOL_PRI_SMPTE431: + case AVCOL_PRI_SMPTE432: return true; + default: break; + } + return false; +} + +void getCMSMatrix(const RawPrimaries &src, const RawPrimaries &dest, float m[3][3]) +{ + float tmp[3][3]; + + // RGBd<-RGBs = RGBd<-XYZd * XYZd<-XYZs * XYZs<-RGBs + // Equations from: http://www.brucelindbloom.com/index.html?Math.html + // Note: Perceptual is treated like relative colorimetric. There's no + // definition for perceptual other than "make it look good". + + // RGBd<-XYZd, inverted from XYZd<-RGBd + getRgb2xyzMatrix(dest, m); + invertMatrix3x3(m); + + // XYZs<-RGBs + getRgb2xyzMatrix(src, tmp); + mulMatrix3x3(m, tmp); +} + +void getRgb2xyzMatrix(const RawPrimaries &space, float m[3][3]) +{ + float S[3], X[4], Z[4]; + + // Convert from CIE xyY to XYZ. Note that Y=1 holds true for all primaries + X[0] = space.red.x() / space.red.y(); + X[1] = space.green.x() / space.green.y(); + X[2] = space.blue.x() / space.blue.y(); + X[3] = space.white.x() / space.white.y(); + + Z[0] = (1 - space.red.x() - space.red.y()) / space.red.y(); + Z[1] = (1 - space.green.x() - space.green.y()) / space.green.y(); + Z[2] = (1 - space.blue.x() - space.blue.y()) / space.blue.y(); + Z[3] = (1 - space.white.x() - space.white.y()) / space.white.y(); + + // S = XYZ^-1 * W + for (int i = 0; i < 3; i++) { + m[0][i] = X[i]; + m[1][i] = 1; + m[2][i] = Z[i]; + } + + invertMatrix3x3(m); + + for (int i = 0; i < 3; i++) { + S[i] = m[i][0] * X[3] + m[i][1] * 1 + m[i][2] * Z[3]; + } + + // M = [Sc * XYZc] + for (int i = 0; i < 3; i++) { + m[0][i] = S[i] * X[i]; + m[1][i] = S[i] * 1; + m[2][i] = S[i] * Z[i]; + } +} + +void invertMatrix3x3(float m[3][3]) +{ + float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], + m20 = m[2][0], m21 = m[2][1], m22 = m[2][2]; + + // calculate the adjoint + m[0][0] = (m11 * m22 - m21 * m12); + m[0][1] = -(m01 * m22 - m21 * m02); + m[0][2] = (m01 * m12 - m11 * m02); + m[1][0] = -(m10 * m22 - m20 * m12); + m[1][1] = (m00 * m22 - m20 * m02); + m[1][2] = -(m00 * m12 - m10 * m02); + m[2][0] = (m10 * m21 - m20 * m11); + m[2][1] = -(m00 * m21 - m20 * m01); + m[2][2] = (m00 * m11 - m10 * m01); + + // calculate the determinant (as inverse == 1/det * adjoint, + // adjoint * m == identity * det, so this calculates the det) + float det = m00 * m[0][0] + m10 * m[0][1] + m20 * m[0][2]; + det = 1.0F / det; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + m[i][j] *= det; + } + } +} + +// A := A * B +void mulMatrix3x3(float a[3][3], float b[3][3]) +{ + float a00 = a[0][0], a01 = a[0][1], a02 = a[0][2], a10 = a[1][0], a11 = a[1][1], a12 = a[1][2], + a20 = a[2][0], a21 = a[2][1], a22 = a[2][2]; + + for (int i = 0; i < 3; i++) { + a[0][i] = a00 * b[0][i] + a01 * b[1][i] + a02 * b[2][i]; + a[1][i] = a10 * b[0][i] + a11 * b[1][i] + a12 * b[2][i]; + a[2][i] = a20 * b[0][i] + a21 * b[1][i] + a22 * b[2][i]; + } +} + +auto Primaries::getAVColorPrimaries(Type type) -> AVColorPrimaries +{ + switch (type) { + case AUTO: + case BT709: return AVCOL_PRI_BT709; + case BT470M: return AVCOL_PRI_BT470M; + case BT470BG: return AVCOL_PRI_BT470BG; + case SMPTE170M: return AVCOL_PRI_SMPTE170M; + case SMPTE240M: return AVCOL_PRI_SMPTE240M; + case BT2020: return AVCOL_PRI_BT2020; + case SMPTE431: return AVCOL_PRI_SMPTE431; + case SMPTE432: return AVCOL_PRI_SMPTE432; + default: break; + } + return AVCOL_PRI_RESERVED0; +} + +} // namespace ColorUtils + +} // namespace Ffmpeg diff --git a/ffmpeg/colorutils.hpp b/ffmpeg/colorutils.hpp new file mode 100644 index 0000000..aa490f7 --- /dev/null +++ b/ffmpeg/colorutils.hpp @@ -0,0 +1,63 @@ +// Most of this code comes from mpv + +#ifndef COLORUTILS_HPP +#define COLORUTILS_HPP + +#include "ffmepg_global.h" + +#include +#include +#include + +extern "C" { +#include +} + +namespace Ffmpeg { + +class Frame; + +namespace ColorUtils { + +struct RawPrimaries +{ + QPointF red = {0, 0}, green = {0, 0}, blue = {0, 0}, white = {0, 0}; +}; + +struct YuvToRgbParam +{ + QVector3D offset; + QMatrix3x3 matrix; +}; + +auto getYuvToRgbParam(Frame *frame) -> YuvToRgbParam; + +auto getRawPrimaries(AVColorPrimaries color_primaries) -> RawPrimaries; + +auto supportConvertColorPrimaries(AVColorPrimaries color_primaries) -> bool; + +void getCMSMatrix(const RawPrimaries &src, const RawPrimaries &dest, float m[3][3]); + +void getRgb2xyzMatrix(const RawPrimaries &space, float m[3][3]); + +void invertMatrix3x3(float m[3][3]); + +void mulMatrix3x3(float a[3][3], float b[3][3]); + +class FFMPEG_EXPORT Primaries : public QObject +{ + Q_OBJECT +public: + enum Type { AUTO, BT709, BT470M, BT470BG, SMPTE170M, SMPTE240M, BT2020, SMPTE431, SMPTE432 }; + Q_ENUM(Type); + + using QObject::QObject; + + static auto getAVColorPrimaries(Type type) -> AVColorPrimaries; +}; + +} // namespace ColorUtils + +} // namespace Ffmpeg + +#endif // COLORUTILS_HPP diff --git a/ffmpeg/ffmpeg.pro b/ffmpeg/ffmpeg.pro index 07cc1d6..a18c89d 100644 --- a/ffmpeg/ffmpeg.pro +++ b/ffmpeg/ffmpeg.pro @@ -24,7 +24,7 @@ SOURCES += \ averrormanager.cc \ clock.cc \ codeccontext.cpp \ - colorspace.cc \ + colorutils.cc \ decoder.cc \ ffmpegutils.cc \ formatcontext.cpp \ @@ -52,7 +52,7 @@ HEADERS += \ averrormanager.hpp \ clock.hpp \ codeccontext.h \ - colorspace.hpp \ + colorutils.hpp \ decoder.h \ ffmepg_global.h \ ffmpegutils.hpp \ diff --git a/ffmpeg/ffmpegutils.hpp b/ffmpeg/ffmpegutils.hpp index 1ebb8e8..bac8917 100644 --- a/ffmpeg/ffmpegutils.hpp +++ b/ffmpeg/ffmpegutils.hpp @@ -24,7 +24,7 @@ void FFMPEG_EXPORT printFfmpegInfo(); void calculatePts(Frame *frame, AVContextInfo *contextInfo, FormatContext *formatContext); void calculatePts(Packet *packet, AVContextInfo *contextInfo); -QVector getCurrentHWDeviceTypes(); +auto getCurrentHWDeviceTypes() -> QVector; auto getPixelFormat(const AVCodec *codec, AVHWDeviceType type) -> AVPixelFormat; @@ -36,9 +36,9 @@ struct CodecInfo QSize size = QSize(-1, -1); }; -QVector FFMPEG_EXPORT getFileCodecInfo(const QString &filePath); +auto FFMPEG_EXPORT getFileCodecInfo(const QString &filePath) -> QVector; -QPair FFMPEG_EXPORT getCodecQuantizer(const QString &codecname); +auto FFMPEG_EXPORT getCodecQuantizer(const QString &codecname) -> QPair; auto FFMPEG_EXPORT getCurrentSupportCodecs(AVMediaType mediaType, bool encoder) -> QStringList; diff --git a/ffmpeg/hdrmetadata.hpp b/ffmpeg/hdrmetadata.hpp index 836164e..b5ca8fb 100644 --- a/ffmpeg/hdrmetadata.hpp +++ b/ffmpeg/hdrmetadata.hpp @@ -1,17 +1,12 @@ #ifndef HDRMETADATA_HPP #define HDRMETADATA_HPP -#include +#include "colorutils.hpp" namespace Ffmpeg { class Frame; -struct RawPrimaries -{ - QPointF red = {0, 0}, green = {0, 0}, blue = {0, 0}, white = {0, 0}; -}; - struct HdrBezier { float targetLuma = 0; // target luminance (cd/m²) for this OOTF @@ -27,7 +22,7 @@ struct HdrMetaData explicit HdrMetaData(Frame *frame); float minLuma = 0, maxLuma = 0; - RawPrimaries primaries; + ColorUtils::RawPrimaries primaries; unsigned MaxCLL = 0, MaxFALL = 0; diff --git a/ffmpeg/videorender/openglrender.cc b/ffmpeg/videorender/openglrender.cc index dd8acb5..f71f3d4 100644 --- a/ffmpeg/videorender/openglrender.cc +++ b/ffmpeg/videorender/openglrender.cc @@ -2,7 +2,7 @@ #include "openglshader.hpp" #include "openglshaderprogram.hpp" -#include +#include #include #include #include @@ -60,6 +60,7 @@ class OpenglRender::OpenglRenderPrivate QSharedPointer subTitleFramePtr; bool subChanged = true; Tonemap::Type tonemapType; + ColorUtils::Primaries::Type destPrimaries; QColor backgroundColor = Qt::black; }; @@ -215,12 +216,18 @@ void OpenglRender::resetShader(Frame *frame) d_ptr->programPtr->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader/video.vert"); OpenglShader shader; d_ptr->programPtr->addShaderFromSourceCode(QOpenGLShader::Fragment, - shader.generate(frame, m_tonemapType)); + shader.generate(frame, + m_tonemapType, + m_destPrimaries)); glBindVertexArray(d_ptr->vao); d_ptr->programPtr->link(); d_ptr->programPtr->bind(); d_ptr->programPtr->initVertex("aPos", "aTexCord"); initTexture(); + if (shader.isConvertPrimaries()) { + d_ptr->programPtr->setUniformValue("cms_matrix", shader.convertPrimariesMatrix()); + qDebug() << "CMS matrix:" << shader.convertPrimariesMatrix(); + } d_ptr->programPtr->release(); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); @@ -232,10 +239,11 @@ void OpenglRender::onUpdateFrame(const QSharedPointer &framePtr) { if (d_ptr->framePtr.isNull() || d_ptr->framePtr->avFrame()->format != framePtr->avFrame()->format - || m_tonemapType != d_ptr->tonemapType) { + || m_tonemapType != d_ptr->tonemapType || m_destPrimaries != d_ptr->destPrimaries) { resetShader(framePtr.data()); d_ptr->frameChanged = true; d_ptr->tonemapType = m_tonemapType; + d_ptr->destPrimaries = m_destPrimaries; } else if (d_ptr->framePtr->avFrame()->width != framePtr->avFrame()->width || d_ptr->framePtr->avFrame()->height != framePtr->avFrame()->height) { d_ptr->frameChanged = true; @@ -287,7 +295,7 @@ void OpenglRender::paintVideoFrame() d_ptr->programPtr->setUniformValue("contrast", m_colorSpaceTrc.contrast); d_ptr->programPtr->setUniformValue("saturation", m_colorSpaceTrc.saturation); d_ptr->programPtr->setUniformValue("brightness", m_colorSpaceTrc.brightness); - auto param = Ffmpeg::ColorSpace::getYuvToRgbParam(d_ptr->framePtr.data()); + auto param = Ffmpeg::ColorUtils::getYuvToRgbParam(d_ptr->framePtr.data()); d_ptr->programPtr->setUniformValue("offset", param.offset); d_ptr->programPtr->setUniformValue("colorConversion", param.matrix); draw(); diff --git a/ffmpeg/videorender/openglshader.cc b/ffmpeg/videorender/openglshader.cc index cbcda07..0bc17d0 100644 --- a/ffmpeg/videorender/openglshader.cc +++ b/ffmpeg/videorender/openglshader.cc @@ -32,6 +32,16 @@ class OpenglShader::OpenglShaderPrivate dstColorTrc = AVCOL_TRC_GAMMA22; } dstHdrMetaData.maxLuma = ShaderUtils::trcNomPeak(dstColorTrc) * MP_REF_WHITE; + + if (dstPrimariesType == ColorUtils::Primaries::AUTO) { + dstPrimaries = avFrame->color_primaries; + if (dstPrimaries == AVCOL_PRI_SMPTE170M || dstPrimaries == AVCOL_PRI_BT470BG) { + } else { + dstPrimaries = AVCOL_PRI_BT709; + } + } else { + dstPrimaries = ColorUtils::Primaries::getAVColorPrimaries(dstPrimariesType); + } } OpenglShader *q_ptr; @@ -40,6 +50,11 @@ class OpenglShader::OpenglShaderPrivate HdrMetaData dstHdrMetaData; AVColorTransferCharacteristic dstColorTrc; + AVColorPrimaries dstPrimaries; + ColorUtils::Primaries::Type dstPrimariesType; + + bool isConvertPrimaries = false; + QMatrix3x3 convertPrimariesMatrix; }; OpenglShader::OpenglShader(QObject *parent) @@ -49,8 +64,11 @@ OpenglShader::OpenglShader(QObject *parent) OpenglShader::~OpenglShader() = default; -auto OpenglShader::generate(Frame *frame, Tonemap::Type type) -> QByteArray +auto OpenglShader::generate(Frame *frame, + Tonemap::Type type, + ColorUtils::Primaries::Type destPrimaries) -> QByteArray { + d_ptr->dstPrimariesType = destPrimaries; d_ptr->init(frame); auto *avFrame = frame->avFrame(); @@ -72,6 +90,13 @@ auto OpenglShader::generate(Frame *frame, Tonemap::Type type) -> QByteArray } Tonemap::toneMap(header, frag, type); + // Convert primaries + d_ptr->isConvertPrimaries = ShaderUtils::convertPrimaries(header, + frag, + avFrame->color_primaries, + d_ptr->dstPrimaries, + d_ptr->convertPrimariesMatrix); + //ShaderUtils::passInverseOotf(frag, d_ptr->dstHdrMetaData.maxLuma, avFrame->color_trc); ShaderUtils::passDeGama(frag, d_ptr->dstColorTrc); ShaderUtils::passDeLinearize(frag, d_ptr->dstColorTrc); @@ -82,4 +107,14 @@ auto OpenglShader::generate(Frame *frame, Tonemap::Type type) -> QByteArray return frag; } +auto OpenglShader::isConvertPrimaries() const -> bool +{ + return d_ptr->isConvertPrimaries; +} + +auto OpenglShader::convertPrimariesMatrix() const -> QMatrix3x3 +{ + return d_ptr->convertPrimariesMatrix; +} + } // namespace Ffmpeg diff --git a/ffmpeg/videorender/openglshader.hpp b/ffmpeg/videorender/openglshader.hpp index a219d15..02efa9d 100644 --- a/ffmpeg/videorender/openglshader.hpp +++ b/ffmpeg/videorender/openglshader.hpp @@ -3,6 +3,10 @@ #include "tonemap.hpp" +#include + +#include + namespace Ffmpeg { class Frame; @@ -12,7 +16,13 @@ class OpenglShader : public QObject explicit OpenglShader(QObject *parent = nullptr); ~OpenglShader() override; - auto generate(Frame *frame, Tonemap::Type type = Tonemap::Type::NONE) -> QByteArray; + auto generate(Frame *frame, + Tonemap::Type type = Tonemap::Type::NONE, + ColorUtils::Primaries::Type destPrimaries = ColorUtils::Primaries::Type::AUTO) + -> QByteArray; + + [[nodiscard]] auto isConvertPrimaries() const -> bool; + [[nodiscard]] auto convertPrimariesMatrix() const -> QMatrix3x3; private: class OpenglShaderPrivate; diff --git a/ffmpeg/videorender/openglshaderprogram.cc b/ffmpeg/videorender/openglshaderprogram.cc index 70ef24f..60e14b8 100644 --- a/ffmpeg/videorender/openglshaderprogram.cc +++ b/ffmpeg/videorender/openglshaderprogram.cc @@ -29,15 +29,15 @@ OpenGLShaderProgram::~OpenGLShaderProgram() void OpenGLShaderProgram::initVertex(const QString &pos, const QString &texCord) { - GLuint posAttr = GLuint(attributeLocation(pos)); - GLuint texCordAttr = GLuint(attributeLocation(texCord)); + auto posAttr = GLuint(attributeLocation(pos)); + auto texCordAttr = GLuint(attributeLocation(texCord)); float vertices[] = { // positions // texture coords - 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, // top right - 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // bottom right - -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // bottom left - -1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left + 1.0F, 1.0F, 0.0F, 1.0F, 1.0F, // top right + 1.0F, -1.0F, 0.0F, 1.0F, 0.0F, // bottom right + -1.0F, -1.0F, 0.0F, 0.0F, 0.0F, // bottom left + -1.0F, 1.0F, 0.0F, 0.0F, 1.0F // top left }; unsigned int indices[] = {0, 1, 3, 1, 2, 3}; diff --git a/ffmpeg/videorender/shaderutils.cc b/ffmpeg/videorender/shaderutils.cc index b33e34e..9b10a6d 100644 --- a/ffmpeg/videorender/shaderutils.cc +++ b/ffmpeg/videorender/shaderutils.cc @@ -2,6 +2,7 @@ #include "shaderutils.hpp" +#include #include namespace Ffmpeg::ShaderUtils { @@ -239,4 +240,31 @@ void passInverseOotf(QByteArray &frag, float peak, AVColorTransferCharacteristic } } +auto convertPrimaries(QByteArray &header, + QByteArray &frag, + AVColorPrimaries srcPrimaries, + AVColorPrimaries dstPrimaries, + QMatrix3x3 &matrix) -> bool +{ + frag.append("\n// convert primaries\n"); + if (srcPrimaries == dstPrimaries) { + return false; + } + if (!ColorUtils::supportConvertColorPrimaries(srcPrimaries)) { + return false; + } + if (!ColorUtils::supportConvertColorPrimaries(dstPrimaries)) { + return false; + } + + header.append(GLSL(uniform mat3 cms_matrix;\n)); + frag.append(GLSL(color.rgb = cms_matrix * color.rgb;\n)); + auto csp_src = ColorUtils::getRawPrimaries(srcPrimaries); + auto csp_dst = ColorUtils::getRawPrimaries(dstPrimaries); + float m[3][3] = {{0}}; + getCMSMatrix(csp_src, csp_dst, m); + matrix = QMatrix3x3(&m[0][0]); + return true; +} + } // namespace Ffmpeg::ShaderUtils diff --git a/ffmpeg/videorender/shaderutils.hpp b/ffmpeg/videorender/shaderutils.hpp index 061669b..92d746a 100644 --- a/ffmpeg/videorender/shaderutils.hpp +++ b/ffmpeg/videorender/shaderutils.hpp @@ -1,6 +1,9 @@ +// Most of this code comes from mpv + #ifndef SHADERUTILS_HPP #define SHADERUTILS_HPP +#include #include extern "C" { @@ -36,6 +39,12 @@ void passOotf(QByteArray &frag, float peak, AVColorTransferCharacteristic colort void passInverseOotf(QByteArray &frag, float peak, AVColorTransferCharacteristic colortTrc); +auto convertPrimaries(QByteArray &header, + QByteArray &frag, + AVColorPrimaries srcPrimaries, + AVColorPrimaries dstPrimaries, + QMatrix3x3 &matrix) -> bool; + void finishFragment(QByteArray &frag); void printShader(const QByteArray &frag); diff --git a/ffmpeg/videorender/tonemap.cc b/ffmpeg/videorender/tonemap.cc index 5be8474..3847e06 100644 --- a/ffmpeg/videorender/tonemap.cc +++ b/ffmpeg/videorender/tonemap.cc @@ -5,10 +5,6 @@ namespace Ffmpeg { -Tonemap::Tonemap(QObject *parent) - : QObject(parent) -{} - // Kodi // https://github.com/xbmc/xbmc/blob/1e499e091f7950c70366d64ab2d8c4f3a18cfbfa/system/shaders/GL/1.5/gl_tonemap.glsl#L4 // MPV diff --git a/ffmpeg/videorender/tonemap.hpp b/ffmpeg/videorender/tonemap.hpp index e6b9845..76c5e1a 100644 --- a/ffmpeg/videorender/tonemap.hpp +++ b/ffmpeg/videorender/tonemap.hpp @@ -26,7 +26,7 @@ class FFMPEG_EXPORT Tonemap : public QObject }; Q_ENUM(Type); - explicit Tonemap(QObject *parent = nullptr); + using QObject::QObject; static void toneMap(QByteArray &header, QByteArray &frag, Type type = NONE); }; diff --git a/ffmpeg/videorender/videorender.hpp b/ffmpeg/videorender/videorender.hpp index 15b2121..3af8590 100644 --- a/ffmpeg/videorender/videorender.hpp +++ b/ffmpeg/videorender/videorender.hpp @@ -3,6 +3,7 @@ #include "tonemap.hpp" +#include #include #include @@ -58,6 +59,12 @@ class FFMPEG_EXPORT VideoRender virtual void setTonemapType(Tonemap::Type type) { m_tonemapType = type; } [[nodiscard]] auto tonemapType() const -> Tonemap::Type { return m_tonemapType; } + virtual void setDestPrimaries(ColorUtils::Primaries::Type type) { m_destPrimaries = type; } + [[nodiscard]] auto destPrimaries() const -> ColorUtils::Primaries::Type + { + return m_destPrimaries; + } + virtual auto widget() -> QWidget * = 0; auto fps() -> float; @@ -70,6 +77,7 @@ class FFMPEG_EXPORT VideoRender ColorSpaceTrc m_colorSpaceTrc; Tonemap::Type m_tonemapType = Tonemap::Type::AUTO; + ColorUtils::Primaries::Type m_destPrimaries = ColorUtils::Primaries::AUTO; private: class VideoRenderPrivate;