From fa15dc1a375e13d2047a857e5aef202179eec0d4 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Mar 2024 17:10:27 +0100 Subject: [PATCH] Extract VideoEncoder as a parent of J2KEncoder. --- src/lib/dcp_film_encoder.cc | 16 +++---- src/lib/dcp_film_encoder.h | 2 +- src/lib/j2k_encoder.cc | 29 ++----------- src/lib/j2k_encoder.h | 23 ++++------- src/lib/video_encoder.cc | 64 ++++++++++++++++++++++++++++ src/lib/video_encoder.h | 69 +++++++++++++++++++++++++++++++ src/lib/wscript | 1 + test/j2k_encode_threading_test.cc | 4 +- 8 files changed, 155 insertions(+), 53 deletions(-) create mode 100644 src/lib/video_encoder.cc create mode 100644 src/lib/video_encoder.h diff --git a/src/lib/dcp_film_encoder.cc b/src/lib/dcp_film_encoder.cc index 0403b2d90..b508b66b6 100644 --- a/src/lib/dcp_film_encoder.cc +++ b/src/lib/dcp_film_encoder.cc @@ -67,7 +67,7 @@ using namespace dcpomatic; DCPFilmEncoder::DCPFilmEncoder(shared_ptr film, weak_ptr 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 data, DCPTime time) { - _j2k_encoder.encode(data, time); + _encoder->encode(data, time); } void @@ -170,11 +170,11 @@ DCPFilmEncoder::atmos(shared_ptr data, DCPTime time, Atmo optional 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(); } diff --git a/src/lib/dcp_film_encoder.h b/src/lib/dcp_film_encoder.h index fbc0aeb13..3c697a115 100644 --- a/src/lib/dcp_film_encoder.h +++ b/src/lib/dcp_film_encoder.h @@ -68,7 +68,7 @@ private: void atmos (std::shared_ptr, dcpomatic::DCPTime, AtmosMetadata metadata); Writer _writer; - J2KEncoder _j2k_encoder; + std::unique_ptr _encoder; bool _finishing; bool _non_burnt_subtitles; diff --git a/src/lib/j2k_encoder.cc b/src/lib/j2k_encoder.cc index de229113b..6154dfb62 100644 --- a/src/lib/j2k_encoder.cc +++ b/src/lib/j2k_encoder.cc @@ -92,9 +92,7 @@ grk_plugin::IMessengerLogger* getMessengerLogger(void) * @param writer Writer that we are using. */ J2KEncoder::J2KEncoder(shared_ptr 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 -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 pv, DCPTime time) { + VideoEncoder::encode(pv, time); + _waker.nudge (); size_t threads = 0; @@ -344,7 +322,6 @@ J2KEncoder::encode (shared_ptr pv, DCPTime time) } _last_player_video[pv->eyes()] = pv; - _last_player_video_time = time; } diff --git a/src/lib/j2k_encoder.h b/src/lib/j2k_encoder.h index 6bfbaea49..c72a1debe 100644 --- a/src/lib/j2k_encoder.h +++ b/src/lib/j2k_encoder.h @@ -34,6 +34,7 @@ #include "exception_store.h" #include "j2k_encoder_thread.h" #include "writer.h" +#include "video_encoder.h" #include #include #include @@ -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 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 pv, dcpomatic::DCPTime time); + void encode (std::shared_ptr 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 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 servers); void terminate_threads (); - /** Film that we are encoding */ - std::shared_ptr _film; - - EventHistory _history; - boost::mutex _threads_mutex; std::vector> _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, Eyes> _last_player_video; - boost::optional _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 index 000000000..590dc7471 --- /dev/null +++ b/src/lib/video_encoder.cc @@ -0,0 +1,64 @@ +/* + Copyright (C) 2024 Carl Hetherington + + 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 . + +*/ + + +#include "video_encoder.h" + + +using std::shared_ptr; +using boost::optional; + + +VideoEncoder::VideoEncoder(shared_ptr film, Writer& writer) + : _film(film) + , _writer(writer) + , _history(200) +{ + +} + + +void +VideoEncoder::encode(shared_ptr, 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 +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 index 000000000..8cc33ef8a --- /dev/null +++ b/src/lib/video_encoder.h @@ -0,0 +1,69 @@ +/* + Copyright (C) 2024 Carl Hetherington + + 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 . + +*/ + + +#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 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 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 current_encoding_rate() const; + +protected: + /** Film that we are encoding */ + std::shared_ptr _film; + Writer& _writer; + EventHistory _history; + boost::optional _last_player_video_time; +}; + + +#endif + diff --git a/src/lib/wscript b/src/lib/wscript index 5e49b1fbf..152d3abaf 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -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 diff --git a/test/j2k_encode_threading_test.cc b/test/j2k_encode_threading_test.cc index 5f66df20a..ee219fbe0 100644 --- a/test/j2k_encode_threading_test.cc +++ b/test/j2k_encode_threading_test.cc @@ -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(job->_encoder)->_j2k_encoder; + auto encoder = dynamic_cast(dynamic_pointer_cast(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); } -- 2.30.2