From 1fc8c0c6d045404732497ba70bd2eccfbe4cc6f6 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Mar 2024 16:29:13 +0100 Subject: Rename Encoder -> FilmEncoder, and subclasses. --- src/lib/dcp_film_encoder.cc | 180 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 src/lib/dcp_film_encoder.cc (limited to 'src/lib/dcp_film_encoder.cc') diff --git a/src/lib/dcp_film_encoder.cc b/src/lib/dcp_film_encoder.cc new file mode 100644 index 000000000..0403b2d90 --- /dev/null +++ b/src/lib/dcp_film_encoder.cc @@ -0,0 +1,180 @@ +/* + Copyright (C) 2012-2020 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 . + +*/ + + +/** @file src/dcp_film_encoder.cc + * @brief A class which takes a Film and some Options, then uses those to encode the film into a DCP. + * + * A decoder is selected according to the content type, and the encoder can be specified + * as a parameter to the constructor. + */ + + +#include "audio_decoder.h" +#include "compose.hpp" +#include "dcp_film_encoder.h" +#include "film.h" +#include "j2k_encoder.h" +#include "job.h" +#include "player.h" +#include "player_video.h" +#include "referenced_reel_asset.h" +#include "text_content.h" +#include "video_decoder.h" +#include "writer.h" +#include +#include + +#include "i18n.h" + + +using std::cout; +using std::dynamic_pointer_cast; +using std::list; +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::vector; +using std::weak_ptr; +using boost::optional; +#if BOOST_VERSION >= 106100 +using namespace boost::placeholders; +#endif +using namespace dcpomatic; + + +/** Construct a DCP encoder. + * @param film Film that we are encoding. + * @param job Job that this encoder is being used in. + */ +DCPFilmEncoder::DCPFilmEncoder(shared_ptr film, weak_ptr job) + : FilmEncoder(film, job) + , _writer(film, job) + , _j2k_encoder(film, _writer) + , _finishing (false) + , _non_burnt_subtitles (false) +{ + _player_video_connection = _player.Video.connect(bind(&DCPFilmEncoder::video, this, _1, _2)); + _player_audio_connection = _player.Audio.connect(bind(&DCPFilmEncoder::audio, this, _1, _2)); + _player_text_connection = _player.Text.connect(bind(&DCPFilmEncoder::text, this, _1, _2, _3, _4)); + _player_atmos_connection = _player.Atmos.connect(bind(&DCPFilmEncoder::atmos, this, _1, _2, _3)); + + for (auto c: film->content ()) { + for (auto i: c->text) { + if (i->use() && !i->burn()) { + _non_burnt_subtitles = true; + } + } + } +} + +DCPFilmEncoder::~DCPFilmEncoder() +{ + /* We must stop receiving more video data before we die */ + _player_video_connection.release (); + _player_audio_connection.release (); + _player_text_connection.release (); + _player_atmos_connection.release (); +} + +void +DCPFilmEncoder::go() +{ + _writer.start(); + _j2k_encoder.begin(); + + { + auto job = _job.lock (); + DCPOMATIC_ASSERT (job); + job->sub (_("Encoding")); + } + + if (_non_burnt_subtitles) { + _writer.write(_player.get_subtitle_fonts()); + } + + while (!_player.pass()) {} + + for (auto i: get_referenced_reel_assets(_film, _film->playlist())) { + _writer.write(i); + } + + _finishing = true; + _j2k_encoder.end(); + _writer.finish(_film->dir(_film->dcp_name())); +} + + +void +DCPFilmEncoder::pause() +{ + _j2k_encoder.pause(); +} + + +void +DCPFilmEncoder::resume() +{ + _j2k_encoder.resume(); +} + +void +DCPFilmEncoder::video(shared_ptr data, DCPTime time) +{ + _j2k_encoder.encode(data, time); +} + +void +DCPFilmEncoder::audio(shared_ptr data, DCPTime time) +{ + _writer.write(data, time); + + auto job = _job.lock (); + DCPOMATIC_ASSERT (job); + job->set_progress (float(time.get()) / _film->length().get()); +} + +void +DCPFilmEncoder::text(PlayerText data, TextType type, optional track, DCPTimePeriod period) +{ + if (type == TextType::CLOSED_CAPTION || _non_burnt_subtitles) { + _writer.write(data, type, track, period); + } +} + + +void +DCPFilmEncoder::atmos(shared_ptr data, DCPTime time, AtmosMetadata metadata) +{ + _writer.write(data, time, metadata); +} + + +optional +DCPFilmEncoder::current_rate() const +{ + return _j2k_encoder.current_encoding_rate(); +} + +Frame +DCPFilmEncoder::frames_done() const +{ + return _j2k_encoder.video_frames_enqueued(); +} -- cgit v1.2.3 From fa15dc1a375e13d2047a857e5aef202179eec0d4 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Mar 2024 17:10:27 +0100 Subject: 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 (limited to 'src/lib/dcp_film_encoder.cc') 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); } -- cgit v1.2.3 From f72a79c93626e773214f1a0318adf2445ac2ee72 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 19 Mar 2024 14:02:47 +0100 Subject: Support encoding of MPEG2 DCPs. --- src/lib/dcp_film_encoder.cc | 11 ++++- src/lib/film.cc | 16 +++++++ src/lib/film.h | 7 +++ src/lib/film_property.h | 1 + src/lib/mpeg2_encoder.cc | 80 +++++++++++++++++++++++++++++++ src/lib/mpeg2_encoder.h | 42 ++++++++++++++++ src/lib/reel_writer.cc | 114 ++++++++++++++++++++++++++++++-------------- src/lib/reel_writer.h | 13 +++-- src/lib/video_encoding.cc | 56 ++++++++++++++++++++++ src/lib/video_encoding.h | 34 +++++++++++++ src/lib/writer.cc | 14 ++++-- src/lib/writer.h | 7 ++- src/lib/wscript | 2 + src/wx/dcp_panel.cc | 16 ++++++- test/burnt_subtitle_test.cc | 2 +- test/data | 2 +- test/overlap_video_test.cc | 2 +- 17 files changed, 369 insertions(+), 50 deletions(-) create mode 100644 src/lib/mpeg2_encoder.cc create mode 100644 src/lib/mpeg2_encoder.h create mode 100644 src/lib/video_encoding.cc create mode 100644 src/lib/video_encoding.h (limited to 'src/lib/dcp_film_encoder.cc') diff --git a/src/lib/dcp_film_encoder.cc b/src/lib/dcp_film_encoder.cc index b508b66b6..17f531693 100644 --- a/src/lib/dcp_film_encoder.cc +++ b/src/lib/dcp_film_encoder.cc @@ -33,6 +33,7 @@ #include "film.h" #include "j2k_encoder.h" #include "job.h" +#include "mpeg2_encoder.h" #include "player.h" #include "player_video.h" #include "referenced_reel_asset.h" @@ -67,10 +68,18 @@ using namespace dcpomatic; DCPFilmEncoder::DCPFilmEncoder(shared_ptr film, weak_ptr job) : FilmEncoder(film, job) , _writer(film, job) - , _encoder(new J2KEncoder(film, _writer)) , _finishing (false) , _non_burnt_subtitles (false) { + switch (_film->video_encoding()) { + case VideoEncoding::JPEG2000: + _encoder.reset(new J2KEncoder(film, _writer)); + break; + case VideoEncoding::MPEG2: + _encoder.reset(new MPEG2Encoder(film, _writer)); + break; + } + _player_video_connection = _player.Video.connect(bind(&DCPFilmEncoder::video, this, _1, _2)); _player_audio_connection = _player.Audio.connect(bind(&DCPFilmEncoder::audio, this, _1, _2)); _player_text_connection = _player.Text.connect(bind(&DCPFilmEncoder::text, this, _1, _2, _3, _4)); diff --git a/src/lib/film.cc b/src/lib/film.cc index 3de536a6d..54c68d756 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -169,6 +169,7 @@ Film::Film (optional dir) , _three_d (false) , _sequence (true) , _interop (Config::instance()->default_interop ()) + , _video_encoding(VideoEncoding::JPEG2000) , _limit_to_smpte_bv20(false) , _audio_processor (0) , _reel_type (ReelType::SINGLE) @@ -251,6 +252,9 @@ Film::video_identifier () const if (_interop) { s += "_I"; + if (_video_encoding == VideoEncoding::MPEG2) { + s += "_M"; + } } else { s += "_S"; if (_limit_to_smpte_bv20) { @@ -405,6 +409,7 @@ Film::metadata (bool with_content_paths) const cxml::add_text_child(root, "ThreeD", _three_d ? "1" : "0"); cxml::add_text_child(root, "Sequence", _sequence ? "1" : "0"); cxml::add_text_child(root, "Interop", _interop ? "1" : "0"); + cxml::add_text_child(root, "VideoEncoding", video_encoding_to_string(_video_encoding)); cxml::add_text_child(root, "LimitToSMPTEBv20", _limit_to_smpte_bv20 ? "1" : "0"); cxml::add_text_child(root, "Encrypted", _encrypted ? "1" : "0"); cxml::add_text_child(root, "Key", _key.hex ()); @@ -595,6 +600,9 @@ Film::read_metadata (optional path) _three_d = f.bool_child ("ThreeD"); _interop = f.bool_child ("Interop"); + if (auto encoding = f.optional_string_child("VideoEncoding")) { + _video_encoding = video_encoding_from_string(*encoding); + } _limit_to_smpte_bv20 = f.optional_bool_child("LimitToSMPTEBv20").get_value_or(false); _key = dcp::Key (f.string_child ("Key")); _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ()); @@ -1219,6 +1227,14 @@ Film::set_interop (bool i) } +void +Film::set_video_encoding(VideoEncoding encoding) +{ + FilmChangeSignaller ch(this, FilmProperty::VIDEO_ENCODING); + _video_encoding = encoding; +} + + void Film::set_limit_to_smpte_bv20(bool limit) { diff --git a/src/lib/film.h b/src/lib/film.h index 392f1dc5e..ff3dee6fc 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -41,6 +41,7 @@ #include "transcode_job.h" #include "types.h" #include "util.h" +#include "video_encoding.h" #include #include #include @@ -275,6 +276,10 @@ public: return _interop; } + VideoEncoding video_encoding() const { + return _video_encoding; + } + bool limit_to_smpte_bv20() const { return _limit_to_smpte_bv20; } @@ -408,6 +413,7 @@ public: void set_isdcf_date_today (); void set_sequence (bool); void set_interop (bool); + void set_video_encoding(VideoEncoding encoding); void set_limit_to_smpte_bv20(bool); void set_audio_processor (AudioProcessor const * processor); void set_reel_type (ReelType); @@ -529,6 +535,7 @@ private: bool _three_d; bool _sequence; bool _interop; + VideoEncoding _video_encoding; bool _limit_to_smpte_bv20; AudioProcessor const * _audio_processor; ReelType _reel_type; diff --git a/src/lib/film_property.h b/src/lib/film_property.h index b755a4887..ebda0e807 100644 --- a/src/lib/film_property.h +++ b/src/lib/film_property.h @@ -47,6 +47,7 @@ enum class FilmProperty { THREE_D, SEQUENCE, INTEROP, + VIDEO_ENCODING, LIMIT_TO_SMPTE_BV20, AUDIO_PROCESSOR, REEL_TYPE, diff --git a/src/lib/mpeg2_encoder.cc b/src/lib/mpeg2_encoder.cc new file mode 100644 index 000000000..49bff19d4 --- /dev/null +++ b/src/lib/mpeg2_encoder.cc @@ -0,0 +1,80 @@ +/* + 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 "mpeg2_encoder.h" +#include "writer.h" +#include +extern "C" { +#include +} + + +using std::shared_ptr; + + +MPEG2Encoder::MPEG2Encoder(shared_ptr film, Writer& writer) + : VideoEncoder(film, writer) + , _transcoder(film->frame_size(), film->video_frame_rate(), film->video_bit_rate()) +{ + +} + + +void +MPEG2Encoder::encode(shared_ptr pv, dcpomatic::DCPTime time) +{ + VideoEncoder::encode(pv, time); + + auto image = pv->image( + [](AVPixelFormat) { return AV_PIX_FMT_YUV420P; }, + VideoRange::VIDEO, + false + ); + + dcp::FFmpegImage ffmpeg_image(time.get() * _film->video_frame_rate() / dcpomatic::DCPTime::HZ); + + DCPOMATIC_ASSERT(image->size() == ffmpeg_image.size()); + + auto height = image->size().height; + + for (int y = 0; y < height; ++y) { + memcpy(ffmpeg_image.y() + ffmpeg_image.y_stride() * y, image->data()[0] + image->stride()[0] * y, ffmpeg_image.y_stride()); + } + + for (int y = 0; y < height / 2; ++y) { + memcpy(ffmpeg_image.u() + ffmpeg_image.u_stride() * y, image->data()[1] + image->stride()[1] * y, ffmpeg_image.u_stride()); + memcpy(ffmpeg_image.v() + ffmpeg_image.v_stride() * y, image->data()[2] + image->stride()[2] * y, ffmpeg_image.v_stride()); + } + + if (auto compressed = _transcoder.compress_frame(std::move(ffmpeg_image))) { + _writer.write(compressed->first, compressed->second); + } +} + + +void +MPEG2Encoder::end() +{ + if (auto compressed = _transcoder.flush()) { + _writer.write(compressed->first, compressed->second); + } +} + diff --git a/src/lib/mpeg2_encoder.h b/src/lib/mpeg2_encoder.h new file mode 100644 index 000000000..1b2259d26 --- /dev/null +++ b/src/lib/mpeg2_encoder.h @@ -0,0 +1,42 @@ +/* + 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" +#include + + +class MPEG2Encoder : public VideoEncoder +{ +public: + MPEG2Encoder(std::shared_ptr film, Writer& writer); + + void encode(std::shared_ptr pv, dcpomatic::DCPTime time) override; + + void pause() override {} + void resume() override {} + + /** Called when a processing run has finished */ + void end() override; + +private: + dcp::MPEG2Compressor _transcoder; +}; + diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 201c23e71..8dc0d7f06 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -146,30 +146,45 @@ ReelWriter::ReelWriter ( dcp::filesystem::rename(asset.string() + ".tmp", asset); } + auto const rate = dcp::Fraction(film()->video_frame_rate(), 1); - if (film()->three_d()) { - _picture_asset = std::make_shared(dcp::Fraction(film()->video_frame_rate(), 1), standard); - } else { - _picture_asset = std::make_shared(dcp::Fraction(film()->video_frame_rate(), 1), standard); - } + auto setup = [this](shared_ptr asset) { + asset->set_size(film()->frame_size()); + asset->set_metadata(mxf_metadata()); - _picture_asset->set_size (film()->frame_size()); - _picture_asset->set_metadata (mxf_metadata()); + if (film()->encrypted()) { + asset->set_key(film()->key()); + asset->set_context_id(film()->context_id()); + } + }; - if (film()->encrypted()) { - _picture_asset->set_key (film()->key()); - _picture_asset->set_context_id (film()->context_id()); + if (film()->video_encoding() == VideoEncoding::JPEG2000) { + if (film()->three_d()) { + _j2k_picture_asset = std::make_shared(rate, standard); + } else { + _j2k_picture_asset = std::make_shared(rate, standard); + } + setup(_j2k_picture_asset); + _j2k_picture_asset->set_file(asset); + _j2k_picture_asset_writer = _j2k_picture_asset->start_write(asset, _first_nonexistent_frame > 0 ? dcp::Behaviour::OVERWRITE_EXISTING : dcp::Behaviour::MAKE_NEW); + } else { + _mpeg2_picture_asset = std::make_shared(rate); + setup(_mpeg2_picture_asset); + _mpeg2_picture_asset->set_file(asset); + _mpeg2_picture_asset_writer = _mpeg2_picture_asset->start_write(asset, _first_nonexistent_frame > 0 ? dcp::Behaviour::OVERWRITE_EXISTING : dcp::Behaviour::MAKE_NEW); } - _picture_asset->set_file (asset); - _picture_asset_writer = _picture_asset->start_write(asset, _first_nonexistent_frame > 0 ? dcp::Behaviour::OVERWRITE_EXISTING : dcp::Behaviour::MAKE_NEW); } else if (!text_only) { /* We already have a complete picture asset that we can just re-use */ /* XXX: what about if the encryption key changes? */ - if (film()->three_d()) { - _picture_asset = make_shared(asset); + if (film()->video_encoding() == VideoEncoding::JPEG2000) { + if (film()->three_d()) { + _j2k_picture_asset = make_shared(asset); + } else { + _j2k_picture_asset = make_shared(asset); + } } else { - _picture_asset = make_shared(asset); + _mpeg2_picture_asset = make_shared(asset); } } @@ -272,7 +287,7 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset) void ReelWriter::write (shared_ptr encoded, Frame frame, Eyes eyes) { - if (!_picture_asset_writer) { + if (!_j2k_picture_asset_writer) { /* We're not writing any data */ return; } @@ -300,21 +315,28 @@ ReelWriter::write (shared_ptr atmos, AtmosMetadata metada void -ReelWriter::fake_write (int size) +ReelWriter::write(shared_ptr image) +{ + _mpeg2_picture_asset_writer->write(image->data(), image->size()); +} + + +void +ReelWriter::fake_write(dcp::J2KFrameInfo const& info) { - if (!_picture_asset_writer) { + if (!_j2k_picture_asset_writer) { /* We're not writing any data */ return; } - _picture_asset_writer->fake_write (size); + _j2k_picture_asset_writer->fake_write(info); } void ReelWriter::repeat_write (Frame frame, Eyes eyes) { - if (!_picture_asset_writer) { + if (!_j2k_picture_asset_writer) { /* We're not writing any data */ return; } @@ -327,10 +349,16 @@ ReelWriter::repeat_write (Frame frame, Eyes eyes) void ReelWriter::finish (boost::filesystem::path output_dcp) { - if (_picture_asset_writer && !_picture_asset_writer->finalize ()) { - /* Nothing was written to the picture asset */ - LOG_GENERAL ("Nothing was written to reel %1 of %2", _reel_index, _reel_count); - _picture_asset.reset (); + if (_j2k_picture_asset_writer && !_j2k_picture_asset_writer->finalize()) { + /* Nothing was written to the J2K picture asset */ + LOG_GENERAL("Nothing was written to J2K asset for reel %1 of %2", _reel_index, _reel_count); + _j2k_picture_asset.reset(); + } + + if (_mpeg2_picture_asset_writer && !_mpeg2_picture_asset_writer->finalize()) { + /* Nothing was written to the MPEG2 picture asset */ + LOG_GENERAL("Nothing was written to MPEG2 asset for reel %1 of %2", _reel_index, _reel_count); + _mpeg2_picture_asset.reset(); } if (_sound_asset_writer && !_sound_asset_writer->finalize ()) { @@ -338,12 +366,21 @@ ReelWriter::finish (boost::filesystem::path output_dcp) _sound_asset.reset (); } + shared_ptr picture_asset; + if (_j2k_picture_asset) { + picture_asset = _j2k_picture_asset; + } else if (_mpeg2_picture_asset) { + picture_asset = _mpeg2_picture_asset; + } + /* Hard-link any video asset file into the DCP */ - if (_picture_asset) { - DCPOMATIC_ASSERT (_picture_asset->file()); - boost::filesystem::path video_from = _picture_asset->file().get(); - boost::filesystem::path video_to = output_dcp; - video_to /= video_asset_filename (_picture_asset, _reel_index, _reel_count, _content_summary); + if (picture_asset) { + auto const file = picture_asset->file(); + DCPOMATIC_ASSERT(file); + + auto video_from = *file; + auto video_to = output_dcp; + video_to /= video_asset_filename(picture_asset, _reel_index, _reel_count, _content_summary); /* There may be an existing "to" file if we are recreating a DCP in the same place without changing any video. */ @@ -371,7 +408,7 @@ ReelWriter::finish (boost::filesystem::path output_dcp) } } - _picture_asset->set_file (video_to); + picture_asset->set_file(video_to); } /* Move the audio asset into the DCP */ @@ -495,17 +532,17 @@ ReelWriter::create_reel_picture (shared_ptr reel, list reel_asset; - if (_picture_asset) { + if (_j2k_picture_asset) { /* We have made a picture asset of our own. Put it into the reel */ - auto mono = dynamic_pointer_cast(_picture_asset); - if (mono) { + if (auto mono = dynamic_pointer_cast(_j2k_picture_asset)) { reel_asset = make_shared(mono, 0); } - auto stereo = dynamic_pointer_cast(_picture_asset); - if (stereo) { + if (auto stereo = dynamic_pointer_cast(_j2k_picture_asset)) { reel_asset = make_shared(stereo, 0); } + } else if (_mpeg2_picture_asset) { + reel_asset = make_shared(_mpeg2_picture_asset, 0); } else { LOG_GENERAL ("no picture asset of our own; look through %1", refs.size()); /* We don't have a picture asset of our own; hopefully we have one to reference */ @@ -734,8 +771,11 @@ try { vector> assets; - if (_picture_asset) { - assets.push_back(_picture_asset); + if (_j2k_picture_asset) { + assets.push_back(_j2k_picture_asset); + } + if (_mpeg2_picture_asset) { + assets.push_back(_mpeg2_picture_asset); } if (_sound_asset) { assets.push_back(_sound_asset); diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index 30abdd563..f6273f8e9 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -31,7 +31,10 @@ #include "weak_film.h" #include #include +#include #include +#include +#include class AudioBuffers; @@ -46,6 +49,7 @@ namespace dcp { class MonoJ2KPictureAssetWriter; class J2KPictureAsset; class J2KPictureAssetWriter; + class MPEG2PictureAsset; class Reel; class ReelAsset; class ReelPictureAsset; @@ -70,11 +74,12 @@ public: ); void write (std::shared_ptr encoded, Frame frame, Eyes eyes); - void fake_write (int size); + void fake_write(dcp::J2KFrameInfo const& info); void repeat_write (Frame frame, Eyes eyes); void write (std::shared_ptr audio); void write(PlayerText text, TextType type, boost::optional track, dcpomatic::DCPTimePeriod period, FontIdMap const& fonts, std::shared_ptr chosen_interop_font); void write (std::shared_ptr atmos, AtmosMetadata metadata); + void write(std::shared_ptr image); void finish (boost::filesystem::path output_dcp); std::shared_ptr create_reel ( @@ -131,9 +136,11 @@ private: dcp::ArrayData _default_font; - std::shared_ptr _picture_asset; + std::shared_ptr _j2k_picture_asset; + std::shared_ptr _mpeg2_picture_asset; /** picture asset writer, or 0 if we are not writing any picture because we already have one */ - std::shared_ptr _picture_asset_writer; + std::shared_ptr _j2k_picture_asset_writer; + std::shared_ptr _mpeg2_picture_asset_writer; std::shared_ptr _sound_asset; std::shared_ptr _sound_asset_writer; std::shared_ptr _subtitle_asset; diff --git a/src/lib/video_encoding.cc b/src/lib/video_encoding.cc new file mode 100644 index 000000000..ead1a2eaf --- /dev/null +++ b/src/lib/video_encoding.cc @@ -0,0 +1,56 @@ +/* + 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 "dcpomatic_assert.h" +#include "video_encoding.h" + + +using std::string; + + +string +video_encoding_to_string(VideoEncoding encoding) +{ + switch (encoding) { + case VideoEncoding::JPEG2000: + return "jpeg2000"; + case VideoEncoding::MPEG2: + return "mpeg2"; + } + + DCPOMATIC_ASSERT(false); +} + + +VideoEncoding +video_encoding_from_string(string const& encoding) +{ + if (encoding == "jpeg2000") { + return VideoEncoding::JPEG2000; + } + + if (encoding == "mpeg2") { + return VideoEncoding::MPEG2; + } + + DCPOMATIC_ASSERT(false); +} + diff --git a/src/lib/video_encoding.h b/src/lib/video_encoding.h new file mode 100644 index 000000000..45ee0b21e --- /dev/null +++ b/src/lib/video_encoding.h @@ -0,0 +1,34 @@ +/* + 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 + + +enum class VideoEncoding +{ + JPEG2000, + MPEG2 +}; + + +std::string video_encoding_to_string(VideoEncoding encoding); +VideoEncoding video_encoding_from_string(std::string const& encoding); + diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 1c04f4065..3dd22718f 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -40,6 +40,7 @@ #include "version.h" #include "writer.h" #include +#include #include #include #include @@ -169,6 +170,13 @@ Writer::write (shared_ptr encoded, Frame frame, Eyes eyes) } +void +Writer::write(shared_ptr image, Frame frame) +{ + _reels[video_reel(frame)].write(image); +} + + bool Writer::can_repeat (Frame frame) const { @@ -230,7 +238,7 @@ Writer::fake_write (Frame frame, Eyes eyes) QueueItem qi; qi.type = QueueItem::Type::FAKE; - qi.size = J2KFrameInfo(film()->info_file_handle(_reels[reel].period(), true), frame_in_reel, eyes).size; + qi.info = J2KFrameInfo(film()->info_file_handle(_reels[reel].period(), true), frame_in_reel, eyes); DCPOMATIC_ASSERT((film()->three_d() && eyes != Eyes::BOTH) || (!film()->three_d() && eyes == Eyes::BOTH)); @@ -401,7 +409,7 @@ try if (i.type == QueueItem::Type::FULL) { LOG_WARNING (N_("- type FULL, frame %1, eyes %2"), i.frame, (int) i.eyes); } else { - LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.size, i.frame, (int) i.eyes); + LOG_WARNING (N_("- type FAKE, size %1, frame %2, eyes %3"), i.info.size, i.frame, (int) i.eyes); } } } @@ -432,7 +440,7 @@ try break; case QueueItem::Type::FAKE: LOG_DEBUG_ENCODE (N_("Writer FAKE-writes %1"), qi.frame); - reel.fake_write (qi.size); + reel.fake_write(qi.info); ++_fake_written; break; case QueueItem::Type::REPEAT: diff --git a/src/lib/writer.h b/src/lib/writer.h index 1cd278221..1dd88d8a9 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -37,6 +37,8 @@ #include "text_type.h" #include "weak_film.h" #include +#include +#include #include #include #include @@ -74,8 +76,8 @@ public: /** encoded data for FULL */ std::shared_ptr encoded; - /** size of data for FAKE */ - int size = 0; + /** info for FAKE */ + dcp::J2KFrameInfo info; /** reel index */ size_t reel = 0; /** frame index within the reel */ @@ -122,6 +124,7 @@ public: void write (std::vector> fonts); void write (ReferencedReelAsset asset); void write (std::shared_ptr atmos, dcpomatic::DCPTime time, AtmosMetadata metadata); + void write (std::shared_ptr image, Frame frame); void finish (boost::filesystem::path output_dcp); void set_encoder_threads (int threads); diff --git a/src/lib/wscript b/src/lib/wscript index 8e839cb49..86cf2e0b6 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -154,6 +154,7 @@ sources = """ maths_util.cc memory_util.cc mid_side_decoder.cc + mpeg2_encoder.cc named_channel.cc overlaps.cc pixel_quanta.cc @@ -212,6 +213,7 @@ sources = """ video_content.cc video_decoder.cc video_encoder.cc + video_encoding.cc video_filter_graph.cc video_filter_graph_set.cc video_frame_type.cc diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index c2f32fbb9..4ae89d443 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -151,6 +151,7 @@ DCPPanel::add_standards() _standard->add(_("SMPTE (Bv2.0 only)"), N_("smpte-bv20")); } _standard->add(_("Interop"), N_("interop")); + _standard->add(_("MPEG2 Interop"), N_("mpeg2-interop")); _sizer->Layout(); } @@ -162,7 +163,11 @@ DCPPanel::set_standard() DCPOMATIC_ASSERT(!_film->limit_to_smpte_bv20() || _standard->GetCount() == 3); if (_film->interop()) { - checked_set(_standard, "interop"); + if (_film->video_encoding() == VideoEncoding::JPEG2000) { + checked_set(_standard, "interop"); + } else { + checked_set(_standard, "mpeg2-interop"); + } } else { checked_set(_standard, _film->limit_to_smpte_bv20() ? "smpte-bv20" : "smpte"); } @@ -184,12 +189,18 @@ DCPPanel::standard_changed () if (*data == N_("interop")) { _film->set_interop(true); _film->set_limit_to_smpte_bv20(false); + _film->set_video_encoding(VideoEncoding::JPEG2000); } else if (*data == N_("smpte")) { _film->set_interop(false); _film->set_limit_to_smpte_bv20(false); + _film->set_video_encoding(VideoEncoding::JPEG2000); } else if (*data == N_("smpte-bv20")) { _film->set_interop(false); _film->set_limit_to_smpte_bv20(true); + _film->set_video_encoding(VideoEncoding::JPEG2000); + } else if (*data == N_("mpeg2-interop")) { + _film->set_interop(true); + _film->set_video_encoding(VideoEncoding::MPEG2); } } @@ -445,6 +456,9 @@ DCPPanel::film_changed(FilmProperty p) setup_dcp_name (); _markers->Enable (!_film->interop()); break; + case FilmProperty::VIDEO_ENCODING: + set_standard(); + break; case FilmProperty::LIMIT_TO_SMPTE_BV20: set_standard(); break; diff --git a/test/burnt_subtitle_test.cc b/test/burnt_subtitle_test.cc index ac14de2c4..e8ecc0048 100644 --- a/test/burnt_subtitle_test.cc +++ b/test/burnt_subtitle_test.cc @@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE (burnt_subtitle_test_onto_dcp) BOOST_REQUIRE (dcp.cpls().front()->reels().front()->main_picture()->asset()); auto pic = dynamic_pointer_cast ( dcp.cpls().front()->reels().front()->main_picture() - )->mono_asset(); + )->mono_j2k_asset(); BOOST_REQUIRE (pic); auto frame = pic->start_read()->get_frame(12); auto xyz = frame->xyz_image (); diff --git a/test/data b/test/data index ddf878730..3ab324522 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit ddf878730354cdec8a802a59543591f6f943f5c0 +Subproject commit 3ab3245220bd2a11cdf2e16a28221e4de063befc diff --git a/test/overlap_video_test.cc b/test/overlap_video_test.cc index 9bef93d67..638f606d3 100644 --- a/test/overlap_video_test.cc +++ b/test/overlap_video_test.cc @@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE (overlap_video_test1) BOOST_REQUIRE (reel->main_picture()); auto mono_picture = dynamic_pointer_cast(reel->main_picture()); BOOST_REQUIRE (mono_picture); - auto asset = mono_picture->mono_asset(); + auto asset = mono_picture->mono_j2k_asset(); BOOST_REQUIRE (asset); BOOST_CHECK_EQUAL (asset->intrinsic_duration(), fps * 5); auto reader = asset->start_read (); -- cgit v1.2.3 From 21ebc2365dd7a66c05d130dc87861f7399ae834b Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 5 Apr 2024 23:17:03 +0200 Subject: Allow specification of video bit rate separately for J2K and MPEG2. --- src/lib/config.cc | 28 +++++++++------ src/lib/config.h | 22 ++++++------ src/lib/create_cli.cc | 6 ++-- src/lib/dcp_film_encoder.cc | 2 ++ src/lib/film.cc | 27 +++++++++------ src/lib/film.h | 11 +++--- src/lib/grok/context.h | 2 +- src/lib/hints.cc | 2 +- src/lib/j2k_encoder.cc | 2 +- src/lib/make_dcp.cc | 2 +- src/lib/mpeg2_encoder.cc | 2 +- src/lib/video_encoding.cc | 2 ++ src/lib/video_encoding.h | 10 +++++- src/tools/dcpomatic_cli.cc | 2 +- src/wx/dcp_panel.cc | 11 +++--- src/wx/full_config_dialog.cc | 75 ++++++++++++++++++++++++++++++---------- test/data | 2 +- test/film_metadata_test.cc | 2 +- test/j2k_video_bit_rate_test.cc | 2 +- test/reels_test.cc | 4 +-- test/required_disk_space_test.cc | 2 +- test/test.cc | 2 +- 22 files changed, 145 insertions(+), 75 deletions(-) (limited to 'src/lib/dcp_film_encoder.cc') diff --git a/src/lib/config.cc b/src/lib/config.cc index 1400b9309..33b1a8656 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -108,7 +108,8 @@ Config::set_defaults () _default_still_length = 10; _default_dcp_content_type = DCPContentType::from_isdcf_name ("FTR"); _default_dcp_audio_channels = 8; - _default_video_bit_rate = 150000000; + _default_video_bit_rate[VideoEncoding::JPEG2000] = 150000000; + _default_video_bit_rate[VideoEncoding::MPEG2] = 5000000; _default_audio_delay = 0; _default_interop = false; _default_metadata.clear (); @@ -127,7 +128,8 @@ Config::set_defaults () _notification_bcc = ""; _check_for_updates = false; _check_for_test_updates = false; - _maximum_video_bit_rate = 250000000; + _maximum_video_bit_rate[VideoEncoding::JPEG2000] = 250000000; + _maximum_video_bit_rate[VideoEncoding::MPEG2] = 50000000; _log_types = LogEntry::TYPE_GENERAL | LogEntry::TYPE_WARNING | LogEntry::TYPE_ERROR | LogEntry::TYPE_DISK; _analyse_ebur128 = true; _automatic_audio_analysis = false; @@ -376,10 +378,11 @@ try _default_still_length = f.optional_number_child("DefaultStillLength").get_value_or (10); if (auto j2k = f.optional_number_child("DefaultJ2KBandwidth")) { - _default_video_bit_rate = *j2k; + _default_video_bit_rate[VideoEncoding::JPEG2000] = *j2k; } else { - _default_video_bit_rate = f.optional_number_child("DefaultVideoBitRate").get_value_or(200000000); + _default_video_bit_rate[VideoEncoding::JPEG2000] = f.optional_number_child("DefaultJ2KVideoBitRate").get_value_or(200000000); } + _default_video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child("DefaultMPEG2VideoBitRate").get_value_or(5000000); _default_audio_delay = f.optional_number_child("DefaultAudioDelay").get_value_or (0); _default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false); @@ -455,10 +458,11 @@ try _check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false); if (auto j2k = f.optional_number_child("MaximumJ2KBandwidth")) { - _maximum_video_bit_rate = *j2k; + _maximum_video_bit_rate[VideoEncoding::JPEG2000] = *j2k; } else { - _maximum_video_bit_rate = f.optional_number_child("MaximumVideoBitRate").get_value_or(250000000); + _maximum_video_bit_rate[VideoEncoding::JPEG2000] = f.optional_number_child("MaximumJ2KVideoBitRate").get_value_or(250000000); } + _maximum_video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child("MaximumMPEG2VideoBitRate").get_value_or(50000000); _allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate").get_value_or (false); _allow_any_container = f.optional_bool_child ("AllowAnyContainer").get_value_or (false); _allow_96khz_audio = f.optional_bool_child("Allow96kHzAudio").get_value_or(false); @@ -815,8 +819,10 @@ Config::write_config () const /* [XML] DefaultStillLength Default length (in seconds) for still images in new films. */ cxml::add_text_child(root, "DefaultStillLength", raw_convert(_default_still_length)); - /* [XML] DefaultVideoBitRate Default bitrate (in bits per second) for JPEG2000 or MPEG2 data in new films. */ - cxml::add_text_child(root, "DefaultVideoBitRate", raw_convert(_default_video_bit_rate)); + /* [XML] DefaultJ2KVideoBitRate Default bitrate (in bits per second) for JPEG2000 data in new films. */ + cxml::add_text_child(root, "DefaultJ2KVideoBitRate", raw_convert(_default_video_bit_rate[VideoEncoding::JPEG2000])); + /* [XML] DefaultMPEG2VideoBitRate Default bitrate (in bits per second) for MPEG2 data in new films. */ + cxml::add_text_child(root, "DefaultMPEG2VideoBitRate", raw_convert(_default_video_bit_rate[VideoEncoding::MPEG2])); /* [XML] DefaultAudioDelay Default delay to apply to audio (positive moves audio later) in milliseconds. */ cxml::add_text_child(root, "DefaultAudioDelay", raw_convert(_default_audio_delay)); /* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */ @@ -896,8 +902,10 @@ Config::write_config () const /* [XML] CheckForUpdates 1 to check dcpomatic.com for new text versions, 0 to check only on request. */ cxml::add_text_child(root, "CheckForTestUpdates", _check_for_test_updates ? "1" : "0"); - /* [XML] MaximumVideoBitRate Maximum video bit rate (in bits per second) that can be specified in the GUI. */ - cxml::add_text_child(root, "MaximumVideoBitRate", raw_convert(_maximum_video_bit_rate)); + /* [XML] MaximumJ2KVideoBitRate Maximum video bit rate (in bits per second) that can be specified in the GUI for JPEG2000 encodes. */ + cxml::add_text_child(root, "MaximumJ2KVideoBitRate", raw_convert(_maximum_video_bit_rate[VideoEncoding::JPEG2000])); + /* [XML] MaximumMPEG2VideoBitRate Maximum video bit rate (in bits per second) that can be specified in the GUI for MPEG2 encodes. */ + cxml::add_text_child(root, "MaximumMPEG2VideoBitRate", raw_convert(_maximum_video_bit_rate[VideoEncoding::MPEG2])); /* [XML] AllowAnyDCPFrameRate 1 to allow users to specify any frame rate when creating DCPs, 0 to limit the GUI to standard rates. */ cxml::add_text_child(root, "AllowAnyDCPFrameRate", _allow_any_dcp_frame_rate ? "1" : "0"); /* [XML] AllowAnyContainer 1 to allow users to user any container ratio for their DCP, 0 to limit the GUI to DCI Flat/Scope */ diff --git a/src/lib/config.h b/src/lib/config.h index ff71f8a38..a7b238c04 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -28,9 +28,11 @@ #include "audio_mapping.h" +#include "enum_indexed_vector.h" #include "export_config.h" #include "rough_duration.h" #include "state.h" +#include "video_encoding.h" #include #include #include @@ -233,8 +235,8 @@ public: return _dcp_j2k_comment; } - int64_t default_video_bit_rate() const { - return _default_video_bit_rate; + int64_t default_video_bit_rate(VideoEncoding encoding) const { + return _default_video_bit_rate[encoding]; } int default_audio_delay () const { @@ -349,8 +351,8 @@ public: return _check_for_test_updates; } - int64_t maximum_video_bit_rate() const { - return _maximum_video_bit_rate; + int64_t maximum_video_bit_rate(VideoEncoding encoding) const { + return _maximum_video_bit_rate[encoding]; } int log_types () const { @@ -811,8 +813,8 @@ public: maybe_set (_dcp_j2k_comment, c); } - void set_default_video_bit_rate(int64_t b) { - maybe_set(_default_video_bit_rate, b); + void set_default_video_bit_rate(VideoEncoding encoding, int64_t b) { + maybe_set(_default_video_bit_rate[encoding], b); } void set_default_audio_delay (int d) { @@ -934,8 +936,8 @@ public: maybe_set (_check_for_test_updates, c); } - void set_maximum_video_bit_rate(int64_t b) { - maybe_set(_maximum_video_bit_rate, b); + void set_maximum_video_bit_rate(VideoEncoding encoding, int64_t b) { + maybe_set(_maximum_video_bit_rate[encoding], b); } void set_log_types (int t) { @@ -1381,7 +1383,7 @@ private: std::string _dcp_product_name; std::string _dcp_product_version; std::string _dcp_j2k_comment; - int64_t _default_video_bit_rate; + EnumIndexedVector _default_video_bit_rate; int _default_audio_delay; bool _default_interop; boost::optional _default_audio_language; @@ -1419,7 +1421,7 @@ private: bool _check_for_updates; bool _check_for_test_updates; /** maximum allowed video bit rate in bits per second */ - int64_t _maximum_video_bit_rate; + EnumIndexedVector _maximum_video_bit_rate; int _log_types; bool _analyse_ebur128; bool _automatic_audio_analysis; diff --git a/src/lib/create_cli.cc b/src/lib/create_cli.cc index b33537974..8fe5a2a14 100644 --- a/src/lib/create_cli.cc +++ b/src/lib/create_cli.cc @@ -320,8 +320,8 @@ CreateCLI::CreateCLI (int argc, char* argv[]) _name = content[0].path.filename().string(); } - if (_video_bit_rate && (*_video_bit_rate < 10000000 || *_video_bit_rate > Config::instance()->maximum_video_bit_rate())) { - error = String::compose("%1: video-bit-rate must be between 10 and %2 Mbit/s", argv[0], (Config::instance()->maximum_video_bit_rate() / 1000000)); + if (_video_bit_rate && (*_video_bit_rate < 10000000 || *_video_bit_rate > Config::instance()->maximum_video_bit_rate(VideoEncoding::JPEG2000))) { + error = String::compose("%1: video-bit-rate must be between 10 and %2 Mbit/s", argv[0], (Config::instance()->maximum_video_bit_rate(VideoEncoding::JPEG2000) / 1000000)); return; } } @@ -373,7 +373,7 @@ CreateCLI::make_film() const film->set_resolution(Resolution::FOUR_K); } if (_video_bit_rate) { - film->set_video_bit_rate(*_video_bit_rate); + film->set_video_bit_rate(VideoEncoding::JPEG2000, *_video_bit_rate); } int channels = 6; diff --git a/src/lib/dcp_film_encoder.cc b/src/lib/dcp_film_encoder.cc index 17f531693..bdcd17f38 100644 --- a/src/lib/dcp_film_encoder.cc +++ b/src/lib/dcp_film_encoder.cc @@ -78,6 +78,8 @@ DCPFilmEncoder::DCPFilmEncoder(shared_ptr film, weak_ptr job) case VideoEncoding::MPEG2: _encoder.reset(new MPEG2Encoder(film, _writer)); break; + case VideoEncoding::COUNT: + DCPOMATIC_ASSERT(false); } _player_video_connection = _player.Video.connect(bind(&DCPFilmEncoder::video, this, _1, _2)); diff --git a/src/lib/film.cc b/src/lib/film.cc index 6dc1b6b6c..a3e78e877 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -163,7 +163,6 @@ Film::Film (optional dir) , _resolution (Resolution::TWO_K) , _encrypted (false) , _context_id (dcp::make_uuid ()) - , _video_bit_rate(Config::instance()->default_video_bit_rate()) , _video_frame_rate (24) , _audio_channels (Config::instance()->default_dcp_audio_channels ()) , _three_d (false) @@ -203,6 +202,10 @@ Film::Film (optional dir) _studio = metadata["studio"]; } + for (auto encoding: {VideoEncoding::JPEG2000, VideoEncoding::MPEG2}) { + _video_bit_rate[encoding] = Config::instance()->default_video_bit_rate(encoding); + } + _playlist_change_connection = _playlist->Change.connect (bind (&Film::playlist_change, this, _1)); _playlist_order_changed_connection = _playlist->OrderChange.connect (bind (&Film::playlist_order_changed, this)); _playlist_content_change_connection = _playlist->ContentChange.connect (bind (&Film::playlist_content_change, this, _1, _2, _3, _4)); @@ -241,7 +244,7 @@ Film::video_identifier () const + "_" + resolution_to_string (_resolution) + "_" + _playlist->video_identifier() + "_" + raw_convert(_video_frame_rate) - + "_" + raw_convert(video_bit_rate()); + + "_" + raw_convert(video_bit_rate(video_encoding())); if (encrypted ()) { /* This is insecure but hey, the key is in plaintext in metadata.xml */ @@ -401,7 +404,8 @@ Film::metadata (bool with_content_paths) const } cxml::add_text_child(root, "Resolution", resolution_to_string(_resolution)); - cxml::add_text_child(root, "VideoBitRate", raw_convert(_video_bit_rate)); + cxml::add_text_child(root, "J2KVideoBitRate", raw_convert(_video_bit_rate[VideoEncoding::JPEG2000])); + cxml::add_text_child(root, "MPEG2VideoBitRate", raw_convert(_video_bit_rate[VideoEncoding::MPEG2])); cxml::add_text_child(root, "VideoFrameRate", raw_convert(_video_frame_rate)); cxml::add_text_child(root, "AudioFrameRate", raw_convert(_audio_frame_rate)); cxml::add_text_child(root, "ISDCFDate", boost::gregorian::to_iso_string(_isdcf_date)); @@ -575,10 +579,11 @@ Film::read_metadata (optional path) _resolution = string_to_resolution (f.string_child ("Resolution")); if (auto j2k = f.optional_number_child("J2KBandwidth")) { - _video_bit_rate = *j2k; + _video_bit_rate[VideoEncoding::JPEG2000] = *j2k; } else { - _video_bit_rate = f.number_child("VideoBitRate"); + _video_bit_rate[VideoEncoding::JPEG2000] = f.number_child("J2KVideoBitRate"); } + _video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child("MPEG2VideoBitRate").get_value_or(Config::instance()->default_video_bit_rate(VideoEncoding::MPEG2)); _video_frame_rate = f.number_child ("VideoFrameRate"); _audio_frame_rate = f.optional_number_child("AudioFrameRate").get_value_or(48000); _encrypted = f.bool_child ("Encrypted"); @@ -1181,10 +1186,10 @@ Film::set_resolution (Resolution r, bool explicit_user) void -Film::set_video_bit_rate(int64_t bit_rate) +Film::set_video_bit_rate(VideoEncoding encoding, int64_t bit_rate) { FilmChangeSignaller ch(this, FilmProperty::VIDEO_BIT_RATE); - _video_bit_rate = bit_rate; + _video_bit_rate[encoding] = bit_rate; } /** @param f New frame rate. @@ -1802,7 +1807,7 @@ Film::make_kdm(boost::filesystem::path cpl_file, dcp::LocalTime from, dcp::Local uint64_t Film::required_disk_space () const { - return _playlist->required_disk_space (shared_from_this(), video_bit_rate(), audio_channels(), audio_frame_rate()); + return _playlist->required_disk_space (shared_from_this(), video_bit_rate(video_encoding()), audio_channels(), audio_frame_rate()); } /** This method checks the disk that the Film is on and tries to decide whether or not @@ -1934,7 +1939,7 @@ Film::reels () const /* Integer-divide reel length by the size of one frame to give the number of frames per reel, * making sure we don't go less than 1s long. */ - Frame const reel_in_frames = max(_reel_length / ((video_bit_rate() / video_frame_rate()) / 8), static_cast(video_frame_rate())); + Frame const reel_in_frames = max(_reel_length / ((video_bit_rate(video_encoding()) / video_frame_rate()) / 8), static_cast(video_frame_rate())); while (current < len) { DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ())); periods.emplace_back(current, end); @@ -1978,7 +1983,9 @@ Film::use_template (string name) _dcp_content_type = _template_film->_dcp_content_type; _container = _template_film->_container; _resolution = _template_film->_resolution; - _video_bit_rate = _template_film->_video_bit_rate; + for (auto encoding: { VideoEncoding::JPEG2000, VideoEncoding::MPEG2 }) { + _video_bit_rate[encoding] = _template_film->_video_bit_rate[encoding]; + } _video_frame_rate = _template_film->_video_frame_rate; _encrypted = _template_film->_encrypted; _audio_channels = _template_film->_audio_channels; diff --git a/src/lib/film.h b/src/lib/film.h index ff3dee6fc..e2e88e2c9 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -32,6 +32,7 @@ #include "change_signaller.h" #include "dcp_text_track.h" #include "dcpomatic_time.h" +#include "enum_indexed_vector.h" #include "film_property.h" #include "frame_rate_change.h" #include "named_channel.h" @@ -251,8 +252,8 @@ public: return _key; } - int video_bit_rate() const { - return _video_bit_rate; + int video_bit_rate(VideoEncoding encoding) const { + return _video_bit_rate[encoding]; } /** @return The frame rate of the DCP */ @@ -406,7 +407,7 @@ public: void set_container (Ratio const *, bool user_explicit = true); void set_resolution (Resolution, bool user_explicit = true); void set_encrypted (bool); - void set_video_bit_rate(int64_t); + void set_video_bit_rate(VideoEncoding encoding, int64_t); void set_video_frame_rate (int rate, bool user_explicit = false); void set_audio_channels (int); void set_three_d (bool); @@ -521,8 +522,8 @@ private: * re-start picture MXF encodes. */ std::string _context_id; - /** bit rate for encoding video using J2K or MPEG2 in bits per second */ - int64_t _video_bit_rate; + /** bit rate for encoding video using in bits per second */ + EnumIndexedVector _video_bit_rate; /** Frames per second to run our DCP at */ int _video_frame_rate; /** The date that we should use in a ISDCF name */ diff --git a/src/lib/grok/context.h b/src/lib/grok/context.h index fc9b74a57..81622ad9d 100644 --- a/src/lib/grok/context.h +++ b/src/lib/grok/context.h @@ -226,7 +226,7 @@ public: device, _dcpomatic_context->film->resolution() == Resolution::FOUR_K, _dcpomatic_context->film->video_frame_rate(), - _dcpomatic_context->film->video_bit_rate(), + _dcpomatic_context->film->video_bit_rate(VideoEncoding::JPEG2000), grok.licence_server, grok.licence_port, grok.licence)) { diff --git a/src/lib/hints.cc b/src/lib/hints.cc index fc59fa703..8e678fdf1 100644 --- a/src/lib/hints.cc +++ b/src/lib/hints.cc @@ -175,7 +175,7 @@ Hints::check_unusual_container () void Hints::check_high_video_bit_rate() { - if (film()->video_bit_rate() >= 245000000) { + if (film()->video_encoding() == VideoEncoding::JPEG2000 && film()->video_bit_rate(VideoEncoding::JPEG2000) >= 245000000) { hint (_("A few projectors have problems playing back very high bit-rate DCPs. It is a good idea to drop the video bit rate down to about 200Mbit/s; this is unlikely to have any visible effect on the image.")); } } diff --git a/src/lib/j2k_encoder.cc b/src/lib/j2k_encoder.cc index d5c7b3eed..094e104ef 100644 --- a/src/lib/j2k_encoder.cc +++ b/src/lib/j2k_encoder.cc @@ -310,7 +310,7 @@ J2KEncoder::encode (shared_ptr pv, DCPTime time) pv, position, _film->video_frame_rate(), - _film->video_bit_rate(), + _film->video_bit_rate(VideoEncoding::JPEG2000), _film->resolution() ); _queue.push_back (dcpv); diff --git a/src/lib/make_dcp.cc b/src/lib/make_dcp.cc index e4707721c..b72756194 100644 --- a/src/lib/make_dcp.cc +++ b/src/lib/make_dcp.cc @@ -91,7 +91,7 @@ make_dcp (shared_ptr film, TranscodeJob::ChangedBehaviour behaviour) LOG_GENERAL ("Content: %1", content->technical_summary()); } LOG_GENERAL ("DCP video rate %1 fps", film->video_frame_rate()); - LOG_GENERAL("Video bit rate %1", film->video_bit_rate()); + LOG_GENERAL("Video bit rate %1", film->video_bit_rate(film->video_encoding())); auto tj = make_shared(film, behaviour); tj->set_encoder(make_shared(film, tj)); diff --git a/src/lib/mpeg2_encoder.cc b/src/lib/mpeg2_encoder.cc index 49bff19d4..9b9cdfd09 100644 --- a/src/lib/mpeg2_encoder.cc +++ b/src/lib/mpeg2_encoder.cc @@ -32,7 +32,7 @@ using std::shared_ptr; MPEG2Encoder::MPEG2Encoder(shared_ptr film, Writer& writer) : VideoEncoder(film, writer) - , _transcoder(film->frame_size(), film->video_frame_rate(), film->video_bit_rate()) + , _transcoder(film->frame_size(), film->video_frame_rate(), film->video_bit_rate(VideoEncoding::MPEG2)) { } diff --git a/src/lib/video_encoding.cc b/src/lib/video_encoding.cc index ead1a2eaf..de68c6ae9 100644 --- a/src/lib/video_encoding.cc +++ b/src/lib/video_encoding.cc @@ -34,6 +34,8 @@ video_encoding_to_string(VideoEncoding encoding) return "jpeg2000"; case VideoEncoding::MPEG2: return "mpeg2"; + case VideoEncoding::COUNT: + DCPOMATIC_ASSERT(false); } DCPOMATIC_ASSERT(false); diff --git a/src/lib/video_encoding.h b/src/lib/video_encoding.h index 45ee0b21e..7c240f06f 100644 --- a/src/lib/video_encoding.h +++ b/src/lib/video_encoding.h @@ -19,16 +19,24 @@ */ +#ifndef DCPOMATIC_VIDEO_ENCODING_H +#define DCPOMATIC_VIDEO_ENCODING_H + + #include enum class VideoEncoding { JPEG2000, - MPEG2 + MPEG2, + COUNT }; std::string video_encoding_to_string(VideoEncoding encoding); VideoEncoding video_encoding_from_string(std::string const& encoding); + +#endif + diff --git a/src/tools/dcpomatic_cli.cc b/src/tools/dcpomatic_cli.cc index b59348908..49caf5cb2 100644 --- a/src/tools/dcpomatic_cli.cc +++ b/src/tools/dcpomatic_cli.cc @@ -94,7 +94,7 @@ print_dump (shared_ptr film) { cout << film->dcp_name (true) << "\n" << film->container()->container_nickname() << " at " << ((film->resolution() == Resolution::TWO_K) ? "2K" : "4K") << "\n" - << (film->video_bit_rate() / 1000000) << "Mbit/s" << "\n" + << (film->video_bit_rate(film->video_encoding()) / 1000000) << "Mbit/s" << "\n" << "Duration " << (film->length().timecode(film->video_frame_rate())) << "\n" << "Output " << film->video_frame_rate() << "fps " << (film->three_d() ? "3D" : "2D") << " " << (film->audio_frame_rate() / 1000) << "kHz\n" << (film->interop() ? "Inter-Op" : "SMPTE") << " " << (film->encrypted() ? "encrypted" : "unencrypted") << "\n"; diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index 8ee95df18..bc8ac859c 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -270,7 +270,7 @@ DCPPanel::video_bit_rate_changed() return; } - _film->set_video_bit_rate(_video_bit_rate->GetValue() * 1000000); + _film->set_video_bit_rate(_film->video_encoding(), _video_bit_rate->GetValue() * 1000000); } @@ -397,7 +397,7 @@ DCPPanel::film_changed(FilmProperty p) setup_dcp_name (); break; case FilmProperty::VIDEO_BIT_RATE: - checked_set(_video_bit_rate, _film->video_bit_rate() / 1000000); + checked_set(_video_bit_rate, _film->video_bit_rate(_film->video_encoding()) / 1000000); break; case FilmProperty::USE_ISDCF_NAME: { @@ -460,6 +460,7 @@ DCPPanel::film_changed(FilmProperty p) set_standard(); setup_container(); setup_sensitivity(); + film_changed(FilmProperty::VIDEO_BIT_RATE); break; case FilmProperty::LIMIT_TO_SMPTE_BV20: set_standard(); @@ -754,7 +755,8 @@ DCPPanel::reencode_j2k_changed () void DCPPanel::config_changed (Config::Property p) { - _video_bit_rate->SetRange(1, Config::instance()->maximum_video_bit_rate() / 1000000); + VideoEncoding const encoding = _film ? _film->video_encoding() : VideoEncoding::JPEG2000; + _video_bit_rate->SetRange(1, Config::instance()->maximum_video_bit_rate(encoding) / 1000000); setup_frame_rate_widget (); if (p == Config::SHOW_EXPERIMENTAL_AUDIO_PROCESSORS) { @@ -836,7 +838,8 @@ DCPPanel::make_video_panel () _frame_rate_choice->add_entry(boost::lexical_cast(i)); } - _video_bit_rate->SetRange(1, Config::instance()->maximum_video_bit_rate() / 1000000); + VideoEncoding const encoding = _film ? _film->video_encoding() : VideoEncoding::JPEG2000; + _video_bit_rate->SetRange(1, Config::instance()->maximum_video_bit_rate(encoding) / 1000000); _frame_rate_spin->SetRange (1, 480); _resolution->add_entry(_("2K")); diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc index 092c9ab26..00e5575b1 100644 --- a/src/wx/full_config_dialog.cc +++ b/src/wx/full_config_dialog.cc @@ -320,10 +320,19 @@ private: table->Add (_dcp_audio_channels); { - add_label_to_sizer (table, _panel, _("Default JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); + add_label_to_sizer(table, _panel, _("Default JPEG2000 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); auto s = new wxBoxSizer (wxHORIZONTAL); - _video_bit_rate = new wxSpinCtrl(_panel); - s->Add(_video_bit_rate); + _j2k_video_bit_rate = new wxSpinCtrl(_panel); + s->Add(_j2k_video_bit_rate); + add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); + table->Add (s, 1); + } + + { + add_label_to_sizer(table, _panel, _("Default MPEG2 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); + auto s = new wxBoxSizer (wxHORIZONTAL); + _mpeg2_video_bit_rate = new wxSpinCtrl(_panel); + s->Add(_mpeg2_video_bit_rate); add_label_to_sizer (s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); table->Add (s, 1); } @@ -410,8 +419,10 @@ private: _dcp_content_type->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_content_type_changed, this)); _dcp_audio_channels->Bind (wxEVT_CHOICE, boost::bind (&DefaultsPage::dcp_audio_channels_changed, this)); - _video_bit_rate->SetRange(50, 250); - _video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&DefaultsPage::video_bit_rate_changed, this)); + _j2k_video_bit_rate->SetRange(50, 250); + _j2k_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&DefaultsPage::j2k_video_bit_rate_changed, this)); + _mpeg2_video_bit_rate->SetRange(1, 50); + _mpeg2_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&DefaultsPage::mpeg2_video_bit_rate_changed, this)); _audio_delay->SetRange (-1000, 1000); _audio_delay->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::audio_delay_changed, this)); @@ -451,8 +462,10 @@ private: _kdm_directory->SetPath (std_to_wx (config->default_kdm_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())).string ())); _kdm_type->set (config->default_kdm_type()); checked_set (_use_isdcf_name_by_default, config->use_isdcf_name_by_default()); - checked_set(_video_bit_rate, config->default_video_bit_rate() / 1000000); - _video_bit_rate->SetRange(50, config->maximum_video_bit_rate() / 1000000); + checked_set(_j2k_video_bit_rate, config->default_video_bit_rate(VideoEncoding::JPEG2000) / 1000000); + _j2k_video_bit_rate->SetRange(50, config->maximum_video_bit_rate(VideoEncoding::JPEG2000) / 1000000); + checked_set(_mpeg2_video_bit_rate, config->default_video_bit_rate(VideoEncoding::MPEG2) / 1000000); + _mpeg2_video_bit_rate->SetRange(1, config->maximum_video_bit_rate(VideoEncoding::MPEG2) / 1000000); checked_set (_dcp_audio_channels, locale_convert (config->default_dcp_audio_channels())); checked_set (_audio_delay, config->default_audio_delay ()); checked_set (_standard, config->default_interop() ? 1 : 0); @@ -527,9 +540,14 @@ private: config->set_default_kdm_duration (RoughDuration(duration, unit)); } - void video_bit_rate_changed() + void j2k_video_bit_rate_changed() + { + Config::instance()->set_default_video_bit_rate(VideoEncoding::JPEG2000, _j2k_video_bit_rate->GetValue() * 1000000); + } + + void mpeg2_video_bit_rate_changed() { - Config::instance()->set_default_video_bit_rate(_video_bit_rate->GetValue() * 1000000); + Config::instance()->set_default_video_bit_rate(VideoEncoding::MPEG2, _mpeg2_video_bit_rate->GetValue() * 1000000); } void audio_delay_changed () @@ -634,7 +652,8 @@ private: } } - wxSpinCtrl* _video_bit_rate; + wxSpinCtrl* _j2k_video_bit_rate; + wxSpinCtrl* _mpeg2_video_bit_rate; wxSpinCtrl* _audio_delay; wxSpinCtrl* _still_length; #ifdef DCPOMATIC_USE_OWN_PICKER @@ -1541,10 +1560,19 @@ private: _panel->GetSizer()->Add(table, 1, wxALL | wxEXPAND, _border); { - add_label_to_sizer(table, _panel, _("Maximum JPEG2000 bandwidth"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); + add_label_to_sizer(table, _panel, _("Maximum JPEG2000 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); auto s = new wxBoxSizer(wxHORIZONTAL); - _maximum_video_bit_rate = new wxSpinCtrl(_panel); - s->Add(_maximum_video_bit_rate, 1); + _maximum_j2k_video_bit_rate = new wxSpinCtrl(_panel); + s->Add(_maximum_j2k_video_bit_rate, 1); + add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL); + table->Add(s, 1); + } + + { + add_label_to_sizer(table, _panel, _("Maximum MPEG2 bit rate"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL); + auto s = new wxBoxSizer(wxHORIZONTAL); + _maximum_mpeg2_video_bit_rate = new wxSpinCtrl(_panel); + s->Add(_maximum_mpeg2_video_bit_rate, 1); add_label_to_sizer(s, _panel, _("Mbit/s"), false, 0, wxLEFT | wxALIGN_CENTRE_VERTICAL); table->Add(s, 1); } @@ -1573,8 +1601,10 @@ private: table->Add(s, 1); } - _maximum_video_bit_rate->SetRange(1, 1000); - _maximum_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_video_bit_rate_changed, this)); + _maximum_j2k_video_bit_rate->SetRange(1, 1000); + _maximum_j2k_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_j2k_video_bit_rate_changed, this)); + _maximum_mpeg2_video_bit_rate->SetRange(1, 100); + _maximum_mpeg2_video_bit_rate->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_mpeg2_video_bit_rate_changed, this)); _allow_any_dcp_frame_rate->bind(&NonStandardPage::allow_any_dcp_frame_rate_changed, this); _allow_any_container->bind(&NonStandardPage::allow_any_container_changed, this); _allow_96khz_audio->bind(&NonStandardPage::allow_96khz_audio_changed, this); @@ -1588,7 +1618,8 @@ private: { auto config = Config::instance(); - checked_set(_maximum_video_bit_rate, config->maximum_video_bit_rate() / 1000000); + checked_set(_maximum_j2k_video_bit_rate, config->maximum_video_bit_rate(VideoEncoding::JPEG2000) / 1000000); + checked_set(_maximum_mpeg2_video_bit_rate, config->maximum_video_bit_rate(VideoEncoding::MPEG2) / 1000000); checked_set(_allow_any_dcp_frame_rate, config->allow_any_dcp_frame_rate()); checked_set(_allow_any_container, config->allow_any_container()); checked_set(_allow_96khz_audio, config->allow_96khz_audio()); @@ -1597,9 +1628,14 @@ private: checked_set(_isdcf_name_part_length, config->isdcf_name_part_length()); } - void maximum_video_bit_rate_changed() + void maximum_j2k_video_bit_rate_changed() + { + Config::instance()->set_maximum_video_bit_rate(VideoEncoding::JPEG2000, _maximum_j2k_video_bit_rate->GetValue() * 1000000); + } + + void maximum_mpeg2_video_bit_rate_changed() { - Config::instance()->set_maximum_video_bit_rate(_maximum_video_bit_rate->GetValue() * 1000000); + Config::instance()->set_maximum_video_bit_rate(VideoEncoding::MPEG2, _maximum_mpeg2_video_bit_rate->GetValue() * 1000000); } void allow_any_dcp_frame_rate_changed() @@ -1632,7 +1668,8 @@ private: Config::instance()->set_isdcf_name_part_length(_isdcf_name_part_length->GetValue()); } - wxSpinCtrl* _maximum_video_bit_rate = nullptr; + wxSpinCtrl* _maximum_j2k_video_bit_rate = nullptr; + wxSpinCtrl* _maximum_mpeg2_video_bit_rate = nullptr; CheckBox* _allow_any_dcp_frame_rate = nullptr; CheckBox* _allow_any_container = nullptr; CheckBox* _allow_96khz_audio = nullptr; diff --git a/test/data b/test/data index 3ab324522..06f2a187a 160000 --- a/test/data +++ b/test/data @@ -1 +1 @@ -Subproject commit 3ab3245220bd2a11cdf2e16a28221e4de063befc +Subproject commit 06f2a187ab398bc2e26034d4e0b178610315b63e diff --git a/test/film_metadata_test.cc b/test/film_metadata_test.cc index 2f1346d1d..c52df6d72 100644 --- a/test/film_metadata_test.cc +++ b/test/film_metadata_test.cc @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE (film_metadata_test) film->set_name ("fred"); film->set_dcp_content_type (DCPContentType::from_isdcf_name ("SHR")); film->set_container (Ratio::from_id ("185")); - film->set_video_bit_rate(200000000); + film->set_video_bit_rate(VideoEncoding::JPEG2000, 200000000); film->set_interop (false); film->set_chain (string("")); film->set_distributor (string("")); diff --git a/test/j2k_video_bit_rate_test.cc b/test/j2k_video_bit_rate_test.cc index 67579a369..ab4c4271e 100644 --- a/test/j2k_video_bit_rate_test.cc +++ b/test/j2k_video_bit_rate_test.cc @@ -47,7 +47,7 @@ check (int target_bits_per_second) auto film = new_test_film (name); film->set_name (name); film->set_dcp_content_type (DCPContentType::from_isdcf_name ("FTR")); - film->set_video_bit_rate(target_bits_per_second); + film->set_video_bit_rate(VideoEncoding::JPEG2000, target_bits_per_second); auto content = make_shared(TestPaths::private_data() / "prophet_frame.tiff"); film->examine_and_add_content (content); BOOST_REQUIRE (!wait_for_jobs()); diff --git a/test/reels_test.cc b/test/reels_test.cc index 5fcddfe78..e6d8cf9c6 100644 --- a/test/reels_test.cc +++ b/test/reels_test.cc @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE (reels_test1) BOOST_CHECK_EQUAL (r.back().from.get(), 288000); BOOST_CHECK_EQUAL (r.back().to.get(), 288000 * 2); - film->set_video_bit_rate(100000000); + film->set_video_bit_rate(VideoEncoding::JPEG2000, 100000000); film->set_reel_type (ReelType::BY_LENGTH); /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */ film->set_reel_length (31253154); @@ -310,7 +310,7 @@ BOOST_AUTO_TEST_CASE (reels_test6) auto A = make_shared("test/data/test2.mp4"); auto film = new_test_film2 ("reels_test6", {A}); - film->set_video_bit_rate(100000000); + film->set_video_bit_rate(VideoEncoding::JPEG2000, 100000000); film->set_reel_type (ReelType::BY_LENGTH); /* This is just over 2.5s at 100Mbit/s; should correspond to 60 frames */ film->set_reel_length (31253154); diff --git a/test/required_disk_space_test.cc b/test/required_disk_space_test.cc index bd969bdb2..e26a09169 100644 --- a/test/required_disk_space_test.cc +++ b/test/required_disk_space_test.cc @@ -45,7 +45,7 @@ void check_within_n (int64_t a, int64_t b, int64_t n) BOOST_AUTO_TEST_CASE (required_disk_space_test) { auto film = new_test_film ("required_disk_space_test"); - film->set_video_bit_rate(100000000); + film->set_video_bit_rate(VideoEncoding::JPEG2000, 100000000); film->set_audio_channels(8); film->set_reel_type (ReelType::BY_VIDEO_CONTENT); auto content_a = content_factory("test/data/flat_blue.png")[0]; diff --git a/test/test.cc b/test/test.cc index 1d5eb9083..ddd3d26f0 100644 --- a/test/test.cc +++ b/test/test.cc @@ -122,7 +122,7 @@ setup_test_config () Config::instance()->set_server_port_base (61921); Config::instance()->set_default_dcp_content_type (static_cast (0)); Config::instance()->set_default_audio_delay (0); - Config::instance()->set_default_video_bit_rate(100000000); + Config::instance()->set_default_video_bit_rate(VideoEncoding::JPEG2000, 100000000); Config::instance()->set_default_interop (false); Config::instance()->set_default_still_length (10); Config::instance()->set_default_dcp_audio_channels(8); -- cgit v1.2.3