Basics of forced reduction of JPEG2000 decode resolution.
authorCarl Hetherington <cth@carlh.net>
Thu, 3 Aug 2017 16:54:06 +0000 (17:54 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 14 Aug 2017 20:07:49 +0000 (21:07 +0100)
src/lib/dcp_decoder.cc
src/lib/dcp_decoder.h
src/lib/j2k_image_proxy.cc
src/lib/j2k_image_proxy.h
src/lib/player.cc
src/lib/player.h
src/lib/video_mxf_decoder.cc
src/tools/dcpomatic_player.cc
src/wx/film_viewer.cc
src/wx/film_viewer.h

index c7294bda75dada03fa9f203597f33b0b35d4e231..ef40f2ec7311949db871dfcaff8ee4db6ed756c4 100644 (file)
@@ -48,6 +48,7 @@ using std::list;
 using std::cout;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
+using boost::optional;
 
 DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log)
        : DCP (c)
@@ -100,20 +101,39 @@ DCPDecoder::pass ()
                if (_mono_reader) {
                        video->emit (
                                shared_ptr<ImageProxy> (
-                                       new J2KImageProxy (_mono_reader->get_frame (entry_point + frame), asset->size(), AV_PIX_FMT_XYZ12LE)
+                                       new J2KImageProxy (
+                                               _mono_reader->get_frame (entry_point + frame),
+                                               asset->size(),
+                                               AV_PIX_FMT_XYZ12LE,
+                                               _forced_reduction
+                                               )
                                        ),
                                _offset + frame
                                );
                } else {
                        video->emit (
                                shared_ptr<ImageProxy> (
-                                       new J2KImageProxy (_stereo_reader->get_frame (entry_point + frame), asset->size(), dcp::EYE_LEFT, AV_PIX_FMT_XYZ12LE)),
+                                       new J2KImageProxy (
+                                               _stereo_reader->get_frame (entry_point + frame),
+                                               asset->size(),
+                                               dcp::EYE_LEFT,
+                                               AV_PIX_FMT_XYZ12LE,
+                                               _forced_reduction
+                                               )
+                                       ),
                                _offset + frame
                                );
 
                        video->emit (
                                shared_ptr<ImageProxy> (
-                                       new J2KImageProxy (_stereo_reader->get_frame (entry_point + frame), asset->size(), dcp::EYE_RIGHT, AV_PIX_FMT_XYZ12LE)),
+                                       new J2KImageProxy (
+                                               _stereo_reader->get_frame (entry_point + frame),
+                                               asset->size(),
+                                               dcp::EYE_RIGHT,
+                                               AV_PIX_FMT_XYZ12LE,
+                                               _forced_reduction
+                                               )
+                                       ),
                                _offset + frame
                                );
                }
@@ -234,3 +254,9 @@ DCPDecoder::set_decode_referenced ()
 {
        _decode_referenced = true;
 }
+
+void
+DCPDecoder::set_forced_reduction (optional<int> reduction)
+{
+       _forced_reduction = reduction;
+}
index 71687ad15df1e3eabe711466f7d27cc21f78e776..aa76b83d6c435f3617210ecfccdd5026c709bebf 100644 (file)
@@ -46,6 +46,7 @@ public:
        }
 
        void set_decode_referenced ();
+       void set_forced_reduction (boost::optional<int> reduction);
 
        bool pass ();
        void seek (ContentTime t, bool accurate);
@@ -71,4 +72,5 @@ private:
        boost::shared_ptr<dcp::SoundAssetReader> _sound_reader;
 
        bool _decode_referenced;
+       boost::optional<int> _forced_reduction;
 };
index 9e68951e95b8e8823ab12d46346e96ac8ff1ab51..a60af1eb26182e299261a4a4fdf00b79e1b8f8a5 100644 (file)
@@ -53,18 +53,31 @@ J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPi
 
 }
 
-J2KImageProxy::J2KImageProxy (shared_ptr<const dcp::MonoPictureFrame> frame, dcp::Size size, AVPixelFormat pixel_format)
+J2KImageProxy::J2KImageProxy (
+       shared_ptr<const dcp::MonoPictureFrame> frame,
+       dcp::Size size,
+       AVPixelFormat pixel_format,
+       optional<int> forced_reduction
+       )
        : _data (frame->j2k_size ())
        , _size (size)
        , _pixel_format (pixel_format)
+       , _forced_reduction (forced_reduction)
 {
        memcpy (_data.data().get(), frame->j2k_data(), _data.size ());
 }
 
-J2KImageProxy::J2KImageProxy (shared_ptr<const dcp::StereoPictureFrame> frame, dcp::Size size, dcp::Eye eye, AVPixelFormat pixel_format)
+J2KImageProxy::J2KImageProxy (
+       shared_ptr<const dcp::StereoPictureFrame> frame,
+       dcp::Size size,
+       dcp::Eye eye,
+       AVPixelFormat pixel_format,
+       optional<int> forced_reduction
+       )
        : _size (size)
        , _eye (eye)
        , _pixel_format (pixel_format)
+       , _forced_reduction (forced_reduction)
 {
        switch (eye) {
        case dcp::EYE_LEFT:
@@ -104,12 +117,17 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
 
        int reduce = 0;
 
-       while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
-               ++reduce;
+       if (_forced_reduction) {
+               reduce = *_forced_reduction;
+       } else {
+               while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
+                       ++reduce;
+               }
+
+               --reduce;
+               reduce = max (0, reduce);
        }
 
-       --reduce;
-       reduce = max (0, reduce);
        _decompressed = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
 
        if (_decompressed->precision(0) < 12) {
index 3133aac20c055642f9667657b920812d9fc52eeb..c20d89340318b2040bf3fc60a6c26b8d95e70845 100644 (file)
@@ -32,8 +32,22 @@ class J2KImageProxy : public ImageProxy
 {
 public:
        J2KImageProxy (boost::filesystem::path path, dcp::Size, AVPixelFormat pixel_format);
-       J2KImageProxy (boost::shared_ptr<const dcp::MonoPictureFrame> frame, dcp::Size, AVPixelFormat pixel_format);
-       J2KImageProxy (boost::shared_ptr<const dcp::StereoPictureFrame> frame, dcp::Size, dcp::Eye, AVPixelFormat pixel_format);
+
+       J2KImageProxy (
+               boost::shared_ptr<const dcp::MonoPictureFrame> frame,
+               dcp::Size,
+               AVPixelFormat pixel_format,
+               boost::optional<int> forced_reduction
+               );
+
+       J2KImageProxy (
+               boost::shared_ptr<const dcp::StereoPictureFrame> frame,
+               dcp::Size,
+               dcp::Eye,
+               AVPixelFormat pixel_format,
+               boost::optional<int> forced_reduction
+               );
+
        J2KImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
 
        boost::shared_ptr<Image> image (
@@ -71,4 +85,5 @@ private:
        mutable boost::optional<dcp::Size> _target_size;
        AVPixelFormat _pixel_format;
        mutable boost::mutex _mutex;
+       boost::optional<int> _forced_reduction;
 };
index 3d191a302db213f651e8a3a02bfb3b8d5ed5b375..7e21ef937b82a98f5ff3198ac07c2ea0a469cbfb 100644 (file)
@@ -127,7 +127,10 @@ Player::setup_pieces ()
 
                shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
                if (dcp && _play_referenced) {
-                       dcp->set_decode_referenced ();
+                       if (_play_referenced) {
+                               dcp->set_decode_referenced ();
+                       }
+                       dcp->set_forced_reduction (_dcp_decode_reduction);
                }
 
                shared_ptr<Piece> piece (new Piece (i, decoder, frc));
@@ -850,6 +853,10 @@ Player::subtitle_stop (weak_ptr<Piece> wp, ContentTime to)
 void
 Player::seek (DCPTime time, bool accurate)
 {
+       if (!_have_valid_pieces) {
+               setup_pieces ();
+       }
+
        if (_audio_processor) {
                _audio_processor->flush ();
        }
@@ -950,3 +957,15 @@ Player::discard_audio (shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTi
        cut->copy_from (audio.get(), remaining_frames, discard_frames, 0);
        return make_pair(cut, time + discard_time);
 }
+
+void
+Player::set_dcp_decode_reduction (optional<int> reduction)
+{
+       if (reduction == _dcp_decode_reduction) {
+               return;
+       }
+
+       _dcp_decode_reduction = reduction;
+       _have_valid_pieces = false;
+       Changed (false);
+}
index ee7f89aaac8773f23e3df0d5204ef3cbc8adcfdc..9dd5afd26ae3114e66b5a2121936702c5ab97014 100644 (file)
@@ -67,6 +67,7 @@ public:
        void set_always_burn_subtitles (bool burn);
        void set_fast ();
        void set_play_referenced ();
+       void set_dcp_decode_reduction (boost::optional<int> reduction);
 
        /** Emitted when something has changed such that if we went back and emitted
         *  the last frame again it would look different.  This is not emitted after
@@ -147,6 +148,8 @@ private:
        /** Time just after the last audio frame we emitted, or the time of the last accurate seek */
        boost::optional<DCPTime> _last_audio_time;
 
+       boost::optional<int> _dcp_decode_reduction;
+
        typedef std::map<boost::weak_ptr<Piece>, boost::shared_ptr<PlayerVideo> > LastVideoMap;
        LastVideoMap _last_video;
 
index 216721375fac0f832bf06c17dc5c117f890e50d1..194830c635e4ec5cab4495c99fb2e6790d38515f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -29,6 +29,7 @@
 #include <dcp/exceptions.h>
 
 using boost::shared_ptr;
+using boost::optional;
 
 VideoMXFDecoder::VideoMXFDecoder (shared_ptr<const VideoMXFContent> content, shared_ptr<Log> log)
        : _content (content)
@@ -78,14 +79,23 @@ VideoMXFDecoder::pass ()
 
        if (_mono_reader) {
                video->emit (
-                       shared_ptr<ImageProxy> (new J2KImageProxy (_mono_reader->get_frame(frame), _size, AV_PIX_FMT_XYZ12LE)), frame
+                       shared_ptr<ImageProxy> (
+                               new J2KImageProxy (_mono_reader->get_frame(frame), _size, AV_PIX_FMT_XYZ12LE, optional<int>())
+                               ),
+                       frame
                        );
        } else {
                video->emit (
-                       shared_ptr<ImageProxy> (new J2KImageProxy (_stereo_reader->get_frame(frame), _size, dcp::EYE_LEFT, AV_PIX_FMT_XYZ12LE)), frame
+                       shared_ptr<ImageProxy> (
+                               new J2KImageProxy (_stereo_reader->get_frame(frame), _size, dcp::EYE_LEFT, AV_PIX_FMT_XYZ12LE, optional<int>())
+                               ),
+                       frame
                        );
                video->emit (
-                       shared_ptr<ImageProxy> (new J2KImageProxy (_stereo_reader->get_frame(frame), _size, dcp::EYE_RIGHT, AV_PIX_FMT_XYZ12LE)), frame
+                       shared_ptr<ImageProxy> (
+                               new J2KImageProxy (_stereo_reader->get_frame(frame), _size, dcp::EYE_RIGHT, AV_PIX_FMT_XYZ12LE, optional<int>())
+                               ),
+                       frame
                        );
        }
 
index 3a7c18f0241b51c3e8f4f93d864315dabf10f8a6..567440f8809013343fae99c75ebf0aeef2786be5 100644 (file)
@@ -50,6 +50,10 @@ using boost::optional;
 
 enum {
        ID_file_open = 1,
+       ID_view_scale_appropriate,
+       ID_view_scale_full,
+       ID_view_scale_half,
+       ID_view_scale_quarter,
        ID_help_report_a_problem,
        ID_tools_check_for_updates,
 };
@@ -77,6 +81,10 @@ public:
 
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_open, this), ID_file_open);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::file_exit, this), wxID_EXIT);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional<int>()), ID_view_scale_appropriate);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional<int>(0)), ID_view_scale_full);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional<int>(1)), ID_view_scale_half);
+               Bind (wxEVT_MENU, boost::bind (&DOMFrame::set_decode_reduction, this, optional<int>(2)), ID_view_scale_quarter);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_about, this), wxID_ABOUT);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::help_report_a_problem, this), ID_help_report_a_problem);
                Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_check_for_updates, this), ID_tools_check_for_updates);
@@ -96,6 +104,11 @@ public:
                UpdateChecker::instance()->StateChanged.connect (boost::bind (&DOMFrame::update_checker_state_changed, this));
        }
 
+       void set_decode_reduction (optional<int> reduction)
+       {
+               _viewer->set_dcp_decode_reduction (reduction);
+       }
+
        void load_dcp (boost::filesystem::path dir)
        {
                _film.reset (new Film (optional<boost::filesystem::path>()));
@@ -146,6 +159,12 @@ private:
                edit->Append (wxID_PREFERENCES, _("&Preferences...\tCtrl-P"));
 #endif
 
+               wxMenu* view = new wxMenu;
+               view->AppendRadioItem (ID_view_scale_appropriate, _("Set decode resolution to match display"));
+               view->AppendRadioItem (ID_view_scale_full, _("Decode at full resolution"));
+               view->AppendRadioItem (ID_view_scale_half, _("Decode at half resolution"));
+               view->AppendRadioItem (ID_view_scale_quarter, _("Decode at quarter resolution"));
+
                wxMenu* tools = new wxMenu;
                tools->Append (ID_tools_check_for_updates, _("Check for updates"));
 
@@ -158,6 +177,10 @@ private:
                help->Append (ID_help_report_a_problem, _("Report a problem..."));
 
                m->Append (file, _("&File"));
+#ifndef __WXOSX__
+               m->Append (edit, _("&Edit"));
+#endif
+               m->Append (view, _("&View"));
                m->Append (tools, _("&Tools"));
                m->Append (help, _("&Help"));
        }
index 776926fbefed8b3682e1e8f0a1e738f1cd1b173c..2b7f73e68328763c85e608617cb8df358269f4a4 100644 (file)
@@ -786,3 +786,9 @@ FilmViewer::average_latency () const
 
         return total / _latency_history.size();
 }
+
+void
+FilmViewer::set_dcp_decode_reduction (optional<int> reduction)
+{
+       _player->set_dcp_decode_reduction (reduction);
+}
index f8658be3834cb1a3f0866e269620bd1edbc0e9c7..3f7a3be331a34c5f6626cb3add171f06c1fb45c2 100644 (file)
@@ -56,6 +56,7 @@ public:
 
        void set_position (DCPTime p);
        void set_coalesce_player_changes (bool c);
+       void set_dcp_decode_reduction (boost::optional<int> reduction);
 
        void refresh ();