summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-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
57 files changed, 996 insertions, 408 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