X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fdcp_content.cc;h=c95aa3203ddbe1d4045183cea8edaa92e665f723;hb=6d95d9689831a74fade32038a808f206c03d8aa2;hp=2bbeba8c78c11dd03cae3028f773db72854cec32;hpb=17847dc1bc68516bda8ca92e8561064dde896530;p=dcpomatic.git diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index 2bbeba8c7..c95aa3203 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2022 Carl Hetherington + Copyright (C) 2014-2023 Carl Hetherington This file is part of DCP-o-matic. @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ using std::cout; using std::dynamic_pointer_cast; +using std::exception; using std::function; using std::list; using std::make_shared; @@ -105,7 +107,8 @@ DCPContent::DCPContent (cxml::ConstNodePtr node, int version) node->optional_number_child("AudioLength").get_value_or ( video->length() * node->number_child("AudioFrameRate") / video_frame_rate().get() ), - AudioMapping (node->node_child ("AudioMapping"), version) + AudioMapping(node->node_child("AudioMapping"), version), + 24 ) ); } @@ -158,6 +161,8 @@ DCPContent::DCPContent (cxml::ConstNodePtr node, int version) for (auto i: node->node_children("ContentVersion")) { _content_versions.push_back (i->content()); } + + _active_audio_channels = node->optional_number_child("ActiveAudioChannels"); } void @@ -190,17 +195,23 @@ DCPContent::read_directory (boost::filesystem::path p) void DCPContent::read_sub_directory (boost::filesystem::path p) { + using namespace boost::filesystem; + LOG_GENERAL ("DCPContent::read_sub_directory reads %1", p.string()); - for (auto i: boost::filesystem::directory_iterator(p)) { - if (boost::filesystem::is_regular_file(i.path())) { - LOG_GENERAL ("Inside there's regular file %1", i.path().string()); - add_path (i.path()); - } else if (boost::filesystem::is_directory(i.path()) && i.path().filename() != ".AppleDouble") { - LOG_GENERAL ("Inside there's directory %1", i.path().string()); - read_sub_directory (i.path()); - } else { - LOG_GENERAL("Ignoring %1 from inside: status is %2", i.path().string(), static_cast(boost::filesystem::status(i.path()).type())); + try { + for (auto i: directory_iterator(p)) { + if (is_regular_file(i.path())) { + LOG_GENERAL ("Inside there's regular file %1", i.path().string()); + add_path (i.path()); + } else if (is_directory(i.path()) && i.path().filename() != ".AppleDouble") { + LOG_GENERAL ("Inside there's directory %1", i.path().string()); + read_sub_directory (i.path()); + } else { + LOG_GENERAL("Ignoring %1 from inside: status is %2", i.path().string(), static_cast(status(i.path()).type())); + } } + } catch (exception& e) { + LOG_GENERAL("Failed to iterate over %1: %2", p.string(), e.what()); } } @@ -212,6 +223,11 @@ DCPContent::examine (shared_ptr film, shared_ptr job) bool const needed_kdm = needs_kdm (); string const old_name = name (); + ContentChangeSignalDespatcher::instance()->suspend(); + dcp::ScopeGuard sg = []() { + ContentChangeSignalDespatcher::instance()->resume(); + }; + ContentChangeSignaller cc_texts (this, DCPContentProperty::TEXTS); ContentChangeSignaller cc_assets (this, DCPContentProperty::NEEDS_ASSETS); ContentChangeSignaller cc_kdm (this, DCPContentProperty::NEEDS_KDM); @@ -229,7 +245,7 @@ DCPContent::examine (shared_ptr film, shared_ptr job) boost::mutex::scoped_lock lm (_mutex); video = make_shared(this); } - video->take_from_examiner (examiner); + video->take_from_examiner(film, examiner); set_default_colour_conversion (); } @@ -238,11 +254,13 @@ DCPContent::examine (shared_ptr film, shared_ptr job) boost::mutex::scoped_lock lm (_mutex); audio = make_shared(this); } - auto as = make_shared(examiner->audio_frame_rate(), examiner->audio_length(), examiner->audio_channels()); + auto as = make_shared(examiner->audio_frame_rate(), examiner->audio_length(), examiner->audio_channels(), 24); audio->set_stream (as); auto m = as->mapping (); m.make_default (film ? film->audio_processor() : 0); as->set_mapping (m); + + _active_audio_channels = examiner->active_audio_channels(); } if (examiner->has_atmos()) { @@ -257,18 +275,19 @@ DCPContent::examine (shared_ptr film, shared_ptr job) atmos->set_length (examiner->atmos_length()); } - list> new_text; + vector> new_text; for (int i = 0; i < examiner->text_count(TextType::OPEN_SUBTITLE); ++i) { auto c = make_shared(this, TextType::OPEN_SUBTITLE, TextType::OPEN_SUBTITLE); c->set_language (examiner->open_subtitle_language()); - add_fonts_from_examiner(c, examiner->fonts()); + examiner->add_fonts(c); new_text.push_back (c); } for (int i = 0; i < examiner->text_count(TextType::CLOSED_CAPTION); ++i) { auto c = make_shared(this, TextType::CLOSED_CAPTION, TextType::CLOSED_CAPTION); c->set_dcp_track (examiner->dcp_text_track(i)); + examiner->add_fonts(c); new_text.push_back (c); } @@ -328,83 +347,90 @@ DCPContent::technical_summary () const return s; } + void -DCPContent::as_xml (xmlpp::Node* node, bool with_paths) const +DCPContent::as_xml(xmlpp::Element* element, bool with_paths) const { - node->add_child("Type")->add_child_text ("DCP"); + cxml::add_text_child(element, "Type", "DCP"); - Content::as_xml (node, with_paths); + Content::as_xml(element, with_paths); if (video) { - video->as_xml (node); + video->as_xml(element); } if (audio) { - audio->as_xml (node); - node->add_child("AudioFrameRate")->add_child_text (raw_convert (audio->stream()->frame_rate())); - node->add_child("AudioLength")->add_child_text (raw_convert (audio->stream()->length())); - audio->stream()->mapping().as_xml (node->add_child("AudioMapping")); + audio->as_xml(element); + cxml::add_text_child(element, "AudioFrameRate", raw_convert(audio->stream()->frame_rate())); + cxml::add_text_child(element, "AudioLength", raw_convert(audio->stream()->length())); + audio->stream()->mapping().as_xml(cxml::add_child(element, "AudioMapping")); } for (auto i: text) { - i->as_xml (node); + i->as_xml(element); } if (atmos) { - atmos->as_xml (node); + atmos->as_xml(element); } boost::mutex::scoped_lock lm (_mutex); - node->add_child("Name")->add_child_text (_name); - node->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0"); - node->add_child("NeedsAssets")->add_child_text (_needs_assets ? "1" : "0"); + + cxml::add_text_child(element, "Name", _name); + cxml::add_text_child(element, "Encrypted", _encrypted ? "1" : "0"); + cxml::add_text_child(element, "NeedsAssets", _needs_assets ? "1" : "0"); if (_kdm) { - node->add_child("KDM")->add_child_text (_kdm->as_xml ()); + cxml::add_text_child(element, "KDM", _kdm->as_xml()); } - node->add_child("KDMValid")->add_child_text (_kdm_valid ? "1" : "0"); - node->add_child("ReferenceVideo")->add_child_text (_reference_video ? "1" : "0"); - node->add_child("ReferenceAudio")->add_child_text (_reference_audio ? "1" : "0"); - node->add_child("ReferenceOpenSubtitle")->add_child_text(_reference_text[TextType::OPEN_SUBTITLE] ? "1" : "0"); - node->add_child("ReferenceClosedCaption")->add_child_text(_reference_text[TextType::CLOSED_CAPTION] ? "1" : "0"); + cxml::add_text_child(element, "KDMValid", _kdm_valid ? "1" : "0"); + cxml::add_text_child(element, "ReferenceVideo", _reference_video ? "1" : "0"); + cxml::add_text_child(element, "ReferenceAudio", _reference_audio ? "1" : "0"); + cxml::add_text_child(element, "ReferenceOpenSubtitle", _reference_text[TextType::OPEN_SUBTITLE] ? "1" : "0"); + cxml::add_text_child(element, "ReferenceClosedCaption", _reference_text[TextType::CLOSED_CAPTION] ? "1" : "0"); if (_standard) { switch (_standard.get ()) { case dcp::Standard::INTEROP: - node->add_child("Standard")->add_child_text ("Interop"); + cxml::add_text_child(element, "Standard", "Interop"); break; case dcp::Standard::SMPTE: - node->add_child("Standard")->add_child_text ("SMPTE"); + cxml::add_text_child(element, "Standard", "SMPTE"); break; default: DCPOMATIC_ASSERT (false); } } - node->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0"); + cxml::add_text_child(element, "ThreeD", _three_d ? "1" : "0"); if (_content_kind) { - node->add_child("ContentKind")->add_child_text(_content_kind->name()); + cxml::add_text_child(element, "ContentKind", _content_kind->name()); } if (_cpl) { - node->add_child("CPL")->add_child_text (_cpl.get ()); + cxml::add_text_child(element, "CPL", _cpl.get()); } for (auto i: _reel_lengths) { - node->add_child("ReelLength")->add_child_text (raw_convert (i)); + cxml::add_text_child(element, "ReelLength", raw_convert(i)); } for (auto const& i: _markers) { - auto marker = node->add_child("Marker"); + auto marker = cxml::add_child(element, "Marker"); marker->set_attribute("type", dcp::marker_to_string(i.first)); marker->add_child_text(raw_convert(i.second.get())); } for (auto i: _ratings) { - auto rating = node->add_child("Rating"); + auto rating = cxml::add_child(element, "Rating"); i.as_xml (rating); } for (auto i: _content_versions) { - node->add_child("ContentVersion")->add_child_text(i); + cxml::add_text_child(element, "ContentVersion", i); + } + + if (_active_audio_channels) { + cxml::add_text_child(element, "ActiveAudioChannels", raw_convert(*_active_audio_channels)); } } + DCPTime DCPContent::full_length (shared_ptr film) const { @@ -437,6 +463,8 @@ DCPContent::identifier () const s += i->identifier () + " "; } + boost::mutex::scoped_lock lm(_mutex); + s += string (_reference_video ? "1" : "0"); for (auto text: _reference_text) { s += string(text ? "1" : "0"); @@ -641,11 +669,6 @@ DCPContent::can_reference (shared_ptr film, function c) -{ - return static_cast(c->video) && c->video->use(); -} bool DCPContent::can_reference_video (shared_ptr film, string& why_not) const @@ -671,14 +694,16 @@ DCPContent::can_reference_video (shared_ptr film, string& why_not) c } /// TRANSLATORS: this string will follow "Cannot reference this DCP: " - return can_reference (film, bind (&check_video, _1), _("it overlaps other video content; remove the other content."), why_not); + return can_reference( + film, + [](shared_ptr c) { + return static_cast(c->video) && c->video->use(); + }, + _("it overlaps other video content; remove the other content."), + why_not + ); } -static -bool check_audio (shared_ptr c) -{ - return static_cast(c->audio) && !c->audio->mapping().mapped_output_channels().empty(); -} bool DCPContent::can_reference_audio (shared_ptr film, string& why_not) const @@ -697,23 +722,16 @@ DCPContent::can_reference_audio (shared_ptr film, string& why_not) c return false; } - for (auto i: decoder->reels()) { - if (!i->main_sound()) { - /// TRANSLATORS: this string will follow "Cannot reference this DCP: " - why_not = _("it does not have sound in all its reels."); - return false; - } - } - /// TRANSLATORS: this string will follow "Cannot reference this DCP: " - return can_reference (film, bind (&check_audio, _1), _("it overlaps other audio content; remove the other content."), why_not); + return can_reference( + film, [](shared_ptr c) { + return static_cast(c->audio) && !c->audio->mapping().mapped_output_channels().empty(); + }, + _("it overlaps other audio content; remove the other content."), + why_not + ); } -static -bool check_text (shared_ptr c) -{ - return !c->text.empty(); -} bool DCPContent::can_reference_text (shared_ptr film, TextType type, string& why_not) const @@ -724,6 +742,9 @@ DCPContent::can_reference_text (shared_ptr film, TextType type, stri } catch (dcp::ReadError &) { /* We couldn't read the DCP, so it's probably missing */ return false; + } catch (DCPError &) { + /* We couldn't read the DCP, so it's probably missing */ + return false; } catch (dcp::KDMDecryptionError &) { /* We have an incorrect KDM */ return false; @@ -731,22 +752,13 @@ DCPContent::can_reference_text (shared_ptr film, TextType type, stri for (auto i: decoder->reels()) { if (type == TextType::OPEN_SUBTITLE) { - if (!i->main_subtitle()) { - /// TRANSLATORS: this string will follow "Cannot reference this DCP: " - why_not = _("it does not have open subtitles in all its reels."); - return false; - } else if (i->main_subtitle()->entry_point().get_value_or(0) != 0) { + if (i->main_subtitle() && i->main_subtitle()->entry_point().get_value_or(0) != 0) { /// TRANSLATORS: this string will follow "Cannot reference this DCP: " why_not = _("one of its subtitle reels has a non-zero entry point so it must be re-written."); return false; } } if (type == TextType::CLOSED_CAPTION) { - if (i->closed_captions().empty()) { - /// TRANSLATORS: this string will follow "Cannot reference this DCP: " - why_not = _("it does not have closed captions in all its reels."); - return false; - } for (auto j: i->closed_captions()) { if (j->entry_point().get_value_or(0) != 0) { /// TRANSLATORS: this string will follow "Cannot reference this DCP: " @@ -764,7 +776,14 @@ DCPContent::can_reference_text (shared_ptr film, TextType type, stri } /// TRANSLATORS: this string will follow "Cannot reference this DCP: " - return can_reference (film, bind (&check_text, _1), _("it overlaps other text content; remove the other content."), why_not); + return can_reference( + film, + [type](shared_ptr c) { + return std::find_if(c->text.begin(), c->text.end(), [type](shared_ptr t) { return t->type() == type; }) != c->text.end(); + }, + _("they overlap other text content; remove the other content."), + why_not + ); } void @@ -775,6 +794,13 @@ DCPContent::take_settings_from (shared_ptr c) return; } + if (this == dc.get()) { + return; + } + + boost::mutex::scoped_lock lm(_mutex); + boost::mutex::scoped_lock lm2(dc->_mutex); + _reference_video = dc->_reference_video; _reference_audio = dc->_reference_audio; _reference_text = dc->_reference_text; @@ -806,7 +832,7 @@ DCPContent::kdm_timing_window_valid () const Resolution DCPContent::resolution () const { - if (video->size().width > 2048 || video->size().height > 1080) { + if (video->size() && (video->size()->width > 2048 || video->size()->height > 1080)) { return Resolution::FOUR_K; } @@ -814,32 +840,6 @@ DCPContent::resolution () const } -void -add_fonts_from_examiner(shared_ptr text, vector>> const & all_fonts) -{ - int reel_number = 0; - for (auto reel_fonts: all_fonts) { - for (auto font: reel_fonts) { - /* Each reel could have its own font with the same ID, so we disambiguate them here - * by prepending the reel number. We do the same disambiguation when emitting the - * subtitles in the DCP decoder. - */ - font->set_id(id_for_font_in_reel(font->id(), reel_number)); - text->add_font(font); - } - ++reel_number; - } - -} - - -string -id_for_font_in_reel(string id, int reel) -{ - return String::compose("%1_%2", reel, id); -} - - void DCPContent::check_font_ids() { @@ -848,6 +848,15 @@ DCPContent::check_font_ids() } DCPExaminer examiner(shared_from_this(), true); - add_fonts_from_examiner(text.front(), examiner.fonts()); + examiner.add_fonts(text.front()); +} + + +int +DCPContent::active_audio_channels() const +{ + return _active_audio_channels.get_value_or( + (audio && audio->stream()) ? audio->stream()->channels() : 0 + ); }