summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2026-02-08 00:52:27 +0100
committerCarl Hetherington <cth@carlh.net>2026-02-12 21:01:59 +0100
commit7b19d27983cb8078e8b35407a25e62da4ec687fe (patch)
tree1d038a938e81eb452dc3c8c970d3e54edd307734 /src/lib
parent55c53dd8621a30d3c39858216da6b5d2e6445fb0 (diff)
Re-use assets verbatim if they do not need to change (#448).
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/dcp_decoder.cc17
-rw-r--r--src/lib/dcp_decoder.h4
-rw-r--r--src/lib/player.cc4
-rw-r--r--src/lib/reel_writer.cc15
-rw-r--r--src/lib/reusable_reel_asset.cc31
5 files changed, 49 insertions, 22 deletions
diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc
index 27d0f3a94..59dd7c008 100644
--- a/src/lib/dcp_decoder.cc
+++ b/src/lib/dcp_decoder.cc
@@ -28,6 +28,7 @@
#include "dcp_decoder.h"
#include "dcpomatic_log.h"
#include "digester.h"
+#include "film.h"
#include "ffmpeg_image_proxy.h"
#include "frame_interval_checker.h"
#include "image.h"
@@ -146,6 +147,12 @@ DCPDecoder::DCPDecoder(shared_ptr<const Film> film, shared_ptr<const DCPContent>
_font_id_allocator.add_fonts_from_reels(_reels);
_font_id_allocator.allocate();
+
+ _can_reuse_video = _dcp_content->can_reuse_video(film) && !film->reencode_j2k();
+ _can_reuse_audio = _dcp_content->can_reuse_audio(film);
+ for (int i = 0; i < static_cast<int>(TextType::COUNT); ++i) {
+ _can_reuse_text[i] = _dcp_content->can_reuse_text(film, static_cast<TextType>(i));
+ }
}
@@ -176,7 +183,7 @@ DCPDecoder::pass()
*/
pass_texts(_next, picture_asset->size());
- if ((_j2k_mono_reader || _j2k_stereo_reader || _mpeg2_mono_reader) && (_decode_reusable || !_dcp_content->reference_video())) {
+ if ((_j2k_mono_reader || _j2k_stereo_reader || _mpeg2_mono_reader) && (_decode_reusable || !_can_reuse_video)) {
auto const entry_point = (*_reel)->main_picture()->entry_point().get_value_or(0);
if (_j2k_mono_reader) {
video->emit(
@@ -233,7 +240,7 @@ DCPDecoder::pass()
}
}
- if (_sound_reader && (_decode_reusable || !_dcp_content->reference_audio())) {
+ if (_sound_reader && (_decode_reusable || !_can_reuse_audio)) {
auto const entry_point = (*_reel)->main_sound()->entry_point().get_value_or(0);
auto sf = _sound_reader->get_frame(entry_point + frame);
auto from = sf->data();
@@ -331,7 +338,7 @@ DCPDecoder::pass_texts(
auto const asset = reel_asset->asset();
auto const entry_point = reel_asset->entry_point().get_value_or(0);
- if (_decode_reusable || !_dcp_content->reference_text(type)) {
+ if (_decode_reusable || !_can_reuse_text[type]) {
auto subs = asset->texts_during(
dcp::Time(entry_point + frame, vfr, vfr),
dcp::Time(entry_point + frame + 1, vfr, vfr),
@@ -522,10 +529,10 @@ DCPDecoder::set_decode_reusable(bool r)
_decode_reusable = r;
if (video) {
- video->set_ignore(_dcp_content->reference_video() && !_decode_reusable);
+ video->set_ignore(_can_reuse_video && !_decode_reusable);
}
if (audio) {
- audio->set_ignore(_dcp_content->reference_audio() && !_decode_reusable);
+ audio->set_ignore(_can_reuse_audio && !_decode_reusable);
}
}
diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h
index dc30526ff..01b85b8d1 100644
--- a/src/lib/dcp_decoder.h
+++ b/src/lib/dcp_decoder.h
@@ -116,4 +116,8 @@ private:
std::string _lazy_digest;
FontIDAllocator _font_id_allocator;
+
+ bool _can_reuse_video;
+ bool _can_reuse_audio;
+ EnumIndexedVector<bool, TextType> _can_reuse_text;
};
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 86f6e1082..316cf7e2b 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -758,10 +758,10 @@ Player::pass()
earliest_content->done = earliest_content->decoder->pass();
auto dcp = dynamic_pointer_cast<DCPContent>(earliest_content->content);
if (dcp && !_play_reusable) {
- if (dcp->reference_video()) {
+ if (dcp->can_reuse_video(film)) {
_next_video_time = dcp->end(film);
}
- if (dcp->reference_audio()) {
+ if (dcp->can_reuse_audio(film)) {
/* We are skipping some referenced DCP audio content, so we need to update _next_audio_time
to `hide' the fact that no audio was emitted during the referenced DCP (though
we need to behave as though it was).
diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc
index 67e707d52..2c4fac6e0 100644
--- a/src/lib/reel_writer.cc
+++ b/src/lib/reel_writer.cc
@@ -411,7 +411,7 @@ ReelWriter::finish(boost::filesystem::path output_dcp)
template <class Asset>
shared_ptr<Asset>
-maybe_reuse_asset(char const* log_name, list<ReusableReelAsset> const& refs, DCPTimePeriod period)
+maybe_reuse_asset(char const* log_name, list<ReusableReelAsset> const& refs, DCPTimePeriod period, boost::filesystem::path output_dir)
{
shared_ptr<Asset> reusable;
@@ -423,6 +423,13 @@ maybe_reuse_asset(char const* log_name, list<ReusableReelAsset> const& refs, DCP
}
if (asset && ref.period == period) {
reusable = asset;
+ if (ref.use == ReusableReelAsset::Use::COPY) {
+ auto mxf = reusable->asset_ref().asset();
+ DCPOMATIC_ASSERT(mxf->file());
+ auto const destination = output_dir / mxf->file()->filename();
+ copy_file(*mxf->file(), destination);
+ mxf->set_file(destination);
+ }
/* If we have a hash for this asset in the CPL, assume that it is correct */
if (reusable->hash()) {
reusable->asset_ref()->set_hash(reusable->hash().get());
@@ -484,7 +491,7 @@ ReelWriter::maybe_add_text(
} else {
/* We don't have a subtitle asset of our own; hopefully we have one to reuse */
- reel_asset = maybe_reuse_asset<dcp::ReelTextAsset>("subtitle", refs, _period);
+ reel_asset = maybe_reuse_asset<dcp::ReelTextAsset>("subtitle", refs, _period, _output_dir);
}
if (reel_asset) {
@@ -519,7 +526,7 @@ ReelWriter::create_reel_picture(shared_ptr<dcp::Reel> reel, list<ReusableReelAss
reel_asset = make_shared<dcp::ReelMonoPictureAsset>(_mpeg2_picture_asset, 0);
} else {
/* We don't have a picture asset of our own; hopefully we have one to reuse */
- reel_asset = maybe_reuse_asset<dcp::ReelPictureAsset>("picture", refs, _period);
+ reel_asset = maybe_reuse_asset<dcp::ReelPictureAsset>("picture", refs, _period, _output_dir);
}
Frame const period_duration = _period.duration().frames_round(film()->video_frame_rate());
@@ -552,7 +559,7 @@ ReelWriter::create_reel_sound(shared_ptr<dcp::Reel> reel, list<ReusableReelAsset
reel_asset = make_shared<dcp::ReelSoundAsset>(_sound_asset, 0);
} else {
/* We don't have a sound asset of our own; hopefully we have one to reuse */
- reel_asset = maybe_reuse_asset<dcp::ReelSoundAsset>("sound", refs, _period);
+ reel_asset = maybe_reuse_asset<dcp::ReelSoundAsset>("sound", refs, _period, _output_dir);
}
auto const period_duration = _period.duration().frames_round(film()->video_frame_rate());
diff --git a/src/lib/reusable_reel_asset.cc b/src/lib/reusable_reel_asset.cc
index ab14460e0..3894cc4e0 100644
--- a/src/lib/reusable_reel_asset.cc
+++ b/src/lib/reusable_reel_asset.cc
@@ -50,8 +50,12 @@ maybe_add_asset(list<ReusableReelAsset>& a, shared_ptr<dcp::ReelFileAsset> r, Fr
r->set_duration (r->actual_duration() - reel_trim_start - reel_trim_end);
if (r->actual_duration() > 0) {
a.push_back (
- ReusableReelAsset(r, DCPTimePeriod(from, from + DCPTime::from_frames(r->actual_duration(), ffr)), ReusableReelAsset::Use::REFERENCE)
- );
+ ReusableReelAsset(
+ r,
+ DCPTimePeriod(from, from + DCPTime::from_frames(r->actual_duration(), ffr)),
+ reference ? ReusableReelAsset::Use::REFERENCE : ReusableReelAsset::Use::COPY
+ )
+ );
}
}
@@ -69,7 +73,12 @@ get_reusable_reel_assets(shared_ptr<const Film> film, shared_ptr<const Playlist>
continue;
}
- if (!dcp->reference_video() && !dcp->reference_audio() && !dcp->reference_text(TextType::OPEN_SUBTITLE) && !dcp->reference_text(TextType::CLOSED_CAPTION)) {
+ if (
+ !dcp->can_reuse_video(film) &&
+ !dcp->can_reuse_audio(film) &&
+ !dcp->can_reuse_text(film, TextType::OPEN_SUBTITLE) &&
+ !dcp->can_reuse_text(film, TextType::CLOSED_CAPTION)
+ ) {
continue;
}
@@ -107,21 +116,21 @@ get_reusable_reel_assets(shared_ptr<const Film> film, shared_ptr<const Playlist>
Frame const reel_trim_end = min(reel_duration, max(int64_t(0), reel_duration - (offset_from_end - trim_end)));
auto const from = content->position() + std::max(DCPTime(), DCPTime::from_frames(offset_from_start - trim_start, frame_rate));
- if (dcp->reference_video()) {
- maybe_add_asset (reel_assets, reel->main_picture(), reel_trim_start, reel_trim_end, from, frame_rate);
+ if (dcp->can_reuse_video(film)) {
+ maybe_add_asset(reel_assets, reel->main_picture(), reel_trim_start, reel_trim_end, from, frame_rate, dcp->reference_video());
}
- if (dcp->reference_audio()) {
- maybe_add_asset (reel_assets, reel->main_sound(), reel_trim_start, reel_trim_end, from, frame_rate);
+ if (dcp->can_reuse_audio(film)) {
+ maybe_add_asset(reel_assets, reel->main_sound(), reel_trim_start, reel_trim_end, from, frame_rate, dcp->reference_audio());
}
- if (dcp->reference_text(TextType::OPEN_SUBTITLE) && reel->main_subtitle()) {
- maybe_add_asset (reel_assets, reel->main_subtitle(), reel_trim_start, reel_trim_end, from, frame_rate);
+ if (dcp->can_reuse_text(film, TextType::OPEN_SUBTITLE) && reel->main_subtitle()) {
+ maybe_add_asset(reel_assets, reel->main_subtitle(), reel_trim_start, reel_trim_end, from, frame_rate, dcp->reference_text(TextType::OPEN_SUBTITLE));
}
- if (dcp->reference_text(TextType::CLOSED_CAPTION)) {
+ if (dcp->can_reuse_text(film, TextType::CLOSED_CAPTION)) {
for (auto caption: reel->closed_captions()) {
- maybe_add_asset (reel_assets, caption, reel_trim_start, reel_trim_end, from, frame_rate);
+ maybe_add_asset(reel_assets, caption, reel_trim_start, reel_trim_end, from, frame_rate, dcp->reference_text(TextType::CLOSED_CAPTION));
}
}