Extract VideoEncoder as a parent of J2KEncoder.
authorCarl Hetherington <cth@carlh.net>
Tue, 19 Mar 2024 16:10:27 +0000 (17:10 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 21 Apr 2024 21:18:23 +0000 (23:18 +0200)
src/lib/dcp_film_encoder.cc
src/lib/dcp_film_encoder.h
src/lib/j2k_encoder.cc
src/lib/j2k_encoder.h
src/lib/video_encoder.cc [new file with mode: 0644]
src/lib/video_encoder.h [new file with mode: 0644]
src/lib/wscript
test/j2k_encode_threading_test.cc

index 0403b2d90e79e9e81de6f77b7c5e53ff26a847b0..b508b66b659287ba20d5e62717390a2e978115ea 100644 (file)
@@ -67,7 +67,7 @@ using namespace dcpomatic;
 DCPFilmEncoder::DCPFilmEncoder(shared_ptr<const Film> film, weak_ptr<Job> job)
        : FilmEncoder(film, job)
        , _writer(film, job)
-       , _j2k_encoder(film, _writer)
+       , _encoder(new J2KEncoder(film, _writer))
        , _finishing (false)
        , _non_burnt_subtitles (false)
 {
@@ -98,7 +98,7 @@ void
 DCPFilmEncoder::go()
 {
        _writer.start();
-       _j2k_encoder.begin();
+       _encoder->begin();
 
        {
                auto job = _job.lock ();
@@ -117,7 +117,7 @@ DCPFilmEncoder::go()
        }
 
        _finishing = true;
-       _j2k_encoder.end();
+       _encoder->end();
        _writer.finish(_film->dir(_film->dcp_name()));
 }
 
@@ -125,20 +125,20 @@ DCPFilmEncoder::go()
 void
 DCPFilmEncoder::pause()
 {
-       _j2k_encoder.pause();
+       _encoder->pause();
 }
 
 
 void
 DCPFilmEncoder::resume()
 {
-       _j2k_encoder.resume();
+       _encoder->resume();
 }
 
 void
 DCPFilmEncoder::video(shared_ptr<PlayerVideo> data, DCPTime time)
 {
-       _j2k_encoder.encode(data, time);
+       _encoder->encode(data, time);
 }
 
 void
@@ -170,11 +170,11 @@ DCPFilmEncoder::atmos(shared_ptr<const dcp::AtmosFrame> data, DCPTime time, Atmo
 optional<float>
 DCPFilmEncoder::current_rate() const
 {
-       return _j2k_encoder.current_encoding_rate();
+       return _encoder->current_encoding_rate();
 }
 
 Frame
 DCPFilmEncoder::frames_done() const
 {
-       return _j2k_encoder.video_frames_enqueued();
+       return _encoder->video_frames_enqueued();
 }
index fbc0aeb1341b3c785ab887c84c8b5c84ee9bc4c5..3c697a11539f4a00c4bd8665cfef738bf033e7ed 100644 (file)
@@ -68,7 +68,7 @@ private:
        void atmos (std::shared_ptr<const dcp::AtmosFrame>, dcpomatic::DCPTime, AtmosMetadata metadata);
 
        Writer _writer;
-       J2KEncoder _j2k_encoder;
+       std::unique_ptr<VideoEncoder> _encoder;
        bool _finishing;
        bool _non_burnt_subtitles;
 
index de229113b3a93234f9af47d91c17c1133ae8575f..6154dfb6262638adfb0de763b678f5d979672235 100644 (file)
@@ -92,9 +92,7 @@ grk_plugin::IMessengerLogger* getMessengerLogger(void)
  *  @param writer Writer that we are using.
  */
 J2KEncoder::J2KEncoder(shared_ptr<const Film> film, Writer& writer)
-       : _film (film)
-       , _history (200)
-       , _writer (writer)
+       : VideoEncoder(film, writer)
 {
 #ifdef DCPOMATIC_GROK
        auto grok = Config::instance()->grok().get_value_or({});
@@ -242,28 +240,6 @@ J2KEncoder::end()
 }
 
 
-/** @return an estimate of the current number of frames we are encoding per second,
- *  if known.
- */
-optional<float>
-J2KEncoder::current_encoding_rate () const
-{
-       return _history.rate ();
-}
-
-
-/** @return Number of video frames that have been queued for encoding */
-int
-J2KEncoder::video_frames_enqueued () const
-{
-       if (!_last_player_video_time) {
-               return 0;
-       }
-
-       return _last_player_video_time->frames_floor (_film->video_frame_rate ());
-}
-
-
 /** Should be called when a frame has been encoded successfully */
 void
 J2KEncoder::frame_done ()
@@ -283,6 +259,8 @@ J2KEncoder::frame_done ()
 void
 J2KEncoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
 {
+       VideoEncoder::encode(pv, time);
+
        _waker.nudge ();
 
        size_t threads = 0;
@@ -344,7 +322,6 @@ J2KEncoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
        }
 
        _last_player_video[pv->eyes()] = pv;
-       _last_player_video_time = time;
 }
 
 
index 6bfbaea496b0743b14e02b922ef57a2be84f8511..c72a1debee87f959f1a98a8cdbb0f9000242d0ed 100644 (file)
@@ -34,6 +34,7 @@
 #include "exception_store.h"
 #include "j2k_encoder_thread.h"
 #include "writer.h"
+#include "video_encoder.h"
 #include <boost/optional.hpp>
 #include <boost/signals2.hpp>
 #include <boost/thread.hpp>
@@ -65,7 +66,7 @@ struct frames_not_lost_when_threads_disappear;
  *  This class keeps a queue of frames to be encoded and distributes
  *  the work around threads and encoding servers.
  */
-class J2KEncoder : public ExceptionStore
+class J2KEncoder : public VideoEncoder, public ExceptionStore
 {
 public:
        J2KEncoder(std::shared_ptr<const Film> film, Writer& writer);
@@ -75,19 +76,16 @@ public:
        J2KEncoder& operator= (J2KEncoder const&) = delete;
 
        /** Called to indicate that a processing run is about to begin */
-       void begin ();
+       void begin() override;
 
        /** Called to pass a bit of video to be encoded as the next DCP frame */
-       void encode (std::shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time);
+       void encode (std::shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time) override;
 
-       void pause();
-       void resume();
+       void pause() override;
+       void resume() override;
 
        /** Called when a processing run has finished */
-       void end();
-
-       boost::optional<float> current_encoding_rate () const;
-       int video_frames_enqueued () const;
+       void end() override;
 
        DCPVideo pop();
        void retry(DCPVideo frame);
@@ -103,11 +101,6 @@ private:
        void remake_threads(int cpu, int gpu, std::list<EncodeServerDescription> servers);
        void terminate_threads ();
 
-       /** Film that we are encoding */
-       std::shared_ptr<const Film> _film;
-
-       EventHistory _history;
-
        boost::mutex _threads_mutex;
        std::vector<std::shared_ptr<J2KEncoderThread>> _threads;
 
@@ -118,11 +111,9 @@ private:
        /** condition to manage thread wakeups when we have too much to do */
        boost::condition _full_condition;
 
-       Writer& _writer;
        Waker _waker;
 
        EnumIndexedVector<std::shared_ptr<PlayerVideo>, Eyes> _last_player_video;
-       boost::optional<dcpomatic::DCPTime> _last_player_video_time;
 
        boost::signals2::scoped_connection _server_found_connection;
 
diff --git a/src/lib/video_encoder.cc b/src/lib/video_encoder.cc
new file mode 100644 (file)
index 0000000..590dc74
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+    Copyright (C) 2024 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "video_encoder.h"
+
+
+using std::shared_ptr;
+using boost::optional;
+
+
+VideoEncoder::VideoEncoder(shared_ptr<const Film> film, Writer& writer)
+       : _film(film)
+       , _writer(writer)
+       , _history(200)
+{
+
+}
+
+
+void
+VideoEncoder::encode(shared_ptr<PlayerVideo>, dcpomatic::DCPTime time)
+{
+       _last_player_video_time = time;
+}
+
+
+/** @return Number of video frames that have been queued for encoding */
+int
+VideoEncoder::video_frames_enqueued() const
+{
+       if (!_last_player_video_time) {
+               return 0;
+       }
+
+       return _last_player_video_time->frames_floor(_film->video_frame_rate());
+}
+
+
+/** @return an estimate of the current number of frames we are encoding per second,
+ *  if known.
+ */
+optional<float>
+VideoEncoder::current_encoding_rate() const
+{
+       return _history.rate();
+}
diff --git a/src/lib/video_encoder.h b/src/lib/video_encoder.h
new file mode 100644 (file)
index 0000000..8cc33ef
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    Copyright (C) 2024 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_VIDEO_ENCODER_H
+#define DCPOMATIC_VIDEO_ENCODER_H
+
+
+#include "dcpomatic_time.h"
+#include "event_history.h"
+#include "film.h"
+#include "player_video.h"
+
+
+class Writer;
+
+
+class VideoEncoder
+{
+public:
+       VideoEncoder(std::shared_ptr<const Film> film, Writer& writer);
+       virtual ~VideoEncoder() {}
+
+       VideoEncoder(VideoEncoder const&) = delete;
+       VideoEncoder& operator=(VideoEncoder const&) = delete;
+
+       /** Called to indicate that a processing run is about to begin */
+       virtual void begin() {}
+
+       /** Called to pass a bit of video to be encoded as the next DCP frame */
+       virtual void encode(std::shared_ptr<PlayerVideo> pv, dcpomatic::DCPTime time);
+
+       virtual void pause() = 0;
+       virtual void resume() = 0;
+
+       /** Called when a processing run has finished */
+       virtual void end() = 0;
+
+       int video_frames_enqueued() const;
+       boost::optional<float> current_encoding_rate() const;
+
+protected:
+       /** Film that we are encoding */
+       std::shared_ptr<const Film> _film;
+       Writer& _writer;
+       EventHistory _history;
+       boost::optional<dcpomatic::DCPTime> _last_player_video_time;
+};
+
+
+#endif
+
index 5e49b1fbf9f25a13bdb568b163ef55fb0e8008ae..152d3abafb89e716a48ad743740134b21c10a701 100644 (file)
@@ -210,6 +210,7 @@ sources = """
           verify_dcp_job.cc
           video_content.cc
           video_decoder.cc
+          video_encoder.cc
           video_filter_graph.cc
           video_filter_graph_set.cc
           video_frame_type.cc
index 5f66df20abc9811662e8fd8f87abf92a8085c4a9..ee219fbe0faf4944fc01c4bf334e05c5972b2b70 100644 (file)
@@ -99,10 +99,10 @@ BOOST_AUTO_TEST_CASE(frames_not_lost_when_threads_disappear)
        auto film = new_test_film2("frames_not_lost", content);
        film->write_metadata();
        auto job = make_dcp(film, TranscodeJob::ChangedBehaviour::IGNORE);
-       auto& encoder = dynamic_pointer_cast<DCPFilmEncoder>(job->_encoder)->_j2k_encoder;
+       auto encoder = dynamic_cast<J2KEncoder*>(dynamic_pointer_cast<DCPFilmEncoder>(job->_encoder)->_encoder.get());
 
        while (JobManager::instance()->work_to_do()) {
-               encoder.remake_threads(rand() % 8, 0, {});
+               encoder->remake_threads(rand() % 8, 0, {});
                dcpomatic_sleep_seconds(1);
        }