diff options
| author | Carl Hetherington <cth@carlh.net> | 2022-10-18 20:26:05 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2022-10-18 20:26:05 +0200 |
| commit | 94eff17bdb94c170d456c7dab5ad3d4c00af4b14 (patch) | |
| tree | 54281a554d800b49c149a69fe037035f903498ad /src | |
| parent | 053bf81d7ef24843cc2eea62e24c2296fed48950 (diff) | |
| parent | c9a1e2040bf5600aeafdefe56a3bf46e15419a16 (diff) | |
Hopefully fix colour of Rec.2020 sources in the preview.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 32 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.h | 4 | ||||
| -rw-r--r-- | src/lib/image.cc | 2 | ||||
| -rw-r--r-- | src/lib/raw_image_proxy.cc | 7 | ||||
| -rw-r--r-- | src/lib/raw_image_proxy.h | 4 | ||||
| -rw-r--r-- | src/lib/video_filter_graph.cc | 48 | ||||
| -rw-r--r-- | src/lib/video_filter_graph.h | 3 | ||||
| -rw-r--r-- | src/lib/video_filter_graph_set.cc | 62 | ||||
| -rw-r--r-- | src/lib/video_filter_graph_set.h | 61 | ||||
| -rw-r--r-- | src/lib/wscript | 1 | ||||
| -rw-r--r-- | src/wx/colour_conversion_editor.cc | 1 | ||||
| -rw-r--r-- | src/wx/gl_video_view.cc | 79 | ||||
| -rw-r--r-- | src/wx/gl_video_view.h | 1 | ||||
| -rw-r--r-- | src/wx/simple_video_view.cc | 10 | ||||
| -rw-r--r-- | src/wx/simple_video_view.h | 7 |
15 files changed, 268 insertions, 54 deletions
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index a5b36d04f..27b7aa7b7 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -77,6 +77,7 @@ using namespace dcpomatic; FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> film, shared_ptr<const FFmpegContent> c, bool fast) : FFmpeg (c) , Decoder (film) + , _filter_graphs(c->filters(), dcp::Fraction(lrint(_ffmpeg_content->video_frame_rate().get_value_or(24) * 1000), 1000)) { if (c->video && c->video->use()) { video = make_shared<VideoDecoder>(this, c); @@ -401,13 +402,10 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) AVSEEK_FLAG_BACKWARD ); - { - /* Force re-creation of filter graphs to reset them and hence to make sure - they don't have any pre-seek frames knocking about. - */ - boost::mutex::scoped_lock lm (_filter_graphs_mutex); - _filter_graphs.clear (); - } + /* Force re-creation of filter graphs to reset them and hence to make sure + they don't have any pre-seek frames knocking about. + */ + _filter_graphs.clear(); if (video_codec_context ()) { avcodec_flush_buffers (video_codec_context()); @@ -580,25 +578,7 @@ FFmpegDecoder::decode_and_process_video_packet (AVPacket* packet) void FFmpegDecoder::process_video_frame () { - boost::mutex::scoped_lock lm (_filter_graphs_mutex); - - shared_ptr<VideoFilterGraph> graph; - - auto i = _filter_graphs.begin(); - while (i != _filter_graphs.end() && !(*i)->can_process(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format)) { - ++i; - } - - if (i == _filter_graphs.end ()) { - dcp::Fraction vfr (lrint(_ffmpeg_content->video_frame_rate().get() * 1000), 1000); - graph = make_shared<VideoFilterGraph>(dcp::Size(_video_frame->width, _video_frame->height), (AVPixelFormat) _video_frame->format, vfr); - graph->setup (_ffmpeg_content->filters ()); - _filter_graphs.push_back (graph); - LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _video_frame->width, _video_frame->height, _video_frame->format); - } else { - graph = *i; - } - + auto graph = _filter_graphs.get(dcp::Size(_video_frame->width, _video_frame->height), static_cast<AVPixelFormat>(_video_frame->format)); auto images = graph->process (_video_frame); for (auto const& i: images) { diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index e1a023453..9de44333c 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -27,6 +27,7 @@ #include "bitmap_text.h" #include "decoder.h" #include "ffmpeg.h" +#include "video_filter_graph_set.h" #include "util.h" extern "C" { #include <libavcodec/avcodec.h> @@ -76,8 +77,7 @@ private: void maybe_add_subtitle (); - std::list<std::shared_ptr<VideoFilterGraph>> _filter_graphs; - boost::mutex _filter_graphs_mutex; + VideoFilterGraphSet _filter_graphs; dcpomatic::ContentTime _pts_offset; boost::optional<dcpomatic::ContentTime> _current_subtitle_to; diff --git a/src/lib/image.cc b/src/lib/image.cc index 2ac84e26c..d84755df3 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -240,6 +240,7 @@ Image::crop_scale_window ( EnumIndexedVector<int, dcp::YUVToRGB> lut; lut[dcp::YUVToRGB::REC601] = SWS_CS_ITU601; lut[dcp::YUVToRGB::REC709] = SWS_CS_ITU709; + lut[dcp::YUVToRGB::REC2020] = SWS_CS_BT2020; /* The 3rd parameter here is: 0 -> source range MPEG (i.e. "video", 16-235) @@ -350,6 +351,7 @@ Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_fo EnumIndexedVector<int, dcp::YUVToRGB> lut; lut[dcp::YUVToRGB::REC601] = SWS_CS_ITU601; lut[dcp::YUVToRGB::REC709] = SWS_CS_ITU709; + lut[dcp::YUVToRGB::REC2020] = SWS_CS_BT2020; /* The 3rd parameter here is: 0 -> source range MPEG (i.e. "video", 16-235) diff --git a/src/lib/raw_image_proxy.cc b/src/lib/raw_image_proxy.cc index 9b486dd5c..c606ddd99 100644 --- a/src/lib/raw_image_proxy.cc +++ b/src/lib/raw_image_proxy.cc @@ -45,7 +45,7 @@ using boost::optional; using dcp::raw_convert; -RawImageProxy::RawImageProxy (shared_ptr<Image> image) +RawImageProxy::RawImageProxy(shared_ptr<const Image> image) : _image (image) { @@ -58,8 +58,9 @@ RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc xml->number_child<int>("Width"), xml->number_child<int>("Height") ); - _image = make_shared<Image>(static_cast<AVPixelFormat>(xml->number_child<int>("PixelFormat")), size, Image::Alignment::PADDED); - _image->read_from_socket (socket); + auto image = make_shared<Image>(static_cast<AVPixelFormat>(xml->number_child<int>("PixelFormat")), size, Image::Alignment::PADDED); + image->read_from_socket (socket); + _image = image; } diff --git a/src/lib/raw_image_proxy.h b/src/lib/raw_image_proxy.h index 1111b66c0..d5ae2457d 100644 --- a/src/lib/raw_image_proxy.h +++ b/src/lib/raw_image_proxy.h @@ -29,7 +29,7 @@ class RawImageProxy : public ImageProxy { public: - explicit RawImageProxy (std::shared_ptr<Image>); + explicit RawImageProxy(std::shared_ptr<const Image>); RawImageProxy (std::shared_ptr<cxml::Node> xml, std::shared_ptr<Socket> socket); Result image ( @@ -43,7 +43,7 @@ public: size_t memory_used () const override; private: - std::shared_ptr<Image> _image; + std::shared_ptr<const Image> _image; }; diff --git a/src/lib/video_filter_graph.cc b/src/lib/video_filter_graph.cc index 1cc142520..26f858437 100644 --- a/src/lib/video_filter_graph.cc +++ b/src/lib/video_filter_graph.cc @@ -21,6 +21,7 @@ #include "compose.hpp" #include "image.h" +#include "scope_guard.h" #include "video_filter_graph.h" extern "C" { #include <libavfilter/buffersrc.h> @@ -48,13 +49,56 @@ VideoFilterGraph::VideoFilterGraph (dcp::Size s, AVPixelFormat p, dcp::Fraction } +list<shared_ptr<const Image>> +VideoFilterGraph::process(shared_ptr<const Image> image) +{ + if (_copy) { + return { image }; + } + + auto frame = av_frame_alloc(); + if (!frame) { + throw std::bad_alloc(); + } + + ScopeGuard sg = [&frame]() { av_frame_free(&frame); }; + + for (int i = 0; i < image->planes(); ++i) { + frame->data[i] = image->data()[i]; + frame->linesize[i] = image->stride()[i]; + } + + frame->width = image->size().width; + frame->height = image->size().height; + frame->format = image->pixel_format(); + + int r = av_buffersrc_write_frame(_buffer_src_context, frame); + if (r < 0) { + throw DecodeError(String::compose(N_("could not push buffer into filter chain (%1)."), r)); + } + + list<shared_ptr<const Image>> images; + + while (true) { + if (av_buffersink_get_frame(_buffer_sink_context, _frame) < 0) { + break; + } + + images.push_back(make_shared<Image>(_frame, Image::Alignment::PADDED)); + av_frame_unref (_frame); + } + + return images; +} + + /** Take an AVFrame and process it using our configured filters, returning a * set of Images. Caller handles memory management of the input frame. */ -list<pair<shared_ptr<Image>, int64_t>> +list<pair<shared_ptr<const Image>, int64_t>> VideoFilterGraph::process (AVFrame* frame) { - list<pair<shared_ptr<Image>, int64_t>> images; + list<pair<shared_ptr<const Image>, int64_t>> images; if (_copy) { images.push_back (make_pair(make_shared<Image>(frame, Image::Alignment::PADDED), frame->best_effort_timestamp)); diff --git a/src/lib/video_filter_graph.h b/src/lib/video_filter_graph.h index d887e551b..1fb322282 100644 --- a/src/lib/video_filter_graph.h +++ b/src/lib/video_filter_graph.h @@ -28,7 +28,8 @@ public: VideoFilterGraph (dcp::Size s, AVPixelFormat p, dcp::Fraction r); bool can_process (dcp::Size s, AVPixelFormat p) const; - std::list<std::pair<std::shared_ptr<Image>, int64_t>> process (AVFrame * frame); + std::list<std::pair<std::shared_ptr<const Image>, int64_t>> process (AVFrame * frame); + std::list<std::shared_ptr<const Image>> process(std::shared_ptr<const Image> image); protected: std::string src_parameters () const override; diff --git a/src/lib/video_filter_graph_set.cc b/src/lib/video_filter_graph_set.cc new file mode 100644 index 000000000..dbae17d4c --- /dev/null +++ b/src/lib/video_filter_graph_set.cc @@ -0,0 +1,62 @@ +/* + Copyright (C) 2022 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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 2 of the License, or + (at your option) any later version. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#include "dcpomatic_log.h" +#include "video_filter_graph.h" +#include "video_filter_graph_set.h" + +#include "i18n.h" + + +using std::make_shared; +using std::shared_ptr; + + +shared_ptr<VideoFilterGraph> +VideoFilterGraphSet::get(dcp::Size size, AVPixelFormat format) +{ + auto graph = std::find_if( + _graphs.begin(), + _graphs.end(), + [size, format](shared_ptr<VideoFilterGraph> g) { + return g->can_process(size, format); + }); + + if (graph != _graphs.end()) { + return *graph; + } + + auto new_graph = make_shared<VideoFilterGraph>(size, format, _frame_rate); + new_graph->setup(_filters); + _graphs.push_back(new_graph); + + LOG_GENERAL(N_("New graph for %1x%2, pixel format %3"), size.width, size.height, static_cast<int>(format)); + + return new_graph; +} + + +void +VideoFilterGraphSet::clear() +{ + _graphs.clear(); +} + diff --git a/src/lib/video_filter_graph_set.h b/src/lib/video_filter_graph_set.h new file mode 100644 index 000000000..935378432 --- /dev/null +++ b/src/lib/video_filter_graph_set.h @@ -0,0 +1,61 @@ +/* + Copyright (C) 2022 Carl Hetherington <cth@carlh.net> + + This file is part of DCP-o-matic. + + DCP-o-matic 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 2 of the License, or + (at your option) any later version. + + DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>. + +*/ + + +#ifndef DCPOMATIC_VIDEO_FILTER_GRAPH_SET_H +#define DCPOMATIC_VIDEO_FILTER_GRAPH_SET_H + + +#include <dcp/types.h> +extern "C" { +#include <libavutil/avutil.h> +} +#include <memory> +#include <vector> + + +class Filter; +class VideoFilterGraph; + + +class VideoFilterGraphSet +{ +public: + VideoFilterGraphSet(std::vector<Filter const*> filters, dcp::Fraction frame_rate) + : _filters(filters) + , _frame_rate(frame_rate) + {} + + VideoFilterGraphSet(VideoFilterGraphSet const&) = delete; + VideoFilterGraphSet& operator=(VideoFilterGraphSet const&) = delete; + + std::shared_ptr<VideoFilterGraph> get(dcp::Size size, AVPixelFormat format); + + void clear(); + +private: + std::vector<Filter const*> _filters; + dcp::Fraction _frame_rate; + std::vector<std::shared_ptr<VideoFilterGraph>> _graphs; +}; + + +#endif + diff --git a/src/lib/wscript b/src/lib/wscript index 55c4e735f..26fcb21e5 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -194,6 +194,7 @@ sources = """ video_content.cc video_decoder.cc video_filter_graph.cc + video_filter_graph_set.cc video_mxf_content.cc video_mxf_decoder.cc video_mxf_examiner.cc diff --git a/src/wx/colour_conversion_editor.cc b/src/wx/colour_conversion_editor.cc index b7c054e27..4cc9aaa9e 100644 --- a/src/wx/colour_conversion_editor.cc +++ b/src/wx/colour_conversion_editor.cc @@ -118,6 +118,7 @@ ColourConversionEditor::ColourConversionEditor (wxWindow* parent, bool yuv) _yuv_to_rgb = new wxChoice (this, wxID_ANY); _yuv_to_rgb->Append (_("Rec. 601")); _yuv_to_rgb->Append (_("Rec. 709")); + _yuv_to_rgb->Append (_("Rec. 2020")); table->Add (_yuv_to_rgb, wxGBPosition (r, 1)); ++r; diff --git a/src/wx/gl_video_view.cc b/src/wx/gl_video_view.cc index 1f60af69d..4fc620936 100644 --- a/src/wx/gl_video_view.cc +++ b/src/wx/gl_video_view.cc @@ -197,13 +197,15 @@ static constexpr char fragment_source[] = /* type = 0: draw outline content rectangle * type = 1: draw crop guess rectangle * type = 2: draw XYZ image - * type = 3: draw RGB image + * type = 3: draw RGB image (with sRGB/Rec709 primaries) + * type = 4: draw RGB image (converting from Rec2020 primaries) * See FragmentType enum below. */ "uniform int type = 0;\n" "uniform vec4 outline_content_colour;\n" "uniform vec4 crop_guess_colour;\n" -"uniform mat4 colour_conversion;\n" +"uniform mat4 xyz_rec709_colour_conversion;\n" +"uniform mat4 rec2020_rec709_colour_conversion;\n" "\n" "out vec4 FragColor;\n" "\n" @@ -271,13 +273,17 @@ static constexpr char fragment_source[] = " FragColor.x = pow(FragColor.x, IN_GAMMA) / DCI_COEFFICIENT;\n" " FragColor.y = pow(FragColor.y, IN_GAMMA) / DCI_COEFFICIENT;\n" " FragColor.z = pow(FragColor.z, IN_GAMMA) / DCI_COEFFICIENT;\n" -" FragColor = colour_conversion * FragColor;\n" +" FragColor = xyz_rec709_colour_conversion * FragColor;\n" " FragColor.x = pow(FragColor.x, OUT_GAMMA);\n" " FragColor.y = pow(FragColor.y, OUT_GAMMA);\n" " FragColor.z = pow(FragColor.z, OUT_GAMMA);\n" " break;\n" " case 3:\n" " FragColor = texture_bicubic(texture_sampler, TexCoord);\n" +" break;\n" +" case 4:\n" +" FragColor = texture_bicubic(texture_sampler, TexCoord);\n" +" FragColor = rec2020_rec709_colour_conversion * FragColor;\n" " break;\n" " }\n" "}\n"; @@ -288,7 +294,8 @@ enum class FragmentType OUTLINE_CONTENT = 0, CROP_GUESS = 1, XYZ_IMAGE = 2, - RGB_IMAGE = 3, + REC709_IMAGE = 3, + REC2020_IMAGE = 4, }; @@ -453,18 +460,48 @@ GLVideoView::setup_shaders () set_outline_content_colour (program); set_crop_guess_colour (program); - auto conversion = dcp::ColourConversion::rec709_to_xyz(); - boost::numeric::ublas::matrix<double> matrix = conversion.xyz_to_rgb (); - GLfloat gl_matrix[] = { - static_cast<float>(matrix(0, 0)), static_cast<float>(matrix(0, 1)), static_cast<float>(matrix(0, 2)), 0.0f, - static_cast<float>(matrix(1, 0)), static_cast<float>(matrix(1, 1)), static_cast<float>(matrix(1, 2)), 0.0f, - static_cast<float>(matrix(2, 0)), static_cast<float>(matrix(2, 1)), static_cast<float>(matrix(2, 2)), 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f - }; + auto ublas_to_gl = [](boost::numeric::ublas::matrix<double> const& ublas, GLfloat* gl) { + gl[0] = static_cast<float>(ublas(0, 0)); + gl[1] = static_cast<float>(ublas(0, 1)); + gl[2] = static_cast<float>(ublas(0, 2)); + gl[3] = 0.0f; + gl[4] = static_cast<float>(ublas(1, 0)); + gl[5] = static_cast<float>(ublas(1, 1)); + gl[6] = static_cast<float>(ublas(1, 2)); + gl[7] = 0.0f; + gl[8] = static_cast<float>(ublas(2, 0)); + gl[9] = static_cast<float>(ublas(2, 1)); + gl[10] = static_cast<float>(ublas(2, 2)); + gl[11] = 0.0f; + gl[12] = 0.0f; + gl[13] = 0.0f; + gl[14] = 0.0f; + gl[15] = 1.0f; + }; - auto colour_conversion = glGetUniformLocation (program, "colour_conversion"); - check_gl_error ("glGetUniformLocation"); - glUniformMatrix4fv (colour_conversion, 1, GL_TRUE, gl_matrix); + { + auto conversion = dcp::ColourConversion::rec709_to_xyz(); + boost::numeric::ublas::matrix<double> matrix = conversion.xyz_to_rgb (); + GLfloat gl_matrix[16]; + ublas_to_gl(matrix, gl_matrix); + + auto xyz_rec709_colour_conversion = glGetUniformLocation(program, "xyz_rec709_colour_conversion"); + check_gl_error ("glGetUniformLocation"); + glUniformMatrix4fv(xyz_rec709_colour_conversion, 1, GL_TRUE, gl_matrix); + } + + { + auto xyz_rec709 = dcp::ColourConversion::rec709_to_xyz().xyz_to_rgb(); + auto rec2020_xyz = dcp::ColourConversion::rec2020_to_xyz().rgb_to_xyz(); + auto product = boost::numeric::ublas::prod(xyz_rec709, rec2020_xyz); + + GLfloat gl_matrix[16]; + ublas_to_gl(product, gl_matrix); + + auto rec2020_rec709_colour_conversion = glGetUniformLocation(program, "rec2020_rec709_colour_conversion"); + check_gl_error("glGetUniformLocation"); + glUniformMatrix4fv(rec2020_rec709_colour_conversion, 1, GL_TRUE, gl_matrix); + } glLineWidth (1.0f); check_gl_error ("glLineWidth"); @@ -522,11 +559,17 @@ GLVideoView::draw () glBindVertexArray(_vao); check_gl_error ("glBindVertexArray"); - glUniform1i(_fragment_type, static_cast<GLint>(_optimise_for_j2k ? FragmentType::XYZ_IMAGE : FragmentType::RGB_IMAGE)); + if (_optimise_for_j2k) { + glUniform1i(_fragment_type, static_cast<GLint>(FragmentType::XYZ_IMAGE)); + } else if (_rec2020) { + glUniform1i(_fragment_type, static_cast<GLint>(FragmentType::REC2020_IMAGE)); + } else { + glUniform1i(_fragment_type, static_cast<GLint>(FragmentType::REC709_IMAGE)); + } _video_texture->bind(); glDrawElements (GL_TRIANGLES, indices_video_texture_number, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_video_texture_offset * sizeof(int))); if (_have_subtitle_to_render) { - glUniform1i(_fragment_type, static_cast<GLint>(FragmentType::RGB_IMAGE)); + glUniform1i(_fragment_type, static_cast<GLint>(FragmentType::REC709_IMAGE)); _subtitle_texture->bind(); glDrawElements (GL_TRIANGLES, indices_subtitle_texture_number, GL_UNSIGNED_INT, reinterpret_cast<void*>(indices_subtitle_texture_offset * sizeof(int))); } @@ -692,6 +735,8 @@ GLVideoView::set_image (shared_ptr<const PlayerVideo> pv) check_gl_error ("glBufferSubData (subtitle)"); } + _rec2020 = pv->colour_conversion() && pv->colour_conversion()->about_equal(dcp::ColourConversion::rec2020_to_xyz(), 1e-6); + /* opt: where should these go? */ glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); diff --git a/src/wx/gl_video_view.h b/src/wx/gl_video_view.h index fd9154a73..964f6f1ab 100644 --- a/src/wx/gl_video_view.h +++ b/src/wx/gl_video_view.h @@ -134,6 +134,7 @@ private: Last<boost::optional<dcpomatic::Rect<float>>> _last_crop_guess; boost::atomic<wxSize> _canvas_size; + boost::atomic<bool> _rec2020; std::unique_ptr<Texture> _video_texture; std::unique_ptr<Texture> _subtitle_texture; bool _have_subtitle_to_render = false; diff --git a/src/wx/simple_video_view.cc b/src/wx/simple_video_view.cc index 00d81ab47..09c172171 100644 --- a/src/wx/simple_video_view.cc +++ b/src/wx/simple_video_view.cc @@ -26,6 +26,8 @@ #include "lib/butler.h" #include "lib/dcpomatic_log.h" #include "lib/image.h" +#include "lib/video_filter_graph.h" +#include "lib/video_filter_graph_set.h" #include <dcp/util.h> #include <dcp/warnings.h> LIBDCP_DISABLE_WARNINGS @@ -46,6 +48,8 @@ using namespace dcpomatic; SimpleVideoView::SimpleVideoView (FilmViewer* viewer, wxWindow* parent) : VideoView (viewer) + , _rec2020_filter("convert", "convert", "", "colorspace=all=bt709:iall=bt2020") + , _rec2020_filter_graph({ &_rec2020_filter }, dcp::Fraction(24, 1)) { _panel = new wxPanel (parent); @@ -241,7 +245,11 @@ SimpleVideoView::update () _state_timer.set ("get image"); - _image = player_video().first->image(boost::bind(&PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, true); + auto const pv = player_video(); + _image = pv.first->image(boost::bind(&PlayerVideo::force, AV_PIX_FMT_RGB24), VideoRange::FULL, true); + if (pv.first->colour_conversion() && pv.first->colour_conversion()->about_equal(dcp::ColourConversion::rec2020_to_xyz(), 1e-6)) { + _image = Image::ensure_alignment(_rec2020_filter_graph.get(_image->size(), _image->pixel_format())->process(_image).front(), Image::Alignment::COMPACT); + } _state_timer.set ("ImageChanged"); _viewer->image_changed (player_video().first); diff --git a/src/wx/simple_video_view.h b/src/wx/simple_video_view.h index 7a48f8ef8..e19068979 100644 --- a/src/wx/simple_video_view.h +++ b/src/wx/simple_video_view.h @@ -20,14 +20,18 @@ #include "video_view.h" +#include "lib/filter.h" #include "lib/position.h" +#include "lib/video_filter_graph_set.h" #include <dcp/types.h> #include <dcp/warnings.h> LIBDCP_DISABLE_WARNINGS #include <wx/wx.h> LIBDCP_ENABLE_WARNINGS + class FilmViewer; +class Filter; class SimpleVideoView : public VideoView @@ -53,4 +57,7 @@ private: wxTimer _timer; Position<int> _inter_position; dcp::Size _inter_size; + + Filter _rec2020_filter; + VideoFilterGraphSet _rec2020_filter_graph; }; |
