From 92784c9c28c48859578cd6e75aa01d5657d0c341 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 24 May 2019 23:29:39 +0100 Subject: [PATCH] Basic ability to set video range (JPEG/MPEG) at least for YUV content. May not work for RGB. See #1509. --- src/lib/dcp_examiner.h | 4 ++++ src/lib/ffmpeg_examiner.cc | 13 +++++++++++ src/lib/ffmpeg_examiner.h | 2 ++ src/lib/image.cc | 6 ++--- src/lib/image.h | 2 +- src/lib/image_examiner.h | 3 +++ src/lib/player.cc | 2 ++ src/lib/player_video.cc | 13 ++++++++--- src/lib/player_video.h | 4 +++- src/lib/types.h | 6 +++++ src/lib/video_content.cc | 25 ++++++++++++++++++--- src/lib/video_content.h | 11 ++++++++- src/lib/video_examiner.h | 1 + src/lib/video_mxf_examiner.h | 3 +++ src/wx/video_panel.cc | 43 ++++++++++++++++++++++++++++++++++++ src/wx/video_panel.h | 3 +++ test/client_server_test.cc | 4 ++++ test/image_test.cc | 2 +- 18 files changed, 134 insertions(+), 13 deletions(-) diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h index 199ac3985..da9092411 100644 --- a/src/lib/dcp_examiner.h +++ b/src/lib/dcp_examiner.h @@ -55,6 +55,10 @@ public: return false; } + VideoRange range () const { + return VIDEO_RANGE_FULL; + } + std::string name () const { return _name; } diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index a4c5eb128..3fb9a53e4 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -388,3 +388,16 @@ FFmpegExaminer::has_video () const { return static_cast (_video_stream); } + +VideoRange +FFmpegExaminer::range () const +{ + switch (color_range()) { + case AVCOL_RANGE_MPEG: + case AVCOL_RANGE_UNSPECIFIED: + return VIDEO_RANGE_VIDEO; + case AVCOL_RANGE_JPEG: + default: + return VIDEO_RANGE_FULL; + } +} diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h index 1c0dad3dc..9ffb8421b 100644 --- a/src/lib/ffmpeg_examiner.h +++ b/src/lib/ffmpeg_examiner.h @@ -53,6 +53,8 @@ public: return _first_video; } + VideoRange range () const; + AVColorRange color_range () const { return video_codec_context()->color_range; } diff --git a/src/lib/image.cc b/src/lib/image.cc index c176c2d65..b473403e4 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -129,7 +129,7 @@ Image::planes () const */ shared_ptr Image::crop_scale_window ( - Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast + Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, VideoRange video_range, AVPixelFormat out_format, bool out_aligned, bool fast ) const { /* Empirical testing suggests that sws_scale() will crash if @@ -198,8 +198,8 @@ Image::crop_scale_window ( */ sws_setColorspaceDetails ( scale_context, - sws_getCoefficients (lut[yuv_to_rgb]), 0, - sws_getCoefficients (lut[yuv_to_rgb]), 0, + sws_getCoefficients (lut[yuv_to_rgb]), video_range == VIDEO_RANGE_VIDEO ? 0 : 1, + sws_getCoefficients (lut[yuv_to_rgb]), 1, 0, 1 << 16, 1 << 16 ); diff --git a/src/lib/image.h b/src/lib/image.h index dbcb38cc7..4b059ff36 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -63,7 +63,7 @@ public: boost::shared_ptr convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const; boost::shared_ptr scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const; boost::shared_ptr crop_scale_window ( - Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast + Crop crop, dcp::Size inter_size, dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, VideoRange video_range, AVPixelFormat out_format, bool aligned, bool fast ) const; void make_black (); diff --git a/src/lib/image_examiner.h b/src/lib/image_examiner.h index 2e743a82e..ca8ecf9c8 100644 --- a/src/lib/image_examiner.h +++ b/src/lib/image_examiner.h @@ -36,6 +36,9 @@ public: return _video_length; } bool yuv () const; + VideoRange range () const { + return VIDEO_RANGE_FULL; + } private: boost::weak_ptr _film; diff --git a/src/lib/player.cc b/src/lib/player.cc index e44edd8dc..f79e379d2 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -332,6 +332,7 @@ Player::black_player_video_frame (Eyes eyes) const eyes, PART_WHOLE, PresetColourConversion::all().front().conversion, + VIDEO_RANGE_FULL, boost::weak_ptr(), boost::optional() ) @@ -826,6 +827,7 @@ Player::video (weak_ptr wp, ContentVideo video) video.eyes, video.part, piece->content->video->colour_conversion(), + piece->content->video->range(), piece->content, video.frame ) diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc index e36eb1984..75479136f 100644 --- a/src/lib/player_video.cc +++ b/src/lib/player_video.cc @@ -52,6 +52,7 @@ PlayerVideo::PlayerVideo ( Eyes eyes, Part part, optional colour_conversion, + VideoRange video_range, weak_ptr content, optional video_frame ) @@ -63,6 +64,7 @@ PlayerVideo::PlayerVideo ( , _eyes (eyes) , _part (part) , _colour_conversion (colour_conversion) + , _video_range (video_range) , _content (content) , _video_frame (video_frame) { @@ -78,6 +80,7 @@ PlayerVideo::PlayerVideo (shared_ptr node, shared_ptr socket _out_size = dcp::Size (node->number_child ("OutWidth"), node->number_child ("OutHeight")); _eyes = (Eyes) node->number_child ("Eyes"); _part = (Part) node->number_child ("Part"); + _video_range = (VideoRange) node->number_child("VideoRange"); /* Assume that the ColourConversion uses the current state version */ _colour_conversion = ColourConversion::from_xml (node, Film::current_state_version); @@ -166,7 +169,7 @@ PlayerVideo::make_image (function pixel_format, b } _image = im->crop_scale_window ( - total_crop, _inter_size, _out_size, yuv_to_rgb, pixel_format (im->pixel_format()), aligned, fast + total_crop, _inter_size, _out_size, yuv_to_rgb, _video_range, pixel_format (im->pixel_format()), aligned, fast ); if (_text) { @@ -192,6 +195,7 @@ PlayerVideo::add_metadata (xmlpp::Node* node) const node->add_child("OutHeight")->add_child_text (raw_convert (_out_size.height)); node->add_child("Eyes")->add_child_text (raw_convert (static_cast (_eyes))); node->add_child("Part")->add_child_text (raw_convert (static_cast (_part))); + node->add_child("VideoRange")->add_child_text(raw_convert(static_cast(_video_range))); if (_colour_conversion) { _colour_conversion.get().as_xml (node); } @@ -249,7 +253,8 @@ PlayerVideo::same (shared_ptr other) const _out_size != other->_out_size || _eyes != other->_eyes || _part != other->_part || - _colour_conversion != other->_colour_conversion) { + _colour_conversion != other->_colour_conversion || + _video_range != other->_video_range) { return false; } @@ -310,13 +315,14 @@ PlayerVideo::shallow_copy () const _eyes, _part, _colour_conversion, + _video_range, _content, _video_frame ) ); } -/** Re-read crop, fade, inter/out size and colour conversion from our content. +/** Re-read crop, fade, inter/out size, colour conversion and video range from our content. * @return true if this was possible, false if not. */ bool @@ -332,6 +338,7 @@ PlayerVideo::reset_metadata (shared_ptr film, dcp::Size video_contai _inter_size = content->video->scale().size(content->video, video_container_size, film_frame_size); _out_size = video_container_size; _colour_conversion = content->video->colour_conversion(); + _video_range = content->video->range(); return true; } diff --git a/src/lib/player_video.h b/src/lib/player_video.h index c38d60641..3cd559409 100644 --- a/src/lib/player_video.h +++ b/src/lib/player_video.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2015 Carl Hetherington + Copyright (C) 2013-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -54,6 +54,7 @@ public: Eyes, Part, boost::optional, + VideoRange video_range, boost::weak_ptr, boost::optional ); @@ -117,6 +118,7 @@ private: Eyes _eyes; Part _part; boost::optional _colour_conversion; + VideoRange _video_range; boost::optional _text; /** Content that we came from. This is so that reset_metadata() can work, and also * for variant:swaroop's non-skippable ads. diff --git a/src/lib/types.h b/src/lib/types.h index 94e101a3d..c9f68a21c 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -136,6 +136,12 @@ enum ChangeType CHANGE_TYPE_CANCELLED }; +enum VideoRange +{ + VIDEO_RANGE_FULL, ///< full, or "JPEG" (0-255 for 8-bit) + VIDEO_RANGE_VIDEO ///< video, or "MPEG" (16-235 for 8-bit) +}; + /** Type of captions. * * The generally accepted definitions seem to be: diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index d8d8adbf8..4eda7d967 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -46,6 +46,7 @@ int const VideoContentProperty::SCALE = 3; int const VideoContentProperty::COLOUR_CONVERSION = 4; int const VideoContentProperty::FADE_IN = 5; int const VideoContentProperty::FADE_OUT = 6; +int const VideoContentProperty::RANGE = 7; using std::string; using std::setprecision; @@ -71,6 +72,7 @@ VideoContent::VideoContent (Content* parent) , _yuv (true) , _fade_in (0) , _fade_out (0) + , _range (VIDEO_RANGE_FULL) { } @@ -152,6 +154,11 @@ VideoContent::VideoContent (Content* parent, cxml::ConstNodePtr node, int versio } else { _fade_in = _fade_out = 0; } + + _range = VIDEO_RANGE_FULL; + if (node->optional_string_child("Range").get_value_or("full") == "video") { + _range = VIDEO_RANGE_VIDEO; + } } VideoContent::VideoContent (Content* parent, vector > c) @@ -202,6 +209,7 @@ VideoContent::VideoContent (Content* parent, vector > c) _colour_conversion = ref->colour_conversion (); _fade_in = ref->fade_in (); _fade_out = ref->fade_out (); + _range = ref->range (); } void @@ -223,6 +231,7 @@ VideoContent::as_xml (xmlpp::Node* node) const node->add_child("YUV")->add_child_text (_yuv ? "1" : "0"); node->add_child("FadeIn")->add_child_text (raw_convert (_fade_in)); node->add_child("FadeOut")->add_child_text (raw_convert (_fade_out)); + node->add_child("Range")->add_child_text(_range == VIDEO_RANGE_FULL ? "full" : "video"); } void @@ -233,10 +242,12 @@ VideoContent::take_from_examiner (shared_ptr d) Frame vl = d->video_length (); optional const ar = d->sample_aspect_ratio (); bool const yuv = d->yuv (); + VideoRange const range = d->range (); ChangeSignaller cc1 (_parent, VideoContentProperty::SIZE); ChangeSignaller cc2 (_parent, VideoContentProperty::SCALE); ChangeSignaller cc3 (_parent, ContentProperty::LENGTH); + ChangeSignaller cc4 (_parent, VideoContentProperty::RANGE); { boost::mutex::scoped_lock lm (_mutex); @@ -244,6 +255,7 @@ VideoContent::take_from_examiner (shared_ptr d) _length = vl; _sample_aspect_ratio = ar; _yuv = yuv; + _range = range; if (Config::instance()->default_scale_to ()) { _scale = VideoContentScale (Config::instance()->default_scale_to ()); @@ -268,14 +280,15 @@ VideoContent::identifier () const { char buffer[256]; snprintf ( - buffer, sizeof(buffer), "%d_%d_%d_%d_%s_%" PRId64 "_%" PRId64, + buffer, sizeof(buffer), "%d_%d_%d_%d_%s_%" PRId64 "_%" PRId64 "_%d", crop().left, crop().right, crop().top, crop().bottom, scale().id().c_str(), _fade_in, - _fade_out + _fade_out, + _range == VIDEO_RANGE_FULL ? 0 : 1 ); string s (buffer); @@ -525,6 +538,12 @@ VideoContent::set_fade_out (Frame t) maybe_set (_fade_out, t, VideoContentProperty::FADE_OUT); } +void +VideoContent::set_range (VideoRange r) +{ + maybe_set (_range, r, VideoContentProperty::RANGE); +} + void VideoContent::take_settings_from (shared_ptr c) { diff --git a/src/lib/video_content.h b/src/lib/video_content.h index 3d07a0947..f5fb6c9ac 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2016 Carl Hetherington + Copyright (C) 2013-2019 Carl Hetherington This file is part of DCP-o-matic. @@ -46,6 +46,7 @@ public: static int const COLOUR_CONVERSION; static int const FADE_IN; static int const FADE_OUT; + static int const RANGE; }; class VideoContent : public ContentPart, public boost::enable_shared_from_this @@ -92,6 +93,8 @@ public: void set_fade_in (Frame); void set_fade_out (Frame); + void set_range (VideoRange); + VideoFrameType frame_type () const { boost::mutex::scoped_lock lm (_mutex); return _frame_type; @@ -153,6 +156,11 @@ public: return _fade_out; } + VideoRange range () const { + boost::mutex::scoped_lock lm (_mutex); + return _range; + } + dcp::Size size_after_3d_split () const; dcp::Size size_after_crop () const; @@ -194,6 +202,7 @@ private: bool _yuv; Frame _fade_in; Frame _fade_out; + VideoRange _range; }; #endif diff --git a/src/lib/video_examiner.h b/src/lib/video_examiner.h index dd5d08c9e..4e5c6dc0a 100644 --- a/src/lib/video_examiner.h +++ b/src/lib/video_examiner.h @@ -48,4 +48,5 @@ public: } /** @return true if this video is in YUV; must not be called if has_video() == false */ virtual bool yuv () const = 0; + virtual VideoRange range () const = 0; }; diff --git a/src/lib/video_mxf_examiner.h b/src/lib/video_mxf_examiner.h index 205daa361..ec2933c27 100644 --- a/src/lib/video_mxf_examiner.h +++ b/src/lib/video_mxf_examiner.h @@ -39,6 +39,9 @@ public: Frame video_length () const; boost::optional sample_aspect_ratio () const; bool yuv () const; + VideoRange range () const { + return VIDEO_RANGE_FULL; + } private: boost::shared_ptr _asset; diff --git a/src/wx/video_panel.cc b/src/wx/video_panel.cc index 9c044e188..7594d98a5 100644 --- a/src/wx/video_panel.cc +++ b/src/wx/video_panel.cc @@ -175,6 +175,11 @@ VideoPanel::VideoPanel (ContentPanel* p) _colour_conversion->Append (S_("Colour|Custom")); _edit_colour_conversion_button = new Button (this, _("Edit...")); + _range_label = create_label (this, _("Range"), true); + _range = new wxChoice (this, wxID_ANY); + _range->Append (_("Full (JPEG, 0-255)")); + _range->Append (_("Video (MPEG, 16-235)")); + _description = new StaticText (this, wxT ("\n \n \n \n \n"), wxDefaultPosition, wxDefaultSize); _description->SetFont(font); @@ -204,6 +209,7 @@ VideoPanel::VideoPanel (ContentPanel* p) _reference->Bind (wxEVT_CHECKBOX, boost::bind (&VideoPanel::reference_clicked, this)); _filters_button->Bind (wxEVT_BUTTON, boost::bind (&VideoPanel::edit_filters_clicked, this)); _colour_conversion->Bind (wxEVT_CHOICE, boost::bind (&VideoPanel::colour_conversion_changed, this)); + _range->Bind (wxEVT_CHOICE, boost::bind (&VideoPanel::range_changed, this)); _edit_colour_conversion_button->Bind (wxEVT_BUTTON, boost::bind (&VideoPanel::edit_colour_conversion_clicked, this)); add_to_grid (); @@ -253,6 +259,8 @@ VideoPanel::add_to_grid () _colour_conversion_label->Show (full); _colour_conversion->Show (full); _edit_colour_conversion_button->Show (full); + _range_label->Show (full); + _range->Show (full); add_label_to_sizer (_grid, _fade_in_label, true, wxGBPosition (r, 0)); _grid->Add (_fade_in, wxGBPosition (r, 1), wxGBSpan (1, 3)); @@ -284,12 +292,36 @@ VideoPanel::add_to_grid () _grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); } ++r; + + add_label_to_sizer (_grid, _range_label, true, wxGBPosition(r, 0)); + _grid->Add (_range, wxGBPosition(r, 1), wxGBSpan(1, 2), wxALIGN_CENTER_VERTICAL); + ++r; } _grid->Add (_description, wxGBPosition (r, 0), wxGBSpan (1, 4), wxEXPAND | wxALIGN_CENTER_VERTICAL, 6); ++r; } +void +VideoPanel::range_changed () +{ + ContentList vc = _parent->selected_video (); + if (vc.size() != 1) { + return; + } + + switch (_range->GetSelection()) { + case 0: + vc.front()->video->set_range (VIDEO_RANGE_FULL); + break; + case 1: + vc.front()->video->set_range (VIDEO_RANGE_VIDEO); + break; + default: + DCPOMATIC_ASSERT (false); + } +} + void VideoPanel::film_changed (Film::Property property) @@ -389,6 +421,14 @@ VideoPanel::film_content_changed (int property) checked_set (_reference, false); } + setup_sensitivity (); + } else if (property == VideoContentProperty::RANGE) { + if (vcs) { + checked_set (_range, vcs->video->range() == VIDEO_RANGE_FULL ? 0 : 1); + } else { + checked_set (_range, 0); + } + setup_sensitivity (); } } @@ -487,6 +527,7 @@ VideoPanel::content_selection_changed () film_content_changed (VideoContentProperty::COLOUR_CONVERSION); film_content_changed (VideoContentProperty::FADE_IN); film_content_changed (VideoContentProperty::FADE_OUT); + film_content_changed (VideoContentProperty::RANGE); film_content_changed (FFmpegContentProperty::FILTERS); film_content_changed (DCPContentProperty::REFERENCE_VIDEO); @@ -520,6 +561,7 @@ VideoPanel::setup_sensitivity () _filters->Enable (false); _filters_button->Enable (false); _colour_conversion->Enable (false); + _range->Enable (false); } else { ContentList video_sel = _parent->selected_video (); FFmpegContentList ffmpeg_sel = _parent->selected_ffmpeg (); @@ -537,6 +579,7 @@ VideoPanel::setup_sensitivity () _filters->Enable (true); _filters_button->Enable (single && !ffmpeg_sel.empty ()); _colour_conversion->Enable (single && !video_sel.empty ()); + _range->Enable (single && !video_sel.empty()); } ContentList vc = _parent->selected_video (); diff --git a/src/wx/video_panel.h b/src/wx/video_panel.h index 8e9a77cab..b13228d2d 100644 --- a/src/wx/video_panel.h +++ b/src/wx/video_panel.h @@ -50,6 +50,7 @@ private: void edit_filters_clicked (); void colour_conversion_changed (); void edit_colour_conversion_clicked (); + void range_changed (); void fade_in_changed (); void fade_out_changed (); void add_to_grid (); @@ -82,4 +83,6 @@ private: wxStaticText* _colour_conversion_label; wxChoice* _colour_conversion; wxButton* _edit_colour_conversion_button; + wxStaticText* _range_label; + wxChoice* _range; }; diff --git a/test/client_server_test.cc b/test/client_server_test.cc index 10286c72a..29f901d34 100644 --- a/test/client_server_test.cc +++ b/test/client_server_test.cc @@ -97,6 +97,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb) EYES_BOTH, PART_WHOLE, ColourConversion(), + VIDEO_RANGE_FULL, weak_ptr(), optional() ) @@ -181,6 +182,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv) EYES_BOTH, PART_WHOLE, ColourConversion(), + VIDEO_RANGE_FULL, weak_ptr(), optional() ) @@ -252,6 +254,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_j2k) EYES_BOTH, PART_WHOLE, ColourConversion(), + VIDEO_RANGE_FULL, weak_ptr(), optional() ) @@ -279,6 +282,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_j2k) EYES_BOTH, PART_WHOLE, PresetColourConversion::all().front().conversion, + VIDEO_RANGE_FULL, weak_ptr(), optional() ) diff --git a/test/image_test.cc b/test/image_test.cc index 8a7feb126..0bae58e60 100644 --- a/test/image_test.cc +++ b/test/image_test.cc @@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE (crop_scale_window_test) { shared_ptr proxy(new FFmpegImageProxy("test/data/flat_red.png")); shared_ptr raw = proxy->image().first; - shared_ptr out = raw->crop_scale_window(Crop(), dcp::Size(1998, 836), dcp::Size(1998, 1080), dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_YUV420P, true, false); + shared_ptr out = raw->crop_scale_window(Crop(), dcp::Size(1998, 836), dcp::Size(1998, 1080), dcp::YUV_TO_RGB_REC709, VIDEO_RANGE_FULL, AV_PIX_FMT_YUV420P, true, false); shared_ptr save = out->scale(dcp::Size(1998, 1080), dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGB24, false, false); write_image(save, "build/test/crop_scale_window_test.png", "RGB"); check_image("test/data/crop_scale_window_test.png", "build/test/crop_scale_window_test.png"); -- 2.30.2