return false;
}
+ VideoRange range () const {
+ return VIDEO_RANGE_FULL;
+ }
+
std::string name () const {
return _name;
}
{
return static_cast<bool> (_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;
+ }
+}
return _first_video;
}
+ VideoRange range () const;
+
AVColorRange color_range () const {
return video_codec_context()->color_range;
}
*/
shared_ptr<Image>
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
*/
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
);
boost::shared_ptr<Image> convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const;
boost::shared_ptr<Image> scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool aligned, bool fast) const;
boost::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 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 ();
return _video_length;
}
bool yuv () const;
+ VideoRange range () const {
+ return VIDEO_RANGE_FULL;
+ }
private:
boost::weak_ptr<const Film> _film;
eyes,
PART_WHOLE,
PresetColourConversion::all().front().conversion,
+ VIDEO_RANGE_FULL,
boost::weak_ptr<Content>(),
boost::optional<Frame>()
)
video.eyes,
video.part,
piece->content->video->colour_conversion(),
+ piece->content->video->range(),
piece->content,
video.frame
)
Eyes eyes,
Part part,
optional<ColourConversion> colour_conversion,
+ VideoRange video_range,
weak_ptr<Content> content,
optional<Frame> video_frame
)
, _eyes (eyes)
, _part (part)
, _colour_conversion (colour_conversion)
+ , _video_range (video_range)
, _content (content)
, _video_frame (video_frame)
{
_out_size = dcp::Size (node->number_child<int> ("OutWidth"), node->number_child<int> ("OutHeight"));
_eyes = (Eyes) node->number_child<int> ("Eyes");
_part = (Part) node->number_child<int> ("Part");
+ _video_range = (VideoRange) node->number_child<int>("VideoRange");
/* Assume that the ColourConversion uses the current state version */
_colour_conversion = ColourConversion::from_xml (node, Film::current_state_version);
}
_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) {
node->add_child("OutHeight")->add_child_text (raw_convert<string> (_out_size.height));
node->add_child("Eyes")->add_child_text (raw_convert<string> (static_cast<int> (_eyes)));
node->add_child("Part")->add_child_text (raw_convert<string> (static_cast<int> (_part)));
+ node->add_child("VideoRange")->add_child_text(raw_convert<string>(static_cast<int>(_video_range)));
if (_colour_conversion) {
_colour_conversion.get().as_xml (node);
}
_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;
}
_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
_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;
}
/*
- Copyright (C) 2013-2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
Eyes,
Part,
boost::optional<ColourConversion>,
+ VideoRange video_range,
boost::weak_ptr<Content>,
boost::optional<Frame>
);
Eyes _eyes;
Part _part;
boost::optional<ColourConversion> _colour_conversion;
+ VideoRange _video_range;
boost::optional<PositionImage> _text;
/** Content that we came from. This is so that reset_metadata() can work, and also
* for variant:swaroop's non-skippable ads.
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:
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
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;
, _yuv (true)
, _fade_in (0)
, _fade_out (0)
+ , _range (VIDEO_RANGE_FULL)
{
}
} 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<shared_ptr<Content> > c)
_colour_conversion = ref->colour_conversion ();
_fade_in = ref->fade_in ();
_fade_out = ref->fade_out ();
+ _range = ref->range ();
}
void
node->add_child("YUV")->add_child_text (_yuv ? "1" : "0");
node->add_child("FadeIn")->add_child_text (raw_convert<string> (_fade_in));
node->add_child("FadeOut")->add_child_text (raw_convert<string> (_fade_out));
+ node->add_child("Range")->add_child_text(_range == VIDEO_RANGE_FULL ? "full" : "video");
}
void
Frame vl = d->video_length ();
optional<double> const ar = d->sample_aspect_ratio ();
bool const yuv = d->yuv ();
+ VideoRange const range = d->range ();
ChangeSignaller<Content> cc1 (_parent, VideoContentProperty::SIZE);
ChangeSignaller<Content> cc2 (_parent, VideoContentProperty::SCALE);
ChangeSignaller<Content> cc3 (_parent, ContentProperty::LENGTH);
+ ChangeSignaller<Content> cc4 (_parent, VideoContentProperty::RANGE);
{
boost::mutex::scoped_lock lm (_mutex);
_length = vl;
_sample_aspect_ratio = ar;
_yuv = yuv;
+ _range = range;
if (Config::instance()->default_scale_to ()) {
_scale = VideoContentScale (Config::instance()->default_scale_to ());
{
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);
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<const VideoContent> c)
{
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
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<VideoContent>
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;
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;
bool _yuv;
Frame _fade_in;
Frame _fade_out;
+ VideoRange _range;
};
#endif
}
/** @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;
};
Frame video_length () const;
boost::optional<double> sample_aspect_ratio () const;
bool yuv () const;
+ VideoRange range () const {
+ return VIDEO_RANGE_FULL;
+ }
private:
boost::shared_ptr<dcp::PictureAsset> _asset;
_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);
_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 ();
_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));
_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)
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 ();
}
}
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);
_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 ();
_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 ();
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 ();
wxStaticText* _colour_conversion_label;
wxChoice* _colour_conversion;
wxButton* _edit_colour_conversion_button;
+ wxStaticText* _range_label;
+ wxChoice* _range;
};
EYES_BOTH,
PART_WHOLE,
ColourConversion(),
+ VIDEO_RANGE_FULL,
weak_ptr<Content>(),
optional<Frame>()
)
EYES_BOTH,
PART_WHOLE,
ColourConversion(),
+ VIDEO_RANGE_FULL,
weak_ptr<Content>(),
optional<Frame>()
)
EYES_BOTH,
PART_WHOLE,
ColourConversion(),
+ VIDEO_RANGE_FULL,
weak_ptr<Content>(),
optional<Frame>()
)
EYES_BOTH,
PART_WHOLE,
PresetColourConversion::all().front().conversion,
+ VIDEO_RANGE_FULL,
weak_ptr<Content>(),
optional<Frame>()
)
{
shared_ptr<FFmpegImageProxy> proxy(new FFmpegImageProxy("test/data/flat_red.png"));
shared_ptr<Image> raw = proxy->image().first;
- shared_ptr<Image> 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<Image> 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<Image> 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");