X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=tools%2Fdcpinfo.cc;h=e812afe40775161afc2c1370a951bb4b56ea2b92;hb=816365d20e0c6ef37b6bf499a42a0d3ecad22c05;hp=92f12a3c1da224ca49a36e8296d727318d7687dc;hpb=ba5915461f1622715a69fa25579e5e27e64fb079;p=libdcp.git diff --git a/tools/dcpinfo.cc b/tools/dcpinfo.cc index 92f12a3c..e812afe4 100644 --- a/tools/dcpinfo.cc +++ b/tools/dcpinfo.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2018 Carl Hetherington + Copyright (C) 2012-2021 Carl Hetherington This file is part of libdcp. @@ -31,44 +31,52 @@ files in the program, then also delete it here. */ + +#include "common.h" +#include "compose.hpp" +#include "cpl.h" #include "dcp.h" +#include "decrypted_kdm.h" +#include "encrypted_kdm.h" #include "exceptions.h" -#include "reel.h" -#include "sound_asset.h" +#include "filesystem.h" +#include "interop_subtitle_asset.h" +#include "mono_picture_asset.h" #include "picture_asset.h" -#include "subtitle_asset.h" +#include "reel.h" #include "reel_picture_asset.h" #include "reel_sound_asset.h" #include "reel_subtitle_asset.h" -#include "subtitle_string.h" -#include "subtitle_image.h" -#include "interop_subtitle_asset.h" #include "smpte_subtitle_asset.h" -#include "mono_picture_asset.h" -#include "encrypted_kdm.h" -#include "decrypted_kdm.h" -#include "cpl.h" -#include "common.h" +#include "sound_asset.h" +#include "subtitle_asset.h" +#include "subtitle_image.h" +#include "subtitle_string.h" #include #include -#include +#include #include #include +#include #include -using std::string; + using std::cerr; using std::cout; +using std::dynamic_pointer_cast; +using std::exception; using std::list; -using std::pair; -using std::min; using std::max; -using std::exception; -using boost::shared_ptr; -using boost::dynamic_pointer_cast; +using std::min; +using std::pair; +using std::shared_ptr; +using std::string; +using std::stringstream; +using std::vector; using boost::optional; using namespace dcp; + static void help (string n) { @@ -76,10 +84,18 @@ help (string n) << " -s, --subtitles list all subtitles\n" << " -p, --picture analyse picture\n" << " -d, --decompress decompress picture when analysing (this is slow)\n" - << " -k, --keep-going carry on in the event of errors, if possible\n" + << " -o, --only only output certain pieces of information; see below.\n" << " --kdm KDM to decrypt DCP\n" << " --private-key private key for the certificate that the KDM is targeted at\n" << " --ignore-missing-assets ignore missing asset files\n"; + + cerr << "--only takes a comma-separated list of strings, one or more of:\n" + " dcp-path DCP path\n" + " cpl-name-id CPL name and ID\n" + " picture picture information\n" + " sound sound information\n" + " subtitle picture information\n" + " total-time total DCP time\n"; } static double @@ -88,127 +104,197 @@ mbits_per_second (int size, Fraction frame_rate) return size * 8 * frame_rate.as_float() / 1e6; } +#define OUTPUT_DCP_PATH(...) maybe_output(only, "dcp-path", String::compose(__VA_ARGS__)); +#define OUTPUT_CPL_NAME_ID(...) maybe_output(only, "cpl-name-id", String::compose(__VA_ARGS__)); +#define OUTPUT_PICTURE(...) maybe_output(only, "picture", String::compose(__VA_ARGS__)); +#define OUTPUT_PICTURE_NC(x) maybe_output(only, "picture", (x)); +#define SHOULD_PICTURE should_output(only, "picture") +#define OUTPUT_SOUND(...) maybe_output(only, "sound", String::compose(__VA_ARGS__)); +#define OUTPUT_SOUND_NC(x) maybe_output(only, "sound", (x)); +#define OUTPUT_SUBTITLE(...) maybe_output(only, "subtitle", String::compose(__VA_ARGS__)); +#define OUTPUT_SUBTITLE_NC(x) maybe_output(only, "subtitle", (x)); +#define OUTPUT_TOTAL_TIME(...) maybe_output(only, "total-time", String::compose(__VA_ARGS__)); + +static bool +should_output(vector const& only, string t) +{ + return only.empty() || find(only.begin(), only.end(), t) != only.end(); +} + static void -main_picture (shared_ptr reel, bool analyse, bool decompress) +maybe_output(vector const& only, string t, string s) { - if (!reel->main_picture()) { - return; + if (should_output(only, t)) { + cout << s; + } +} + +static +dcp::Time +main_picture (vector const& only, shared_ptr reel, bool analyse, bool decompress) +{ + shared_ptr mp = reel->main_picture (); + if (!mp) { + return dcp::Time(); + } + + OUTPUT_PICTURE(" Picture ID: %1", mp->id()); + if (mp->entry_point()) { + OUTPUT_PICTURE(" entry %1", *mp->entry_point()); + } + if (mp->duration()) { + OUTPUT_PICTURE( + " duration %1 (%2) intrinsic %3", + *mp->duration(), + dcp::Time(*mp->duration(), mp->frame_rate().as_float(), mp->frame_rate().as_float()).as_string(dcp::Standard::SMPTE), + mp->intrinsic_duration() + ); + } else { + OUTPUT_PICTURE(" intrinsic duration %1", mp->intrinsic_duration()); } - cout << " Picture ID: " << reel->main_picture()->id() - << " entry " << reel->main_picture()->entry_point() - << " duration " << reel->main_picture()->duration() - << " intrinsic " << reel->main_picture()->intrinsic_duration(); - - if (reel->main_picture()->asset_ref().resolved()) { - if (reel->main_picture()->asset()) { - cout << "\n Picture: " - << reel->main_picture()->asset()->size().width - << "x" - << reel->main_picture()->asset()->size().height << "\n"; + if (mp->asset_ref().resolved()) { + if (mp->asset()) { + OUTPUT_PICTURE("\n Picture: %1x%2\n", mp->asset()->size().width, mp->asset()->size().height); } - shared_ptr ma = dynamic_pointer_cast(reel->main_picture()->asset()); + shared_ptr ma = dynamic_pointer_cast(mp->asset()); if (analyse && ma) { shared_ptr reader = ma->start_read (); pair j2k_size_range (INT_MAX, 0); for (int64_t i = 0; i < ma->intrinsic_duration(); ++i) { shared_ptr frame = reader->get_frame (i); - printf("Frame %" PRId64 " J2K size %7d", i, frame->j2k_size()); - j2k_size_range.first = min(j2k_size_range.first, frame->j2k_size()); - j2k_size_range.second = max(j2k_size_range.second, frame->j2k_size()); + if (SHOULD_PICTURE) { + printf("Frame %" PRId64 " J2K size %7d", i, frame->size()); + } + j2k_size_range.first = min(j2k_size_range.first, frame->size()); + j2k_size_range.second = max(j2k_size_range.second, frame->size()); if (decompress) { try { frame->xyz_image(); - printf(" decrypted OK"); + if (SHOULD_PICTURE) { + printf(" decrypted OK"); + } } catch (exception& e) { - printf(" decryption FAILED"); + if (SHOULD_PICTURE) { + printf(" decryption FAILED"); + } } } - printf("\n"); + if (SHOULD_PICTURE) { + printf("\n"); + } } - printf( - "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n", - j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()), - j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate()) - ); + if (SHOULD_PICTURE) { + printf( + "J2K size ranges from %d (%.1f Mbit/s) to %d (%.1f Mbit/s)\n", + j2k_size_range.first, mbits_per_second(j2k_size_range.first, ma->frame_rate()), + j2k_size_range.second, mbits_per_second(j2k_size_range.second, ma->frame_rate()) + ); + } } } else { - cout << " - not present in this DCP.\n"; + OUTPUT_PICTURE_NC(" - not present in this DCP.\n"); } + + return dcp::Time ( + mp->duration().get_value_or(mp->intrinsic_duration()), + mp->frame_rate().as_float(), + mp->frame_rate().as_float() + ); } -static void -main_sound (shared_ptr reel) +static +void +main_sound (vector const& only, shared_ptr reel) { - if (reel->main_sound()) { - cout << " Sound ID: " << reel->main_sound()->id() - << " entry " << reel->main_picture()->entry_point() - << " duration " << reel->main_picture()->duration() - << " intrinsic " << reel->main_picture()->intrinsic_duration(); - if (reel->main_sound()->asset_ref().resolved()) { - if (reel->main_sound()->asset()) { - cout << "\n Sound: " - << reel->main_sound()->asset()->channels() - << " channels at " - << reel->main_sound()->asset()->sampling_rate() << "Hz\n"; - } - } else { - cout << " - not present in this DCP.\n"; + shared_ptr ms = reel->main_sound (); + if (!ms) { + return; + } + + OUTPUT_SOUND(" Sound ID: %1", ms->id()); + if (ms->entry_point()) { + OUTPUT_SOUND(" entry %1", *ms->entry_point()); + } + if (ms->duration()) { + OUTPUT_SOUND(" duration %1 intrinsic %2", *ms->duration(), ms->intrinsic_duration()); + } else { + OUTPUT_SOUND(" intrinsic duration %1", ms->intrinsic_duration()); + } + + if (ms->asset_ref().resolved()) { + if (ms->asset()) { + OUTPUT_SOUND( + "\n Sound: %1 channels at %2Hz\n", + ms->asset()->channels(), + ms->asset()->sampling_rate() + ); } + } else { + OUTPUT_SOUND_NC(" - not present in this DCP.\n"); } } -static void -main_subtitle (shared_ptr reel, bool list_subtitles) +static +void +main_subtitle (vector const& only, shared_ptr reel, bool list_subtitles) { - if (!reel->main_subtitle()) { + shared_ptr ms = reel->main_subtitle (); + if (!ms) { return; } - cout << " Subtitle ID: " << reel->main_subtitle()->id(); + OUTPUT_SUBTITLE(" Subtitle ID: %1", ms->id()); - if (reel->main_subtitle()->asset_ref().resolved()) { - list > subs = reel->main_subtitle()->asset()->subtitles (); - cout << "\n Subtitle: " << subs.size() << " subtitles"; - shared_ptr iop = dynamic_pointer_cast (reel->main_subtitle()->asset()); + if (ms->asset_ref().resolved()) { + auto subs = ms->asset()->subtitles (); + OUTPUT_SUBTITLE("\n Subtitle: %1 subtitles", subs.size()); + shared_ptr iop = dynamic_pointer_cast (ms->asset()); if (iop) { - cout << " in " << iop->language() << "\n"; + OUTPUT_SUBTITLE(" in %1\n", iop->language()); } - shared_ptr smpte = dynamic_pointer_cast (reel->main_subtitle()->asset()); + shared_ptr smpte = dynamic_pointer_cast (ms->asset()); if (smpte && smpte->language ()) { - cout << " in " << smpte->language().get() << "\n"; + OUTPUT_SUBTITLE(" in %1\n", smpte->language().get()); } if (list_subtitles) { - BOOST_FOREACH (shared_ptr k, subs) { - shared_ptr ks = dynamic_pointer_cast (k); + for (auto k: subs) { + auto ks = dynamic_pointer_cast(k); if (ks) { - cout << *ks << "\n"; + stringstream s; + s << *ks; + OUTPUT_SUBTITLE("%1\n", s.str()); } - shared_ptr is = dynamic_pointer_cast (k); + auto is = dynamic_pointer_cast(k); if (is) { - cout << *is << "\n"; + stringstream s; + s << *is; + OUTPUT_SUBTITLE("%1\n", s.str()); } } } } else { - cout << " - not present in this DCP.\n"; + OUTPUT_SUBTITLE_NC(" - not present in this DCP.\n"); } } + int main (int argc, char* argv[]) { + dcp::init (); + bool subtitles = false; - bool keep_going = false; bool picture = false; bool decompress = false; bool ignore_missing_assets = false; optional kdm; optional private_key; + optional only_string; int option_index = 0; while (true) { @@ -216,16 +302,16 @@ main (int argc, char* argv[]) { "version", no_argument, 0, 'v' }, { "help", no_argument, 0, 'h' }, { "subtitles", no_argument, 0, 's' }, - { "keep-going", no_argument, 0, 'k' }, { "picture", no_argument, 0, 'p' }, { "decompress", no_argument, 0, 'd' }, + { "only", required_argument, 0, 'o' }, { "ignore-missing-assets", no_argument, 0, 'A' }, { "kdm", required_argument, 0, 'B' }, { "private-key", required_argument, 0, 'C' }, { 0, 0, 0, 0 } }; - int c = getopt_long (argc, argv, "vhskpdAB:C:", long_options, &option_index); + int c = getopt_long (argc, argv, "vhspdo:AB:C:", long_options, &option_index); if (c == -1) { break; @@ -233,7 +319,7 @@ main (int argc, char* argv[]) switch (c) { case 'v': - cout << "libdcp version " << LIBDCP_VERSION << "\n"; + cout << "libdcp version " << dcp::version << "\n"; exit (EXIT_SUCCESS); case 'h': help (argv[0]); @@ -241,15 +327,15 @@ main (int argc, char* argv[]) case 's': subtitles = true; break; - case 'k': - keep_going = true; - break; case 'p': picture = true; break; case 'd': decompress = true; break; + case 'o': + only_string = optarg; + break; case 'A': ignore_missing_assets = true; break; @@ -267,25 +353,30 @@ main (int argc, char* argv[]) exit (EXIT_FAILURE); } - if (!boost::filesystem::exists (argv[optind])) { + if (!filesystem::exists(argv[optind])) { cerr << argv[0] << ": DCP or CPL " << argv[optind] << " not found.\n"; exit (EXIT_FAILURE); } - list > cpls; + vector only; + if (only_string) { + only = boost::split(only, *only_string, boost::is_any_of(",")); + } + + vector> cpls; if (boost::filesystem::is_directory(argv[optind])) { DCP* dcp = 0; - DCP::ReadErrors errors; + vector notes; try { dcp = new DCP (argv[optind]); - dcp->read (keep_going, &errors); + dcp->read (¬es); if (kdm && private_key) { dcp->add(DecryptedKDM(EncryptedKDM(file_to_string(*kdm)), file_to_string(*private_key))); } } catch (FileError& e) { cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n"; exit (EXIT_FAILURE); - } catch (DCPReadError& e) { + } catch (ReadError& e) { cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n"; exit (EXIT_FAILURE); } catch (KDMDecryptionError& e) { @@ -293,60 +384,51 @@ main (int argc, char* argv[]) exit (EXIT_FAILURE); } - cout << "DCP: " << boost::filesystem::path(argv[optind]).string() << "\n"; + OUTPUT_DCP_PATH("DCP: %1\n", boost::filesystem::path(argv[optind]).string()); - dcp::filter_errors (errors, ignore_missing_assets); - for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) { - cerr << "Error: " << (*i)->what() << "\n"; + dcp::filter_notes (notes, ignore_missing_assets); + for (auto i: notes) { + cerr << "Error: " << note_to_string(i) << "\n"; } cpls = dcp->cpls (); } else { cpls.push_back (shared_ptr(new CPL(boost::filesystem::path(argv[optind])))); - keep_going = true; ignore_missing_assets = true; } - BOOST_FOREACH (shared_ptr i, cpls) { - cout << " CPL: " << i->annotation_text() << "\n"; + dcp::Time total_time; + + for (auto i: cpls) { + OUTPUT_CPL_NAME_ID(" CPL: %1 %2\n", i->annotation_text().get_value_or(""), i->id()); int R = 1; - BOOST_FOREACH (shared_ptr j, i->reels()) { - cout << " Reel " << R << "\n"; + for (auto j: i->reels()) { + if (should_output(only, "picture") || should_output(only, "sound") || should_output(only, "subtitle")) { + cout << " Reel " << R << "\n"; + } try { - main_picture (j, picture, decompress); + total_time += main_picture(only, j, picture, decompress); } catch (UnresolvedRefError& e) { - if (keep_going) { - if (!ignore_missing_assets) { - cerr << e.what() << " (for main picture)\n"; - } - } else { - throw; + if (!ignore_missing_assets) { + cerr << e.what() << " (for main picture)\n"; } } try { - main_sound (j); + main_sound(only, j); } catch (UnresolvedRefError& e) { - if (keep_going) { - if (!ignore_missing_assets) { - cerr << e.what() << " (for main sound)\n"; - } - } else { - throw; + if (!ignore_missing_assets) { + cerr << e.what() << " (for main sound)\n"; } } try { - main_subtitle (j, subtitles); + main_subtitle (only, j, subtitles); } catch (UnresolvedRefError& e) { - if (keep_going) { - if (!ignore_missing_assets) { - cerr << e.what() << " (for main subtitle)\n"; - } - } else { - throw; + if (!ignore_missing_assets) { + cerr << e.what() << " (for main subtitle)\n"; } } @@ -354,5 +436,7 @@ main (int argc, char* argv[]) } } + OUTPUT_TOTAL_TIME("Total: %1\n", total_time.as_string(dcp::Standard::SMPTE)); + return 0; }