diff --git a/README.md b/README.md index 58b9213..6ebd680 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,19 @@ void mp_get_csp_matrix(struct mp_csp_params *params, struct mp_cmat *m); ``` +4. 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), + }); + ``` + ### OpenGL 渲染图像,怎么实现画质增强的效果? ### Ffmpeg(5.0)在解码字幕与4.4.3不太一样 diff --git a/examples/player/mainwindow.cpp b/examples/player/mainwindow.cpp index c27411c..a6d45c7 100644 --- a/examples/player/mainwindow.cpp +++ b/examples/player/mainwindow.cpp @@ -218,7 +218,7 @@ MainWindow::MainWindow(QWidget *parent) d_ptr->playlistView->installEventFilter(this); installEventFilter(this); - resize(1000, 650); + resize(1100, 680); } MainWindow::~MainWindow() diff --git a/ffmpeg/CMakeLists.txt b/ffmpeg/CMakeLists.txt index 5b1872c..e6afc40 100644 --- a/ffmpeg/CMakeLists.txt +++ b/ffmpeg/CMakeLists.txt @@ -30,6 +30,8 @@ set(PROJECT_SOURCES videorender/openglshader.hpp videorender/openglshaderprogram.cc videorender/openglshaderprogram.hpp + videorender/shaderutils.cc + videorender/shaderutils.hpp videorender/videopreviewwidget.cc videorender/videopreviewwidget.hpp videorender/videorender.cc diff --git a/ffmpeg/videorender/openglrender.cc b/ffmpeg/videorender/openglrender.cc index 549a412..24eb877 100644 --- a/ffmpeg/videorender/openglrender.cc +++ b/ffmpeg/videorender/openglrender.cc @@ -182,15 +182,6 @@ void OpenglRender::initSubTexture() glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } -void OpenglRender::setColorTrc() -{ - auto *avFrame = d_ptr->framePtr->avFrame(); - switch (avFrame->color_trc) { - case AVCOL_TRC_SMPTE2084: break; - default: break; - } -} - auto OpenglRender::fitToScreen(const QSize &size) -> QMatrix4x4 { auto factor_w = static_cast(width()) / size.width(); @@ -216,13 +207,13 @@ void OpenglRender::cleanup() } } -void OpenglRender::resetShader(int format) +void OpenglRender::resetShader(Frame *frame) { makeCurrent(); cleanup(); d_ptr->programPtr->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader/video.vert"); OpenglShader shader; - d_ptr->programPtr->addShaderFromSourceCode(QOpenGLShader::Fragment, shader.generate(format)); + d_ptr->programPtr->addShaderFromSourceCode(QOpenGLShader::Fragment, shader.generate(frame)); glBindVertexArray(d_ptr->vao); d_ptr->programPtr->link(); d_ptr->programPtr->bind(); @@ -239,7 +230,7 @@ void OpenglRender::onUpdateFrame(const QSharedPointer &framePtr) { if (d_ptr->framePtr.isNull() || d_ptr->framePtr->avFrame()->format != framePtr->avFrame()->format) { - resetShader(framePtr->avFrame()->format); + resetShader(framePtr.data()); d_ptr->frameChanged = true; } else if (d_ptr->framePtr->avFrame()->width != framePtr->avFrame()->width || d_ptr->framePtr->avFrame()->height != framePtr->avFrame()->height) { @@ -295,7 +286,6 @@ void OpenglRender::paintVideoFrame() auto param = Ffmpeg::ColorSpace::getYuvToRgbParam(d_ptr->framePtr.data()); d_ptr->programPtr->setUniformValue("offset", param.offset); d_ptr->programPtr->setUniformValue("colorConversion", param.matrix); - setColorTrc(); draw(); d_ptr->programPtr->release(); d_ptr->frameChanged = false; diff --git a/ffmpeg/videorender/openglrender.hpp b/ffmpeg/videorender/openglrender.hpp index a150099..83aef3e 100644 --- a/ffmpeg/videorender/openglrender.hpp +++ b/ffmpeg/videorender/openglrender.hpp @@ -41,14 +41,13 @@ class FFMPEG_EXPORT OpenglRender : public VideoRender, void initSubTexture(); auto fitToScreen(const QSize &size) -> QMatrix4x4; void cleanup(); - void resetShader(int format); + void resetShader(Frame *frame); void onUpdateFrame(const QSharedPointer &framePtr); void onUpdateSubTitleFrame(const QSharedPointer &framePtr); void paintVideoFrame(); void paintSubTitleFrame(); - void setColorTrc(); void updateYUV420P(); void updateYUYV422(); diff --git a/ffmpeg/videorender/openglshader.cc b/ffmpeg/videorender/openglshader.cc index b5c134f..3635abb 100644 --- a/ffmpeg/videorender/openglshader.cc +++ b/ffmpeg/videorender/openglshader.cc @@ -1,11 +1,15 @@ +// Most of this code comes from mpv + #include "openglshader.hpp" +#include "shaderutils.hpp" +#include #include #include extern "C" { -#include +#include } namespace Ffmpeg { @@ -27,35 +31,28 @@ OpenglShader::OpenglShader(QObject *parent) OpenglShader::~OpenglShader() = default; -QByteArray OpenglShader::generate(int format) +auto OpenglShader::generate(Frame *frame) -> QByteArray { + auto *avFrame = frame->avFrame(); + auto format = avFrame->format; qInfo() << "Generate Shader:" << format; - auto frag = Utils::readAllFile(":/shader/video_header.frag"); - frag.append("\n"); - frag.append(Utils::readAllFile(":/shader/video_color.frag")); - frag.append("\n"); - switch (format) { - case AV_PIX_FMT_YUV420P: - case AV_PIX_FMT_YUV422P: - case AV_PIX_FMT_YUV444P: - case AV_PIX_FMT_YUV410P: - case AV_PIX_FMT_YUV411P: frag.append(Utils::readAllFile(":/shader/video_yuv420p.frag")); break; - case AV_PIX_FMT_YUYV422: frag.append(Utils::readAllFile(":/shader/video_yuyv422.frag")); break; - case AV_PIX_FMT_RGB24: - case AV_PIX_FMT_BGR8: - case AV_PIX_FMT_RGB8: frag.append(Utils::readAllFile(":/shader/video_rgb24.frag")); break; - case AV_PIX_FMT_BGR24: frag.append(Utils::readAllFile(":/shader/video_bgr24.frag")); break; - case AV_PIX_FMT_UYVY422: frag.append(Utils::readAllFile(":/shader/video_uyvy422.frag")); break; - case AV_PIX_FMT_NV12: - case AV_PIX_FMT_P010LE: frag.append(Utils::readAllFile(":/shader/video_nv12.frag")); break; - case AV_PIX_FMT_NV21: frag.append(Utils::readAllFile(":/shader/video_nv21.frag")); break; - case AV_PIX_FMT_ARGB: frag.append(Utils::readAllFile(":/shader/video_argb.frag")); break; - case AV_PIX_FMT_RGBA: frag.append(Utils::readAllFile(":/shader/video_rgba.frag")); break; - case AV_PIX_FMT_ABGR: frag.append(Utils::readAllFile(":/shader/video_abgr.frag")); break; - case AV_PIX_FMT_BGRA: frag.append(Utils::readAllFile(":/shader/video_bgra.frag")); break; - default: qWarning() << "UnSupported format:" << format; break; + auto frag = ShaderUtils::header(); + if (!ShaderUtils::beginFragment(frag, format)) { + return {}; } - frag.append("\n"); + ShaderUtils::passLinearize(frag, avFrame->color_trc); + + auto temp = QString("color.rgb *= vec3(%1);\n").arg(ShaderUtils::trcNomPeak(avFrame->color_trc)); + frag.append(temp.toUtf8()); + + // HDR + // ShaderUtils::toneMap(frag, avFrame); + + auto color_trc = ShaderUtils::trcIsHdr(avFrame->color_trc) ? AVCOL_TRC_GAMMA22 + : avFrame->color_trc; + ShaderUtils::passDeLinearize(frag, color_trc); + ShaderUtils::finishFragment(frag); + ShaderUtils::printShader(frag); return frag; } diff --git a/ffmpeg/videorender/openglshader.hpp b/ffmpeg/videorender/openglshader.hpp index e961c71..0775b65 100644 --- a/ffmpeg/videorender/openglshader.hpp +++ b/ffmpeg/videorender/openglshader.hpp @@ -5,13 +5,14 @@ namespace Ffmpeg { +class Frame; class OpenglShader : public QObject { public: explicit OpenglShader(QObject *parent = nullptr); ~OpenglShader() override; - auto generate(int format) -> QByteArray; + auto generate(Frame *frame) -> QByteArray; private: class OpenglShaderPrivate; diff --git a/ffmpeg/videorender/shader/video_abgr.frag b/ffmpeg/videorender/shader/video_abgr.frag deleted file mode 100644 index dc18b70..0000000 --- a/ffmpeg/videorender/shader/video_abgr.frag +++ /dev/null @@ -1,10 +0,0 @@ -void main() -{ - vec4 color = texture(tex_y, TexCord).abgr; - - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} diff --git a/ffmpeg/videorender/shader/video_argb.frag b/ffmpeg/videorender/shader/video_argb.frag deleted file mode 100644 index f968029..0000000 --- a/ffmpeg/videorender/shader/video_argb.frag +++ /dev/null @@ -1,10 +0,0 @@ -void main() -{ - vec4 color = texture(tex_y, TexCord).gbar; - - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} diff --git a/ffmpeg/videorender/shader/video_bgr24.frag b/ffmpeg/videorender/shader/video_bgr24.frag deleted file mode 100644 index df2f82a..0000000 --- a/ffmpeg/videorender/shader/video_bgr24.frag +++ /dev/null @@ -1,11 +0,0 @@ -void main() -{ - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - color.rgb = texture(tex_y, TexCord).bgr; - - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} diff --git a/ffmpeg/videorender/shader/video_bgra.frag b/ffmpeg/videorender/shader/video_bgra.frag deleted file mode 100644 index 5b3214b..0000000 --- a/ffmpeg/videorender/shader/video_bgra.frag +++ /dev/null @@ -1,10 +0,0 @@ -void main() -{ - vec4 color = texture(tex_y, TexCord).bgra; - - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} diff --git a/ffmpeg/videorender/shader/video_nv12.frag b/ffmpeg/videorender/shader/video_nv12.frag index 21adacd..13986e5 100644 --- a/ffmpeg/videorender/shader/video_nv12.frag +++ b/ffmpeg/videorender/shader/video_nv12.frag @@ -1,17 +1,9 @@ -void main() -{ - vec3 yuv; - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv.x = texture(tex_y, TexCord).r; - yuv.yz = texture(tex_u, TexCord).rg; +vec3 yuv; +vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv += offset; - color.rgb = yuv * colorConversion; +yuv.x = texture(tex_y, TexCord).r; +yuv.yz = texture(tex_u, TexCord).rg; - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} +yuv += offset; +color.rgb = yuv * colorConversion; \ No newline at end of file diff --git a/ffmpeg/videorender/shader/video_nv21.frag b/ffmpeg/videorender/shader/video_nv21.frag index 2ac3d75..9811125 100644 --- a/ffmpeg/videorender/shader/video_nv21.frag +++ b/ffmpeg/videorender/shader/video_nv21.frag @@ -1,17 +1,9 @@ -void main() -{ - vec3 yuv; - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv.x = texture(tex_y, TexCord).r; - yuv.yz = texture(tex_u, TexCord).gr; +vec3 yuv; +vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv += offset; - color.rgb = yuv * colorConversion; +yuv.x = texture(tex_y, TexCord).r; +yuv.yz = texture(tex_u, TexCord).gr; - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} +yuv += offset; +color.rgb = yuv * colorConversion; diff --git a/ffmpeg/videorender/shader/video_p010le.frag b/ffmpeg/videorender/shader/video_p010le.frag index 21adacd..90ee6ac 100644 --- a/ffmpeg/videorender/shader/video_p010le.frag +++ b/ffmpeg/videorender/shader/video_p010le.frag @@ -1,17 +1,9 @@ -void main() -{ - vec3 yuv; - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv.x = texture(tex_y, TexCord).r; - yuv.yz = texture(tex_u, TexCord).rg; +vec3 yuv; +vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv += offset; - color.rgb = yuv * colorConversion; +yuv.x = texture(tex_y, TexCord).r; +yuv.yz = texture(tex_u, TexCord).rg; - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} +yuv += offset; +color.rgb = yuv * colorConversion; diff --git a/ffmpeg/videorender/shader/video_rgb24.frag b/ffmpeg/videorender/shader/video_rgb24.frag deleted file mode 100644 index 5981416..0000000 --- a/ffmpeg/videorender/shader/video_rgb24.frag +++ /dev/null @@ -1,11 +0,0 @@ -void main() -{ - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - color.rgb = texture(tex_y, TexCord).rgb; - - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} diff --git a/ffmpeg/videorender/shader/video_rgba.frag b/ffmpeg/videorender/shader/video_rgba.frag deleted file mode 100644 index 5878c23..0000000 --- a/ffmpeg/videorender/shader/video_rgba.frag +++ /dev/null @@ -1,10 +0,0 @@ -void main() -{ - vec4 color = texture(tex_y, TexCord).rgba; - - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} diff --git a/ffmpeg/videorender/shader/video_uyvy422.frag b/ffmpeg/videorender/shader/video_uyvy422.frag index fc10a3a..37292b0 100644 --- a/ffmpeg/videorender/shader/video_uyvy422.frag +++ b/ffmpeg/videorender/shader/video_uyvy422.frag @@ -1,25 +1,17 @@ -void main() -{ - vec3 yuv; - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - int width = textureSize(tex_y, 0).x * 2; - float tex_x = TexCord.x; - int pixel = int(floor(width * tex_x)) % 2; - vec4 tc = texture(tex_y, TexCord).rgba; - float cb = tc.r; - float y1 = tc.g; - float cr = tc.b; - float y2 = tc.a; - float y = (pixel == 1) ? y2 : y1; - yuv = vec3(y, cb, cr); +vec3 yuv; +vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv += offset; - color.rgb = yuv * colorConversion; +int width = textureSize(tex_y, 0).x * 2; +float tex_x = TexCord.x; +int pixel = int(floor(width * tex_x)) % 2; +vec4 tc = texture(tex_y, TexCord).rgba; +float cb = tc.r; +float y1 = tc.g; +float cr = tc.b; +float y2 = tc.a; +float y = (pixel == 1) ? y2 : y1; +yuv = vec3(y, cb, cr); - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} +yuv += offset; +color.rgb = yuv * colorConversion; diff --git a/ffmpeg/videorender/shader/video_yuv420p.frag b/ffmpeg/videorender/shader/video_yuv420p.frag index 53b1fd5..3b5b60d 100644 --- a/ffmpeg/videorender/shader/video_yuv420p.frag +++ b/ffmpeg/videorender/shader/video_yuv420p.frag @@ -1,18 +1,10 @@ -void main() -{ - vec3 yuv; - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv.x = texture(tex_y, TexCord).r; - yuv.y = texture(tex_u, TexCord).r; - yuv.z = texture(tex_v, TexCord).r; +vec3 yuv; +vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv += offset; - color.rgb = yuv * colorConversion; +yuv.x = texture(tex_y, TexCord).r; +yuv.y = texture(tex_u, TexCord).r; +yuv.z = texture(tex_v, TexCord).r; - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} +yuv += offset; +color.rgb = yuv * colorConversion; diff --git a/ffmpeg/videorender/shader/video_yuyv422.frag b/ffmpeg/videorender/shader/video_yuyv422.frag index bc9fcc8..790da68 100644 --- a/ffmpeg/videorender/shader/video_yuyv422.frag +++ b/ffmpeg/videorender/shader/video_yuyv422.frag @@ -1,16 +1,8 @@ -void main() -{ - vec3 yuv; - vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv.xyz = texture(tex_y, TexCord).rga; +vec3 yuv; +vec4 color = vec4(0.0, 0.0, 0.0, 1.0); - yuv += offset; - color.rgb = yuv * colorConversion; +yuv.xyz = texture(tex_y, TexCord).rga; - color.rgb = adjustContrast(color.rgb, contrast); - color.rgb = adjustSaturation(color.rgb, saturation); - color.rgb = adjustBrightness(color.rgb, brightness); - - FragColor = color; -} +yuv += offset; +color.rgb = yuv * colorConversion; diff --git a/ffmpeg/videorender/shaders.qrc b/ffmpeg/videorender/shaders.qrc index 483815e..44da32e 100644 --- a/ffmpeg/videorender/shaders.qrc +++ b/ffmpeg/videorender/shaders.qrc @@ -8,14 +8,8 @@ shader/video_nv12.frag shader/video_yuv420p.frag shader/video_yuyv422.frag - shader/video_rgb24.frag - shader/video_bgr24.frag shader/video_uyvy422.frag shader/video_nv21.frag - shader/video_argb.frag - shader/video_rgba.frag - shader/video_abgr.frag - shader/video_bgra.frag shader/video_color.frag shader/video_header.frag shader/video_p010le.frag diff --git a/ffmpeg/videorender/shaderutils.cc b/ffmpeg/videorender/shaderutils.cc new file mode 100644 index 0000000..5b2b470 --- /dev/null +++ b/ffmpeg/videorender/shaderutils.cc @@ -0,0 +1,225 @@ +#include "shaderutils.hpp" + +#include + +extern "C" { +#include +#include +} + +namespace Ffmpeg::ShaderUtils { + +// Common constants for SMPTE ST.2084 (HDR) +static const float PQ_M1 = 2610. / 4096 * 1. / 4, PQ_M2 = 2523. / 4096 * 128, PQ_C1 = 3424. / 4096, + PQ_C2 = 2413. / 4096 * 32, PQ_C3 = 2392. / 4096 * 32; +// Common constants for ARIB STD-B67 (HLG) +static const float HLG_A = 0.17883277F, HLG_B = 0.28466892F, HLG_C = 0.55991073F; + +auto trcNomPeak(AVColorTransferCharacteristic colortTrc) -> float +{ + switch (colortTrc) { + case AVCOL_TRC_SMPTEST2084: return static_cast(10000.0 / MP_REF_WHITE); + case AVCOL_TRC_ARIB_STD_B67: return static_cast(12.0 / MP_REF_WHITE_HLG); + default: break; + } + return 1.0; +} + +auto trcIsHdr(AVColorTransferCharacteristic colortTrc) -> bool +{ + return trcNomPeak(colortTrc) > 1.0; +} + +auto header() -> QByteArray +{ + auto frag = Utils::readAllFile(":/shader/video_header.frag"); + frag.append(GLSL(\n)); + frag.append(Utils::readAllFile(":/shader/video_color.frag")); + frag.append(GLSL(\nvoid main()\n)); + frag.append("{\n"); + return frag; +} + +auto beginFragment(QByteArray &frag, int format) -> bool +{ + switch (format) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUV410P: + case AV_PIX_FMT_YUV411P: frag.append(Utils::readAllFile(":/shader/video_yuv420p.frag")); break; + case AV_PIX_FMT_YUYV422: frag.append(Utils::readAllFile(":/shader/video_yuyv422.frag")); break; + case AV_PIX_FMT_RGB24: + case AV_PIX_FMT_BGR8: + case AV_PIX_FMT_RGB8: + frag.append(GLSL(vec4 color = vec4(0.0, 0.0, 0.0, 1.0);\n)); + frag.append(GLSL(color.rgb = texture(tex_y, TexCord).rgb;\n)); + break; + case AV_PIX_FMT_BGR24: + frag.append(GLSL(vec4 color = vec4(0.0, 0.0, 0.0, 1.0);\n)); + frag.append(GLSL(color.rgb = texture(tex_y, TexCord).bgr;\n)); + break; + case AV_PIX_FMT_UYVY422: frag.append(Utils::readAllFile(":/shader/video_uyvy422.frag")); break; + case AV_PIX_FMT_NV12: + case AV_PIX_FMT_P010LE: frag.append(Utils::readAllFile(":/shader/video_nv12.frag")); break; + case AV_PIX_FMT_NV21: frag.append(Utils::readAllFile(":/shader/video_nv21.frag")); break; + case AV_PIX_FMT_ARGB: frag.append(GLSL(vec4 color = texture(tex_y, TexCord).gbar;\n)); break; + case AV_PIX_FMT_RGBA: frag.append(GLSL(vec4 color = texture(tex_y, TexCord).rgba;\n)); break; + case AV_PIX_FMT_ABGR: frag.append(GLSL(vec4 color = texture(tex_y, TexCord).abgr;\n)); break; + case AV_PIX_FMT_BGRA: frag.append(GLSL(vec4 color = texture(tex_y, TexCord).bgra;\n)); break; + default: qWarning() << "UnSupported format:" << format; return false; + } + return true; +} + +void passLinearize(QByteArray &frag, AVColorTransferCharacteristic colortTrc) +{ + if (colortTrc == AVCOL_TRC_LINEAR) { + return; + } + frag.append("\n// pass linearize\n"); + frag.append(GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);\n)); + switch (colortTrc) { + case AVCOL_TRC_BT709: + case AVCOL_TRC_SMPTE170M: + case AVCOL_TRC_SMPTE240M: + case AVCOL_TRC_BT1361_ECG: + case AVCOL_TRC_BT2020_10: + case AVCOL_TRC_BT2020_12: frag.append(GLSL(color.rgb = pow(color.rgb, vec3(2.4));\n)); break; + case AVCOL_TRC_GAMMA22: frag.append(GLSL(color.rgb = pow(color.rgb, vec3(2.2));\n)); break; + case AVCOL_TRC_GAMMA28: frag.append(GLSL(color.rgb = pow(color.rgb, vec3(2.8));\n)); break; + case AVCOL_TRC_IEC61966_2_1: + frag.append("color.rgb = mix(color.rgb * vec3(1.0/12.92), \n" + " pow((color.rgb + vec3(0.055))/vec3(1.055), vec3(2.4)), \n" + " vec3(lessThan(vec3(0.04045), color.rgb))); \n"); + break; + case AVCOL_TRC_SMPTEST2084: { + auto temp = QString("color.rgb = pow(color.rgb, vec3(1.0/%1));\n").arg(PQ_M2); + temp.append( + QString("color.rgb = max(color.rgb - vec3(%1), vec3(0.0)) \n" + " / (vec3(%2) - vec3(%3) * color.rgb);\n") + .arg(QString::number(PQ_C1), QString::number(PQ_C2), QString::number(PQ_C3))); + temp.append(QString("color.rgb = pow(color.rgb, vec3(%1));\n").arg(1.0 / PQ_M1)); + // PQ's output range is 0-10000, but we need it to be relative to + // MP_REF_WHITE instead, so rescale + temp.append(QString("color.rgb *= vec3(%1);\n").arg(10000 / MP_REF_WHITE)); + frag.append(temp.toUtf8()); + } break; + case AVCOL_TRC_SMPTE428: + frag.append(GLSL(color.rgb = vec3(52.37 / 48.0) * pow(color.rgb, vec3(2.6));\n)); + break; + case AVCOL_TRC_ARIB_STD_B67: { + auto temp = QString("color.rgb *= vec3(%1);\n").arg(MP_REF_WHITE_HLG); + temp.append( + QString("color.rgb = mix(vec3(0.5) * sqrt(color.rgb),\n" + " vec3(%1) * log(color.rgb - vec3(%2)) + vec3(%3),\n" + " vec3(lessThan(vec3(1.0), color.rgb)));\n") + .arg(QString::number(HLG_A), QString::number(HLG_B), QString::number(HLG_C))); + frag.append(temp.toUtf8()); + } break; + default: break; + }; + auto temp = QString("color.rgb *= vec3(1.0/%1);\n").arg(trcNomPeak(colortTrc)); + frag.append(temp.toUtf8()); +} + +void passDeLinearize(QByteArray &frag, AVColorTransferCharacteristic colortTrc) +{ + if (colortTrc == AVCOL_TRC_LINEAR) { + return; + } + frag.append("\n// pass delinearize\n"); + frag.append(GLSL(color.rgb = clamp(color.rgb, 0.0, 1.0);\n)); + auto temp = QString("color.rgb *= vec3(%1);\n").arg(trcNomPeak(colortTrc)); + frag.append(temp.toUtf8()); + switch (colortTrc) { + case AVCOL_TRC_BT709: + case AVCOL_TRC_SMPTE170M: + case AVCOL_TRC_SMPTE240M: + case AVCOL_TRC_BT1361_ECG: + case AVCOL_TRC_BT2020_10: + case AVCOL_TRC_BT2020_12: + frag.append(GLSL(color.rgb = pow(color.rgb, vec3(1.0 / 2.4));\n)); + break; + case AVCOL_TRC_GAMMA22: + frag.append(GLSL(color.rgb = pow(color.rgb, vec3(1.0 / 2.2));\n)); + break; + case AVCOL_TRC_GAMMA28: + frag.append(GLSL(color.rgb = pow(color.rgb, vec3(1.0 / 2.8));\n)); + break; + case AVCOL_TRC_IEC61966_2_1: + frag.append("color.rgb = mix(color.rgb * vec3(12.92),\n" + " vec3(1.055) * pow(color.rgb, vec3(1.0/2.4)) - vec3(0.055),\n" + " vec3(lessThanEqual(vec3(0.0031308), color.rgb)));\n"); + break; + case AVCOL_TRC_SMPTEST2084: { + auto temp = QString("color.rgb *= vec3(1.0/%1);\n").arg(10000 / MP_REF_WHITE); + temp.append(QString("color.rgb = pow(color.rgb, vec3(%1));\n").arg(PQ_M1)); + temp.append( + QString("color.rgb = (vec3(%1) + vec3(%2) * color.rgb) \n" + " / (vec3(1.0) + vec3(%3) * color.rgb);\n") + .arg(QString::number(PQ_C1), QString::number(PQ_C2), QString::number(PQ_C3))); + temp.append(QString("color.rgb = pow(color.rgb, vec3(%1));\n").arg(PQ_M2)); + frag.append(temp.toUtf8()); + } break; + case AVCOL_TRC_SMPTE428: + frag.append(GLSL(color.rgb = pow(color.rgb * vec3(48.0 / 52.37), vec3(1.0 / 2.6));\n)); + break; + case AVCOL_TRC_ARIB_STD_B67: { + auto temp = QString("color.rgb *= vec3(%1);\n").arg(MP_REF_WHITE_HLG); + temp.append( + QString("color.rgb = mix(vec3(0.5) * sqrt(color.rgb),\n" + " vec3(%1) * log(color.rgb - vec3(%2)) + vec3(%3),\n" + " vec3(lessThan(vec3(1.0), color.rgb)));\n") + .arg(QString::number(HLG_A), QString::number(HLG_B), QString::number(HLG_C))); + frag.append(temp.toUtf8()); + } break; + default: break; + } +} + +void toneMap(QByteArray &frag, AVFrame *avFrame) +{ + // PL_LIBAV_API void pl_map_hdr_metadata(struct pl_hdr_metadata * out, + // const struct pl_av_hdr_metadata *data) + auto *mdm = av_frame_get_side_data(avFrame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + auto *clm = av_frame_get_side_data(avFrame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + auto *dhp = av_frame_get_side_data(avFrame, AV_FRAME_DATA_DYNAMIC_HDR_PLUS); + + if (mdm) { + auto *mdmPtr = reinterpret_cast(mdm); + } + + if (clm) { + auto *clmPtr = reinterpret_cast(clm); + } + + if (dhp) { + auto *dhpPtr = reinterpret_cast(dhp); + } + + // Kodi + // https://github.com/xbmc/xbmc/blob/1e499e091f7950c70366d64ab2d8c4f3a18cfbfa/system/shaders/GL/1.5/gl_tonemap.glsl#L4 + // MPV + // static void pass_tone_map(struct gl_shader_cache *sc, + // float src_peak, + // float dst_peak, + // const struct gl_tone_map_opts *opts) +} + +void finishFragment(QByteArray &frag) +{ + frag.append(GLSL(\n)); + frag.append(GLSL(color.rgb = adjustContrast(color.rgb, contrast);\n)); + frag.append(GLSL(color.rgb = adjustSaturation(color.rgb, saturation);\n)); + frag.append(GLSL(color.rgb = adjustBrightness(color.rgb, brightness);\n)); + frag.append(GLSL(FragColor = color;\n)); + frag.append("}\n"); +} + +void printShader(const QByteArray &frag) +{ + qInfo().noquote() << "Video fragment shader:\n" << frag; +} + +} // namespace Ffmpeg::ShaderUtils diff --git a/ffmpeg/videorender/shaderutils.hpp b/ffmpeg/videorender/shaderutils.hpp new file mode 100644 index 0000000..8d6a2ff --- /dev/null +++ b/ffmpeg/videorender/shaderutils.hpp @@ -0,0 +1,39 @@ +#ifndef SHADERUTILS_HPP +#define SHADERUTILS_HPP + +#include + +extern "C" { +#include +} + +#define GLSL(shader) #shader + +#define MP_REF_WHITE 203.0 +#define MP_REF_WHITE_HLG 3.17955 + +struct AVFrame; + +namespace Ffmpeg::ShaderUtils { + +auto trcNomPeak(AVColorTransferCharacteristic colortTrc) -> float; + +auto trcIsHdr(AVColorTransferCharacteristic colortTrc) -> bool; + +auto header() -> QByteArray; + +auto beginFragment(QByteArray &frag, int format) -> bool; + +void passLinearize(QByteArray &frag, AVColorTransferCharacteristic colortTrc); + +void passDeLinearize(QByteArray &frag, AVColorTransferCharacteristic colortTrc); + +void toneMap(QByteArray &frag, AVFrame *avFrame); + +void finishFragment(QByteArray &frag); + +void printShader(const QByteArray &frag); + +} // namespace Ffmpeg::ShaderUtils + +#endif // SHADERUTILS_HPP diff --git a/ffmpeg/videorender/videorender.pri b/ffmpeg/videorender/videorender.pri index a70abcb..6c2cbd1 100644 --- a/ffmpeg/videorender/videorender.pri +++ b/ffmpeg/videorender/videorender.pri @@ -5,6 +5,7 @@ HEADERS += \ $$PWD/openglrender.hpp \ $$PWD/openglshader.hpp \ $$PWD/openglshaderprogram.hpp \ + $$PWD/shaderutils.hpp \ $$PWD/videopreviewwidget.hpp \ $$PWD/videorender.hpp \ $$PWD/videorendercreate.hpp \ @@ -14,6 +15,7 @@ SOURCES += \ $$PWD/openglrender.cc \ $$PWD/openglshader.cc \ $$PWD/openglshaderprogram.cc \ + $$PWD/shaderutils.cc \ $$PWD/videopreviewwidget.cc \ $$PWD/videorender.cc \ $$PWD/videorendercreate.cc \