summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/config.cc32
-rw-r--r--src/lib/config.h27
-rw-r--r--src/lib/create_cli.cc16
-rw-r--r--src/lib/create_cli.h2
-rw-r--r--src/lib/dcp_decoder.cc88
-rw-r--r--src/lib/dcp_decoder.h18
-rw-r--r--src/lib/dcp_examiner.cc38
-rw-r--r--src/lib/dcp_examiner.h3
-rw-r--r--src/lib/dcp_film_encoder.cc (renamed from src/lib/dcp_encoder.cc)63
-rw-r--r--src/lib/dcp_film_encoder.h (renamed from src/lib/dcp_encoder.h)12
-rw-r--r--src/lib/dcp_video.cc14
-rw-r--r--src/lib/dcp_video.h4
-rw-r--r--src/lib/encode_server.cc1
-rw-r--r--src/lib/encode_server_finder.cc1
-rw-r--r--src/lib/ffmpeg_file_encoder.cc2
-rw-r--r--src/lib/ffmpeg_file_encoder.h4
-rw-r--r--src/lib/ffmpeg_film_encoder.cc (renamed from src/lib/ffmpeg_encoder.cc)24
-rw-r--r--src/lib/ffmpeg_film_encoder.h (renamed from src/lib/ffmpeg_encoder.h)6
-rw-r--r--src/lib/film.cc66
-rw-r--r--src/lib/film.h18
-rw-r--r--src/lib/film_encoder.cc (renamed from src/lib/encoder.cc)8
-rw-r--r--src/lib/film_encoder.h (renamed from src/lib/encoder.h)17
-rw-r--r--src/lib/film_property.h3
-rw-r--r--src/lib/frame_info.cc84
-rw-r--r--src/lib/frame_info.h45
-rw-r--r--src/lib/grok/context.h2
-rw-r--r--src/lib/hints.cc12
-rw-r--r--src/lib/hints.h11
-rw-r--r--src/lib/j2k_encoder.cc31
-rw-r--r--src/lib/j2k_encoder.h23
-rw-r--r--src/lib/j2k_image_proxy.cc8
-rw-r--r--src/lib/j2k_image_proxy.h8
-rw-r--r--src/lib/make_dcp.cc6
-rw-r--r--src/lib/map_cli.cc4
-rw-r--r--src/lib/mpeg2_encoder.cc80
-rw-r--r--src/lib/mpeg2_encoder.h42
-rw-r--r--src/lib/player.cc6
-rw-r--r--src/lib/playlist.cc6
-rw-r--r--src/lib/playlist.h2
-rw-r--r--src/lib/reel_writer.cc183
-rw-r--r--src/lib/reel_writer.h34
-rw-r--r--src/lib/subtitle_film_encoder.cc (renamed from src/lib/subtitle_encoder.cc)14
-rw-r--r--src/lib/subtitle_film_encoder.h (renamed from src/lib/subtitle_encoder.h)8
-rw-r--r--src/lib/transcode_job.cc8
-rw-r--r--src/lib/transcode_job.h6
-rw-r--r--src/lib/util.cc7
-rw-r--r--src/lib/video_encoder.cc64
-rw-r--r--src/lib/video_encoder.h69
-rw-r--r--src/lib/video_encoding.cc58
-rw-r--r--src/lib/video_encoding.h42
-rw-r--r--src/lib/video_mxf_content.cc8
-rw-r--r--src/lib/video_mxf_decoder.cc12
-rw-r--r--src/lib/video_mxf_decoder.h8
-rw-r--r--src/lib/video_mxf_examiner.cc8
-rw-r--r--src/lib/writer.cc19
-rw-r--r--src/lib/writer.h7
-rw-r--r--src/lib/wscript12
-rw-r--r--src/tools/dcpomatic.cc8
-rw-r--r--src/tools/dcpomatic_cli.cc6
-rw-r--r--src/tools/dcpomatic_combiner.cc1
-rw-r--r--src/tools/dcpomatic_editor.cc1
-rw-r--r--src/tools/dcpomatic_kdm.cc1
-rw-r--r--src/wx/config_dialog.cc1
-rw-r--r--src/wx/dcp_panel.cc111
-rw-r--r--src/wx/dcp_panel.h6
-rw-r--r--src/wx/dcp_timeline.cc8
-rw-r--r--src/wx/dcp_timeline.h2
-rw-r--r--src/wx/dcpomatic_choice.cc22
-rw-r--r--src/wx/dcpomatic_choice.h10
-rw-r--r--src/wx/export_video_file_dialog.h2
-rw-r--r--src/wx/film_viewer.h2
-rw-r--r--src/wx/full_config_dialog.cc87
-rw-r--r--src/wx/kdm_cpl_panel.cc1
-rw-r--r--src/wx/kdm_timing_panel.cc2
-rw-r--r--src/wx/metadata_dialog.cc10
-rw-r--r--src/wx/timeline_content_view.cc1
76 files changed, 1176 insertions, 510 deletions
diff --git a/src/lib/config.cc b/src/lib/config.cc
index fb7a413de..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_j2k_bandwidth = 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_j2k_bandwidth = 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;
@@ -375,7 +377,12 @@ try
_dcp_j2k_comment = f.optional_string_child("DCPJ2KComment").get_value_or("");
_default_still_length = f.optional_number_child<int>("DefaultStillLength").get_value_or (10);
- _default_j2k_bandwidth = f.optional_number_child<int>("DefaultJ2KBandwidth").get_value_or (200000000);
+ if (auto j2k = f.optional_number_child<int>("DefaultJ2KBandwidth")) {
+ _default_video_bit_rate[VideoEncoding::JPEG2000] = *j2k;
+ } else {
+ _default_video_bit_rate[VideoEncoding::JPEG2000] = f.optional_number_child<int64_t>("DefaultJ2KVideoBitRate").get_value_or(200000000);
+ }
+ _default_video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child<int64_t>("DefaultMPEG2VideoBitRate").get_value_or(5000000);
_default_audio_delay = f.optional_number_child<int>("DefaultAudioDelay").get_value_or (0);
_default_interop = f.optional_bool_child("DefaultInterop").get_value_or (false);
@@ -450,7 +457,12 @@ try
_check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
_check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
- _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
+ if (auto j2k = f.optional_number_child<int>("MaximumJ2KBandwidth")) {
+ _maximum_video_bit_rate[VideoEncoding::JPEG2000] = *j2k;
+ } else {
+ _maximum_video_bit_rate[VideoEncoding::JPEG2000] = f.optional_number_child<int64_t>("MaximumJ2KVideoBitRate").get_value_or(250000000);
+ }
+ _maximum_video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child<int64_t>("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);
@@ -807,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<string>(_default_still_length));
- /* [XML] DefaultJ2KBandwidth Default bitrate (in bits per second) for JPEG2000 data in new films. */
- cxml::add_text_child(root, "DefaultJ2KBandwidth", raw_convert<string>(_default_j2k_bandwidth));
+ /* [XML] DefaultJ2KVideoBitRate Default bitrate (in bits per second) for JPEG2000 data in new films. */
+ cxml::add_text_child(root, "DefaultJ2KVideoBitRate", raw_convert<string>(_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<string>(_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<string>(_default_audio_delay));
/* [XML] DefaultInterop 1 to default new films to Interop, 0 for SMPTE. */
@@ -888,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] MaximumJ2KBandwidth Maximum J2K bandwidth (in bits per second) that can be specified in the GUI. */
- cxml::add_text_child(root, "MaximumJ2KBandwidth", raw_convert<string>(_maximum_j2k_bandwidth));
+ /* [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<string>(_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<string>(_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 ec804c9bf..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 <dcp/name_format.h>
#include <dcp/certificate_chain.h>
#include <dcp/encrypted_kdm.h>
@@ -97,6 +99,7 @@ public:
AUTO_CROP_THRESHOLD,
ALLOW_SMPTE_BV20,
ISDCF_NAME_PART_LENGTH,
+ ALLOW_ANY_CONTAINER,
#ifdef DCPOMATIC_GROK
GROK,
#endif
@@ -232,8 +235,8 @@ public:
return _dcp_j2k_comment;
}
- int default_j2k_bandwidth () const {
- return _default_j2k_bandwidth;
+ int64_t default_video_bit_rate(VideoEncoding encoding) const {
+ return _default_video_bit_rate[encoding];
}
int default_audio_delay () const {
@@ -348,8 +351,8 @@ public:
return _check_for_test_updates;
}
- int maximum_j2k_bandwidth () const {
- return _maximum_j2k_bandwidth;
+ int64_t maximum_video_bit_rate(VideoEncoding encoding) const {
+ return _maximum_video_bit_rate[encoding];
}
int log_types () const {
@@ -742,7 +745,7 @@ public:
}
void set_allow_any_container (bool a) {
- maybe_set (_allow_any_container, a);
+ maybe_set(_allow_any_container, a, ALLOW_ANY_CONTAINER);
}
void set_allow_96hhz_audio (bool a) {
@@ -810,8 +813,8 @@ public:
maybe_set (_dcp_j2k_comment, c);
}
- void set_default_j2k_bandwidth (int b) {
- maybe_set (_default_j2k_bandwidth, 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) {
@@ -933,8 +936,8 @@ public:
maybe_set (_check_for_test_updates, c);
}
- void set_maximum_j2k_bandwidth (int b) {
- maybe_set (_maximum_j2k_bandwidth, 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) {
@@ -1380,7 +1383,7 @@ private:
std::string _dcp_product_name;
std::string _dcp_product_version;
std::string _dcp_j2k_comment;
- int _default_j2k_bandwidth;
+ EnumIndexedVector<int64_t, VideoEncoding> _default_video_bit_rate;
int _default_audio_delay;
bool _default_interop;
boost::optional<dcp::LanguageTag> _default_audio_language;
@@ -1417,8 +1420,8 @@ private:
/** true to check for updates on startup */
bool _check_for_updates;
bool _check_for_test_updates;
- /** maximum allowed J2K bandwidth in bits per second */
- int _maximum_j2k_bandwidth;
+ /** maximum allowed video bit rate in bits per second */
+ EnumIndexedVector<int64_t, VideoEncoding> _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 cf903e376..8fe5a2a14 100644
--- a/src/lib/create_cli.cc
+++ b/src/lib/create_cli.cc
@@ -133,7 +133,7 @@ CreateCLI::CreateCLI (int argc, char* argv[])
optional<string> standard_string;
int dcp_frame_rate_int = 0;
string template_name_string;
- int j2k_bandwidth_int = 0;
+ int64_t video_bit_rate_int = 0;
auto next_frame_type = VideoFrameType::TWO_D;
optional<dcp::Channel> channel;
optional<float> gain;
@@ -206,7 +206,7 @@ CreateCLI::CreateCLI (int argc, char* argv[])
argument_option(i, argc, argv, "", "--standard", &claimed, &error, &standard_string, string_to_string);
argument_option(i, argc, argv, "", "--config", &claimed, &error, &config_dir, string_to_path);
argument_option(i, argc, argv, "-o", "--output", &claimed, &error, &output_dir, string_to_path);
- argument_option(i, argc, argv, "", "--j2k-bandwidth", &claimed, &error, &j2k_bandwidth_int);
+ argument_option(i, argc, argv, "", "--video-bit-rate", &claimed, &error, &video_bit_rate_int);
std::function<optional<dcp::Channel> (string)> convert_channel = [](string channel) -> optional<dcp::Channel>{
if (channel == "L") {
@@ -272,8 +272,8 @@ CreateCLI::CreateCLI (int argc, char* argv[])
dcp_frame_rate = dcp_frame_rate_int;
}
- if (j2k_bandwidth_int) {
- _j2k_bandwidth = j2k_bandwidth_int * 1000000;
+ if (video_bit_rate_int) {
+ _video_bit_rate = video_bit_rate_int * 1000000;
}
if (dcp_content_type_string) {
@@ -320,8 +320,8 @@ CreateCLI::CreateCLI (int argc, char* argv[])
_name = content[0].path.filename().string();
}
- if (_j2k_bandwidth && (*_j2k_bandwidth < 10000000 || *_j2k_bandwidth > Config::instance()->maximum_j2k_bandwidth())) {
- error = String::compose("%1: j2k-bandwidth must be between 10 and %2 Mbit/s", argv[0], (Config::instance()->maximum_j2k_bandwidth() / 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;
}
}
@@ -372,8 +372,8 @@ CreateCLI::make_film() const
if (_fourk) {
film->set_resolution(Resolution::FOUR_K);
}
- if (_j2k_bandwidth) {
- film->set_j2k_bandwidth(*_j2k_bandwidth);
+ if (_video_bit_rate) {
+ film->set_video_bit_rate(VideoEncoding::JPEG2000, *_video_bit_rate);
}
int channels = 6;
diff --git a/src/lib/create_cli.h b/src/lib/create_cli.h
index 782aaf974..850cddea9 100644
--- a/src/lib/create_cli.h
+++ b/src/lib/create_cli.h
@@ -72,7 +72,7 @@ private:
bool _no_use_isdcf_name = false;
bool _twok = false;
bool _fourk = false;
- boost::optional<int> _j2k_bandwidth;
+ boost::optional<int64_t> _video_bit_rate;
static std::string _help;
};
diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc
index 303126caa..e471a237f 100644
--- a/src/lib/dcp_decoder.cc
+++ b/src/lib/dcp_decoder.cc
@@ -26,19 +26,23 @@
#include "constants.h"
#include "dcp_content.h"
#include "dcp_decoder.h"
+#include "dcpomatic_log.h"
#include "digester.h"
#include "ffmpeg_image_proxy.h"
#include "frame_interval_checker.h"
#include "image.h"
#include "j2k_image_proxy.h"
+#include "raw_image_proxy.h"
#include "text_decoder.h"
+#include "util.h"
#include "video_decoder.h"
#include <dcp/cpl.h>
#include <dcp/dcp.h>
#include <dcp/decrypted_kdm.h>
-#include <dcp/mono_picture_asset.h>
-#include <dcp/mono_picture_asset_reader.h>
-#include <dcp/mono_picture_frame.h>
+#include <dcp/mono_j2k_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset_reader.h>
+#include <dcp/mono_j2k_picture_frame.h>
+#include <dcp/mono_mpeg2_picture_asset.h>
#include <dcp/reel.h>
#include <dcp/reel_atmos_asset.h>
#include <dcp/reel_closed_caption_asset.h>
@@ -48,9 +52,9 @@
#include <dcp/search.h>
#include <dcp/sound_asset_reader.h>
#include <dcp/sound_frame.h>
-#include <dcp/stereo_picture_asset.h>
-#include <dcp/stereo_picture_asset_reader.h>
-#include <dcp/stereo_picture_frame.h>
+#include <dcp/stereo_j2k_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset_reader.h>
+#include <dcp/stereo_j2k_picture_frame.h>
#include <dcp/subtitle_image.h>
#include <iostream>
@@ -169,24 +173,24 @@ DCPDecoder::pass ()
*/
pass_texts (_next, picture_asset->size());
- if ((_mono_reader || _stereo_reader) && (_decode_referenced || !_dcp_content->reference_video())) {
+ if ((_j2k_mono_reader || _j2k_stereo_reader || _mpeg2_mono_reader) && (_decode_referenced || !_dcp_content->reference_video())) {
auto const entry_point = (*_reel)->main_picture()->entry_point().get_value_or(0);
- if (_mono_reader) {
+ if (_j2k_mono_reader) {
video->emit (
film(),
std::make_shared<J2KImageProxy>(
- _mono_reader->get_frame (entry_point + frame),
+ _j2k_mono_reader->get_frame(entry_point + frame),
picture_asset->size(),
AV_PIX_FMT_XYZ12LE,
_forced_reduction
),
ContentTime::from_frames(_offset + frame, vfr)
);
- } else {
+ } else if (_j2k_stereo_reader) {
video->emit (
film(),
std::make_shared<J2KImageProxy>(
- _stereo_reader->get_frame (entry_point + frame),
+ _j2k_stereo_reader->get_frame (entry_point + frame),
picture_asset->size(),
dcp::Eye::LEFT,
AV_PIX_FMT_XYZ12LE,
@@ -198,7 +202,7 @@ DCPDecoder::pass ()
video->emit (
film(),
std::make_shared<J2KImageProxy>(
- _stereo_reader->get_frame (entry_point + frame),
+ _j2k_stereo_reader->get_frame (entry_point + frame),
picture_asset->size(),
dcp::Eye::RIGHT,
AV_PIX_FMT_XYZ12LE,
@@ -206,6 +210,23 @@ DCPDecoder::pass ()
),
ContentTime::from_frames(_offset + frame, vfr)
);
+ } else if (_mpeg2_mono_reader) {
+ /* XXX: got to flush this at some point */
+ try {
+ for (auto const& image: _mpeg2_decompressor->decompress_frame(_mpeg2_mono_reader->get_frame(entry_point + frame))) {
+ video->emit(
+ film(),
+ /* XXX: should this be PADDED? */
+ std::make_shared<RawImageProxy>(std::make_shared<Image>(image.frame(), Image::Alignment::COMPACT)),
+ /* XXX: this will be wrong */
+ ContentTime::from_frames(_offset + frame, vfr)
+ );
+ }
+ } catch (dcp::MPEG2DecompressionError& e) {
+ LOG_ERROR("Failed to decompress MPEG video frame %1 (%2)", entry_point + frame, e.what());
+ } catch (dcp::ReadError& e) {
+ LOG_ERROR("Failed to read MPEG2 video frame %1 (%2)", entry_point + frame, e.what());
+ }
}
}
@@ -367,38 +388,40 @@ DCPDecoder::next_reel ()
void
DCPDecoder::get_readers ()
{
+ _j2k_mono_reader.reset();
+ _j2k_stereo_reader.reset();
+ _mpeg2_mono_reader.reset();
+ _sound_reader.reset();
+ _atmos_reader.reset();
+ _mpeg2_decompressor.reset();
+ _atmos_metadata = boost::none;
+
if (_reel == _reels.end() || !_dcp_content->can_be_played ()) {
- _mono_reader.reset ();
- _stereo_reader.reset ();
- _sound_reader.reset ();
- _atmos_reader.reset ();
return;
}
if (video && !video->ignore() && (*_reel)->main_picture()) {
auto asset = (*_reel)->main_picture()->asset ();
- auto mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (asset);
- auto stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (asset);
- DCPOMATIC_ASSERT (mono || stereo);
- if (mono) {
- _mono_reader = mono->start_read ();
- _mono_reader->set_check_hmac (false);
- _stereo_reader.reset ();
+ auto j2k_mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(asset);
+ auto j2k_stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(asset);
+ auto mpeg2_mono = dynamic_pointer_cast<dcp::MonoMPEG2PictureAsset>(asset);
+ DCPOMATIC_ASSERT(j2k_mono || j2k_stereo || mpeg2_mono)
+ if (j2k_mono) {
+ _j2k_mono_reader = j2k_mono->start_read();
+ _j2k_mono_reader->set_check_hmac(false);
+ } else if (j2k_stereo) {
+ _j2k_stereo_reader = j2k_stereo->start_read();
+ _j2k_stereo_reader->set_check_hmac(false);
} else {
- _stereo_reader = stereo->start_read ();
- _stereo_reader->set_check_hmac (false);
- _mono_reader.reset ();
+ _mpeg2_mono_reader = mpeg2_mono->start_read();
+ _mpeg2_mono_reader->set_check_hmac(false);
+ _mpeg2_decompressor = std::make_shared<dcp::MPEG2Decompressor>();
}
- } else {
- _mono_reader.reset ();
- _stereo_reader.reset ();
}
if (audio && !audio->ignore() && (*_reel)->main_sound()) {
_sound_reader = (*_reel)->main_sound()->asset()->start_read ();
_sound_reader->set_check_hmac (false);
- } else {
- _sound_reader.reset ();
}
if ((*_reel)->atmos()) {
@@ -406,9 +429,6 @@ DCPDecoder::get_readers ()
_atmos_reader = asset->start_read();
_atmos_reader->set_check_hmac (false);
_atmos_metadata = AtmosMetadata (asset);
- } else {
- _atmos_reader.reset ();
- _atmos_metadata = boost::none;
}
}
diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h
index 2c0cd8f41..ee0f30694 100644
--- a/src/lib/dcp_decoder.h
+++ b/src/lib/dcp_decoder.h
@@ -27,8 +27,10 @@
#include "atmos_metadata.h"
#include "decoder.h"
#include "font_id_allocator.h"
-#include <dcp/mono_picture_asset_reader.h>
-#include <dcp/stereo_picture_asset_reader.h>
+#include <dcp/mono_j2k_picture_asset_reader.h>
+#include <dcp/stereo_j2k_picture_asset_reader.h>
+#include <dcp/mono_mpeg2_picture_asset_reader.h>
+#include <dcp/mpeg2_transcode.h>
#include <dcp/sound_asset_reader.h>
#include <dcp/subtitle_asset.h>
@@ -94,15 +96,19 @@ private:
std::vector<std::shared_ptr<dcp::Reel>>::iterator _reel;
/** Offset of _reel from the start of the content in frames */
int64_t _offset = 0;
- /** Reader for current mono picture asset, if applicable */
- std::shared_ptr<dcp::MonoPictureAssetReader> _mono_reader;
- /** Reader for current stereo picture asset, if applicable */
- std::shared_ptr<dcp::StereoPictureAssetReader> _stereo_reader;
+ /** Reader for current J2K mono picture asset, if applicable */
+ std::shared_ptr<dcp::MonoJ2KPictureAssetReader> _j2k_mono_reader;
+ /** Reader for current J2K stereo picture asset, if applicable */
+ std::shared_ptr<dcp::StereoJ2KPictureAssetReader> _j2k_stereo_reader;
+ /** Reader for current MPEG2 mono picture asset, if applicable */
+ std::shared_ptr<dcp::MonoMPEG2PictureAssetReader> _mpeg2_mono_reader;
/** Reader for current sound asset, if applicable */
std::shared_ptr<dcp::SoundAssetReader> _sound_reader;
std::shared_ptr<dcp::AtmosAssetReader> _atmos_reader;
boost::optional<AtmosMetadata> _atmos_metadata;
+ std::shared_ptr<dcp::MPEG2Decompressor> _mpeg2_decompressor;
+
bool _decode_referenced = false;
boost::optional<int> _forced_reduction;
diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc
index 3515f34c2..229786868 100644
--- a/src/lib/dcp_examiner.cc
+++ b/src/lib/dcp_examiner.cc
@@ -32,9 +32,11 @@
#include <dcp/cpl.h>
#include <dcp/dcp.h>
#include <dcp/decrypted_kdm.h>
-#include <dcp/mono_picture_asset.h>
-#include <dcp/mono_picture_asset_reader.h>
-#include <dcp/mono_picture_frame.h>
+#include <dcp/mono_j2k_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset_reader.h>
+#include <dcp/mono_j2k_picture_frame.h>
+#include <dcp/mono_mpeg2_picture_asset.h>
+#include <dcp/mpeg2_transcode.h>
#include <dcp/reel.h>
#include <dcp/reel_atmos_asset.h>
#include <dcp/reel_closed_caption_asset.h>
@@ -46,9 +48,9 @@
#include <dcp/sound_asset.h>
#include <dcp/sound_asset.h>
#include <dcp/sound_asset_reader.h>
-#include <dcp/stereo_picture_asset.h>
-#include <dcp/stereo_picture_asset_reader.h>
-#include <dcp/stereo_picture_frame.h>
+#include <dcp/stereo_j2k_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset_reader.h>
+#include <dcp/stereo_j2k_picture_frame.h>
#include <dcp/subtitle_asset.h>
#include <iostream>
@@ -162,6 +164,10 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
} else if (_video_size.get() != asset->size ()) {
throw DCPError (_("Mismatched video sizes in DCP"));
}
+
+ if (dynamic_pointer_cast<dcp::MPEG2PictureAsset>(asset)) {
+ _video_range = VideoRange::VIDEO;
+ }
}
}
@@ -293,17 +299,23 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
LOG_GENERAL_NC ("Picture has no key");
break;
}
- auto mono = dynamic_pointer_cast<dcp::MonoPictureAsset>(pic);
- auto stereo = dynamic_pointer_cast<dcp::StereoPictureAsset>(pic);
+ auto j2k_mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(pic);
+ auto j2k_stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(pic);
+ auto mpeg2_mono = dynamic_pointer_cast<dcp::MonoMPEG2PictureAsset>(pic);
- if (mono) {
- auto reader = mono->start_read();
+ if (j2k_mono) {
+ auto reader = j2k_mono->start_read();
reader->set_check_hmac (false);
reader->get_frame(0)->xyz_image();
- } else {
- auto reader = stereo->start_read();
+ } else if (j2k_stereo) {
+ auto reader = j2k_stereo->start_read();
reader->set_check_hmac (false);
reader->get_frame(0)->xyz_image(dcp::Eye::LEFT);
+ } else if (mpeg2_mono) {
+ auto reader = mpeg2_mono->start_read();
+ reader->set_check_hmac(false);
+ dcp::MPEG2Decompressor decompressor;
+ decompressor.decompress_frame(reader->get_frame(0));
}
}
@@ -354,7 +366,7 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content, bool tolerant)
_standard = selected_cpl->standard();
if (!selected_cpl->reels().empty()) {
auto first_reel = selected_cpl->reels()[0];
- _three_d = first_reel->main_picture() && first_reel->main_picture()->asset_ref().resolved() && dynamic_pointer_cast<dcp::StereoPictureAsset>(first_reel->main_picture()->asset());
+ _three_d = first_reel->main_picture() && first_reel->main_picture()->asset_ref().resolved() && dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(first_reel->main_picture()->asset());
}
_ratings = selected_cpl->ratings();
for (auto version: selected_cpl->content_versions()) {
diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h
index 425552632..d6baf7d50 100644
--- a/src/lib/dcp_examiner.h
+++ b/src/lib/dcp_examiner.h
@@ -61,7 +61,7 @@ public:
}
VideoRange range () const override {
- return VideoRange::FULL;
+ return _video_range;
}
PixelQuanta pixel_quanta () const override {
@@ -211,6 +211,7 @@ private:
Frame _atmos_length = 0;
dcp::Fraction _atmos_edit_rate;
EnumIndexedVector<bool, TextType> _has_non_zero_entry_point;
+ VideoRange _video_range = VideoRange::FULL;
struct Font
{
diff --git a/src/lib/dcp_encoder.cc b/src/lib/dcp_film_encoder.cc
index bd78312fa..bdcd17f38 100644
--- a/src/lib/dcp_encoder.cc
+++ b/src/lib/dcp_film_encoder.cc
@@ -19,7 +19,7 @@
*/
-/** @file src/dcp_encoder.cc
+/** @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
@@ -29,10 +29,11 @@
#include "audio_decoder.h"
#include "compose.hpp"
-#include "dcp_encoder.h"
+#include "dcp_film_encoder.h"
#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"
@@ -64,17 +65,27 @@ using namespace dcpomatic;
* @param film Film that we are encoding.
* @param job Job that this encoder is being used in.
*/
-DCPEncoder::DCPEncoder (shared_ptr<const Film> film, weak_ptr<Job> job)
- : Encoder (film, job)
+DCPFilmEncoder::DCPFilmEncoder(shared_ptr<const Film> film, weak_ptr<Job> job)
+ : FilmEncoder(film, job)
, _writer(film, job)
- , _j2k_encoder(film, _writer)
, _finishing (false)
, _non_burnt_subtitles (false)
{
- _player_video_connection = _player.Video.connect(bind(&DCPEncoder::video, this, _1, _2));
- _player_audio_connection = _player.Audio.connect(bind(&DCPEncoder::audio, this, _1, _2));
- _player_text_connection = _player.Text.connect(bind(&DCPEncoder::text, this, _1, _2, _3, _4));
- _player_atmos_connection = _player.Atmos.connect(bind(&DCPEncoder::atmos, this, _1, _2, _3));
+ switch (_film->video_encoding()) {
+ case VideoEncoding::JPEG2000:
+ _encoder.reset(new J2KEncoder(film, _writer));
+ break;
+ 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));
+ _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) {
@@ -85,7 +96,7 @@ DCPEncoder::DCPEncoder (shared_ptr<const Film> film, weak_ptr<Job> job)
}
}
-DCPEncoder::~DCPEncoder ()
+DCPFilmEncoder::~DCPFilmEncoder()
{
/* We must stop receiving more video data before we die */
_player_video_connection.release ();
@@ -95,10 +106,10 @@ DCPEncoder::~DCPEncoder ()
}
void
-DCPEncoder::go ()
+DCPFilmEncoder::go()
{
_writer.start();
- _j2k_encoder.begin();
+ _encoder->begin();
{
auto job = _job.lock ();
@@ -117,32 +128,32 @@ DCPEncoder::go ()
}
_finishing = true;
- _j2k_encoder.end();
+ _encoder->end();
_writer.finish(_film->dir(_film->dcp_name()));
}
void
-DCPEncoder::pause()
+DCPFilmEncoder::pause()
{
- _j2k_encoder.pause();
+ _encoder->pause();
}
void
-DCPEncoder::resume()
+DCPFilmEncoder::resume()
{
- _j2k_encoder.resume();
+ _encoder->resume();
}
void
-DCPEncoder::video (shared_ptr<PlayerVideo> data, DCPTime time)
+DCPFilmEncoder::video(shared_ptr<PlayerVideo> data, DCPTime time)
{
- _j2k_encoder.encode(data, time);
+ _encoder->encode(data, time);
}
void
-DCPEncoder::audio (shared_ptr<AudioBuffers> data, DCPTime time)
+DCPFilmEncoder::audio(shared_ptr<AudioBuffers> data, DCPTime time)
{
_writer.write(data, time);
@@ -152,7 +163,7 @@ DCPEncoder::audio (shared_ptr<AudioBuffers> data, DCPTime time)
}
void
-DCPEncoder::text (PlayerText data, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
+DCPFilmEncoder::text(PlayerText data, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
{
if (type == TextType::CLOSED_CAPTION || _non_burnt_subtitles) {
_writer.write(data, type, track, period);
@@ -161,20 +172,20 @@ DCPEncoder::text (PlayerText data, TextType type, optional<DCPTextTrack> track,
void
-DCPEncoder::atmos (shared_ptr<const dcp::AtmosFrame> data, DCPTime time, AtmosMetadata metadata)
+DCPFilmEncoder::atmos(shared_ptr<const dcp::AtmosFrame> data, DCPTime time, AtmosMetadata metadata)
{
_writer.write(data, time, metadata);
}
optional<float>
-DCPEncoder::current_rate () const
+DCPFilmEncoder::current_rate() const
{
- return _j2k_encoder.current_encoding_rate();
+ return _encoder->current_encoding_rate();
}
Frame
-DCPEncoder::frames_done () const
+DCPFilmEncoder::frames_done() const
{
- return _j2k_encoder.video_frames_enqueued();
+ return _encoder->video_frames_enqueued();
}
diff --git a/src/lib/dcp_encoder.h b/src/lib/dcp_film_encoder.h
index ce0b72204..3c697a115 100644
--- a/src/lib/dcp_encoder.h
+++ b/src/lib/dcp_film_encoder.h
@@ -22,7 +22,7 @@
#include "atmos_metadata.h"
#include "dcp_text_track.h"
#include "dcpomatic_time.h"
-#include "encoder.h"
+#include "film_encoder.h"
#include "player_text.h"
#include "j2k_encoder.h"
#include "writer.h"
@@ -38,12 +38,12 @@ class PlayerVideo;
struct frames_not_lost_when_threads_disappear;
-/** @class DCPEncoder */
-class DCPEncoder : public Encoder
+/** @class DCPFilmEncoder */
+class DCPFilmEncoder : public FilmEncoder
{
public:
- DCPEncoder (std::shared_ptr<const Film> film, std::weak_ptr<Job> job);
- ~DCPEncoder ();
+ DCPFilmEncoder(std::shared_ptr<const Film> film, std::weak_ptr<Job> job);
+ ~DCPFilmEncoder();
void go () override;
@@ -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;
diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc
index 6580ac448..ec88888dd 100644
--- a/src/lib/dcp_video.cc
+++ b/src/lib/dcp_video.cc
@@ -74,15 +74,15 @@ using namespace boost::placeholders;
/** Construct a DCP video frame.
* @param frame Input frame.
* @param index Index of the frame within the DCP.
- * @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
+ * @param bit_rate Video bit rate to use.
*/
DCPVideo::DCPVideo (
- shared_ptr<const PlayerVideo> frame, int index, int dcp_fps, int bw, Resolution r
+ shared_ptr<const PlayerVideo> frame, int index, int dcp_fps, int64_t bit_rate, Resolution r
)
: _frame (frame)
, _index (index)
, _frames_per_second (dcp_fps)
- , _j2k_bandwidth (bw)
+ , _video_bit_rate(bit_rate)
, _resolution (r)
{
@@ -93,7 +93,7 @@ DCPVideo::DCPVideo (shared_ptr<const PlayerVideo> frame, shared_ptr<const cxml::
{
_index = node->number_child<int> ("Index");
_frames_per_second = node->number_child<int> ("FramesPerSecond");
- _j2k_bandwidth = node->number_child<int> ("J2KBandwidth");
+ _video_bit_rate = node->number_child<int64_t>("VideoBitRate");
_resolution = Resolution (node->optional_number_child<int>("Resolution").get_value_or(static_cast<int>(Resolution::TWO_K)));
}
@@ -160,7 +160,7 @@ DCPVideo::encode_locally () const
while (true) {
enc = dcp::compress_j2k (
xyz,
- _j2k_bandwidth,
+ _video_bit_rate,
_frames_per_second,
_frame->eyes() == Eyes::LEFT || _frame->eyes() == Eyes::RIGHT,
_resolution == Resolution::FOUR_K,
@@ -280,7 +280,7 @@ DCPVideo::add_metadata (xmlpp::Element* el) const
{
cxml::add_text_child(el, "Index", raw_convert<string>(_index));
cxml::add_text_child(el, "FramesPerSecond", raw_convert<string>(_frames_per_second));
- cxml::add_text_child(el, "J2KBandwidth", raw_convert<string>(_j2k_bandwidth));
+ cxml::add_text_child(el, "VideoBitRate", raw_convert<string>(_video_bit_rate));
cxml::add_text_child(el, "Resolution", raw_convert<string>(int(_resolution)));
_frame->add_metadata (el);
}
@@ -298,7 +298,7 @@ bool
DCPVideo::same (shared_ptr<const DCPVideo> other) const
{
if (_frames_per_second != other->_frames_per_second ||
- _j2k_bandwidth != other->_j2k_bandwidth ||
+ _video_bit_rate != other->_video_bit_rate ||
_resolution != other->_resolution) {
return false;
}
diff --git a/src/lib/dcp_video.h b/src/lib/dcp_video.h
index d07c8322b..92c155f0c 100644
--- a/src/lib/dcp_video.h
+++ b/src/lib/dcp_video.h
@@ -49,7 +49,7 @@ class PlayerVideo;
class DCPVideo
{
public:
- DCPVideo (std::shared_ptr<const PlayerVideo>, int index, int dcp_fps, int bandwidth, Resolution r);
+ DCPVideo(std::shared_ptr<const PlayerVideo>, int index, int dcp_fps, int64_t bit_rate, Resolution r);
DCPVideo (std::shared_ptr<const PlayerVideo>, cxml::ConstNodePtr);
DCPVideo (DCPVideo const&) = default;
@@ -78,7 +78,7 @@ private:
std::shared_ptr<const PlayerVideo> _frame;
int _index; ///< frame index within the DCP's intrinsic duration
int _frames_per_second; ///< Frames per second that we will use for the DCP
- int _j2k_bandwidth; ///< J2K bandwidth to use
+ int64_t _video_bit_rate; ///< Video bit rate to use
Resolution _resolution; ///< Resolution (2K or 4K)
};
diff --git a/src/lib/encode_server.cc b/src/lib/encode_server.cc
index da5c7270e..68ee871f8 100644
--- a/src/lib/encode_server.cc
+++ b/src/lib/encode_server.cc
@@ -37,6 +37,7 @@
#include "image.h"
#include "log.h"
#include "player_video.h"
+#include "util.h"
#include "variant.h"
#include "version.h"
#include <dcp/raw_convert.h>
diff --git a/src/lib/encode_server_finder.cc b/src/lib/encode_server_finder.cc
index 9ce9e1691..143569e16 100644
--- a/src/lib/encode_server_finder.cc
+++ b/src/lib/encode_server_finder.cc
@@ -26,6 +26,7 @@
#include "encode_server_description.h"
#include "encode_server_finder.h"
#include "exceptions.h"
+#include "util.h"
#include "variant.h"
#include <dcp/raw_convert.h>
#include <libcxml/cxml.h>
diff --git a/src/lib/ffmpeg_file_encoder.cc b/src/lib/ffmpeg_file_encoder.cc
index d7833265d..4547a8e8e 100644
--- a/src/lib/ffmpeg_file_encoder.cc
+++ b/src/lib/ffmpeg_file_encoder.cc
@@ -21,7 +21,7 @@
#include "compose.hpp"
#include "cross.h"
-#include "ffmpeg_encoder.h"
+#include "ffmpeg_file_encoder.h"
#include "ffmpeg_wrapper.h"
#include "film.h"
#include "image.h"
diff --git a/src/lib/ffmpeg_file_encoder.h b/src/lib/ffmpeg_file_encoder.h
index 78840d6a8..907eca53d 100644
--- a/src/lib/ffmpeg_file_encoder.h
+++ b/src/lib/ffmpeg_file_encoder.h
@@ -23,12 +23,14 @@
#define DCPOMATIC_FFMPEG_FILE_ENCODER_H
+#include "audio_buffers.h"
#include "audio_mapping.h"
#include "dcpomatic_time.h"
-#include "encoder.h"
#include "event_history.h"
#include "image_store.h"
#include "log.h"
+#include "player_text.h"
+#include "player_video.h"
#include <dcp/key.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
diff --git a/src/lib/ffmpeg_encoder.cc b/src/lib/ffmpeg_film_encoder.cc
index 60241b233..2d100f7bc 100644
--- a/src/lib/ffmpeg_encoder.cc
+++ b/src/lib/ffmpeg_film_encoder.cc
@@ -21,7 +21,7 @@
#include "butler.h"
#include "cross.h"
-#include "ffmpeg_encoder.h"
+#include "ffmpeg_film_encoder.h"
#include "film.h"
#include "image.h"
#include "job.h"
@@ -48,7 +48,7 @@ using namespace boost::placeholders;
#endif
-FFmpegEncoder::FFmpegEncoder (
+FFmpegFilmEncoder::FFmpegFilmEncoder(
shared_ptr<const Film> film,
weak_ptr<Job> job,
boost::filesystem::path output,
@@ -58,7 +58,7 @@ FFmpegEncoder::FFmpegEncoder (
bool audio_stream_per_channel,
int x264_crf
)
- : Encoder (film, job)
+ : FilmEncoder(film, job)
, _output_audio_channels(mixdown_to_stereo ? 2 : (_film->audio_channels() > 8 ? 16 : _film->audio_channels()))
, _history (200)
, _output (output)
@@ -85,7 +85,7 @@ FFmpegEncoder::FFmpegEncoder (
AudioMapping
-FFmpegEncoder::stereo_map() const
+FFmpegFilmEncoder::stereo_map() const
{
auto map = AudioMapping(_film->audio_channels(), 2);
float const overall_gain = 2 / (4 + sqrt(2));
@@ -117,7 +117,7 @@ FFmpegEncoder::stereo_map() const
AudioMapping
-FFmpegEncoder::many_channel_map() const
+FFmpegFilmEncoder::many_channel_map() const
{
auto map = AudioMapping(_film->audio_channels(), _output_audio_channels);
for (int i = 0; i < _film->audio_channels(); ++i) {
@@ -128,7 +128,7 @@ FFmpegEncoder::many_channel_map() const
void
-FFmpegEncoder::go ()
+FFmpegFilmEncoder::go()
{
{
auto job = _job.lock ();
@@ -235,19 +235,19 @@ FFmpegEncoder::go ()
}
optional<float>
-FFmpegEncoder::current_rate () const
+FFmpegFilmEncoder::current_rate() const
{
return _history.rate ();
}
Frame
-FFmpegEncoder::frames_done () const
+FFmpegFilmEncoder::frames_done() const
{
boost::mutex::scoped_lock lm (_mutex);
return _last_time.frames_round (_film->video_frame_rate ());
}
-FFmpegEncoder::FileEncoderSet::FileEncoderSet (
+FFmpegFilmEncoder::FileEncoderSet::FileEncoderSet(
dcp::Size video_frame_size,
int video_frame_rate,
int audio_frame_rate,
@@ -280,7 +280,7 @@ FFmpegEncoder::FileEncoderSet::FileEncoderSet (
}
shared_ptr<FFmpegFileEncoder>
-FFmpegEncoder::FileEncoderSet::get (Eyes eyes) const
+FFmpegFilmEncoder::FileEncoderSet::get(Eyes eyes) const
{
if (_encoders.size() == 1) {
/* We are doing a 2D export... */
@@ -299,7 +299,7 @@ FFmpegEncoder::FileEncoderSet::get (Eyes eyes) const
}
void
-FFmpegEncoder::FileEncoderSet::flush ()
+FFmpegFilmEncoder::FileEncoderSet::flush()
{
for (auto& i: _encoders) {
i.second->flush ();
@@ -307,7 +307,7 @@ FFmpegEncoder::FileEncoderSet::flush ()
}
void
-FFmpegEncoder::FileEncoderSet::audio (shared_ptr<AudioBuffers> a)
+FFmpegFilmEncoder::FileEncoderSet::audio(shared_ptr<AudioBuffers> a)
{
for (auto& i: _encoders) {
i.second->audio (a);
diff --git a/src/lib/ffmpeg_encoder.h b/src/lib/ffmpeg_film_encoder.h
index 2d5c6af87..ec6bb4594 100644
--- a/src/lib/ffmpeg_encoder.h
+++ b/src/lib/ffmpeg_film_encoder.h
@@ -23,15 +23,15 @@
#include "audio_mapping.h"
#include "butler.h"
-#include "encoder.h"
#include "event_history.h"
#include "ffmpeg_file_encoder.h"
+#include "film_encoder.h"
-class FFmpegEncoder : public Encoder
+class FFmpegFilmEncoder : public FilmEncoder
{
public:
- FFmpegEncoder (
+ FFmpegFilmEncoder(
std::shared_ptr<const Film> film,
std::weak_ptr<Job> job,
boost::filesystem::path output,
diff --git a/src/lib/film.cc b/src/lib/film.cc
index 540d0b9b9..a3e78e877 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -36,7 +36,7 @@
#include "cross.h"
#include "dcp_content.h"
#include "dcp_content_type.h"
-#include "dcp_encoder.h"
+#include "dcp_film_encoder.h"
#include "dcpomatic_log.h"
#include "digester.h"
#include "environment_info.h"
@@ -163,12 +163,12 @@ Film::Film (optional<boost::filesystem::path> dir)
, _resolution (Resolution::TWO_K)
, _encrypted (false)
, _context_id (dcp::make_uuid ())
- , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
, _video_frame_rate (24)
, _audio_channels (Config::instance()->default_dcp_audio_channels ())
, _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)
@@ -202,6 +202,10 @@ Film::Film (optional<boost::filesystem::path> 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));
@@ -240,7 +244,7 @@ Film::video_identifier () const
+ "_" + resolution_to_string (_resolution)
+ "_" + _playlist->video_identifier()
+ "_" + raw_convert<string>(_video_frame_rate)
- + "_" + raw_convert<string>(j2k_bandwidth());
+ + "_" + raw_convert<string>(video_bit_rate(video_encoding()));
if (encrypted ()) {
/* This is insecure but hey, the key is in plaintext in metadata.xml */
@@ -251,6 +255,9 @@ Film::video_identifier () const
if (_interop) {
s += "_I";
+ if (_video_encoding == VideoEncoding::MPEG2) {
+ s += "_M";
+ }
} else {
s += "_S";
if (_limit_to_smpte_bv20) {
@@ -397,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, "J2KBandwidth", raw_convert<string>(_j2k_bandwidth));
+ cxml::add_text_child(root, "J2KVideoBitRate", raw_convert<string>(_video_bit_rate[VideoEncoding::JPEG2000]));
+ cxml::add_text_child(root, "MPEG2VideoBitRate", raw_convert<string>(_video_bit_rate[VideoEncoding::MPEG2]));
cxml::add_text_child(root, "VideoFrameRate", raw_convert<string>(_video_frame_rate));
cxml::add_text_child(root, "AudioFrameRate", raw_convert<string>(_audio_frame_rate));
cxml::add_text_child(root, "ISDCFDate", boost::gregorian::to_iso_string(_isdcf_date));
@@ -405,6 +413,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 ());
@@ -569,7 +578,12 @@ Film::read_metadata (optional<boost::filesystem::path> path)
}
_resolution = string_to_resolution (f.string_child ("Resolution"));
- _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
+ if (auto j2k = f.optional_number_child<int>("J2KBandwidth")) {
+ _video_bit_rate[VideoEncoding::JPEG2000] = *j2k;
+ } else {
+ _video_bit_rate[VideoEncoding::JPEG2000] = f.number_child<int64_t>("J2KVideoBitRate");
+ }
+ _video_bit_rate[VideoEncoding::MPEG2] = f.optional_number_child<int64_t>("MPEG2VideoBitRate").get_value_or(Config::instance()->default_video_bit_rate(VideoEncoding::MPEG2));
_video_frame_rate = f.number_child<int> ("VideoFrameRate");
_audio_frame_rate = f.optional_number_child<int>("AudioFrameRate").get_value_or(48000);
_encrypted = f.bool_child ("Encrypted");
@@ -591,6 +605,9 @@ Film::read_metadata (optional<boost::filesystem::path> 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 ());
@@ -1169,10 +1186,10 @@ Film::set_resolution (Resolution r, bool explicit_user)
void
-Film::set_j2k_bandwidth (int b)
+Film::set_video_bit_rate(VideoEncoding encoding, int64_t bit_rate)
{
- FilmChangeSignaller ch(this, FilmProperty::J2K_BANDWIDTH);
- _j2k_bandwidth = b;
+ FilmChangeSignaller ch(this, FilmProperty::VIDEO_BIT_RATE);
+ _video_bit_rate[encoding] = bit_rate;
}
/** @param f New frame rate.
@@ -1216,6 +1233,15 @@ Film::set_interop (bool i)
void
+Film::set_video_encoding(VideoEncoding encoding)
+{
+ FilmChangeSignaller ch(this, FilmProperty::VIDEO_ENCODING);
+ _video_encoding = encoding;
+ check_settings_consistency();
+}
+
+
+void
Film::set_limit_to_smpte_bv20(bool limit)
{
FilmChangeSignaller ch(this, FilmProperty::LIMIT_TO_SMPTE_BV20);
@@ -1640,6 +1666,22 @@ Film::check_settings_consistency ()
set_custom_reel_boundaries(boundaries);
}
}
+
+ auto const hd = Ratio::from_id("178");
+
+ if (video_encoding() == VideoEncoding::MPEG2) {
+ if (container() != hd) {
+ set_container(hd);
+ Message(_("DCP-o-matic had to set your container to 1920x1080 as it's the only one that can be used with MPEG2 encoding."));
+ }
+ if (three_d()) {
+ set_three_d(false);
+ Message(_("DCP-o-matic had to set your film to 2D as 3D is not yet supported with MPEG2 encoding."));
+ }
+ } else if (container() == hd && !Config::instance()->allow_any_container()) {
+ set_container(Ratio::from_id("185"));
+ Message(_("DCP-o-matic set your container to DCI Flat as it was previously 1920x1080 and that is not a standard ratio with JPEG2000 encoding."));
+ }
}
void
@@ -1765,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(), j2k_bandwidth(), 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
@@ -1897,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 / ((j2k_bandwidth() / video_frame_rate()) / 8), static_cast<Frame>(video_frame_rate()));
+ Frame const reel_in_frames = max(_reel_length / ((video_bit_rate(video_encoding()) / video_frame_rate()) / 8), static_cast<Frame>(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);
@@ -1941,7 +1983,9 @@ Film::use_template (string name)
_dcp_content_type = _template_film->_dcp_content_type;
_container = _template_film->_container;
_resolution = _template_film->_resolution;
- _j2k_bandwidth = _template_film->_j2k_bandwidth;
+ 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 0a0c5a4e1..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"
@@ -41,6 +42,7 @@
#include "transcode_job.h"
#include "types.h"
#include "util.h"
+#include "video_encoding.h"
#include <dcp/encrypted_kdm.h>
#include <dcp/file.h>
#include <dcp/key.h>
@@ -250,8 +252,8 @@ public:
return _key;
}
- int j2k_bandwidth () const {
- return _j2k_bandwidth;
+ int video_bit_rate(VideoEncoding encoding) const {
+ return _video_bit_rate[encoding];
}
/** @return The frame rate of the DCP */
@@ -275,6 +277,10 @@ public:
return _interop;
}
+ VideoEncoding video_encoding() const {
+ return _video_encoding;
+ }
+
bool limit_to_smpte_bv20() const {
return _limit_to_smpte_bv20;
}
@@ -401,13 +407,14 @@ public:
void set_container (Ratio const *, bool user_explicit = true);
void set_resolution (Resolution, bool user_explicit = true);
void set_encrypted (bool);
- void set_j2k_bandwidth (int);
+ 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);
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);
@@ -515,8 +522,8 @@ private:
* re-start picture MXF encodes.
*/
std::string _context_id;
- /** bandwidth for J2K files in bits per second */
- int _j2k_bandwidth;
+ /** bit rate for encoding video using in bits per second */
+ EnumIndexedVector<int64_t, VideoEncoding> _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 */
@@ -529,6 +536,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/encoder.cc b/src/lib/film_encoder.cc
index 5268d8620..05b911daf 100644
--- a/src/lib/encoder.cc
+++ b/src/lib/film_encoder.cc
@@ -19,7 +19,7 @@
*/
-/** @file src/encoder.cc
+/** @file src/film_encoder.cc
* @brief A class which takes a Film and some Options, then uses those to encode the film
* into some output format.
*
@@ -28,17 +28,17 @@
*/
-#include "encoder.h"
+#include "film_encoder.h"
#include "player.h"
#include "i18n.h"
-/** Construct an encoder.
+/** Construct a FilmEncoder.
* @param film Film that we are encoding.
* @param job Job that this encoder is being used in.
*/
-Encoder::Encoder (std::shared_ptr<const Film> film, std::weak_ptr<Job> job)
+FilmEncoder::FilmEncoder(std::shared_ptr<const Film> film, std::weak_ptr<Job> job)
: _film (film)
, _job (job)
, _player(film, Image::Alignment::PADDED)
diff --git a/src/lib/encoder.h b/src/lib/film_encoder.h
index aeaf7f620..ed7626c68 100644
--- a/src/lib/encoder.h
+++ b/src/lib/film_encoder.h
@@ -19,8 +19,8 @@
*/
-#ifndef DCPOMATIC_ENCODER_H
-#define DCPOMATIC_ENCODER_H
+#ifndef DCPOMATIC_FILM_ENCODER_H
+#define DCPOMATIC_FILM_ENCODER_H
#include "player.h"
@@ -29,24 +29,23 @@
class Film;
-class Encoder;
class Player;
class Job;
class PlayerVideo;
class AudioBuffers;
-/** @class Encoder
+/** @class FilmEncoder
* @brief Parent class for something that can encode a film into some format
*/
-class Encoder
+class FilmEncoder
{
public:
- Encoder (std::shared_ptr<const Film> film, std::weak_ptr<Job> job);
- virtual ~Encoder () {}
+ FilmEncoder(std::shared_ptr<const Film> film, std::weak_ptr<Job> job);
+ virtual ~FilmEncoder() {}
- Encoder (Encoder const&) = delete;
- Encoder& operator= (Encoder const&) = delete;
+ FilmEncoder(FilmEncoder const&) = delete;
+ FilmEncoder& operator=(FilmEncoder const&) = delete;
virtual void go () = 0;
diff --git a/src/lib/film_property.h b/src/lib/film_property.h
index 0841caa5c..ebda0e807 100644
--- a/src/lib/film_property.h
+++ b/src/lib/film_property.h
@@ -39,7 +39,7 @@ enum class FilmProperty {
CONTAINER,
RESOLUTION,
ENCRYPTED,
- J2K_BANDWIDTH,
+ VIDEO_BIT_RATE,
VIDEO_FRAME_RATE,
AUDIO_FRAME_RATE,
AUDIO_CHANNELS,
@@ -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/frame_info.cc b/src/lib/frame_info.cc
new file mode 100644
index 000000000..f348bca6a
--- /dev/null
+++ b/src/lib/frame_info.cc
@@ -0,0 +1,84 @@
+/*
+ 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 "frame_info.h"
+#include <string>
+
+
+using std::shared_ptr;
+using std::string;
+
+
+J2KFrameInfo::J2KFrameInfo(uint64_t offset_, uint64_t size_, string hash_)
+ : dcp::J2KFrameInfo(offset_, size_, hash_)
+{
+
+}
+
+
+J2KFrameInfo::J2KFrameInfo(dcp::J2KFrameInfo const& info)
+ : dcp::J2KFrameInfo(info)
+{
+
+}
+
+
+J2KFrameInfo::J2KFrameInfo(shared_ptr<InfoFileHandle> info_file, Frame frame, Eyes eyes)
+{
+ info_file->get().seek(position(frame, eyes), SEEK_SET);
+ info_file->get().checked_read(&offset, sizeof(offset));
+ info_file->get().checked_read(&size, sizeof(size));
+
+ char hash_buffer[33];
+ info_file->get().checked_read(hash_buffer, 32);
+ hash_buffer[32] = '\0';
+ hash = hash_buffer;
+}
+
+
+long
+J2KFrameInfo::position(Frame frame, Eyes eyes) const
+{
+ switch (eyes) {
+ case Eyes::BOTH:
+ return frame * _size_on_disk;
+ case Eyes::LEFT:
+ return frame * _size_on_disk * 2;
+ case Eyes::RIGHT:
+ return frame * _size_on_disk * 2 + _size_on_disk;
+ default:
+ DCPOMATIC_ASSERT(false);
+ }
+
+ DCPOMATIC_ASSERT(false);
+}
+
+
+/** @param frame reel-relative frame */
+void
+J2KFrameInfo::write(shared_ptr<InfoFileHandle> info_file, Frame frame, Eyes eyes) const
+{
+ info_file->get().seek(position(frame, eyes), SEEK_SET);
+ info_file->get().checked_write(&offset, sizeof(offset));
+ info_file->get().checked_write(&size, sizeof(size));
+ info_file->get().checked_write(hash.c_str(), hash.size());
+}
+
diff --git a/src/lib/frame_info.h b/src/lib/frame_info.h
new file mode 100644
index 000000000..a5a22bd9e
--- /dev/null
+++ b/src/lib/frame_info.h
@@ -0,0 +1,45 @@
+/*
+ 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 "film.h"
+#include <dcp/frame_info.h>
+#include <memory>
+
+
+class J2KFrameInfo : public dcp::J2KFrameInfo
+{
+public:
+ J2KFrameInfo(dcp::J2KFrameInfo const& info);
+ J2KFrameInfo(uint64_t offset_, uint64_t size_, std::string hash_);
+ J2KFrameInfo(std::shared_ptr<InfoFileHandle> info_file, Frame frame, Eyes eyes);
+
+ void write(std::shared_ptr<InfoFileHandle> info_file, Frame frame, Eyes eyes) const;
+
+ static int size_on_disk() {
+ return _size_on_disk;
+ }
+
+private:
+ long position(Frame frame, Eyes eyes) const;
+
+ static constexpr auto _size_on_disk = 32 + sizeof(dcp::J2KFrameInfo::offset) + sizeof(dcp::J2KFrameInfo::size);
+};
+
diff --git a/src/lib/grok/context.h b/src/lib/grok/context.h
index 521faae8d..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->j2k_bandwidth(),
+ _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 7a6a2a4bf..ca69832a9 100644
--- a/src/lib/hints.cc
+++ b/src/lib/hints.cc
@@ -166,17 +166,17 @@ void
Hints::check_unusual_container ()
{
auto const film_container = film()->container()->id();
- if (film_container != "185" && film_container != "239") {
+ if (film()->video_encoding() != VideoEncoding::MPEG2 && film_container != "185" && film_container != "239") {
hint (_("Your DCP uses an unusual container ratio. This may cause problems on some projectors. If possible, use Flat or Scope for the DCP container ratio."));
}
}
void
-Hints::check_high_j2k_bandwidth ()
+Hints::check_high_video_bit_rate()
{
- if (film()->j2k_bandwidth() >= 245000000) {
- hint (_("A few projectors have problems playing back very high bit-rate DCPs. It is a good idea to drop the JPEG2000 bandwidth down to about 200Mbit/s; this is unlikely to have any visible effect on the image."));
+ 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."));
}
}
@@ -468,7 +468,7 @@ try
check_upmixers ();
check_incorrect_container ();
check_unusual_container ();
- check_high_j2k_bandwidth ();
+ check_high_video_bit_rate();
check_frame_rate ();
check_4k_3d ();
check_speed_up ();
@@ -756,7 +756,7 @@ void
Hints::check_8_or_16_audio_channels()
{
auto const channels = film()->audio_channels();
- if (channels != 8 && channels != 16) {
+ if (film()->video_encoding() != VideoEncoding::MPEG2 && channels != 8 && channels != 16) {
hint(String::compose(_("Your DCP has %1 audio channels, rather than 8 or 16. This may cause some distributors to raise QC errors when they check your DCP. To avoid this, set the DCP audio channels to 8 or 16."), channels));
}
}
diff --git a/src/lib/hints.h b/src/lib/hints.h
index 0d65edc21..a46678af8 100644
--- a/src/lib/hints.h
+++ b/src/lib/hints.h
@@ -20,15 +20,16 @@
#include "audio_analyser.h"
-#include "signaller.h"
-#include "player_text.h"
#include "dcp_text_track.h"
#include "dcpomatic_time.h"
+#include "player_text.h"
+#include "signaller.h"
+#include "text_type.h"
#include "weak_film.h"
-#include <boost/signals2.hpp>
#include <boost/atomic.hpp>
-#include <vector>
+#include <boost/signals2.hpp>
#include <string>
+#include <vector>
class Film;
@@ -73,7 +74,7 @@ private:
void check_upmixers ();
void check_incorrect_container ();
void check_unusual_container ();
- void check_high_j2k_bandwidth ();
+ void check_high_video_bit_rate();
void check_frame_rate ();
void check_4k_3d ();
void check_speed_up ();
diff --git a/src/lib/j2k_encoder.cc b/src/lib/j2k_encoder.cc
index de229113b..094e104ef 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<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;
@@ -332,7 +310,7 @@ J2KEncoder::encode (shared_ptr<PlayerVideo> pv, DCPTime time)
pv,
position,
_film->video_frame_rate(),
- _film->j2k_bandwidth(),
+ _film->video_bit_rate(VideoEncoding::JPEG2000),
_film->resolution()
);
_queue.push_back (dcpv);
@@ -344,7 +322,6 @@ J2KEncoder::encode (shared_ptr<PlayerVideo> 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 <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/j2k_image_proxy.cc b/src/lib/j2k_image_proxy.cc
index 869e96967..023723c8b 100644
--- a/src/lib/j2k_image_proxy.cc
+++ b/src/lib/j2k_image_proxy.cc
@@ -25,11 +25,11 @@
#include "j2k_image_proxy.h"
#include <dcp/colour_conversion.h>
#include <dcp/j2k_transcode.h>
-#include <dcp/mono_picture_frame.h>
+#include <dcp/mono_j2k_picture_frame.h>
#include <dcp/openjpeg_image.h>
#include <dcp/raw_convert.h>
#include <dcp/rgb_xyz.h>
-#include <dcp/stereo_picture_frame.h>
+#include <dcp/stereo_j2k_picture_frame.h>
#include <dcp/warnings.h>
#include <libcxml/cxml.h>
LIBDCP_DISABLE_WARNINGS
@@ -64,7 +64,7 @@ J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPi
J2KImageProxy::J2KImageProxy (
- shared_ptr<const dcp::MonoPictureFrame> frame,
+ shared_ptr<const dcp::MonoJ2KPictureFrame> frame,
dcp::Size size,
AVPixelFormat pixel_format,
optional<int> forced_reduction
@@ -81,7 +81,7 @@ J2KImageProxy::J2KImageProxy (
J2KImageProxy::J2KImageProxy (
- shared_ptr<const dcp::StereoPictureFrame> frame,
+ shared_ptr<const dcp::StereoJ2KPictureFrame> frame,
dcp::Size size,
dcp::Eye eye,
AVPixelFormat pixel_format,
diff --git a/src/lib/j2k_image_proxy.h b/src/lib/j2k_image_proxy.h
index 3346fff08..1d2d5cc21 100644
--- a/src/lib/j2k_image_proxy.h
+++ b/src/lib/j2k_image_proxy.h
@@ -26,8 +26,8 @@
namespace dcp {
- class MonoPictureFrame;
- class StereoPictureFrame;
+ class MonoJ2KPictureFrame;
+ class StereoJ2KPictureFrame;
}
@@ -37,14 +37,14 @@ public:
J2KImageProxy (boost::filesystem::path path, dcp::Size, AVPixelFormat pixel_format);
J2KImageProxy (
- std::shared_ptr<const dcp::MonoPictureFrame> frame,
+ std::shared_ptr<const dcp::MonoJ2KPictureFrame> frame,
dcp::Size,
AVPixelFormat pixel_format,
boost::optional<int> forced_reduction
);
J2KImageProxy (
- std::shared_ptr<const dcp::StereoPictureFrame> frame,
+ std::shared_ptr<const dcp::StereoJ2KPictureFrame> frame,
dcp::Size,
dcp::Eye,
AVPixelFormat pixel_format,
diff --git a/src/lib/make_dcp.cc b/src/lib/make_dcp.cc
index ddd231243..b72756194 100644
--- a/src/lib/make_dcp.cc
+++ b/src/lib/make_dcp.cc
@@ -21,7 +21,7 @@
#include "config.h"
#include "dcp_content.h"
-#include "dcp_encoder.h"
+#include "dcp_film_encoder.h"
#include "dcp_transcode_job.h"
#include "dcpomatic_log.h"
#include "environment_info.h"
@@ -91,10 +91,10 @@ make_dcp (shared_ptr<Film> film, TranscodeJob::ChangedBehaviour behaviour)
LOG_GENERAL ("Content: %1", content->technical_summary());
}
LOG_GENERAL ("DCP video rate %1 fps", film->video_frame_rate());
- LOG_GENERAL ("J2K bandwidth %1", film->j2k_bandwidth());
+ LOG_GENERAL("Video bit rate %1", film->video_bit_rate(film->video_encoding()));
auto tj = make_shared<DCPTranscodeJob>(film, behaviour);
- tj->set_encoder (make_shared<DCPEncoder>(film, tj));
+ tj->set_encoder(make_shared<DCPFilmEncoder>(film, tj));
JobManager::instance()->add (tj);
return tj;
diff --git a/src/lib/map_cli.cc b/src/lib/map_cli.cc
index b158499a8..be3841deb 100644
--- a/src/lib/map_cli.cc
+++ b/src/lib/map_cli.cc
@@ -27,7 +27,7 @@
#include <dcp/interop_subtitle_asset.h>
#include <dcp/filesystem.h>
#include <dcp/font_asset.h>
-#include <dcp/mono_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset.h>
#include <dcp/reel.h>
#include <dcp/reel_atmos_asset.h>
#include <dcp/reel_closed_caption_asset.h>
@@ -37,7 +37,7 @@
#include <dcp/reel_subtitle_asset.h>
#include <dcp/smpte_subtitle_asset.h>
#include <dcp/sound_asset.h>
-#include <dcp/stereo_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset.h>
#include <boost/optional.hpp>
#include <getopt.h>
#include <algorithm>
diff --git a/src/lib/mpeg2_encoder.cc b/src/lib/mpeg2_encoder.cc
new file mode 100644
index 000000000..9b9cdfd09
--- /dev/null
+++ b/src/lib/mpeg2_encoder.cc
@@ -0,0 +1,80 @@
+/*
+ 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 "mpeg2_encoder.h"
+#include "writer.h"
+#include <dcp/ffmpeg_image.h>
+extern "C" {
+#include <libavutil/pixfmt.h>
+}
+
+
+using std::shared_ptr;
+
+
+MPEG2Encoder::MPEG2Encoder(shared_ptr<const Film> film, Writer& writer)
+ : VideoEncoder(film, writer)
+ , _transcoder(film->frame_size(), film->video_frame_rate(), film->video_bit_rate(VideoEncoding::MPEG2))
+{
+
+}
+
+
+void
+MPEG2Encoder::encode(shared_ptr<PlayerVideo> 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 <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"
+#include <dcp/mpeg2_transcode.h>
+
+
+class MPEG2Encoder : public VideoEncoder
+{
+public:
+ MPEG2Encoder(std::shared_ptr<const Film> film, Writer& writer);
+
+ void encode(std::shared_ptr<PlayerVideo> 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/player.cc b/src/lib/player.cc
index dba02cfba..913d7392f 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -887,8 +887,10 @@ Player::pass ()
}
if (done) {
- LOG_DEBUG_PLAYER("Done: emit video until end of film at %1", to_string(film->length()));
- emit_video_until(film->length());
+ if (_next_video_time) {
+ LOG_DEBUG_PLAYER("Done: emit video until end of film at %1", to_string(film->length()));
+ emit_video_until(film->length());
+ }
if (_shuffler) {
_shuffler->flush ();
diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc
index 7f83e9474..d2af000e5 100644
--- a/src/lib/playlist.cc
+++ b/src/lib/playlist.cc
@@ -645,16 +645,16 @@ Playlist::move_later (shared_ptr<const Film> film, shared_ptr<Content> c)
int64_t
-Playlist::required_disk_space (shared_ptr<const Film> film, int j2k_bandwidth, int audio_channels, int audio_frame_rate) const
+Playlist::required_disk_space(shared_ptr<const Film> film, int64_t video_bit_rate, int audio_channels, int audio_frame_rate) const
{
- int64_t video = uint64_t(j2k_bandwidth / 8) * length(film).seconds();
+ int64_t video = uint64_t(video_bit_rate / 8) * length(film).seconds();
int64_t audio = uint64_t(audio_channels) * audio_frame_rate * 3 * length(film).seconds();
for (auto i: content()) {
auto d = dynamic_pointer_cast<DCPContent> (i);
if (d) {
if (d->reference_video()) {
- video -= uint64_t (j2k_bandwidth / 8) * d->length_after_trim(film).seconds();
+ video -= uint64_t(video_bit_rate / 8) * d->length_after_trim(film).seconds();
}
if (d->reference_audio()) {
audio -= uint64_t(audio_channels) * audio_frame_rate * 3 * d->length_after_trim(film).seconds();
diff --git a/src/lib/playlist.h b/src/lib/playlist.h
index 7b9bd19fa..3868c0b51 100644
--- a/src/lib/playlist.h
+++ b/src/lib/playlist.h
@@ -65,7 +65,7 @@ public:
dcpomatic::DCPTime length (std::shared_ptr<const Film> film) const;
boost::optional<dcpomatic::DCPTime> start () const;
- int64_t required_disk_space (std::shared_ptr<const Film> film, int j2k_bandwidth, int audio_channels, int audio_frame_rate) const;
+ int64_t required_disk_space(std::shared_ptr<const Film> film, int64_t video_bit_rate, int audio_channels, int audio_frame_rate) const;
int best_video_frame_rate () const;
dcpomatic::DCPTime video_end (std::shared_ptr<const Film> film) const;
diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc
index 1b33cae85..8dc0d7f06 100644
--- a/src/lib/reel_writer.cc
+++ b/src/lib/reel_writer.cc
@@ -28,6 +28,7 @@
#include "digester.h"
#include "film.h"
#include "film_util.h"
+#include "frame_info.h"
#include "image.h"
#include "image_png.h"
#include "job.h"
@@ -40,7 +41,7 @@
#include <dcp/dcp.h>
#include <dcp/filesystem.h>
#include <dcp/interop_subtitle_asset.h>
-#include <dcp/mono_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset.h>
#include <dcp/raw_convert.h>
#include <dcp/reel.h>
#include <dcp/reel_atmos_asset.h>
@@ -55,7 +56,7 @@
#include <dcp/smpte_subtitle_asset.h>
#include <dcp/sound_asset.h>
#include <dcp/sound_asset_writer.h>
-#include <dcp/stereo_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset.h>
#include <dcp/subtitle_image.h>
#include "i18n.h"
@@ -81,9 +82,6 @@ using dcp::raw_convert;
using namespace dcpomatic;
-int const ReelWriter::_info_size = 48;
-
-
static dcp::MXFMetadata
mxf_metadata ()
{
@@ -148,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.reset (new dcp::StereoPictureAsset(dcp::Fraction(film()->video_frame_rate(), 1), standard));
- } else {
- _picture_asset.reset (new dcp::MonoPictureAsset(dcp::Fraction(film()->video_frame_rate(), 1), standard));
- }
+ auto setup = [this](shared_ptr<dcp::PictureAsset> 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<dcp::StereoJ2KPictureAsset>(rate, standard);
+ } else {
+ _j2k_picture_asset = std::make_shared<dcp::MonoJ2KPictureAsset>(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<dcp::MonoMPEG2PictureAsset>(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::PictureAsset::Behaviour::OVERWRITE_EXISTING : dcp::PictureAsset::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<dcp::StereoPictureAsset>(asset);
+ if (film()->video_encoding() == VideoEncoding::JPEG2000) {
+ if (film()->three_d()) {
+ _j2k_picture_asset = make_shared<dcp::StereoJ2KPictureAsset>(asset);
+ } else {
+ _j2k_picture_asset = make_shared<dcp::MonoJ2KPictureAsset>(asset);
+ }
} else {
- _picture_asset = make_shared<dcp::MonoPictureAsset>(asset);
+ _mpeg2_picture_asset = make_shared<dcp::MonoMPEG2PictureAsset>(asset);
}
}
@@ -215,53 +228,6 @@ ReelWriter::ReelWriter (
}
-/** @param frame reel-relative frame */
-void
-ReelWriter::write_frame_info (Frame frame, Eyes eyes, dcp::FrameInfo info) const
-{
- auto handle = film()->info_file_handle(_period, false);
- handle->get().seek(frame_info_position(frame, eyes), SEEK_SET);
- handle->get().checked_write(&info.offset, sizeof(info.offset));
- handle->get().checked_write(&info.size, sizeof(info.size));
- handle->get().checked_write(info.hash.c_str(), info.hash.size());
-}
-
-
-dcp::FrameInfo
-ReelWriter::read_frame_info (shared_ptr<InfoFileHandle> info, Frame frame, Eyes eyes) const
-{
- dcp::FrameInfo frame_info;
- info->get().seek(frame_info_position(frame, eyes), SEEK_SET);
- info->get().checked_read(&frame_info.offset, sizeof(frame_info.offset));
- info->get().checked_read(&frame_info.size, sizeof(frame_info.size));
-
- char hash_buffer[33];
- info->get().checked_read(hash_buffer, 32);
- hash_buffer[32] = '\0';
- frame_info.hash = hash_buffer;
-
- return frame_info;
-}
-
-
-long
-ReelWriter::frame_info_position (Frame frame, Eyes eyes) const
-{
- switch (eyes) {
- case Eyes::BOTH:
- return frame * _info_size;
- case Eyes::LEFT:
- return frame * _info_size * 2;
- case Eyes::RIGHT:
- return frame * _info_size * 2 + _info_size;
- default:
- DCPOMATIC_ASSERT (false);
- }
-
- DCPOMATIC_ASSERT (false);
-}
-
-
Frame
ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
{
@@ -290,8 +256,8 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
}
/* Offset of the last dcp::FrameInfo in the info file */
- int const n = (dcp::filesystem::file_size(info_file->get().path()) / _info_size) - 1;
- LOG_GENERAL ("The last FI is %1; info file is %2, info size %3", n, dcp::filesystem::file_size(info_file->get().path()), _info_size);
+ int const n = (dcp::filesystem::file_size(info_file->get().path()) / J2KFrameInfo::size_on_disk()) - 1;
+ LOG_GENERAL("The last FI is %1; info file is %2, info size %3", n, dcp::filesystem::file_size(info_file->get().path()), J2KFrameInfo::size_on_disk())
Frame first_nonexistent_frame;
if (film()->three_d()) {
@@ -321,13 +287,13 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
void
ReelWriter::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes)
{
- if (!_picture_asset_writer) {
+ if (!_j2k_picture_asset_writer) {
/* We're not writing any data */
return;
}
- auto fin = _picture_asset_writer->write (encoded->data(), encoded->size());
- write_frame_info (frame, eyes, fin);
+ auto fin = J2KFrameInfo(_j2k_picture_asset_writer->write(encoded->data(), encoded->size()));
+ fin.write(film()->info_file_handle(_period, false), frame, eyes);
_last_written[eyes] = encoded;
}
@@ -349,37 +315,50 @@ ReelWriter::write (shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metada
void
-ReelWriter::fake_write (int size)
+ReelWriter::write(shared_ptr<dcp::MonoMPEG2PictureFrame> 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;
}
- auto fin = _picture_asset_writer->write(_last_written[eyes]->data(), _last_written[eyes]->size());
- write_frame_info (frame, eyes, fin);
+ auto fin = J2KFrameInfo(_j2k_picture_asset_writer->write(_last_written[eyes]->data(), _last_written[eyes]->size()));
+ fin.write(film()->info_file_handle(_period, false), frame, 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 ()) {
@@ -387,12 +366,21 @@ ReelWriter::finish (boost::filesystem::path output_dcp)
_sound_asset.reset ();
}
+ shared_ptr<dcp::PictureAsset> 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.
*/
@@ -420,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 */
@@ -544,17 +532,17 @@ ReelWriter::create_reel_picture (shared_ptr<dcp::Reel> reel, list<ReferencedReel
{
shared_ptr<dcp::ReelPictureAsset> 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<dcp::MonoPictureAsset> (_picture_asset);
- if (mono) {
+ if (auto mono = dynamic_pointer_cast<dcp::MonoJ2KPictureAsset>(_j2k_picture_asset)) {
reel_asset = make_shared<dcp::ReelMonoPictureAsset>(mono, 0);
}
- auto stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (_picture_asset);
- if (stereo) {
+ if (auto stereo = dynamic_pointer_cast<dcp::StereoJ2KPictureAsset>(_j2k_picture_asset)) {
reel_asset = make_shared<dcp::ReelStereoPictureAsset>(stereo, 0);
}
+ } else if (_mpeg2_picture_asset) {
+ reel_asset = make_shared<dcp::ReelMonoPictureAsset>(_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 */
@@ -783,8 +771,11 @@ try
{
vector<shared_ptr<const dcp::Asset>> 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);
@@ -1012,7 +1003,7 @@ ReelWriter::existing_picture_frame_ok (dcp::File& asset_file, shared_ptr<InfoFil
/* Read the data from the info file; for 3D we just check the left
frames until we find a good one.
*/
- auto const info = read_frame_info (info_file, frame, film()->three_d() ? Eyes::LEFT : Eyes::BOTH);
+ auto const info = J2KFrameInfo(info_file, frame, film()->three_d() ? Eyes::LEFT : Eyes::BOTH);
bool ok = true;
diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h
index c9052c832..f6273f8e9 100644
--- a/src/lib/reel_writer.h
+++ b/src/lib/reel_writer.h
@@ -27,10 +27,14 @@
#include "player_text.h"
#include "referenced_reel_asset.h"
#include "render_text.h"
+#include "text_type.h"
#include "weak_film.h"
#include <dcp/atmos_asset_writer.h>
#include <dcp/file.h>
-#include <dcp/picture_asset_writer.h>
+#include <dcp/ffmpeg_image.h>
+#include <dcp/j2k_picture_asset_writer.h>
+#include <dcp/mono_mpeg2_picture_frame.h>
+#include <dcp/mpeg2_picture_asset_writer.h>
class AudioBuffers;
@@ -41,17 +45,18 @@ struct write_frame_info_test;
namespace dcp {
class AtmosAsset;
- class MonoPictureAsset;
- class MonoPictureAssetWriter;
- class PictureAsset;
- class PictureAssetWriter;
+ class MonoJ2KPictureAsset;
+ class MonoJ2KPictureAssetWriter;
+ class J2KPictureAsset;
+ class J2KPictureAssetWriter;
+ class MPEG2PictureAsset;
class Reel;
class ReelAsset;
class ReelPictureAsset;
class SoundAsset;
class SoundAssetWriter;
- class StereoPictureAsset;
- class StereoPictureAssetWriter;
+ class StereoJ2KPictureAsset;
+ class StereoJ2KPictureAssetWriter;
class SubtitleAsset;
}
@@ -69,11 +74,12 @@ public:
);
void write (std::shared_ptr<const dcp::Data> 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<const AudioBuffers> audio);
void write(PlayerText text, TextType type, boost::optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period, FontIdMap const& fonts, std::shared_ptr<dcpomatic::Font> chosen_interop_font);
void write (std::shared_ptr<const dcp::AtmosFrame> atmos, AtmosMetadata metadata);
+ void write(std::shared_ptr<dcp::MonoMPEG2PictureFrame> image);
void finish (boost::filesystem::path output_dcp);
std::shared_ptr<dcp::Reel> create_reel (
@@ -94,14 +100,10 @@ public:
return _first_nonexistent_frame;
}
- dcp::FrameInfo read_frame_info (std::shared_ptr<InfoFileHandle> info, Frame frame, Eyes eyes) const;
-
private:
friend struct ::write_frame_info_test;
- void write_frame_info (Frame frame, Eyes eyes, dcp::FrameInfo info) const;
- long frame_info_position (Frame frame, Eyes eyes) const;
Frame check_existing_picture_asset (boost::filesystem::path asset);
bool existing_picture_frame_ok (dcp::File& asset_file, std::shared_ptr<InfoFileHandle> info_file, Frame frame) const;
std::shared_ptr<dcp::SubtitleAsset> empty_text_asset (TextType type, boost::optional<DCPTextTrack> track, bool with_dummy) const;
@@ -134,9 +136,11 @@ private:
dcp::ArrayData _default_font;
- std::shared_ptr<dcp::PictureAsset> _picture_asset;
+ std::shared_ptr<dcp::J2KPictureAsset> _j2k_picture_asset;
+ std::shared_ptr<dcp::MPEG2PictureAsset> _mpeg2_picture_asset;
/** picture asset writer, or 0 if we are not writing any picture because we already have one */
- std::shared_ptr<dcp::PictureAssetWriter> _picture_asset_writer;
+ std::shared_ptr<dcp::J2KPictureAssetWriter> _j2k_picture_asset_writer;
+ std::shared_ptr<dcp::MPEG2PictureAssetWriter> _mpeg2_picture_asset_writer;
std::shared_ptr<dcp::SoundAsset> _sound_asset;
std::shared_ptr<dcp::SoundAssetWriter> _sound_asset_writer;
std::shared_ptr<dcp::SubtitleAsset> _subtitle_asset;
@@ -145,6 +149,4 @@ private:
std::shared_ptr<dcp::AtmosAssetWriter> _atmos_asset_writer;
mutable FontMetrics _font_metrics;
-
- static int const _info_size;
};
diff --git a/src/lib/subtitle_encoder.cc b/src/lib/subtitle_film_encoder.cc
index 8b1d9a15b..93ccc177b 100644
--- a/src/lib/subtitle_encoder.cc
+++ b/src/lib/subtitle_film_encoder.cc
@@ -23,7 +23,7 @@
#include "film.h"
#include "job.h"
#include "player.h"
-#include "subtitle_encoder.h"
+#include "subtitle_film_encoder.h"
#include <dcp/filesystem.h>
#include <dcp/interop_subtitle_asset.h>
#include <dcp/raw_convert.h>
@@ -51,8 +51,8 @@ using dcp::raw_convert;
* @param initial_name Hint that may be used to create filenames, if @ref output is a directory.
* @param include_font true to refer to and export any font file (for Interop; ignored for SMPTE).
*/
-SubtitleEncoder::SubtitleEncoder (shared_ptr<const Film> film, shared_ptr<Job> job, boost::filesystem::path output, string initial_name, bool split_reels, bool include_font)
- : Encoder (film, job)
+SubtitleFilmEncoder::SubtitleFilmEncoder(shared_ptr<const Film> film, shared_ptr<Job> job, boost::filesystem::path output, string initial_name, bool split_reels, bool include_font)
+ : FilmEncoder(film, job)
, _split_reels (split_reels)
, _include_font (include_font)
, _reel_index (0)
@@ -61,7 +61,7 @@ SubtitleEncoder::SubtitleEncoder (shared_ptr<const Film> film, shared_ptr<Job> j
_player.set_play_referenced();
_player.set_ignore_video();
_player.set_ignore_audio();
- _player.Text.connect(boost::bind(&SubtitleEncoder::text, this, _1, _2, _3, _4));
+ _player.Text.connect(boost::bind(&SubtitleFilmEncoder::text, this, _1, _2, _3, _4));
string const extension = film->interop() ? ".xml" : ".mxf";
@@ -91,7 +91,7 @@ SubtitleEncoder::SubtitleEncoder (shared_ptr<const Film> film, shared_ptr<Job> j
void
-SubtitleEncoder::go ()
+SubtitleFilmEncoder::go()
{
{
shared_ptr<Job> job = _job.lock ();
@@ -133,7 +133,7 @@ SubtitleEncoder::go ()
void
-SubtitleEncoder::text (PlayerText subs, TextType type, optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period)
+SubtitleFilmEncoder::text(PlayerText subs, TextType type, optional<DCPTextTrack> track, dcpomatic::DCPTimePeriod period)
{
if (type != TextType::OPEN_SUBTITLE) {
return;
@@ -193,7 +193,7 @@ SubtitleEncoder::text (PlayerText subs, TextType type, optional<DCPTextTrack> tr
Frame
-SubtitleEncoder::frames_done () const
+SubtitleFilmEncoder::frames_done() const
{
if (!_last) {
return 0;
diff --git a/src/lib/subtitle_encoder.h b/src/lib/subtitle_film_encoder.h
index 0815b1fff..54231794d 100644
--- a/src/lib/subtitle_encoder.h
+++ b/src/lib/subtitle_film_encoder.h
@@ -21,7 +21,7 @@
#include "dcp_text_track.h"
#include "dcpomatic_time.h"
-#include "encoder.h"
+#include "film_encoder.h"
#include "player_text.h"
@@ -33,13 +33,13 @@ namespace dcp {
class Film;
-/** @class SubtitleEncoder.
+/** @class SubtitleFilmEncoder.
* @brief An `encoder' which extracts a film's subtitles to DCP XML format.
*/
-class SubtitleEncoder : public Encoder
+class SubtitleFilmEncoder : public FilmEncoder
{
public:
- SubtitleEncoder (std::shared_ptr<const Film> film, std::shared_ptr<Job> job, boost::filesystem::path output, std::string initial_name, bool split_reels, bool include_font);
+ SubtitleFilmEncoder(std::shared_ptr<const Film> film, std::shared_ptr<Job> job, boost::filesystem::path output, std::string initial_name, bool split_reels, bool include_font);
void go () override;
diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc
index ba420ab94..98c6784a9 100644
--- a/src/lib/transcode_job.cc
+++ b/src/lib/transcode_job.cc
@@ -28,11 +28,11 @@
#include "compose.hpp"
#include "content.h"
#include "config.h"
-#include "dcp_encoder.h"
+#include "dcp_film_encoder.h"
#include "dcpomatic_log.h"
-#include "encoder.h"
#include "examine_content_job.h"
#include "film.h"
+#include "film_encoder.h"
#include "job_manager.h"
#include "log.h"
#include "transcode_job.h"
@@ -84,7 +84,7 @@ TranscodeJob::json_name () const
void
-TranscodeJob::set_encoder (shared_ptr<Encoder> e)
+TranscodeJob::set_encoder(shared_ptr<FilmEncoder> e)
{
_encoder = e;
}
@@ -133,7 +133,7 @@ TranscodeJob::run ()
LOG_GENERAL(N_("Transcode job completed successfully: %1 fps"), dcp::locale_convert<string>(frames_per_second(), 2, true));
- if (dynamic_pointer_cast<DCPEncoder>(_encoder)) {
+ if (dynamic_pointer_cast<DCPFilmEncoder>(_encoder)) {
try {
Analytics::instance()->successful_dcp_encode();
} catch (FileError& e) {
diff --git a/src/lib/transcode_job.h b/src/lib/transcode_job.h
index 35870231d..720d7f99b 100644
--- a/src/lib/transcode_job.h
+++ b/src/lib/transcode_job.h
@@ -35,7 +35,7 @@
#undef IGNORE
-class Encoder;
+class FilmEncoder;
struct frames_not_lost_when_threads_disappear;
@@ -65,7 +65,7 @@ public:
return true;
}
- void set_encoder (std::shared_ptr<Encoder> t);
+ void set_encoder(std::shared_ptr<FilmEncoder> encoder);
private:
friend struct ::frames_not_lost_when_threads_disappear;
@@ -75,7 +75,7 @@ private:
int remaining_time () const override;
- std::shared_ptr<Encoder> _encoder;
+ std::shared_ptr<FilmEncoder> _encoder;
ChangedBehaviour _changed;
};
diff --git a/src/lib/util.cc b/src/lib/util.cc
index 60117f61d..b727d7b68 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -56,6 +56,7 @@
#include <dcp/file.h>
#include <dcp/filesystem.h>
#include <dcp/locale_convert.h>
+#include <dcp/mpeg2_picture_asset.h>
#include <dcp/picture_asset.h>
#include <dcp/raw_convert.h>
#include <dcp/scope_guard.h>
@@ -103,6 +104,7 @@ LIBDCP_ENABLE_WARNINGS
using std::bad_alloc;
using std::cout;
+using std::dynamic_pointer_cast;
using std::endl;
using std::istream;
using std::list;
@@ -748,9 +750,10 @@ asset_filename (shared_ptr<dcp::Asset> asset, string type, int reel_index, int r
string
-video_asset_filename (shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count, optional<string> summary)
+video_asset_filename(shared_ptr<dcp::PictureAsset> asset, int reel_index, int reel_count, optional<string> summary)
{
- return asset_filename(asset, "j2c", reel_index, reel_count, summary, ".mxf");
+ string type = dynamic_pointer_cast<dcp::MPEG2PictureAsset>(asset) ? "mpeg2" : "j2c";
+ return asset_filename(asset, type, reel_index, reel_count, summary, ".mxf");
}
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 <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
index 000000000..8cc33ef8a
--- /dev/null
+++ b/src/lib/video_encoder.h
@@ -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
+
diff --git a/src/lib/video_encoding.cc b/src/lib/video_encoding.cc
new file mode 100644
index 000000000..de68c6ae9
--- /dev/null
+++ b/src/lib/video_encoding.cc
@@ -0,0 +1,58 @@
+/*
+ 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 "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";
+ case VideoEncoding::COUNT:
+ DCPOMATIC_ASSERT(false);
+ }
+
+ 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..7c240f06f
--- /dev/null
+++ b/src/lib/video_encoding.h
@@ -0,0 +1,42 @@
+/*
+ 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_ENCODING_H
+#define DCPOMATIC_VIDEO_ENCODING_H
+
+
+#include <string>
+
+
+enum class VideoEncoding
+{
+ JPEG2000,
+ MPEG2,
+ COUNT
+};
+
+
+std::string video_encoding_to_string(VideoEncoding encoding);
+VideoEncoding video_encoding_from_string(std::string const& encoding);
+
+
+#endif
+
diff --git a/src/lib/video_mxf_content.cc b/src/lib/video_mxf_content.cc
index 546e1445b..eeee6222c 100644
--- a/src/lib/video_mxf_content.cc
+++ b/src/lib/video_mxf_content.cc
@@ -26,8 +26,8 @@
#include "film.h"
#include "compose.hpp"
#include <asdcp/KM_log.h>
-#include <dcp/mono_picture_asset.h>
-#include <dcp/stereo_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset.h>
#include <dcp/exceptions.h>
#include <libxml++/libxml++.h>
@@ -61,7 +61,7 @@ VideoMXFContent::valid_mxf (boost::filesystem::path path)
Kumu::DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_ALL);
try {
- dcp::MonoPictureAsset mp (path);
+ dcp::MonoJ2KPictureAsset mp(path);
return true;
} catch (dcp::MXFFileError& e) {
@@ -71,7 +71,7 @@ VideoMXFContent::valid_mxf (boost::filesystem::path path)
try {
Kumu::DefaultLogSink().SetFilterFlag(0);
- dcp::StereoPictureAsset sp (path);
+ dcp::StereoJ2KPictureAsset sp (path);
return true;
} catch (dcp::MXFFileError& e) {
diff --git a/src/lib/video_mxf_decoder.cc b/src/lib/video_mxf_decoder.cc
index 9f451297b..3d1ed7f4e 100644
--- a/src/lib/video_mxf_decoder.cc
+++ b/src/lib/video_mxf_decoder.cc
@@ -24,10 +24,10 @@
#include "video_mxf_content.h"
#include "j2k_image_proxy.h"
#include "frame_interval_checker.h"
-#include <dcp/mono_picture_asset.h>
-#include <dcp/mono_picture_asset_reader.h>
-#include <dcp/stereo_picture_asset.h>
-#include <dcp/stereo_picture_asset_reader.h>
+#include <dcp/mono_j2k_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset_reader.h>
+#include <dcp/stereo_j2k_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset_reader.h>
#include <dcp/exceptions.h>
@@ -44,7 +44,7 @@ VideoMXFDecoder::VideoMXFDecoder (shared_ptr<const Film> film, shared_ptr<const
video = make_shared<VideoDecoder>(this, content);
try {
- auto mono = make_shared<dcp::MonoPictureAsset>(_content->path(0));
+ auto mono = make_shared<dcp::MonoJ2KPictureAsset>(_content->path(0));
_mono_reader = mono->start_read ();
_mono_reader->set_check_hmac (false);
_size = mono->size ();
@@ -55,7 +55,7 @@ VideoMXFDecoder::VideoMXFDecoder (shared_ptr<const Film> film, shared_ptr<const
/* maybe it's stereo */
}
- auto stereo = make_shared<dcp::StereoPictureAsset>(_content->path(0));
+ auto stereo = make_shared<dcp::StereoJ2KPictureAsset>(_content->path(0));
_stereo_reader = stereo->start_read ();
_stereo_reader->set_check_hmac (false);
_size = stereo->size ();
diff --git a/src/lib/video_mxf_decoder.h b/src/lib/video_mxf_decoder.h
index 774e269c6..16d8b112e 100644
--- a/src/lib/video_mxf_decoder.h
+++ b/src/lib/video_mxf_decoder.h
@@ -20,8 +20,8 @@
#include "decoder.h"
-#include <dcp/mono_picture_asset_reader.h>
-#include <dcp/stereo_picture_asset_reader.h>
+#include <dcp/mono_j2k_picture_asset_reader.h>
+#include <dcp/stereo_j2k_picture_asset_reader.h>
class VideoMXFContent;
@@ -42,7 +42,7 @@ private:
/** Time of next thing to return from pass */
dcpomatic::ContentTime _next;
- std::shared_ptr<dcp::MonoPictureAssetReader> _mono_reader;
- std::shared_ptr<dcp::StereoPictureAssetReader> _stereo_reader;
+ std::shared_ptr<dcp::MonoJ2KPictureAssetReader> _mono_reader;
+ std::shared_ptr<dcp::StereoJ2KPictureAssetReader> _stereo_reader;
dcp::Size _size;
};
diff --git a/src/lib/video_mxf_examiner.cc b/src/lib/video_mxf_examiner.cc
index 7a05f3369..276664a14 100644
--- a/src/lib/video_mxf_examiner.cc
+++ b/src/lib/video_mxf_examiner.cc
@@ -21,8 +21,8 @@
#include "video_mxf_content.h"
#include "video_mxf_examiner.h"
#include <dcp/exceptions.h>
-#include <dcp/mono_picture_asset.h>
-#include <dcp/stereo_picture_asset.h>
+#include <dcp/mono_j2k_picture_asset.h>
+#include <dcp/stereo_j2k_picture_asset.h>
using std::shared_ptr;
using boost::optional;
@@ -30,7 +30,7 @@ using boost::optional;
VideoMXFExaminer::VideoMXFExaminer (shared_ptr<const VideoMXFContent> content)
{
try {
- _asset.reset (new dcp::MonoPictureAsset (content->path(0)));
+ _asset = std::make_shared<dcp::MonoJ2KPictureAsset>(content->path(0));
} catch (dcp::MXFFileError& e) {
/* maybe it's stereo */
} catch (dcp::ReadError& e) {
@@ -38,7 +38,7 @@ VideoMXFExaminer::VideoMXFExaminer (shared_ptr<const VideoMXFContent> content)
}
if (!_asset) {
- _asset.reset (new dcp::StereoPictureAsset (content->path(0)));
+ _asset = std::make_shared<dcp::StereoJ2KPictureAsset>(content->path(0));
}
}
diff --git a/src/lib/writer.cc b/src/lib/writer.cc
index fbe2d248d..3dd22718f 100644
--- a/src/lib/writer.cc
+++ b/src/lib/writer.cc
@@ -30,6 +30,7 @@
#include "dcpomatic_log.h"
#include "film.h"
#include "film_util.h"
+#include "frame_info.h"
#include "job.h"
#include "log.h"
#include "ratio.h"
@@ -39,6 +40,7 @@
#include "version.h"
#include "writer.h"
#include <dcp/cpl.h>
+#include <dcp/mono_mpeg2_picture_frame.h>
#include <dcp/locale_convert.h>
#include <dcp/raw_convert.h>
#include <dcp/reel_closed_caption_asset.h>
@@ -168,6 +170,13 @@ Writer::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes)
}
+void
+Writer::write(shared_ptr<dcp::MonoMPEG2PictureFrame> image, Frame frame)
+{
+ _reels[video_reel(frame)].write(image);
+}
+
+
bool
Writer::can_repeat (Frame frame) const
{
@@ -229,11 +238,7 @@ Writer::fake_write (Frame frame, Eyes eyes)
QueueItem qi;
qi.type = QueueItem::Type::FAKE;
-
- {
- shared_ptr<InfoFileHandle> info_file = film()->info_file_handle(_reels[reel].period(), true);
- qi.size = _reels[reel].read_frame_info(info_file, 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));
@@ -404,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);
}
}
}
@@ -435,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 <dcp/atmos_frame.h>
+#include <dcp/frame_info.h>
+#include <dcp/mono_mpeg2_picture_frame.h>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include <list>
@@ -74,8 +76,8 @@ public:
/** encoded data for FULL */
std::shared_ptr<const dcp::Data> 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<std::shared_ptr<dcpomatic::Font>> fonts);
void write (ReferencedReelAsset asset);
void write (std::shared_ptr<const dcp::AtmosFrame> atmos, dcpomatic::DCPTime time, AtmosMetadata metadata);
+ void write (std::shared_ptr<dcp::MonoMPEG2PictureFrame> 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 4eeeb578e..86cf2e0b6 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -68,9 +68,9 @@ sources = """
dcp_content.cc
dcp_content_type.cc
dcp_decoder.cc
- dcp_encoder.cc
dcp_examiner.cc
dcp_digest_file.cc
+ dcp_film_encoder.cc
dcp_subtitle.cc
dcp_subtitle_content.cc
dcp_subtitle_decoder.cc
@@ -89,7 +89,6 @@ sources = """
dolby_cp750.cc
email.cc
empty.cc
- encoder.cc
encode_server.cc
encode_server_finder.cc
encoded_log_entry.cc
@@ -99,6 +98,7 @@ sources = """
examine_ffmpeg_subtitles_job.cc
exceptions.cc
export_config.cc
+ frame_info.cc
file_group.cc
file_log.cc
filter_graph.cc
@@ -107,14 +107,15 @@ sources = """
ffmpeg_audio_stream.cc
ffmpeg_content.cc
ffmpeg_decoder.cc
- ffmpeg_encoder.cc
ffmpeg_examiner.cc
ffmpeg_file_encoder.cc
+ ffmpeg_film_encoder.cc
ffmpeg_image_proxy.cc
ffmpeg_stream.cc
ffmpeg_subtitle_stream.cc
ffmpeg_wrapper.cc
film.cc
+ film_encoder.cc
film_util.cc
filter.cc
font.cc
@@ -153,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
@@ -187,7 +189,7 @@ sources = """
string_text_file_content.cc
string_text_file_decoder.cc
subtitle_analysis.cc
- subtitle_encoder.cc
+ subtitle_film_encoder.cc
territory_type.cc
text_ring_buffers.cc
text_type.cc
@@ -210,6 +212,8 @@ sources = """
verify_dcp_job.cc
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/tools/dcpomatic.cc b/src/tools/dcpomatic.cc
index 5f723ba0f..de4ddebc2 100644
--- a/src/tools/dcpomatic.cc
+++ b/src/tools/dcpomatic.cc
@@ -76,7 +76,7 @@
#include "lib/email.h"
#include "lib/encode_server_finder.h"
#include "lib/exceptions.h"
-#include "lib/ffmpeg_encoder.h"
+#include "lib/ffmpeg_film_encoder.h"
#include "lib/film.h"
#include "lib/font_config.h"
#ifdef DCPOMATIC_GROK
@@ -91,7 +91,7 @@
#include "lib/screen.h"
#include "lib/send_kdm_email_job.h"
#include "lib/signal_manager.h"
-#include "lib/subtitle_encoder.h"
+#include "lib/subtitle_film_encoder.h"
#include "lib/text_content.h"
#include "lib/transcode_job.h"
#include "lib/update_checker.h"
@@ -1029,7 +1029,7 @@ private:
auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
job->set_encoder (
- make_shared<FFmpegEncoder> (
+ make_shared<FFmpegFilmEncoder>(
_film, job, dialog.path(), dialog.format(), dialog.mixdown_to_stereo(), dialog.split_reels(), dialog.split_streams(), dialog.x264_crf())
);
JobManager::instance()->add (job);
@@ -1044,7 +1044,7 @@ private:
}
auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
job->set_encoder(
- make_shared<SubtitleEncoder>(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font())
+ make_shared<SubtitleFilmEncoder>(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font())
);
JobManager::instance()->add(job);
}
diff --git a/src/tools/dcpomatic_cli.cc b/src/tools/dcpomatic_cli.cc
index c087f89e7..49caf5cb2 100644
--- a/src/tools/dcpomatic_cli.cc
+++ b/src/tools/dcpomatic_cli.cc
@@ -25,7 +25,7 @@
#include "lib/cross.h"
#include "lib/dcpomatic_log.h"
#include "lib/encode_server_finder.h"
-#include "lib/ffmpeg_encoder.h"
+#include "lib/ffmpeg_film_encoder.h"
#include "lib/film.h"
#include "lib/filter.h"
#ifdef DCPOMATIC_GROK
@@ -94,7 +94,7 @@ print_dump (shared_ptr<Film> film)
{
cout << film->dcp_name (true) << "\n"
<< film->container()->container_nickname() << " at " << ((film->resolution() == Resolution::TWO_K) ? "2K" : "4K") << "\n"
- << (film->j2k_bandwidth() / 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";
@@ -519,7 +519,7 @@ main (int argc, char* argv[])
if (export_format) {
auto job = std::make_shared<TranscodeJob>(film, behaviour);
job->set_encoder (
- std::make_shared<FFmpegEncoder> (
+ std::make_shared<FFmpegFilmEncoder>(
film, job, *export_filename, *export_format == "mp4" ? ExportFormat::H264_AAC : ExportFormat::PRORES_HQ, false, false, false, 23
)
);
diff --git a/src/tools/dcpomatic_combiner.cc b/src/tools/dcpomatic_combiner.cc
index 6588b23b4..a28e063e5 100644
--- a/src/tools/dcpomatic_combiner.cc
+++ b/src/tools/dcpomatic_combiner.cc
@@ -30,6 +30,7 @@
#include "lib/constants.h"
#include "lib/cross.h"
#include "lib/job_manager.h"
+#include "lib/util.h"
#include <dcp/combine.h>
LIBDCP_DISABLE_WARNINGS
#include <wx/filepicker.h>
diff --git a/src/tools/dcpomatic_editor.cc b/src/tools/dcpomatic_editor.cc
index e1f541fe8..fc04ce017 100644
--- a/src/tools/dcpomatic_editor.cc
+++ b/src/tools/dcpomatic_editor.cc
@@ -29,6 +29,7 @@
#include "lib/cross.h"
#include "lib/dcpomatic_log.h"
#include "lib/null_log.h"
+#include "lib/util.h"
#include "lib/variant.h"
#include <dcp/cpl.h>
#include <dcp/dcp.h>
diff --git a/src/tools/dcpomatic_kdm.cc b/src/tools/dcpomatic_kdm.cc
index d6c8b3945..1eda05162 100644
--- a/src/tools/dcpomatic_kdm.cc
+++ b/src/tools/dcpomatic_kdm.cc
@@ -53,6 +53,7 @@
#include "lib/kdm_with_metadata.h"
#include "lib/screen.h"
#include "lib/send_kdm_email_job.h"
+#include "lib/util.h"
#include "lib/variant.h"
#include <dcp/encrypted_kdm.h>
#include <dcp/decrypted_kdm.h>
diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc
index 04bb26c2e..05c3f281c 100644
--- a/src/wx/config_dialog.cc
+++ b/src/wx/config_dialog.cc
@@ -27,6 +27,7 @@
#include "static_text.h"
#include "wx_variant.h"
#include "lib/constants.h"
+#include "lib/util.h"
#include <dcp/file.h>
#include <dcp/filesystem.h>
#include <dcp/raw_convert.h>
diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc
index 781c95de7..bc8ac859c 100644
--- a/src/wx/dcp_panel.cc
+++ b/src/wx/dcp_panel.cc
@@ -131,7 +131,7 @@ DCPPanel::DCPPanel(wxNotebook* n, shared_ptr<Film> film, FilmViewer& viewer)
_reels->Bind(wxEVT_BUTTON, boost::bind(&DCPPanel::reels_clicked, this));
for (auto i: DCPContentType::all()) {
- _dcp_content_type->add(i->pretty_name());
+ _dcp_content_type->add_entry(i->pretty_name());
}
add_standards();
@@ -146,11 +146,12 @@ DCPPanel::DCPPanel(wxNotebook* n, shared_ptr<Film> film, FilmViewer& viewer)
void
DCPPanel::add_standards()
{
- _standard->add(_("SMPTE"), N_("smpte"));
+ _standard->add_entry(_("SMPTE"), N_("smpte"));
if (Config::instance()->allow_smpte_bv20() || (_film && _film->limit_to_smpte_bv20())) {
- _standard->add(_("SMPTE (Bv2.0 only)"), N_("smpte-bv20"));
+ _standard->add_entry(_("SMPTE (Bv2.0 only)"), N_("smpte-bv20"));
}
- _standard->add(_("Interop"), N_("interop"));
+ _standard->add_entry(_("Interop"), N_("interop"));
+ _standard->add_entry(_("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);
}
}
@@ -253,13 +264,13 @@ DCPPanel::name_changed ()
void
-DCPPanel::j2k_bandwidth_changed ()
+DCPPanel::video_bit_rate_changed()
{
if (!_film) {
return;
}
- _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
+ _film->set_video_bit_rate(_film->video_encoding(), _video_bit_rate->GetValue() * 1000000);
}
@@ -385,8 +396,8 @@ DCPPanel::film_changed(FilmProperty p)
setup_container ();
setup_dcp_name ();
break;
- case FilmProperty::J2K_BANDWIDTH:
- checked_set (_j2k_bandwidth, _film->j2k_bandwidth() / 1000000);
+ case FilmProperty::VIDEO_BIT_RATE:
+ checked_set(_video_bit_rate, _film->video_bit_rate(_film->video_encoding()) / 1000000);
break;
case FilmProperty::USE_ISDCF_NAME:
{
@@ -445,6 +456,12 @@ DCPPanel::film_changed(FilmProperty p)
setup_dcp_name ();
_markers->Enable (!_film->interop());
break;
+ case FilmProperty::VIDEO_ENCODING:
+ set_standard();
+ setup_container();
+ setup_sensitivity();
+ film_changed(FilmProperty::VIDEO_BIT_RATE);
+ break;
case FilmProperty::LIMIT_TO_SMPTE_BV20:
set_standard();
break;
@@ -526,24 +543,28 @@ DCPPanel::film_content_changed (int property)
void
DCPPanel::setup_container ()
{
- int n = 0;
- auto ratios = Ratio::containers ();
- auto i = ratios.begin ();
- while (i != ratios.end() && *i != _film->container()) {
- ++i;
- ++n;
+ auto ratios = Ratio::containers();
+ if (std::find(ratios.begin(), ratios.end(), _film->container()) == ratios.end()) {
+ ratios.push_back(_film->container());
}
- if (i == ratios.end()) {
- checked_set (_container, -1);
- checked_set (_container_size, wxT(""));
- } else {
- checked_set (_container, n);
- auto const size = fit_ratio_within (_film->container()->ratio(), _film->full_frame ());
- checked_set (_container_size, wxString::Format("%dx%d", size.width, size.height));
+ wxArrayString new_ratios;
+ for (auto ratio: ratios) {
+ new_ratios.Add(std_to_wx(ratio->container_nickname()));
}
+ _container->set_entries(new_ratios);
+
+ auto iter = std::find_if(ratios.begin(), ratios.end(), [this](Ratio const* ratio) { return ratio == _film->container(); });
+ DCPOMATIC_ASSERT(iter != ratios.end());
+
+ checked_set(_container, iter - ratios.begin());
+ auto const size = fit_ratio_within(_film->container()->ratio(), _film->full_frame ());
+ checked_set(_container_size, wxString::Format("%dx%d", size.width, size.height));
+
setup_dcp_name ();
+
+ _video_grid->Layout();
}
@@ -605,7 +626,7 @@ DCPPanel::set_film (shared_ptr<Film> film)
film_changed(FilmProperty::CONTAINER);
film_changed(FilmProperty::RESOLUTION);
film_changed(FilmProperty::ENCRYPTED);
- film_changed(FilmProperty::J2K_BANDWIDTH);
+ film_changed(FilmProperty::VIDEO_BIT_RATE);
film_changed(FilmProperty::VIDEO_FRAME_RATE);
film_changed(FilmProperty::AUDIO_CHANNELS);
film_changed(FilmProperty::SEQUENCE);
@@ -634,6 +655,8 @@ DCPPanel::set_general_sensitivity (bool s)
void
DCPPanel::setup_sensitivity ()
{
+ auto const mpeg2 = _film && _film->video_encoding() == VideoEncoding::MPEG2;
+
_name->Enable (_generally_sensitive);
_use_isdcf_name->Enable (_generally_sensitive);
_dcp_content_type->Enable (_generally_sensitive);
@@ -649,8 +672,8 @@ DCPPanel::setup_sensitivity ()
_frame_rate_spin->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->contains_atmos_content());
_audio_channels->Enable (_generally_sensitive && _film && !_film->references_dcp_audio());
_audio_processor->Enable (_generally_sensitive && _film && !_film->references_dcp_audio());
- _j2k_bandwidth->Enable (_generally_sensitive && _film && !_film->references_dcp_video());
- _container->Enable (_generally_sensitive && _film && !_film->references_dcp_video());
+ _video_bit_rate->Enable (_generally_sensitive && _film && !_film->references_dcp_video());
+ _container->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !mpeg2);
_best_frame_rate->Enable (
_generally_sensitive &&
_film &&
@@ -658,8 +681,8 @@ DCPPanel::setup_sensitivity ()
!_film->references_dcp_video() &&
!_film->contains_atmos_content()
);
- _resolution->Enable (_generally_sensitive && _film && !_film->references_dcp_video());
- _three_d->Enable (_generally_sensitive && _film && !_film->references_dcp_video());
+ _resolution->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !mpeg2);
+ _three_d->Enable (_generally_sensitive && _film && !_film->references_dcp_video() && !mpeg2);
_standard->Enable (
_generally_sensitive &&
@@ -732,7 +755,8 @@ DCPPanel::reencode_j2k_changed ()
void
DCPPanel::config_changed (Config::Property p)
{
- _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 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) {
@@ -750,6 +774,8 @@ DCPPanel::config_changed (Config::Property p)
}
} else if (p == Config::ISDCF_NAME_PART_LENGTH) {
setup_dcp_name();
+ } else if (p == Config::ALLOW_ANY_CONTAINER) {
+ setup_container();
}
}
@@ -791,8 +817,8 @@ DCPPanel::make_video_panel ()
_three_d = new CheckBox (panel, _("3D"));
- _j2k_bandwidth_label = create_label (panel, _("JPEG2000 bandwidth\nfor newly-encoded data"), true);
- _j2k_bandwidth = new SpinCtrl (panel, DCPOMATIC_SPIN_CTRL_WIDTH);
+ _video_bit_rate_label = create_label(panel, _("Video bit rate\nfor newly-encoded data"), true);
+ _video_bit_rate = new SpinCtrl(panel, DCPOMATIC_SPIN_CTRL_WIDTH);
_mbits_label = create_label (panel, _("Mbit/s"), false);
_reencode_j2k = new CheckBox (panel, _("Re-encode JPEG2000 data from input"));
@@ -801,26 +827,23 @@ DCPPanel::make_video_panel ()
_frame_rate_choice->Bind (wxEVT_CHOICE, boost::bind(&DCPPanel::frame_rate_choice_changed, this));
_frame_rate_spin->Bind (wxEVT_SPINCTRL, boost::bind(&DCPPanel::frame_rate_spin_changed, this));
_best_frame_rate->Bind (wxEVT_BUTTON, boost::bind(&DCPPanel::best_frame_rate_clicked, this));
- _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind(&DCPPanel::j2k_bandwidth_changed, this));
+ _video_bit_rate->Bind (wxEVT_SPINCTRL, boost::bind(&DCPPanel::video_bit_rate_changed, this));
/* Also listen to wxEVT_TEXT so that typing numbers directly in is always noticed */
- _j2k_bandwidth->Bind (wxEVT_TEXT, boost::bind(&DCPPanel::j2k_bandwidth_changed, this));
+ _video_bit_rate->Bind (wxEVT_TEXT, boost::bind(&DCPPanel::video_bit_rate_changed, this));
_resolution->Bind (wxEVT_CHOICE, boost::bind(&DCPPanel::resolution_changed, this));
_three_d->bind(&DCPPanel::three_d_changed, this);
_reencode_j2k->bind(&DCPPanel::reencode_j2k_changed, this);
- for (auto i: Ratio::containers()) {
- _container->add(i->container_nickname());
- }
-
for (auto i: Config::instance()->allowed_dcp_frame_rates()) {
- _frame_rate_choice->add(boost::lexical_cast<string>(i));
+ _frame_rate_choice->add_entry(boost::lexical_cast<string>(i));
}
- _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 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(_("2K"));
- _resolution->add(_("4K"));
+ _resolution->add_entry(_("2K"));
+ _resolution->add_entry(_("4K"));
add_video_panel_to_grid ();
setup_frame_rate_widget();
@@ -860,9 +883,9 @@ DCPPanel::add_video_panel_to_grid ()
_video_grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
++r;
- add_label_to_sizer (_video_grid, _j2k_bandwidth_label, true, wxGBPosition (r, 0));
+ add_label_to_sizer(_video_grid, _video_bit_rate_label, true, wxGBPosition (r, 0));
auto s = new wxBoxSizer (wxHORIZONTAL);
- s->Add (_j2k_bandwidth, 0, wxALIGN_CENTER_VERTICAL);
+ s->Add(_video_bit_rate, 0, wxALIGN_CENTER_VERTICAL);
add_label_to_sizer (s, _mbits_label, false, 0, wxLEFT | wxALIGN_CENTER_VERTICAL);
_video_grid->Add (s, wxGBPosition(r, 1), wxDefaultSpan);
++r;
@@ -1010,9 +1033,9 @@ DCPPanel::show_audio_clicked ()
void
DCPPanel::add_audio_processors ()
{
- _audio_processor->add(_("None"), new wxStringClientData(N_("none")));
+ _audio_processor->add_entry(_("None"), new wxStringClientData(N_("none")));
for (auto ap: AudioProcessor::visible()) {
- _audio_processor->add(std_to_wx(ap->name()), new wxStringClientData(std_to_wx(ap->id())));
+ _audio_processor->add_entry(std_to_wx(ap->name()), new wxStringClientData(std_to_wx(ap->id())));
}
_audio_panel_sizer->Layout();
}
diff --git a/src/wx/dcp_panel.h b/src/wx/dcp_panel.h
index 6c97a41c3..c686a9c55 100644
--- a/src/wx/dcp_panel.h
+++ b/src/wx/dcp_panel.h
@@ -71,7 +71,7 @@ private:
void copy_isdcf_name_button_clicked ();
void container_changed ();
void dcp_content_type_changed ();
- void j2k_bandwidth_changed ();
+ void video_bit_rate_changed();
void frame_rate_choice_changed ();
void frame_rate_spin_changed ();
void best_frame_rate_clicked ();
@@ -129,9 +129,9 @@ private:
Choice* _container;
wxStaticText* _container_size;
wxButton* _copy_isdcf_name_button;
- wxStaticText* _j2k_bandwidth_label;
+ wxStaticText* _video_bit_rate_label;
wxStaticText* _mbits_label;
- wxSpinCtrl* _j2k_bandwidth;
+ wxSpinCtrl* _video_bit_rate;
wxStaticText* _dcp_content_type_label;
Choice* _dcp_content_type;
wxStaticText* _frame_rate_label;
diff --git a/src/wx/dcp_timeline.cc b/src/wx/dcp_timeline.cc
index 6474a8428..7b8e93325 100644
--- a/src/wx/dcp_timeline.cc
+++ b/src/wx/dcp_timeline.cc
@@ -230,10 +230,10 @@ DCPTimeline::setup_reel_settings()
int r = 0;
add_label_to_sizer(sizer, _reel_settings, _("Reel mode"), true, wxGBPosition(r, 0));
_reel_type = new Choice(_reel_settings);
- _reel_type->add(_("Single reel"));
- _reel_type->add(_("Split by video content"));
- _reel_type->add(_("Split by maximum reel size"));
- _reel_type->add(_("Custom"));
+ _reel_type->add_entry(_("Single reel"));
+ _reel_type->add_entry(_("Split by video content"));
+ _reel_type->add_entry(_("Split by maximum reel size"));
+ _reel_type->add_entry(_("Custom"));
sizer->Add(_reel_type, wxGBPosition(r, 1));
++r;
diff --git a/src/wx/dcp_timeline.h b/src/wx/dcp_timeline.h
index 3413c2814..23644c03f 100644
--- a/src/wx/dcp_timeline.h
+++ b/src/wx/dcp_timeline.h
@@ -25,6 +25,8 @@
#include "timecode.h"
#include "timeline.h"
+#include "lib/change_signaller.h"
+#include "lib/film_property.h"
#include "lib/rect.h"
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
diff --git a/src/wx/dcpomatic_choice.cc b/src/wx/dcpomatic_choice.cc
index 2f1a6f0e9..f2e215439 100644
--- a/src/wx/dcpomatic_choice.cc
+++ b/src/wx/dcpomatic_choice.cc
@@ -40,14 +40,14 @@ Choice::Choice(wxWindow* parent)
void
-Choice::add(string const& entry)
+Choice::add_entry(string const& entry)
{
- add(std_to_wx(entry));
+ add_entry(std_to_wx(entry));
}
void
-Choice::add(wxString const& entry)
+Choice::add_entry(wxString const& entry)
{
if (_needs_clearing) {
Clear();
@@ -59,7 +59,7 @@ Choice::add(wxString const& entry)
void
-Choice::add(wxString const& entry, wxClientData* data)
+Choice::add_entry(wxString const& entry, wxClientData* data)
{
if (_needs_clearing) {
Clear();
@@ -71,7 +71,7 @@ Choice::add(wxString const& entry, wxClientData* data)
void
-Choice::add(wxString const& entry, wxString const& data)
+Choice::add_entry(wxString const& entry, wxString const& data)
{
if (_needs_clearing) {
Clear();
@@ -83,6 +83,18 @@ Choice::add(wxString const& entry, wxString const& data)
void
+Choice::set_entries(wxArrayString const& entries)
+{
+ if (GetStrings() == entries) {
+ return;
+ }
+
+ Clear();
+ Set(entries);
+}
+
+
+void
Choice::set(int index)
{
SetSelection(index);
diff --git a/src/wx/dcpomatic_choice.h b/src/wx/dcpomatic_choice.h
index dec0a3701..cc8115d20 100644
--- a/src/wx/dcpomatic_choice.h
+++ b/src/wx/dcpomatic_choice.h
@@ -32,10 +32,12 @@ class Choice : public wxChoice
public:
Choice(wxWindow* parent);
- void add(wxString const& entry);
- void add(wxString const& entry, wxClientData* data);
- void add(wxString const& entry, wxString const& data);
- void add(std::string const& entry);
+ void add_entry(wxString const& entry);
+ void add_entry(wxString const& entry, wxClientData* data);
+ void add_entry(wxString const& entry, wxString const& data);
+ void add_entry(std::string const& entry);
+ void set_entries(wxArrayString const& entries);
+
void set(int index);
void set_by_data(wxString const& data);
boost::optional<int> get() const;
diff --git a/src/wx/export_video_file_dialog.h b/src/wx/export_video_file_dialog.h
index beb33610b..4e626be6b 100644
--- a/src/wx/export_video_file_dialog.h
+++ b/src/wx/export_video_file_dialog.h
@@ -20,7 +20,7 @@
#include "table_dialog.h"
-#include "lib/ffmpeg_encoder.h"
+#include "lib/ffmpeg_file_encoder.h"
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
#include <wx/wx.h>
diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h
index 5824f8baa..63aa113d1 100644
--- a/src/wx/film_viewer.h
+++ b/src/wx/film_viewer.h
@@ -25,8 +25,10 @@
#include "video_view.h"
+#include "lib/change_signaller.h"
#include "lib/config.h"
#include "lib/film_property.h"
+#include "lib/player.h"
#include "lib/player_text.h"
#include "lib/signaller.h"
#include "lib/timer.h"
diff --git a/src/wx/full_config_dialog.cc b/src/wx/full_config_dialog.cc
index 193392f29..00e5575b1 100644
--- a/src/wx/full_config_dialog.cc
+++ b/src/wx/full_config_dialog.cc
@@ -147,8 +147,8 @@ private:
add_update_controls (table, r);
- _default_add_file_location->add(_("Same place as last time"));
- _default_add_file_location->add(_("Same place as project"));
+ _default_add_file_location->add_entry(_("Same place as last time"));
+ _default_add_file_location->add_entry(_("Same place as project"));
_default_add_file_location->bind(&FullGeneralPage::default_add_file_location_changed, this);
_config_file->Bind (wxEVT_FILEPICKER_CHANGED, boost::bind(&FullGeneralPage::config_file_changed, this));
@@ -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);
- _j2k_bandwidth = new wxSpinCtrl (_panel);
- s->Add (_j2k_bandwidth);
+ _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));
- _j2k_bandwidth->SetRange (50, 250);
- _j2k_bandwidth->Bind (wxEVT_SPINCTRL, boost::bind (&DefaultsPage::j2k_bandwidth_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 (_j2k_bandwidth, config->default_j2k_bandwidth() / 1000000);
- _j2k_bandwidth->SetRange (50, config->maximum_j2k_bandwidth() / 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<string> (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 j2k_bandwidth_changed ()
+ void j2k_video_bit_rate_changed()
{
- Config::instance()->set_default_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
+ 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(VideoEncoding::MPEG2, _mpeg2_video_bit_rate->GetValue() * 1000000);
}
void audio_delay_changed ()
@@ -634,7 +652,8 @@ private:
}
}
- wxSpinCtrl* _j2k_bandwidth;
+ 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_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_j2k_bandwidth = new wxSpinCtrl(_panel);
- s->Add(_maximum_j2k_bandwidth, 1);
+ _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);
}
@@ -1559,13 +1587,7 @@ private:
_allow_any_container = new CheckBox(_panel, _("Allow full-frame and non-standard container ratios"));
table->Add(_allow_any_container, 1, wxEXPAND | wxLEFT, DCPOMATIC_SIZER_GAP);
- auto restart = new StaticText(_panel, variant::wx::insert_dcpomatic(_("(restart %s to see all ratios)")));
- auto font = restart->GetFont();
- font.SetStyle(wxFONTSTYLE_ITALIC);
- font.SetPointSize(font.GetPointSize() - 1);
- restart->SetFont(font);
- table->Add(restart, 1, wxALIGN_CENTRE_VERTICAL | wxBOTTOM, DCPOMATIC_CHECKBOX_BOTTOM_PAD);
- restart->SetFont(font);
+ table->AddSpacer(0);
checkbox(_("Allow creation of DCPs with 96kHz audio"), _allow_96khz_audio);
checkbox(_("Allow mapping to all audio channels"), _use_all_audio_channels);
@@ -1579,8 +1601,10 @@ private:
table->Add(s, 1);
}
- _maximum_j2k_bandwidth->SetRange(1, 1000);
- _maximum_j2k_bandwidth->Bind(wxEVT_SPINCTRL, boost::bind(&NonStandardPage::maximum_j2k_bandwidth_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);
@@ -1594,7 +1618,8 @@ private:
{
auto config = Config::instance();
- checked_set(_maximum_j2k_bandwidth, config->maximum_j2k_bandwidth() / 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());
@@ -1603,9 +1628,14 @@ private:
checked_set(_isdcf_name_part_length, config->isdcf_name_part_length());
}
- void maximum_j2k_bandwidth_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_j2k_bandwidth(_maximum_j2k_bandwidth->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()
@@ -1638,7 +1668,8 @@ private:
Config::instance()->set_isdcf_name_part_length(_isdcf_name_part_length->GetValue());
}
- wxSpinCtrl* _maximum_j2k_bandwidth = 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/src/wx/kdm_cpl_panel.cc b/src/wx/kdm_cpl_panel.cc
index 4e1eb8f34..523d0c369 100644
--- a/src/wx/kdm_cpl_panel.cc
+++ b/src/wx/kdm_cpl_panel.cc
@@ -23,6 +23,7 @@
#include "kdm_cpl_panel.h"
#include "static_text.h"
#include "wx_util.h"
+#include <dcp/filesystem.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
#include <libxml++/libxml++.h>
diff --git a/src/wx/kdm_timing_panel.cc b/src/wx/kdm_timing_panel.cc
index f4112cb08..6e362a3b2 100644
--- a/src/wx/kdm_timing_panel.cc
+++ b/src/wx/kdm_timing_panel.cc
@@ -124,7 +124,7 @@ KDMTimingPanel::KDMTimingPanel (wxWindow* parent)
/* Default to UTC */
size_t sel = get_offsets(_offsets);
for (size_t i = 0; i < _offsets.size(); ++i) {
- _utc_offset->add(_offsets[i].name);
+ _utc_offset->add_entry(_offsets[i].name);
if (_offsets[i].hour == 0 && _offsets[i].minute == 0) {
sel = i;
}
diff --git a/src/wx/metadata_dialog.cc b/src/wx/metadata_dialog.cc
index 347f2fffd..1d0758175 100644
--- a/src/wx/metadata_dialog.cc
+++ b/src/wx/metadata_dialog.cc
@@ -204,9 +204,9 @@ MetadataDialog::setup_standard (wxPanel* panel, wxSizer* sizer)
{
add_label_to_sizer(sizer, panel, _("Territory type"), true, 0, wxALIGN_CENTER_VERTICAL);
_territory_type = new Choice(panel);
- _territory_type->add(_("Specific"), wx_to_std(territory_type_to_string(TerritoryType::SPECIFIC)));
- _territory_type->add(_("International texted"), wx_to_std(territory_type_to_string(TerritoryType::INTERNATIONAL_TEXTED)));
- _territory_type->add(_("International textless"), wx_to_std(territory_type_to_string(TerritoryType::INTERNATIONAL_TEXTLESS)));
+ _territory_type->add_entry(_("Specific"), wx_to_std(territory_type_to_string(TerritoryType::SPECIFIC)));
+ _territory_type->add_entry(_("International texted"), wx_to_std(territory_type_to_string(TerritoryType::INTERNATIONAL_TEXTED)));
+ _territory_type->add_entry(_("International textless"), wx_to_std(territory_type_to_string(TerritoryType::INTERNATIONAL_TEXTLESS)));
sizer->Add(_territory_type);
_enable_release_territory = new CheckBox(panel, _("Release territory"));
@@ -330,8 +330,8 @@ MetadataDialog::setup_advanced (wxPanel* panel, wxSizer* sizer)
sizer->Add (s, 1, wxEXPAND);
}
- _luminance_unit->add(_("candela per m²"));
- _luminance_unit->add(_("foot lambert"));
+ _luminance_unit->add_entry(_("candela per m²"));
+ _luminance_unit->add_entry(_("foot lambert"));
}
diff --git a/src/wx/timeline_content_view.cc b/src/wx/timeline_content_view.cc
index cb0d10240..69a675c42 100644
--- a/src/wx/timeline_content_view.cc
+++ b/src/wx/timeline_content_view.cc
@@ -23,6 +23,7 @@
#include "timeline_content_view.h"
#include "wx_util.h"
#include "lib/content.h"
+#include "lib/util.h"
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
#include <wx/graphics.h>