summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2018-07-11 09:19:22 +0100
committerCarl Hetherington <cth@carlh.net>2018-07-11 09:19:22 +0100
commit26f2e20607096cd285110c8ced9e32beb837b55e (patch)
tree71ba60bf699b9a73ed44966492c30d579718dac0 /src/lib
parent8e79595c7867a634d89fe9da37dc142d63182a02 (diff)
Keep information about the video range (full/JPEG or video/MPEG) in
Image and use it when setting up swscale.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/dcp_video.cc2
-rw-r--r--src/lib/ffmpeg_decoder.cc8
-rw-r--r--src/lib/ffmpeg_encoder.cc2
-rw-r--r--src/lib/image.cc45
-rw-r--r--src/lib/image.h13
-rw-r--r--src/lib/j2k_image_proxy.cc2
-rw-r--r--src/lib/magick_image_proxy.cc2
-rw-r--r--src/lib/player.cc3
-rw-r--r--src/lib/player_video.cc6
-rw-r--r--src/lib/player_video.h2
-rw-r--r--src/lib/raw_image_proxy.cc3
-rw-r--r--src/lib/render_subtitles.cc2
12 files changed, 60 insertions, 30 deletions
diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc
index 655c37382..25fd02715 100644
--- a/src/lib/dcp_video.cc
+++ b/src/lib/dcp_video.cc
@@ -99,7 +99,7 @@ DCPVideo::convert_to_xyz (shared_ptr<const PlayerVideo> frame, dcp::NoteHandler
{
shared_ptr<dcp::OpenJPEGImage> xyz;
- shared_ptr<Image> image = frame->image (note, bind (&PlayerVideo::keep_xyz_or_rgb, _1), true, false);
+ shared_ptr<Image> image = frame->image (note, bind (&PlayerVideo::keep_xyz_or_rgb, _1), true, true, false);
if (frame->colour_conversion()) {
xyz = dcp::rgb_to_xyz (
image->data()[0],
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 0746458e5..862cb3c26 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -87,7 +87,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log>
video.reset (new VideoDecoder (this, c, log));
_pts_offset = pts_offset (c->ffmpeg_audio_streams(), c->first_video(), c->active_video_frame_rate());
/* It doesn't matter what size or pixel format this is, it just needs to be black */
- _black_image.reset (new Image (AV_PIX_FMT_RGB24, dcp::Size (128, 128), true));
+ _black_image.reset (new Image (AV_PIX_FMT_RGB24, true, dcp::Size (128, 128), true));
_black_image->make_black ();
} else {
_pts_offset = ContentTime ();
@@ -498,6 +498,10 @@ FFmpegDecoder::decode_video_packet ()
return false;
}
+ if (_frame->color_range == AVCOL_RANGE_UNSPECIFIED) {
+ _frame->color_range = AVCOL_RANGE_JPEG;
+ }
+
boost::mutex::scoped_lock lm (_filter_graphs_mutex);
shared_ptr<VideoFilterGraph> graph;
@@ -605,7 +609,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime
/* Note BGRA is expressed little-endian, so the first byte in the word is B, second
G, third R, fourth A.
*/
- shared_ptr<Image> image (new Image (AV_PIX_FMT_BGRA, dcp::Size (rect->w, rect->h), true));
+ shared_ptr<Image> image (new Image (AV_PIX_FMT_BGRA, true, dcp::Size (rect->w, rect->h), true));
#ifdef DCPOMATIC_HAVE_AVSUBTITLERECT_PICT
/* Start of the first line in the subtitle */
diff --git a/src/lib/ffmpeg_encoder.cc b/src/lib/ffmpeg_encoder.cc
index e55622abf..ea6bc7200 100644
--- a/src/lib/ffmpeg_encoder.cc
+++ b/src/lib/ffmpeg_encoder.cc
@@ -270,10 +270,12 @@ FFmpegEncoder::go ()
void
FFmpegEncoder::video (shared_ptr<PlayerVideo> video, DCPTime time)
{
+ /* XXX: what should full_range be here? */
shared_ptr<Image> image = video->image (
bind (&Log::dcp_log, _film->log().get(), _1, _2),
bind (&force_pixel_format, _1, _pixel_format),
true,
+ true,
false
);
diff --git a/src/lib/image.cc b/src/lib/image.cc
index 08507ec5f..5ba4fd316 100644
--- a/src/lib/image.cc
+++ b/src/lib/image.cc
@@ -121,7 +121,7 @@ Image::planes () const
*/
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, AVPixelFormat out_format, bool out_full_range, bool out_aligned, bool fast
) const
{
/* Empirical testing suggests that sws_scale() will crash if
@@ -154,7 +154,7 @@ Image::crop_scale_window (
To get around this, we ask Image to overallocate its buffers by the overrun.
*/
- shared_ptr<Image> out (new Image (out_format, out_size, out_aligned, (out_size.width - inter_size.width) / 2));
+ shared_ptr<Image> out (new Image (out_format, out_full_range, out_size, out_aligned, (out_size.width - inter_size.width) / 2));
out->make_black ();
/* Size of the image after any crop */
@@ -179,8 +179,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]), _full_range,
+ sws_getCoefficients (lut[yuv_to_rgb]), out_full_range,
0, 1 << 16, 1 << 16
);
@@ -229,9 +229,9 @@ Image::crop_scale_window (
}
shared_ptr<Image>
-Image::convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast) const
+Image::convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_full_range, bool out_aligned, bool fast) const
{
- return scale(size(), yuv_to_rgb, out_format, out_aligned, fast);
+ return scale(size(), yuv_to_rgb, out_format, out_full_range, out_aligned, fast);
}
/** @param out_size Size to scale to.
@@ -242,14 +242,14 @@ Image::convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format,
* fast bilinear rather than bicubic scaling.
*/
shared_ptr<Image>
-Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_aligned, bool fast) const
+Image::scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_full_range, bool out_aligned, bool fast) const
{
/* Empirical testing suggests that sws_scale() will crash if
the input image is not aligned.
*/
DCPOMATIC_ASSERT (aligned ());
- shared_ptr<Image> scaled (new Image (out_format, out_size, out_aligned));
+ shared_ptr<Image> scaled (new Image (out_format, out_full_range, out_size, out_aligned));
struct SwsContext* scale_context = sws_getContext (
size().width, size().height, pixel_format(),
@@ -581,7 +581,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
}
case AV_PIX_FMT_YUV420P:
{
- shared_ptr<Image> yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, false, false);
+ shared_ptr<Image> yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, _full_range, false, false);
dcp::Size const ts = size();
dcp::Size const os = yuv->size();
for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) {
@@ -616,7 +616,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
}
case AV_PIX_FMT_YUV420P10:
{
- shared_ptr<Image> yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, false, false);
+ shared_ptr<Image> yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, _full_range, false, false);
dcp::Size const ts = size();
dcp::Size const os = yuv->size();
for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) {
@@ -651,7 +651,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
}
case AV_PIX_FMT_YUV422P10LE:
{
- shared_ptr<Image> yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, false, false);
+ shared_ptr<Image> yuv = other->convert_pixel_format (dcp::YUV_TO_RGB_REC709, _pixel_format, _full_range, false, false);
dcp::Size const ts = size();
dcp::Size const os = yuv->size();
for (int ty = start_ty, oy = start_oy; ty < ts.height && oy < os.height; ++ty, ++oy) {
@@ -782,9 +782,10 @@ Image::bytes_per_pixel (int c) const
* @param aligned true to make each row of this image aligned to a 32-byte boundary.
* @param extra_pixels Amount of extra "run-off" memory to allocate at the end of each plane in pixels.
*/
-Image::Image (AVPixelFormat p, dcp::Size s, bool aligned, int extra_pixels)
+Image::Image (AVPixelFormat p, bool f, dcp::Size s, bool aligned, int extra_pixels)
: _size (s)
, _pixel_format (p)
+ , _full_range (f)
, _aligned (aligned)
, _extra_pixels (extra_pixels)
{
@@ -829,6 +830,7 @@ Image::Image (Image const & other)
: boost::enable_shared_from_this<Image>(other)
, _size (other._size)
, _pixel_format (other._pixel_format)
+ , _full_range (other._full_range)
, _aligned (other._aligned)
, _extra_pixels (other._extra_pixels)
{
@@ -865,11 +867,23 @@ Image::Image (AVFrame* frame)
q += frame->linesize[i];
}
}
+
+ switch (frame->color_range) {
+ case AVCOL_RANGE_MPEG:
+ _full_range = false;
+ break;
+ case AVCOL_RANGE_JPEG:
+ _full_range = true;
+ break;
+ default:
+ DCPOMATIC_ASSERT (false);
+ }
}
Image::Image (shared_ptr<const Image> other, bool aligned)
: _size (other->_size)
, _pixel_format (other->_pixel_format)
+ , _full_range (other->_full_range)
, _aligned (aligned)
, _extra_pixels (other->_extra_pixels)
{
@@ -905,6 +919,7 @@ Image::swap (Image & other)
{
std::swap (_size, other._size);
std::swap (_pixel_format, other._pixel_format);
+ std::swap (_full_range, other._full_range);
for (int i = 0; i < 4; ++i) {
std::swap (_data[i], other._data[i]);
@@ -974,7 +989,7 @@ merge (list<PositionImage> images)
all.extend (dcpomatic::Rect<int> (i->position, i->image->size().width, i->image->size().height));
}
- shared_ptr<Image> merged (new Image (images.front().image->pixel_format (), dcp::Size (all.width, all.height), true));
+ shared_ptr<Image> merged (new Image (images.front().image->pixel_format(), images.front().image->full_range(), dcp::Size (all.width, all.height), true));
merged->make_transparent ();
for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
merged->alpha_blend (i->image, i->position - all.position());
@@ -986,7 +1001,7 @@ merge (list<PositionImage> images)
bool
operator== (Image const & a, Image const & b)
{
- if (a.planes() != b.planes() || a.pixel_format() != b.pixel_format() || a.aligned() != b.aligned()) {
+ if (a.planes() != b.planes() || a.pixel_format() != b.pixel_format() || a.full_range() != b.full_range() || a.aligned() != b.aligned()) {
return false;
}
@@ -1156,6 +1171,8 @@ Image::as_png () const
using namespace MagickLib;
#endif
+ /* XXX: take full_range into account here */
+
string format;
switch (_pixel_format) {
case AV_PIX_FMT_RGB24:
diff --git a/src/lib/image.h b/src/lib/image.h
index 73f2313c1..c1742401b 100644
--- a/src/lib/image.h
+++ b/src/lib/image.h
@@ -41,7 +41,7 @@ class Socket;
class Image : public boost::enable_shared_from_this<Image>
{
public:
- Image (AVPixelFormat p, dcp::Size s, bool aligned, int extra_pixels = 0);
+ Image (AVPixelFormat p, bool full_range, dcp::Size s, bool aligned, int extra_pixels = 0);
explicit Image (AVFrame *);
explicit Image (Image const &);
Image (boost::shared_ptr<const Image>, bool);
@@ -60,10 +60,10 @@ public:
dcp::Size sample_size (int) const;
float bytes_per_pixel (int) const;
- 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> convert_pixel_format (dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_full_range, bool aligned, bool fast) const;
+ boost::shared_ptr<Image> scale (dcp::Size out_size, dcp::YUVToRGB yuv_to_rgb, AVPixelFormat out_format, bool out_full_range, 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, AVPixelFormat out_format, bool out_full_range, bool out_aligned, bool fast
) const;
void make_black ();
@@ -79,6 +79,10 @@ public:
return _pixel_format;
}
+ bool full_range () const {
+ return _full_range;
+ }
+
size_t memory_used () const;
dcp::Data as_png () const;
@@ -95,6 +99,7 @@ private:
dcp::Size _size;
AVPixelFormat _pixel_format; ///< FFmpeg's way of describing the pixel format of this Image
+ bool _full_range;
uint8_t** _data; ///< array of pointers to components
int* _line_size; ///< array of sizes of the data in each line, in bytes (without any alignment padding bytes)
int* _stride; ///< array of strides for each line, in bytes (including any alignment padding bytes)
diff --git a/src/lib/j2k_image_proxy.cc b/src/lib/j2k_image_proxy.cc
index 9100414e4..916723052 100644
--- a/src/lib/j2k_image_proxy.cc
+++ b/src/lib/j2k_image_proxy.cc
@@ -157,7 +157,7 @@ J2KImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size> target_siz
{
int const reduce = prepare (target_size);
- shared_ptr<Image> image (new Image (_pixel_format, _decompressed->size(), true));
+ shared_ptr<Image> image (new Image (_pixel_format, true, _decompressed->size(), true));
/* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
the data is 12-bit either way.
diff --git a/src/lib/magick_image_proxy.cc b/src/lib/magick_image_proxy.cc
index b12a81db5..d47c43275 100644
--- a/src/lib/magick_image_proxy.cc
+++ b/src/lib/magick_image_proxy.cc
@@ -122,7 +122,7 @@ MagickImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size>) const
dcp::Size size (magick_image->columns(), magick_image->rows());
- _image.reset (new Image (AV_PIX_FMT_RGB24, size, true));
+ _image.reset (new Image (AV_PIX_FMT_RGB24, true, size, true));
/* Write line-by-line here as _image must be aligned, and write() cannot be told about strides */
uint8_t* p = _image->data()[0];
diff --git a/src/lib/player.cc b/src/lib/player.cc
index df58ed223..118535f31 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -251,7 +251,7 @@ Player::set_video_container_size (dcp::Size s)
_video_container_size = s;
- _black_image.reset (new Image (AV_PIX_FMT_RGB24, _video_container_size, true));
+ _black_image.reset (new Image (AV_PIX_FMT_RGB24, true,_video_container_size, true));
_black_image->make_black ();
Changed (PlayerProperty::VIDEO_CONTAINER_SIZE, false);
@@ -309,6 +309,7 @@ Player::transform_image_subtitles (list<ImageSubtitle> subs) const
dcp::YUV_TO_RGB_REC601,
i->image->pixel_format (),
true,
+ true,
_fast
),
Position<int> (
diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc
index a50b196a2..498b563dd 100644
--- a/src/lib/player_video.cc
+++ b/src/lib/player_video.cc
@@ -87,7 +87,7 @@ PlayerVideo::PlayerVideo (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket
if (node->optional_number_child<int> ("SubtitleX")) {
shared_ptr<Image> image (
- new Image (AV_PIX_FMT_BGRA, dcp::Size (node->number_child<int> ("SubtitleWidth"), node->number_child<int> ("SubtitleHeight")), true)
+ new Image (AV_PIX_FMT_BGRA, true, dcp::Size (node->number_child<int> ("SubtitleWidth"), node->number_child<int> ("SubtitleHeight")), true)
);
image->read_from_socket (socket);
@@ -111,7 +111,7 @@ PlayerVideo::set_subtitle (PositionImage image)
* @param fast true to be fast at the expense of quality.
*/
shared_ptr<Image>
-PlayerVideo::image (dcp::NoteHandler note, function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
+PlayerVideo::image (dcp::NoteHandler note, function<AVPixelFormat (AVPixelFormat)> pixel_format, bool full_range, bool aligned, bool fast) const
{
pair<shared_ptr<Image>, int> prox = _in->image (optional<dcp::NoteHandler> (note), _inter_size);
shared_ptr<Image> im = prox.first;
@@ -150,7 +150,7 @@ PlayerVideo::image (dcp::NoteHandler note, function<AVPixelFormat (AVPixelFormat
}
shared_ptr<Image> out = im->crop_scale_window (
- total_crop, _inter_size, _out_size, yuv_to_rgb, pixel_format (_in->pixel_format()), aligned, fast
+ total_crop, _inter_size, _out_size, yuv_to_rgb, pixel_format (_in->pixel_format()), full_range, aligned, fast
);
if (_subtitle) {
diff --git a/src/lib/player_video.h b/src/lib/player_video.h
index f4bf2a471..7946573ee 100644
--- a/src/lib/player_video.h
+++ b/src/lib/player_video.h
@@ -63,7 +63,7 @@ public:
void set_subtitle (PositionImage);
void prepare ();
- boost::shared_ptr<Image> image (dcp::NoteHandler note, boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
+ boost::shared_ptr<Image> image (dcp::NoteHandler note, boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool full_range, bool aligned, bool fast) const;
static AVPixelFormat always_rgb (AVPixelFormat);
static AVPixelFormat keep_xyz_or_rgb (AVPixelFormat);
diff --git a/src/lib/raw_image_proxy.cc b/src/lib/raw_image_proxy.cc
index c3b565f3c..6403e7358 100644
--- a/src/lib/raw_image_proxy.cc
+++ b/src/lib/raw_image_proxy.cc
@@ -50,7 +50,7 @@ RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
);
- _image.reset (new Image (static_cast<AVPixelFormat> (xml->number_child<int> ("PixelFormat")), size, true));
+ _image.reset (new Image (static_cast<AVPixelFormat> (xml->number_child<int>("PixelFormat")), xml->bool_child("FullRange"), size, true));
_image->read_from_socket (socket);
}
@@ -67,6 +67,7 @@ RawImageProxy::add_metadata (xmlpp::Node* node) const
node->add_child("Width")->add_child_text (raw_convert<string> (_image->size().width));
node->add_child("Height")->add_child_text (raw_convert<string> (_image->size().height));
node->add_child("PixelFormat")->add_child_text (raw_convert<string> (static_cast<int> (_image->pixel_format ())));
+ node->add_child("FullRange")->add_child_text (_image->full_range() ? "1" : "0");
}
void
diff --git a/src/lib/render_subtitles.cc b/src/lib/render_subtitles.cc
index 35fcd8b93..61b09ecc1 100644
--- a/src/lib/render_subtitles.cc
+++ b/src/lib/render_subtitles.cc
@@ -130,7 +130,7 @@ render_line (list<SubtitleString> subtitles, list<shared_ptr<Font> > fonts, dcp:
height += target.height / 11;
/* FFmpeg BGRA means first byte blue, second byte green, third byte red, fourth byte alpha */
- shared_ptr<Image> image (new Image (AV_PIX_FMT_BGRA, dcp::Size (target.width, height), false));
+ shared_ptr<Image> image (new Image (AV_PIX_FMT_BGRA, true, dcp::Size (target.width, height), false));
image->make_black ();
#ifdef DCPOMATIC_HAVE_FORMAT_STRIDE_FOR_WIDTH