summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2014-11-11 00:04:02 +0000
committerCarl Hetherington <cth@carlh.net>2014-11-11 00:04:02 +0000
commit58f0530b1687673ea6e24a083063c3fdf2bff0b2 (patch)
tree1f76acad0a2877e527c936f754dde16eaf2d768e /src/lib
parent985a83ffbf251db481150221914ef74fbfe549b2 (diff)
Hand-apply 80562fe5dce5fd625da583ca6f7c2833f9db8754 from master (remove default scale and use a scale guessed from the content's size and sample aspect ratio).
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/config.cc7
-rw-r--r--src/lib/config.h10
-rw-r--r--src/lib/ffmpeg_examiner.cc7
-rw-r--r--src/lib/ffmpeg_examiner.h1
-rw-r--r--src/lib/ratio.cc16
-rw-r--r--src/lib/ratio.h1
-rw-r--r--src/lib/video_content.cc30
-rw-r--r--src/lib/video_content.h9
-rw-r--r--src/lib/video_decoder.cc149
-rw-r--r--src/lib/video_decoder.h6
-rw-r--r--src/lib/video_examiner.h3
11 files changed, 195 insertions, 44 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index 79ab4e9a3..5f48262fb 100644
--- a/src/lib/config.cc
+++ b/src/lib/config.cc
@@ -67,7 +67,6 @@ Config::Config ()
, _cinema_sound_processor (CinemaSoundProcessor::from_id (N_("dolby_cp750")))
, _allow_any_dcp_frame_rate (false)
, _default_still_length (10)
- , _default_scale (VideoContentScale (Ratio::from_id ("185")))
, _default_container (Ratio::from_id ("185"))
, _default_dcp_content_type (DCPContentType::from_isdcf_name ("FTR"))
, _default_j2k_bandwidth (100000000)
@@ -148,11 +147,6 @@ Config::read ()
_language = f.optional_string_child ("Language");
- c = f.optional_string_child ("DefaultScale");
- if (c) {
- _default_scale = VideoContentScale::from_id (c.get ());
- }
-
c = f.optional_string_child ("DefaultContainer");
if (c) {
_default_container = Ratio::from_id (c.get ());
@@ -331,7 +325,6 @@ Config::write () const
if (_language) {
root->add_child("Language")->add_child_text (_language.get());
}
- root->add_child("DefaultScale")->add_child_text (_default_scale.id ());
if (_default_container) {
root->add_child("DefaultContainer")->add_child_text (_default_container->id ());
}
diff --git a/src/lib/config.h b/src/lib/config.h
index 55a172d78..28b1dbd1d 100644
--- a/src/lib/config.h
+++ b/src/lib/config.h
@@ -135,10 +135,6 @@ public:
return _default_still_length;
}
- VideoContentScale default_scale () const {
- return _default_scale;
- }
-
Ratio const * default_container () const {
return _default_container;
}
@@ -314,11 +310,6 @@ public:
changed ();
}
- void set_default_scale (VideoContentScale s) {
- _default_scale = s;
- changed ();
- }
-
void set_default_container (Ratio const * c) {
_default_container = c;
changed ();
@@ -482,7 +473,6 @@ private:
ISDCFMetadata _default_isdcf_metadata;
boost::optional<std::string> _language;
int _default_still_length;
- VideoContentScale _default_scale;
Ratio const * _default_container;
DCPContentType const * _default_dcp_content_type;
std::string _dcp_issuer;
diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc
index 48d85da6f..46e93b16c 100644
--- a/src/lib/ffmpeg_examiner.cc
+++ b/src/lib/ffmpeg_examiner.cc
@@ -180,6 +180,13 @@ FFmpegExaminer::video_length () const
return ContentTime (max (ContentTime::Type (1), length.get ()));
}
+optional<float>
+FFmpegExaminer::sample_aspect_ratio () const
+{
+ AVRational sar = av_guess_sample_aspect_ratio (_format_context, _format_context->streams[_video_stream], 0);
+ return float (sar.num) / sar.den;
+}
+
string
FFmpegExaminer::audio_stream_name (AVStream* s) const
{
diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h
index 8c31eb2c4..59c3299b2 100644
--- a/src/lib/ffmpeg_examiner.h
+++ b/src/lib/ffmpeg_examiner.h
@@ -32,6 +32,7 @@ public:
float video_frame_rate () const;
dcp::Size video_size () const;
ContentTime video_length () const;
+ boost::optional<float> sample_aspect_ratio () const;
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
return _subtitle_streams;
diff --git a/src/lib/ratio.cc b/src/lib/ratio.cc
index fc36415c5..bc83ae87c 100644
--- a/src/lib/ratio.cc
+++ b/src/lib/ratio.cc
@@ -73,3 +73,19 @@ Ratio::from_ratio (float r)
return *j;
}
+Ratio const *
+Ratio::nearest_from_ratio (float r)
+{
+ Ratio const * nearest = 0;
+ float distance = FLT_MAX;
+
+ for (vector<Ratio const *>::iterator i = _ratios.begin (); i != _ratios.end(); ++i) {
+ float const d = fabs ((*i)->ratio() - r);
+ if (d < distance) {
+ distance = d;
+ nearest = *i;
+ }
+ }
+
+ return nearest;
+}
diff --git a/src/lib/ratio.h b/src/lib/ratio.h
index 69e3726c8..f1a180d8f 100644
--- a/src/lib/ratio.h
+++ b/src/lib/ratio.h
@@ -53,6 +53,7 @@ public:
static void setup_ratios ();
static Ratio const * from_id (std::string i);
static Ratio const * from_ratio (float r);
+ static Ratio const * nearest_from_ratio (float r);
static std::vector<Ratio const *> all () {
return _ratios;
}
diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc
index 3470e62bf..8e07174e3 100644
--- a/src/lib/video_content.cc
+++ b/src/lib/video_content.cc
@@ -66,7 +66,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f)
, _video_length (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _scale (Config::instance()->default_scale ())
+ , _scale (VideoContentScale (Ratio::from_id ("178")))
{
set_default_colour_conversion (false);
}
@@ -76,7 +76,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len
, _video_length (len)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _scale (Config::instance()->default_scale ())
+ , _scale (VideoContentScale (Ratio::from_id ("178")))
{
set_default_colour_conversion (false);
}
@@ -86,7 +86,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
, _video_length (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
- , _scale (Config::instance()->default_scale ())
+ , _scale (VideoContentScale (Ratio::from_id ("178")))
{
set_default_colour_conversion (false);
}
@@ -106,6 +106,7 @@ VideoContent::VideoContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, i
}
_video_frame_type = static_cast<VideoFrameType> (node->number_child<int> ("VideoFrameType"));
+ _sample_aspect_ratio = node->optional_number_child<float> ("SampleAspectRatio");
_crop.left = node->number_child<int> ("LeftCrop");
_crop.right = node->number_child<int> ("RightCrop");
_crop.top = node->number_child<int> ("TopCrop");
@@ -190,6 +191,9 @@ VideoContent::as_xml (xmlpp::Node* node) const
node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
node->add_child("VideoFrameType")->add_child_text (raw_convert<string> (static_cast<int> (_video_frame_type)));
+ if (_sample_aspect_ratio) {
+ node->add_child("SampleAspectRatio")->add_child_text (raw_convert<string> (_sample_aspect_ratio.get ()));
+ }
_crop.as_xml (node);
_scale.as_xml (node->add_child("Scale"));
if (_colour_conversion) {
@@ -219,12 +223,19 @@ VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
dcp::Size const vs = d->video_size ();
float const vfr = d->video_frame_rate ();
ContentTime vl = d->video_length ();
+ optional<float> const ar = d->sample_aspect_ratio ();
{
boost::mutex::scoped_lock lm (_mutex);
_video_size = vs;
_video_frame_rate = vfr;
_video_length = vl;
+ _sample_aspect_ratio = ar;
+
+ /* Guess correct scale from size and sample aspect ratio */
+ _scale = VideoContentScale (
+ Ratio::nearest_from_ratio (float (_video_size.width) * ar.get_value_or (1) / _video_size.height)
+ );
}
shared_ptr<const Film> film = _film.lock ();
@@ -233,6 +244,7 @@ VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
signal_changed (VideoContentProperty::VIDEO_SIZE);
signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
+ signal_changed (VideoContentProperty::VIDEO_SCALE);
signal_changed (ContentProperty::LENGTH);
}
@@ -252,6 +264,10 @@ VideoContent::information () const
video_size().height,
setprecision (3), video_size().ratio ()
);
+
+ if (sample_aspect_ratio ()) {
+ s << String::compose (_(" sample aspect ratio %1:1"), sample_aspect_ratio().get ());
+ }
return s.str ();
}
@@ -365,13 +381,19 @@ VideoContent::set_video_frame_type (VideoFrameType t)
string
VideoContent::technical_summary () const
{
- return String::compose (
+ string s = String::compose (
"video: length %1, size %2x%3, rate %4",
video_length_after_3d_combine().seconds(),
video_size().width,
video_size().height,
video_frame_rate()
);
+
+ if (sample_aspect_ratio ()) {
+ s += String::compose (_(", sample aspect ratio %1"), (sample_aspect_ratio().get ()));
+ }
+
+ return s;
}
dcp::Size
diff --git a/src/lib/video_content.h b/src/lib/video_content.h
index 64112c1da..30dcac231 100644
--- a/src/lib/video_content.h
+++ b/src/lib/video_content.h
@@ -137,6 +137,11 @@ public:
return _colour_conversion;
}
+ boost::optional<float> sample_aspect_ratio () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _sample_aspect_ratio;
+ }
+
ContentTime fade_in () const {
boost::mutex::scoped_lock lm (_mutex);
return _fade_in;
@@ -178,6 +183,10 @@ private:
Crop _crop;
VideoContentScale _scale;
boost::optional<ColourConversion> _colour_conversion;
+ /** Sample aspect ratio obtained from the content file's header,
+ if there is one.
+ */
+ boost::optional<float> _sample_aspect_ratio;
ContentTime _fade_in;
ContentTime _fade_out;
};
diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc
index 64c66ea55..9b27055c7 100644
--- a/src/lib/video_decoder.cc
+++ b/src/lib/video_decoder.cc
@@ -20,6 +20,7 @@
#include "video_decoder.h"
#include "image.h"
#include "image_proxy.h"
+#include "raw_image_proxy.h"
#include "content_video.h"
#include "i18n.h"
@@ -27,6 +28,7 @@
using std::cout;
using std::list;
using std::max;
+using std::back_inserter;
using boost::shared_ptr;
using boost::optional;
@@ -39,7 +41,8 @@ VideoDecoder::VideoDecoder (shared_ptr<const VideoContent> c)
#endif
, _same (false)
{
-
+ _black_image.reset (new Image (PIX_FMT_RGB24, _video_content->video_size(), true));
+ _black_image->make_black ();
}
list<ContentVideo>
@@ -113,7 +116,8 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate)
}
}
- /* Clean up _decoded_video; keep the frame we are returning, but nothing before that */
+ /* Clean up _decoded_video; keep the frame we are returning (which may have two images
+ for 3D), but nothing before that */
while (!_decoded_video.empty() && _decoded_video.front().frame < dec.front().frame) {
_decoded_video.pop_front ();
}
@@ -121,57 +125,156 @@ VideoDecoder::get_video (VideoFrame frame, bool accurate)
return dec;
}
+/** Fill _decoded_video up to, but not including, the specified frame */
+void
+VideoDecoder::fill_up_to_2d (VideoFrame frame)
+{
+ if (frame == 0) {
+ /* Already OK */
+ return;
+ }
-/** Called by subclasses when they have a video frame ready */
+ /* Fill with black... */
+ boost::shared_ptr<const ImageProxy> filler_image (new RawImageProxy (_black_image));
+ Part filler_part = PART_WHOLE;
+
+ /* ...unless there's some video we can fill with */
+ if (!_decoded_video.empty ()) {
+ filler_image = _decoded_video.back().image;
+ filler_part = _decoded_video.back().part;
+ }
+
+ VideoFrame filler_frame = _decoded_video.empty() ? 0 : (_decoded_video.back().frame + 1);
+ while (filler_frame < frame) {
+
+#ifdef DCPOMATIC_DEBUG
+ test_gaps++;
+#endif
+
+ _decoded_video.push_back (
+ ContentVideo (filler_image, EYES_BOTH, filler_part, filler_frame)
+ );
+
+ ++filler_frame;
+ }
+}
+
+/** Fill _decoded_video up to, but not including, the specified frame and eye */
void
-VideoDecoder::video (shared_ptr<const ImageProxy> image, VideoFrame frame)
+VideoDecoder::fill_up_to_3d (VideoFrame frame, Eyes eye)
{
- /* We may receive the same frame index twice for 3D, and we need to know
- when that happens.
- */
- _same = (!_decoded_video.empty() && frame == _decoded_video.back().frame);
+ if (frame == 0 && eye == EYES_LEFT) {
+ /* Already OK */
+ return;
+ }
+
+ /* Fill with black... */
+ boost::shared_ptr<const ImageProxy> filler_left_image (new RawImageProxy (_black_image));
+ boost::shared_ptr<const ImageProxy> filler_right_image (new RawImageProxy (_black_image));
+ Part filler_left_part = PART_WHOLE;
+ Part filler_right_part = PART_WHOLE;
+
+ /* ...unless there's some video we can fill with */
+ for (list<ContentVideo>::const_reverse_iterator i = _decoded_video.rbegin(); i != _decoded_video.rend(); ++i) {
+ if (i->eyes == EYES_LEFT && !filler_left_image) {
+ filler_left_image = i->image;
+ filler_left_part = i->part;
+ } else if (i->eyes == EYES_RIGHT && !filler_right_image) {
+ filler_right_image = i->image;
+ filler_right_part = i->part;
+ }
+
+ if (filler_left_image && filler_right_image) {
+ break;
+ }
+ }
+
+ VideoFrame filler_frame = _decoded_video.empty() ? 0 : _decoded_video.back().frame;
+ Eyes filler_eye = _decoded_video.empty() ? EYES_LEFT : _decoded_video.back().eyes;
+
+ if (_decoded_video.empty ()) {
+ filler_frame = 0;
+ filler_eye = EYES_LEFT;
+ } else if (_decoded_video.back().eyes == EYES_LEFT) {
+ filler_frame = _decoded_video.back().frame;
+ filler_eye = EYES_RIGHT;
+ } else if (_decoded_video.back().eyes == EYES_RIGHT) {
+ filler_frame = _decoded_video.back().frame + 1;
+ filler_eye = EYES_LEFT;
+ }
- /* Fill in gaps */
- /* XXX: 3D */
+ while (filler_frame != frame || filler_eye != eye) {
- while (!_decoded_video.empty () && (_decoded_video.back().frame + 1) < frame) {
#ifdef DCPOMATIC_DEBUG
test_gaps++;
#endif
+
_decoded_video.push_back (
ContentVideo (
- _decoded_video.back().image,
- _decoded_video.back().eyes,
- _decoded_video.back().part,
- _decoded_video.back().frame + 1
+ filler_eye == EYES_LEFT ? filler_left_image : filler_right_image,
+ filler_eye,
+ filler_eye == EYES_LEFT ? filler_left_part : filler_right_part,
+ filler_frame
)
);
+
+ if (filler_eye == EYES_LEFT) {
+ filler_eye = EYES_RIGHT;
+ } else {
+ filler_eye = EYES_LEFT;
+ ++filler_frame;
+ }
}
+}
+/** Called by subclasses when they have a video frame ready */
+void
+VideoDecoder::video (shared_ptr<const ImageProxy> image, VideoFrame frame)
+{
+ /* We may receive the same frame index twice for 3D, and we need to know
+ when that happens.
+ */
+ _same = (!_decoded_video.empty() && frame == _decoded_video.back().frame);
+
+ /* Work out what we are going to push into _decoded_video next */
+ list<ContentVideo> to_push;
switch (_video_content->video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
- _decoded_video.push_back (ContentVideo (image, EYES_BOTH, PART_WHOLE, frame));
+ to_push.push_back (ContentVideo (image, EYES_BOTH, PART_WHOLE, frame));
break;
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
- _decoded_video.push_back (ContentVideo (image, _same ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, frame));
+ to_push.push_back (ContentVideo (image, _same ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, frame));
break;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_LEFT_HALF, frame));
- _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_RIGHT_HALF, frame));
+ to_push.push_back (ContentVideo (image, EYES_LEFT, PART_LEFT_HALF, frame));
+ to_push.push_back (ContentVideo (image, EYES_RIGHT, PART_RIGHT_HALF, frame));
break;
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_TOP_HALF, frame));
- _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_BOTTOM_HALF, frame));
+ to_push.push_back (ContentVideo (image, EYES_LEFT, PART_TOP_HALF, frame));
+ to_push.push_back (ContentVideo (image, EYES_RIGHT, PART_BOTTOM_HALF, frame));
break;
case VIDEO_FRAME_TYPE_3D_LEFT:
- _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_WHOLE, frame));
+ to_push.push_back (ContentVideo (image, EYES_LEFT, PART_WHOLE, frame));
break;
case VIDEO_FRAME_TYPE_3D_RIGHT:
- _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_WHOLE, frame));
+ to_push.push_back (ContentVideo (image, EYES_RIGHT, PART_WHOLE, frame));
break;
default:
assert (false);
}
+
+ /* Now VideoDecoder is required never to have gaps in the frames that it presents
+ via get_video(). Hence we need to fill in any gap between the last thing in _decoded_video
+ and the things we are about to push.
+ */
+
+ if (_video_content->video_frame_type() == VIDEO_FRAME_TYPE_2D) {
+ fill_up_to_2d (to_push.front().frame);
+ } else {
+ fill_up_to_3d (to_push.front().frame, to_push.front().eyes);
+ }
+
+ copy (to_push.begin(), to_push.end(), back_inserter (_decoded_video));
}
void
diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h
index f5c3cd743..9e56546df 100644
--- a/src/lib/video_decoder.h
+++ b/src/lib/video_decoder.h
@@ -33,6 +33,7 @@
class VideoContent;
class ImageProxy;
+class Image;
/** @class VideoDecoder
* @brief Parent for classes which decode video.
@@ -53,14 +54,19 @@ public:
#endif
protected:
+ friend struct video_decoder_fill_test1;
+ friend struct video_decoder_fill_test2;
void seek (ContentTime time, bool accurate);
void video (boost::shared_ptr<const ImageProxy>, VideoFrame frame);
std::list<ContentVideo> decoded_video (VideoFrame frame);
+ void fill_up_to_2d (VideoFrame);
+ void fill_up_to_3d (VideoFrame, Eyes);
boost::shared_ptr<const VideoContent> _video_content;
std::list<ContentVideo> _decoded_video;
bool _same;
+ boost::shared_ptr<Image> _black_image;
};
#endif
diff --git a/src/lib/video_examiner.h b/src/lib/video_examiner.h
index 87e9a0428..de7966507 100644
--- a/src/lib/video_examiner.h
+++ b/src/lib/video_examiner.h
@@ -35,4 +35,7 @@ public:
virtual float video_frame_rate () const = 0;
virtual dcp::Size video_size () const = 0;
virtual ContentTime video_length () const = 0;
+ virtual boost::optional<float> sample_aspect_ratio () const {
+ return boost::optional<float> ();
+ }
};