Merge master.
authorCarl Hetherington <cth@carlh.net>
Wed, 14 May 2014 08:33:23 +0000 (09:33 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 14 May 2014 08:33:23 +0000 (09:33 +0100)
200 files changed:
.gitignore
.gitmodules [deleted file]
Doxyfile
asdcplib/src/wscript
doc/mainpage.txt
examples/make_dcp.cc
examples/read_dcp.cc [new file with mode: 0644]
examples/wscript
libdcp-1.0.pc.in [new file with mode: 0644]
libdcp.pc.in [deleted file]
run/examples/read_dcp [new file with mode: 0755]
run/tests
src/argb_frame.cc
src/argb_frame.h
src/asset.cc
src/asset.h
src/certificates.cc
src/certificates.h
src/colour_matrix.cc
src/colour_matrix.h
src/content.cc [new file with mode: 0644]
src/content.h [new file with mode: 0644]
src/cpl.cc
src/cpl.h
src/dcp.cc
src/dcp.h
src/dcp_time.cc
src/dcp_time.h
src/decrypted_kdm.cc [new file with mode: 0644]
src/decrypted_kdm.h [new file with mode: 0644]
src/decrypted_kdm_key.cc [new file with mode: 0644]
src/decrypted_kdm_key.h [new file with mode: 0644]
src/encrypted_kdm.cc [new file with mode: 0644]
src/encrypted_kdm.h [new file with mode: 0644]
src/exceptions.cc
src/exceptions.h
src/file.cc [new file with mode: 0644]
src/file.h [new file with mode: 0644]
src/font.cc [new file with mode: 0644]
src/font.h [new file with mode: 0644]
src/gamma_lut.cc
src/gamma_lut.h
src/image.cc
src/image.h
src/kdm.cc [deleted file]
src/kdm.h [deleted file]
src/key.cc
src/key.h
src/load_font.cc [new file with mode: 0644]
src/load_font.h [new file with mode: 0644]
src/local_time.cc [new file with mode: 0644]
src/local_time.h [new file with mode: 0644]
src/lut.h [deleted file]
src/lut_cache.h
src/metadata.cc
src/metadata.h
src/mono_picture_asset.cc [deleted file]
src/mono_picture_asset.h [deleted file]
src/mono_picture_asset_writer.cc [deleted file]
src/mono_picture_asset_writer.h [deleted file]
src/mono_picture_frame.cc
src/mono_picture_frame.h
src/mono_picture_mxf.cc [new file with mode: 0644]
src/mono_picture_mxf.h [new file with mode: 0644]
src/mono_picture_mxf_writer.cc [new file with mode: 0644]
src/mono_picture_mxf_writer.h [new file with mode: 0644]
src/mxf.cc [new file with mode: 0644]
src/mxf.h [new file with mode: 0644]
src/mxf_asset.cc [deleted file]
src/mxf_asset.h [deleted file]
src/mxf_writer.cc [new file with mode: 0644]
src/mxf_writer.h [new file with mode: 0644]
src/object.cc [new file with mode: 0644]
src/object.h [new file with mode: 0644]
src/parse/asset_map.cc [deleted file]
src/parse/asset_map.h [deleted file]
src/parse/cpl.cc [deleted file]
src/parse/cpl.h [deleted file]
src/parse/pkl.cc [deleted file]
src/parse/pkl.h [deleted file]
src/parse/subtitle.cc [deleted file]
src/parse/subtitle.h [deleted file]
src/picture_asset.cc [deleted file]
src/picture_asset.h [deleted file]
src/picture_asset_writer.cc [deleted file]
src/picture_asset_writer.h [deleted file]
src/picture_asset_writer_common.cc [deleted file]
src/picture_mxf.cc [new file with mode: 0644]
src/picture_mxf.h [new file with mode: 0644]
src/picture_mxf_writer.cc [new file with mode: 0644]
src/picture_mxf_writer.h [new file with mode: 0644]
src/picture_mxf_writer_common.cc [new file with mode: 0644]
src/raw_convert.h
src/rec709_linearised_gamma_lut.cc [deleted file]
src/rec709_linearised_gamma_lut.h [deleted file]
src/reel.cc
src/reel.h
src/reel_asset.cc [new file with mode: 0644]
src/reel_asset.h [new file with mode: 0644]
src/reel_mono_picture_asset.cc [new file with mode: 0644]
src/reel_mono_picture_asset.h [new file with mode: 0644]
src/reel_picture_asset.cc [new file with mode: 0644]
src/reel_picture_asset.h [new file with mode: 0644]
src/reel_sound_asset.cc [new file with mode: 0644]
src/reel_sound_asset.h [new file with mode: 0644]
src/reel_stereo_picture_asset.cc [new file with mode: 0644]
src/reel_stereo_picture_asset.h [new file with mode: 0644]
src/reel_subtitle_asset.cc [new file with mode: 0644]
src/reel_subtitle_asset.h [new file with mode: 0644]
src/ref.h [new file with mode: 0644]
src/rgb_xyz.cc
src/rgb_xyz.h
src/signer.cc
src/signer.h
src/signer_chain.cc
src/signer_chain.h
src/sound_asset.cc [deleted file]
src/sound_asset.h [deleted file]
src/sound_frame.cc
src/sound_frame.h
src/sound_mxf.cc [new file with mode: 0644]
src/sound_mxf.h [new file with mode: 0644]
src/sound_mxf_writer.cc [new file with mode: 0644]
src/sound_mxf_writer.h [new file with mode: 0644]
src/srgb_linearised_gamma_lut.cc [deleted file]
src/srgb_linearised_gamma_lut.h [deleted file]
src/stereo_picture_asset.cc [deleted file]
src/stereo_picture_asset.h [deleted file]
src/stereo_picture_asset_writer.cc [deleted file]
src/stereo_picture_asset_writer.h [deleted file]
src/stereo_picture_frame.cc
src/stereo_picture_frame.h
src/stereo_picture_mxf.cc [new file with mode: 0644]
src/stereo_picture_mxf.h [new file with mode: 0644]
src/stereo_picture_mxf_writer.cc [new file with mode: 0644]
src/stereo_picture_mxf_writer.h [new file with mode: 0644]
src/subtitle.cc [new file with mode: 0644]
src/subtitle.h [new file with mode: 0644]
src/subtitle_asset.cc [deleted file]
src/subtitle_asset.h [deleted file]
src/subtitle_content.cc [new file with mode: 0644]
src/subtitle_content.h [new file with mode: 0644]
src/subtitle_string.cc [new file with mode: 0644]
src/subtitle_string.h [new file with mode: 0644]
src/text.cc [new file with mode: 0644]
src/text.h [new file with mode: 0644]
src/types.cc
src/types.h
src/util.cc
src/util.h
src/version.h
src/wscript
src/xml.h
src/xml/kdm_smpte.h [deleted file]
src/xyz_frame.cc
src/xyz_frame.h
test/certificates_test.cc
test/color_test.cc
test/cpl_sar.cc [deleted file]
test/cpl_sar_test.cc [new file with mode: 0644]
test/dcp_test.cc
test/dcp_time_test.cc
test/decryption_test.cc
test/encryption_test.cc
test/error_test.cc [deleted file]
test/frame_info_test.cc
test/kdm_key_test.cc [deleted file]
test/kdm_test.cc
test/local_time_test.cc [new file with mode: 0644]
test/lut_test.cc [deleted file]
test/read_dcp_test.cc
test/recovery_test.cc
test/ref/DCP/bar/3a8a01d1-0e1c-4ff0-977d-63d32995a1fd_pkl.xml [new file with mode: 0644]
test/ref/DCP/bar/793170e7-e913-404b-8942-1c7f2e5cf012_cpl.xml [new file with mode: 0644]
test/ref/DCP/bar/7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c_cpl.xml [deleted file]
test/ref/DCP/bar/ASSETMAP.xml
test/ref/DCP/bar/a12d2c1f-0daa-4a30-a4cd-c4266ac172a7_pkl.xml [deleted file]
test/ref/DCP/bar/audio.mxf
test/ref/DCP/bar/video.mxf
test/ref/DCP/foo/18be072e-5a0f-44e1-b2eb-c8a52ae12789_pkl.xml [new file with mode: 0644]
test/ref/DCP/foo/81fb54df-e1bf-4647-8788-ea7ba154375b_cpl.xml
test/ref/DCP/foo/ASSETMAP.xml
test/ref/DCP/foo/audio.mxf
test/ref/DCP/foo/df0e4141-13c3-4a7a-bef8-b5a04fcbc4bb_pkl.xml [deleted file]
test/ref/DCP/foo/video.mxf
test/rewrite_subs.cc
test/round_trip_test.cc
test/subs_in_out.cc
test/subtitle_tests.cc
test/test.cc
test/test.h
test/utc_offset_to_string_test.cc [deleted file]
test/util_test.cc
test/wscript
tools/common.cc [new file with mode: 0644]
tools/common.h [new file with mode: 0644]
tools/dcpdiff.cc
tools/dcpinfo.cc
tools/wscript
wscript

index 2170048b4af63cdfc2fc3f50e3636643a0fe293b..4c1bc112d3d27bf89b55e475460c2e2eb6fba014 100644 (file)
@@ -12,3 +12,4 @@ GPATH
 GRTAGS
 GSYMS
 GTAGS
+.*.swp
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644 (file)
index b7b5c23..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "test/private"]
-       path = test/private
-       url = ../libdcp-test.git
index 7e9dd59b2f9bd6f40a82d534ba2f7192753c2a7d..6c11ae9750ccfae4247fc23e600443979ac6e910 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
@@ -1,8 +1,10 @@
-# Doxyfile 1.8.1.2
+# Doxyfile 1.8.4
 
 # This file describes the settings to be used by the documentation system
 # doxygen (www.doxygen.org) for a project.
 #
+# All text after a double hash (##) is considered a comment and is placed
+# in front of the TAG it is preceding .
 # All text after a hash (#) is considered a comment and will be ignored.
 # The format is:
 #       TAG = value [value, ...]
@@ -70,9 +72,9 @@ CREATE_SUBDIRS         = NO
 # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
 # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
 # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
-# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
-# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
-# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian,
+# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic,
+# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
 
 OUTPUT_LANGUAGE        = English
 
@@ -126,7 +128,9 @@ FULL_PATH_NAMES        = YES
 # only done if one of the specified strings matches the left-hand part of
 # the path. The tag can be used to show relative paths in the file list.
 # If left blank the directory from which doxygen is run is used as the
-# path to strip.
+# path to strip. Note that you specify absolute paths here, but also
+# relative paths, which will be relative from the directory where doxygen is
+# started.
 
 STRIP_FROM_PATH        =
 
@@ -229,14 +233,15 @@ OPTIMIZE_FOR_FORTRAN   = NO
 OPTIMIZE_OUTPUT_VHDL   = NO
 
 # Doxygen selects the parser to use depending on the extension of the files it
-# parses. With this tag you can assign which parser to use for a given extension.
-# Doxygen has a built-in mapping, but you can override or extend it using this
-# tag. The format is ext=language, where ext is a file extension, and language
-# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
-# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
-# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
-# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
-# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension,
+# and language is one of the parsers supported by doxygen: IDL, Java,
+# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C,
+# C++. For instance to make doxygen treat .inc files as Fortran files (default
+# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note
+# that for custom extensions you also need to set FILE_PATTERNS otherwise the
+# files are not read by doxygen.
 
 EXTENSION_MAPPING      =
 
@@ -249,6 +254,13 @@ EXTENSION_MAPPING      =
 
 MARKDOWN_SUPPORT       = YES
 
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by by putting a % sign in front of the word
+# or globally by setting AUTOLINK_SUPPORT to NO.
+
+AUTOLINK_SUPPORT       = YES
+
 # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
 # to include (a tag file for) the STL sources as input, then you should
 # set this tag to YES in order to let doxygen match functions declarations and
@@ -269,10 +281,10 @@ CPP_CLI_SUPPORT        = NO
 
 SIP_SUPPORT            = NO
 
-# For Microsoft's IDL there are propget and propput attributes to indicate getter
-# and setter methods for a property. Setting this option to YES (the default)
-# will make doxygen replace the get and set methods by a property in the
-# documentation. This will only work if the methods are indeed getting or
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES (the
+# default) will make doxygen replace the get and set methods by a property in
+# the documentation. This will only work if the methods are indeed getting or
 # setting a simple type. If this is not the case, or you want to show the
 # methods anyway, you should set this option to NO.
 
@@ -301,11 +313,11 @@ SUBGROUPING            = YES
 INLINE_GROUPED_CLASSES = NO
 
 # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
-# unions with only public data fields will be shown inline in the documentation
-# of the scope in which they are defined (i.e. file, namespace, or group
-# documentation), provided this scope is documented. If set to NO (the default),
-# structs, classes, and unions are shown on a separate page (for HTML and Man
-# pages) or section (for LaTeX and RTF).
+# unions with only public data fields or simple typedef fields will be shown
+# inline in the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO (the default), structs, classes, and unions are shown on a separate
+# page (for HTML and Man pages) or section (for LaTeX and RTF).
 
 INLINE_SIMPLE_STRUCTS  = NO
 
@@ -319,30 +331,14 @@ INLINE_SIMPLE_STRUCTS  = NO
 
 TYPEDEF_HIDES_STRUCT   = NO
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols.
-
-SYMBOL_CACHE_SIZE      = 0
-
-# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
-# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
-# their name and scope. Since this can be an expensive process and often the
-# same symbol appear multiple times in the code, doxygen keeps a cache of
-# pre-resolved symbols. If the cache is too small doxygen will become slower.
-# If the cache is too large, memory is wasted. The cache size is given by this
-# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols.
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can
+# be an expensive process and often the same symbol appear multiple times in
+# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too
+# small doxygen will become slower. If the cache is too large, memory is wasted.
+# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid
+# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536
+# symbols.
 
 LOOKUP_CACHE_SIZE      = 0
 
@@ -353,16 +349,17 @@ LOOKUP_CACHE_SIZE      = 0
 # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
 # documentation are documented, even if no documentation was available.
 # Private class members and static file members will be hidden unless
-# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES
 
-EXTRACT_ALL            = YES
+EXTRACT_ALL            = NO
 
 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class
 # will be included in the documentation.
 
-EXTRACT_PRIVATE        = NO
+EXTRACT_PRIVATE        = YES
 
-# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation.
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
+# scope will be included in the documentation.
 
 EXTRACT_PACKAGE        = NO
 
@@ -533,7 +530,8 @@ GENERATE_BUGLIST       = YES
 GENERATE_DEPRECATEDLIST= YES
 
 # The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# documentation sections, marked by \if section-label ... \endif
+# and \cond section-label ... \endcond blocks.
 
 ENABLED_SECTIONS       =
 
@@ -591,7 +589,8 @@ LAYOUT_FILE            =
 # requires the bibtex tool to be installed. See also
 # http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
 # of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
-# feature you need bibtex and perl available in the search path.
+# feature you need bibtex and perl available in the search path. Do not use
+# file names with spaces, bibtex cannot handle them.
 
 CITE_BIB_FILES         =
 
@@ -656,7 +655,7 @@ WARN_LOGFILE           =
 # with spaces.
 
 INPUT                  = src \
-                        examples \
+                         examples \
                          doc/mainpage.txt
 
 # This tag can be used to specify the character encoding of the source files
@@ -745,8 +744,10 @@ IMAGE_PATH             =
 # is the value of the INPUT_FILTER tag, and <input-file> is the name of an
 # input file. Doxygen will then use the output that the filter program writes
 # to standard output.
-# If FILTER_PATTERNS is specified, this tag will be
-# ignored.
+# If FILTER_PATTERNS is specified, this tag will be ignored.
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
 
 INPUT_FILTER           =
 
@@ -775,6 +776,13 @@ FILTER_SOURCE_FILES    = NO
 
 FILTER_SOURCE_PATTERNS =
 
+# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
 #---------------------------------------------------------------------------
 # configuration options related to source browsing
 #---------------------------------------------------------------------------
@@ -896,17 +904,27 @@ HTML_FOOTER            =
 
 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading
 # style sheet that is used by each HTML page. It can be used to
-# fine-tune the look of the HTML output. If the tag is left blank doxygen
-# will generate a default style sheet. Note that doxygen will try to copy
-# the style sheet file to the HTML output directory, so don't put your own
-# style sheet in the HTML output directory as well, or it will be erased!
+# fine-tune the look of the HTML output. If left blank doxygen will
+# generate a default style sheet. Note that it is recommended to use
+# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this
+# tag will in the future become obsolete.
 
 HTML_STYLESHEET        =
 
+# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional
+# user-defined cascading style sheet that is included after the standard
+# style sheets created by doxygen. Using this option one can overrule
+# certain style aspects. This is preferred over using HTML_STYLESHEET
+# since it does not replace the standard style sheet and is therefor more
+# robust against future updates. Doxygen will copy the style sheet file to
+# the output directory.
+
+HTML_EXTRA_STYLESHEET  =
+
 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
 # other source files which should be copied to the HTML output directory. Note
 # that these files will be copied to the base HTML output directory. Use the
-# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
 # files. In the HTML_STYLESHEET file, use the file name only. Also note that
 # the files will be copied as-is; there are no commands or markers available.
 
@@ -987,9 +1005,9 @@ DOCSET_FEEDNAME        = "Doxygen generated docs"
 
 DOCSET_BUNDLE_ID       = org.doxygen.Project
 
-# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
-# the documentation publisher. This should be a reverse domain-name style
-# string, e.g. com.mycompany.MyDocSet.documentation.
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely
+# identify the documentation publisher. This should be a reverse domain-name
+# style string, e.g. com.mycompany.MyDocSet.documentation.
 
 DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
 
@@ -1174,6 +1192,13 @@ FORMULA_TRANSPARENT    = YES
 
 USE_MATHJAX            = NO
 
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and
+# SVG. The default value is HTML-CSS, which is slower, but has the best
+# compatibility.
+
+MATHJAX_FORMAT         = HTML-CSS
+
 # When MathJax is enabled you need to specify the location relative to the
 # HTML output directory using the MATHJAX_RELPATH option. The destination
 # directory should contain the MathJax.js script. For instance, if the mathjax
@@ -1191,6 +1216,11 @@ MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
 
 MATHJAX_EXTENSIONS     =
 
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript
+# pieces of code that will be used on startup of the MathJax code.
+
+MATHJAX_CODEFILE       =
+
 # When the SEARCHENGINE tag is enabled doxygen will generate a search box
 # for the HTML output. The underlying search engine uses javascript
 # and DHTML and should work on any modern browser. Note that when using
@@ -1202,15 +1232,55 @@ MATHJAX_EXTENSIONS     =
 SEARCHENGINE           = YES
 
 # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
-# implemented using a PHP enabled web server instead of at the web client
-# using Javascript. Doxygen will generate the search PHP script and index
-# file to put on the web server. The advantage of the server
-# based approach is that it scales better to large projects and allows
-# full text search. The disadvantages are that it is more difficult to setup
-# and does not have live searching capabilities.
+# implemented using a web server instead of a web client using Javascript.
+# There are two flavours of web server based search depending on the
+# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
+# searching and an index file used by the script. When EXTERNAL_SEARCH is
+# enabled the indexing and searching needs to be provided by external tools.
+# See the manual for details.
 
 SERVER_BASED_SEARCH    = NO
 
+# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain
+# the search results. Doxygen ships with an example indexer (doxyindexer) and
+# search engine (doxysearch.cgi) which are based on the open source search
+# engine library Xapian. See the manual for configuration details.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will returned the search results when EXTERNAL_SEARCH is enabled.
+# Doxygen ships with an example search engine (doxysearch) which is based on
+# the open source search engine library Xapian. See the manual for configuration
+# details.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id
+# of to a relative location where the documentation can be found.
+# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ...
+
+EXTRA_SEARCH_MAPPINGS  =
+
 #---------------------------------------------------------------------------
 # configuration options related to the LaTeX output
 #---------------------------------------------------------------------------
@@ -1248,7 +1318,7 @@ COMPACT_LATEX          = NO
 
 # The PAPER_TYPE tag can be used to set the paper type that is used
 # by the printer. Possible values are: a4, letter, legal and
-# executive. If left blank a4wide will be used.
+# executive. If left blank a4 will be used.
 
 PAPER_TYPE             = a4wide
 
@@ -1271,6 +1341,13 @@ LATEX_HEADER           =
 
 LATEX_FOOTER           =
 
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images
+# or other source files which should be copied to the LaTeX output directory.
+# Note that the files will be copied as-is; there are no commands or markers
+# available.
+
+LATEX_EXTRA_FILES      =
+
 # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
 # is prepared for conversion to pdf (using ps2pdf). The pdf file will
 # contain links (just like the HTML output) instead of page references
@@ -1415,6 +1492,21 @@ XML_DTD                =
 
 XML_PROGRAMLISTING     = YES
 
+#---------------------------------------------------------------------------
+# configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files
+# that can be used to generate PDF.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it. If left blank docbook will be used as the default path.
+
+DOCBOOK_OUTPUT         = docbook
+
 #---------------------------------------------------------------------------
 # configuration options for the AutoGen Definitions output
 #---------------------------------------------------------------------------
@@ -1564,6 +1656,12 @@ ALLEXTERNALS           = NO
 
 EXTERNAL_GROUPS        = YES
 
+# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed
+# in the related pages index. If set to NO, only the current project's
+# pages will be listed.
+
+EXTERNAL_PAGES         = YES
+
 # The PERL_PATH should be the absolute path and name of the perl script
 # interpreter (i.e. the result of `which perl').
 
@@ -1618,7 +1716,7 @@ DOT_NUM_THREADS        = 0
 # the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
 # directory containing the font.
 
-DOT_FONTNAME           = FreeSans.ttf
+DOT_FONTNAME           =
 
 # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
 # The default size is 10pt.
@@ -1660,7 +1758,7 @@ UML_LOOK               = NO
 # the class node. If there are many fields or methods and many nodes the
 # graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
 # threshold limits the number of items for each type to make the size more
-# managable. Set this to 0 for no limit. Note that the threshold may be
+# manageable. Set this to 0 for no limit. Note that the threshold may be
 # exceeded by 50% before the limit is enforced.
 
 UML_LIMIT_NUM_FIELDS   = 10
index 661971ac20189d7c1155c86da889755e33850873..62f3a00955ed9d6d1f0d05628c8d887f95381c62 100644 (file)
@@ -13,8 +13,8 @@ def build(bld):
     else:
         obj = bld(features = 'cxx cxxshlib')
 
-    obj.name = 'libkumu-libdcp'
-    obj.target = 'kumu-libdcp'
+    obj.name = 'libkumu-libdcp%s' % bld.env.API_VERSION
+    obj.target = 'kumu-libdcp%s' % bld.env.API_VERSION
     obj.uselib = 'OPENSSL BOOST_FILESYSTEM'
     obj.includes = ['.']
     obj.export_includes = ['.']
@@ -32,10 +32,10 @@ def build(bld):
     else:
         obj = bld(features = 'cxx cxxshlib')
 
-    obj.name = 'libasdcp-libdcp'
-    obj.target = 'asdcp-libdcp'
+    obj.name = 'libasdcp-libdcp%s' % bld.env.API_VERSION
+    obj.target = 'asdcp-libdcp%s' % bld.env.API_VERSION
     obj.uselib = 'OPENSSL'
-    obj.use = 'libkumu-libdcp'
+    obj.use = 'libkumu-libdcp%s' % bld.env.API_VERSION
     obj.includes = ['.']
     obj.export_includes = ['.']
     obj.source = """
@@ -67,5 +67,5 @@ def build(bld):
                  """
 
     if bld.env.STATIC:
-        bld.install_files('${PREFIX}/lib', 'libkumu-libdcp.a')
-        bld.install_files('${PREFIX}/lib', 'libasdcp-libdcp.a')
+        bld.install_files('${PREFIX}/lib', 'libkumu-libdcp%s.a' % bld.env.API_VERSION)
+        bld.install_files('${PREFIX}/lib', 'libasdcp-libdcp%s.a' % bld.env.API_VERSION)
index b4cb21d57cefc6c4b346c4fc85ee246587161e1d..6e2f33974bb8c5d9a8efa8f4415fde984eab0de8 100644 (file)
@@ -2,11 +2,13 @@
 
 @mainpage libdcp
 
-libdcp is a library to create Digital Cinema Packages (DCPs) from JPEG2000 and WAV files, and also to
-read and process existing DCPs.
+libdcp is a library to create Digital Cinema Packages (DCPs) from
+video, audio and subtitle data, and also to read and process existing
+DCPs.
 
-Most of the hard work is done by a (slightly patched) version of asdcplib (http://www.cinecert.com/asdcplib/)
-which is included in the source distribution for libdcp.
+Most of the hard work is done by a (slightly patched) version of
+asdcplib (http://www.cinecert.com/asdcplib/) which is included in the
+source distribution for libdcp.
 
 libdcp is distributed under the GNU GPL.
 
@@ -18,71 +20,7 @@ An example of DCP creation is given in examples/make_dcp.cc.
 Reading existing DCPs
 --
 
-libdcp can be used to read existing DCPs and examine their content.  You might do
-
-@code
-#include <libdcp/dcp.h>
-using namespace std;
-
-libdcp::DCP dcp ("some-DCP-directory");
-@endcode
-
-libdcp will look at the XML files that make up the DCP and find its assets.  You can then
-do things like
-
-@code
-boost::shared_ptr<Reel> reel = dcp.reels.front ();
-boost::shared_ptr<libdcp::PictureAsset> p = reel->main_picture ();
-boost::shared_ptr<libdcp::MonoPictureAsset> mp = boost::dynamic_pointer_cast<libdcp::MonoPictureAsset> (p);
-boost::shared_ptr<libdcp::ARGBFrame> f = mp->get_frame(42)->argb_frame ();
-uint8_t* data = f->data ();
-int size = f->size ();
-@endcode
-
-This will extract the image of frame 42 from the first reel of the DCP and make its ARGB data available
-for examination.  We have to do a <code>dynamic_pointer_cast</code> from <code>libdcp::PictureAsset</code>
-to <code>libdcp::MonoPictureAsset</code>, as the picture asset may be either 2D (monoscopic) or 3D (stereoscopic).
-
-Audio data is accessed in chunks equal in length to the duration of a video frame.  To
-get the audio that accompanies frame 66, you can do
-
-@code
-boost::shared_ptr<libdcp::SoundAsset> s = reel->main_sound ();
-cout << "Sound has " << s->channels() << " channels at " << s->sampling_rate() << "Hz\n";
-boost::shared_ptr<libdcp::SoundFrame> f = s->get_frame(66);
-uint8_t* data = f->data ();
-int size = f->size ();
-@endcode
-
-The returned data are interleaved 24-bit samples, so that
-
-@code
-int left = data[0] | (data[1] << 8) | (data[2] << 16);
-data += 3;
-int right = data[0] | (data[1] << 8) | (data[2] << 16);
-data += 3;
-int centre = data[0] | (data[1] << 8) | (data[2] << 16);
-data += 3;
-int lfe = data[0] | (data[1] << 8) | (data[2] << 16);
-data += 3;
-int ls = data[0] | (data[1] << 8) | (data[2] << 16);
-data += 3;
-int rs = data[0] | (data[1] << 8) | (data[2] << 16);
-data += 3;
-@endcode
-
-would obtain the first sample from each of the 6 channels for that frame.
-
-Subtitles can be read using code like
-
-@code
-boost::shared_ptr<SubtitleAsset> s = dcp.subtitle_asset ();
-list<boost::shared_ptr<libdcp::Text> > texts = s->subtitles_at (libdcp::Time (0, 3, 2, 5));
-@endcode
-
-This returns the subtitles that should be shown at 3 minutes, 2
-seconds, 5 ticks (where 1 tick is 4ms) into the DCP.
-
+An example of DCP reading is given in examples/read_dcp.cc.
 */
 
 
index 6aa4268bd4051d49f7706a328efb4ae3e8e25b0c..910fb8cb7263293046cb5d44cd98ecf153c2c0bd 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 */
 
 /** @file examples/make_dcp.cc
- *  @brief Shows how to make a DCP from some JPEG2000 and WAV files.
+ *  @brief Shows how to make a DCP from some JPEG2000 and audio data.
  */
 
 #include <vector>
 
 #include "dcp.h"
 #include "cpl.h"
-#include "mono_picture_asset.h"
-#include "sound_asset.h"
+#include "mono_picture_mxf.h"
+#include "mono_picture_mxf_writer.h"
+#include "sound_mxf.h"
+#include "sound_mxf_writer.h"
 #include "reel.h"
-
-/* This method returns the filename of the JPEG2000 file to use for a given frame.
-   In this example, we are using the same file for each frame, so we don't bother
-   looking at the frame parameter, but it will called with frame=0, frame=1, ...
-*/
-std::string
-video_frame (int /* frame */)
-{
-       return "examples/help.j2c";
-}
+#include "file.h"
+#include "reel_mono_picture_asset.h"
+#include "reel_sound_asset.h"
+#include <cmath>
 
 int
 main ()
 {
-       /* Make a DCP object.  "My Film DCP" is the directory name for the DCP */
-       libdcp::DCP dcp ("My Film DCP");
+       /* Create a directory to put the DCP in */
+       boost::filesystem::create_directory ("DCP");
        
-       /* Now make a CPL object.
-
-          "My Film" is the title that will be shown on the projector / TMS when the DCP is ingested.
-          FEATURE is the type that the projector will list the DCP as.
-          24 is the frame rate, and the DCP will be 48 frames long (ie 2 seconds at 24 fps).
-       */      
-       boost::shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("My Film DCP", "My Film", libdcp::FEATURE, 24, 48));
-
-       /* And add the CPL to the DCP */
-       dcp.add_cpl (cpl);
-
-       /* Now make a `picture asset'.  This is a collection of the JPEG2000 files that make up the picture; one per frame.
-          First, create the object.
+       /* Make a picture MXF.  This is a file which combines JPEG2000 files together to make
+          up the video of the DCP.  First, create the object, specifying a frame rate of 24 frames
+          per second.
        */
 
-       boost::shared_ptr<libdcp::MonoPictureAsset> picture_asset (
-               new libdcp::MonoPictureAsset ("My Film DCP", "video.mxf")
-               );
+       boost::shared_ptr<dcp::MonoPictureMXF> picture_mxf (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
 
-       /* Now set up its parameters; we have the frame rate, the
-          number of frames and the resolution of the frames;
-          1998x1080 is the DCI Flat specification for 2K projectors.
-       */
-
-       picture_asset->set_edit_rate (24);
-       picture_asset->set_intrinsic_duration (24);
-       picture_asset->set_size (libdcp::Size (1998, 1080));
-
-       /* Now we can create the asset itself.  Here using a function (video_frame) to obtain the name of the JPEG2000 file for each frame.
-          The result will be an MXF file written to the directory "My Film DCP" (which should be the same as the DCP's
-          directory above) called "video.mxf".
-       */
+       /* Start off a write to it */
+       boost::shared_ptr<dcp::PictureMXFWriter> picture_writer = picture_mxf->start_write ("DCP/picture.mxf", dcp::SMPTE, false);
 
-       picture_asset->create (video_frame);
+       /* Write 24 frames of the same JPEG2000 file */
+       dcp::File picture ("examples/help.j2c");
+       for (int i = 0; i < 24; ++i) {
+               picture_writer->write (picture.data(), picture.size());
+       }
 
-       /* Now we will create a `sound asset', which is made up of a WAV file for each channel of audio.  Here we're using
-          stereo, so we add two WAV files to a vector.
+       /* And finish off */
+       picture_writer->finalize ();
 
-          We could add more files here to use more channels; the file order is:
-              Left
-              Right
-              Centre
-              LFE (sub)
-              Left surround
-              Right surround
+       /* Now create a sound MXF.  As before, create an object and a writer.
+          When creating the object we specify the sampling rate (48kHz) and the number of channels (2).
        */
-       std::vector<boost::filesystem::path> sound_files;
-       sound_files.push_back ("examples/sine_440_-12dB.wav");
-       sound_files.push_back ("examples/sine_880_-12dB.wav");
-
-       /* Now we can create the sound asset using these files */
-       boost::shared_ptr<libdcp::SoundAsset> sound_asset (new libdcp::SoundAsset ("My Film DCP", "audio.mxf"));
-       sound_asset->set_edit_rate (24);
-       sound_asset->set_intrinsic_duration (48);
-       sound_asset->create (sound_files);
-
-       /* Now that we have the assets, we can create a Reel to put them in and add it to the CPL */
-       cpl->add_reel (
-               boost::shared_ptr<libdcp::Reel> (
-                       new libdcp::Reel (picture_asset, sound_asset, boost::shared_ptr<libdcp::SubtitleAsset> ())
-                       )
-               );
-
-       /* Finally, we call this to write the XML description files to the DCP.  After this, the DCP
-          is ready to ingest and play.
+       boost::shared_ptr<dcp::SoundMXF> sound_mxf (new dcp::SoundMXF (dcp::Fraction (24, 1), 48000, 2));
+       boost::shared_ptr<dcp::SoundMXFWriter> sound_writer = sound_mxf->start_write ("DCP/sound.mxf", dcp::SMPTE);
+
+       /* Write some sine waves */
+       float* audio[2];
+       audio[0] = new float[48000];
+       audio[1] = new float[48000];
+       for (int i = 0; i < 48000; ++i) {
+               audio[0][i] = sin (2 * M_PI * i * 440 / 48000) * 0.25;
+               audio[1][i] = sin (2 * M_PI * i * 880 / 48000) * 0.25;
+       }
+       sound_writer->write (audio, 48000);
+
+       /* And tidy up */
+       delete[] audio[0];
+       delete[] audio[1];
+       sound_writer->finalize ();
+
+       /* Now create a reel */
+       boost::shared_ptr<dcp::Reel> reel (new dcp::Reel ());
+
+       /* Add picture and sound to it.  The zeros are the `entry points', i.e. the first
+          (video) frame from the MXFs that the reel should play.
        */
-       libdcp::XMLMetadata metadata;
-       dcp.write_xml (false, metadata);
-
+       reel->add (boost::shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelMonoPictureAsset (picture_mxf, 0)));
+       reel->add (boost::shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (sound_mxf, 0)));
+
+       /* Make a CPL with this reel */
+       boost::shared_ptr<dcp::CPL> cpl (new dcp::CPL ("My film", dcp::FEATURE));
+       cpl->add (reel);
+
+       /* Write the DCP */
+       dcp::DCP dcp ("DCP");
+       dcp.add (cpl);
+       dcp.add (picture_mxf);
+       dcp.add (sound_mxf);
+       dcp.write_xml (dcp::SMPTE);
+       
        return 0;
 }
diff --git a/examples/read_dcp.cc b/examples/read_dcp.cc
new file mode 100644 (file)
index 0000000..f3c47a3
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/* If you are using an installed libdcp, these #includes would need to be changed to
+#include <libdcp/dcp.h>
+#include <libdcp/picture_mxf.h>
+... etc. ...
+*/
+
+#include "dcp.h"
+#include "cpl.h"
+#include "reel.h"
+#include "reel_picture_asset.h"
+#include "mono_picture_frame.h"
+#include "mono_picture_mxf.h"
+#include "stereo_picture_mxf.h"
+#include "sound_mxf.h"
+#include "subtitle_content.h"
+#include "argb_frame.h"
+#include <Magick++.h>
+
+/** @file examples/read_dcp.cc
+ *  @brief Shows how to read a DCP.
+ */
+
+int
+main ()
+{
+       /* Create a DCP, specifying where our existing data is */
+       dcp::DCP dcp ("/home/carl/diagonal.com/APPASSIONATA_TLR_F_UK-DEFR_CH_51_2K_LOK_20121115_DGL_OV");
+       /* Read the DCP to find out about it */
+       dcp.read ();
+
+       if (dcp.encrypted ()) {
+               std::cout << "DCP is encrypted.\n";
+       } else {
+               std::cout << "DCP is not encrypted.\n";
+       }
+
+       std::cout << "DCP has " << dcp.cpls().size() << " CPLs.\n";
+       std::list<boost::shared_ptr<dcp::Asset> > assets = dcp.assets ();
+       std::cout << "DCP has " << assets.size() << " assets.\n";
+       for (std::list<boost::shared_ptr<dcp::Asset> >::const_iterator i = assets.begin(); i != assets.end(); ++i) {
+               if (boost::dynamic_pointer_cast<dcp::MonoPictureMXF> (*i)) {
+                       std::cout << "2D picture\n";
+               } else if (boost::dynamic_pointer_cast<dcp::StereoPictureMXF> (*i)) {
+                       std::cout << "3D picture\n";
+               } else if (boost::dynamic_pointer_cast<dcp::SoundMXF> (*i)) {
+                       std::cout << "Sound\n";
+               } else if (boost::dynamic_pointer_cast<dcp::SubtitleContent> (*i)) {
+                       std::cout << "Subtitle\n";
+               } else if (boost::dynamic_pointer_cast<dcp::CPL> (*i)) {
+                       std::cout << "CPL\n";
+               }
+               std::cout << "\t" << (*i)->file().leaf().string() << "\n";
+       }
+
+       /* Take the first CPL */
+       boost::shared_ptr<dcp::CPL> cpl = dcp.cpls().front ();
+
+       /* Get the MXF of the picture asset in the first reel */
+       boost::shared_ptr<dcp::MonoPictureMXF> picture_mxf = boost::dynamic_pointer_cast<dcp::MonoPictureMXF> (cpl->reels().front()->main_picture()->mxf ());
+
+       /* Get the 1000th frame of it */
+       boost::shared_ptr<const dcp::MonoPictureFrame> picture_frame_j2k = picture_mxf->get_frame(999);
+
+       /* Get a ARGB copy of it */
+       boost::shared_ptr<dcp::ARGBFrame> picture_frame_rgb = picture_frame_j2k->argb_frame ();
+
+       Magick::Image image (picture_frame_rgb->size().width, picture_frame_rgb->size().height, "BGRA", Magick::CharPixel, picture_frame_rgb->data ());
+       image.write ("frame.png");
+
+       return 0;
+}
index 05cca98fa60bafff752827e7b9ad99bbad0392e9..abb0885c4159141695043146cdbaebc590da5850 100644 (file)
@@ -1,8 +1,16 @@
 def build(bld):
     obj = bld(features = 'cxx cxxprogram')
-    obj.name   = 'examples'
-    obj.use    = 'libdcp'
+    obj.name   = 'make_dcp'
+    obj.use    = 'libdcp%s' % bld.env.API_VERSION
     obj.uselib = 'OPENJPEG CXML'
     obj.source = 'make_dcp.cc'
     obj.target = 'make_dcp'
     obj.install_path = ''
+
+    obj = bld(features = 'cxx cxxprogram')
+    obj.name   = 'read_dcp'
+    obj.use    = 'libdcp%s' % bld.env.API_VERSION
+    obj.uselib = 'OPENJPEG CXML MAGICK'
+    obj.source = 'read_dcp.cc'
+    obj.target = 'read_dcp'
+    obj.install_path = ''
diff --git a/libdcp-1.0.pc.in b/libdcp-1.0.pc.in
new file mode 100644 (file)
index 0000000..0f3239b
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=@prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libdcp
+Description: DCP reading and writing library
+Version: @version@
+Requires: sigc++-2.0 openssl libxml++-2.6 xmlsec1
+Libs: @libs@
+Cflags: -I${includedir}
diff --git a/libdcp.pc.in b/libdcp.pc.in
deleted file mode 100644 (file)
index 0f3239b..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-prefix=@prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: libdcp
-Description: DCP reading and writing library
-Version: @version@
-Requires: sigc++-2.0 openssl libxml++-2.6 xmlsec1
-Libs: @libs@
-Cflags: -I${includedir}
diff --git a/run/examples/read_dcp b/run/examples/read_dcp
new file mode 100755 (executable)
index 0000000..92f25b2
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+export LD_LIBRARY_PATH=build/src
+if [ "$1" == "--debug" ]; then
+    shift
+    gdb --args build/examples/read_dcp "$@"
+elif [ "$1" == "--valgrind" ]; then
+    shift
+    valgrind --tool="memcheck" --leak-check=full --show-reachable=yes build/examples/read_dcp "$@"
+else
+    build/examples/read_dcp "$@"
+fi
index 0bbc047b412e18a7301286ecdfebc76fb21fded7..5113b3afab3cfadafe969698823b2981bfc2d96d 100755 (executable)
--- a/run/tests
+++ b/run/tests
@@ -55,7 +55,7 @@ fi
 rm -f $work/info.log
 for d in `find $private/metadata -mindepth 1 -maxdepth 1 -type d | sort`; do
     if [ `basename $d` != ".git" ]; then
-        $dcpinfo -s $d >> $work/info.log
+        $dcpinfo --ignore-missing-assets -k -s $d 2> /dev/null >> $work/info.log
         if [ "$?" != "0" ]; then
             echo "FAIL: dcpinfo failed for $d"
             exit 1
@@ -79,7 +79,7 @@ cp -r $private/* $work/private
 for d in `find $work/private/metadata -mindepth 1 -maxdepth 1 -type d | sort`; do
     if [ `basename $d` != ".git" ]; then
         $work/rewrite_subs $d
-        $dcpinfo -s $d >> $work/info2.log
+        $dcpinfo --ignore-missing-assets -k -s $d >> $work/info2.log
     fi
 done
 
index a48f80bb76364a3ca7aea061690780f9f095d8d7..99d6c8145154a3efb0fa6caa5d76502cec42611a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/argb_frame.cc
+ *  @brief ARGBFrame class. 
+ */
+
 #include "argb_frame.h"
 
-using namespace libdcp;
+using namespace dcp;
 
 /** Construct an empty ARGBFrame of a given size and with
  *  undefined contents.
index f26436cd4b08a5d65961572ad3b1f4d536a77372..419227bbbb19bd1504d18ae661db4cbd9944d831 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/argb_frame.h
- *  @brief Container for a single image from a picture asset.
+ *  @brief ARGBFrame class. 
  */
 
-#include <stdint.h>
 #include "util.h"
+#include <stdint.h>
 
-namespace libdcp
+namespace dcp
 {
 
 /** @class ARGBFrame
@@ -42,26 +42,28 @@ namespace libdcp
  *
  *  Lines are packed so that the second row directly follows the first.
  */
-class ARGBFrame
+class ARGBFrame : boost::noncopyable
 {
 public:
        ARGBFrame (Size size);
        ~ARGBFrame ();
 
+       /** @return pointer to the image data */
        uint8_t* data () const {
                return _data;
        }
 
-       /** Length of one picture row in bytes */
+       /** @return length of one picture row in bytes */
        int stride () const;
 
+       /** @return size of the picture in pixels */
        Size size () const {
                return _size;
        }
 
 private:
-       Size _size;
-       uint8_t* _data;
+       Size _size;     ///< frame size in pixels
+       uint8_t* _data; ///< pointer to image data
 };
 
 }
index 00ad67bc5a17dfc46e8a8bc495552eb78a40a4c7..6bf8fec5dcc1fc70dc65596d8df381e9b5d859b3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/asset.cc
- *  @brief Parent class for assets of DCPs.
+ *  @brief Asset class.
  */
 
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <boost/function.hpp>
-#include <libxml++/nodes/element.h>
-#include "AS_DCP.h"
-#include "KM_util.h"
+#include "raw_convert.h"
 #include "asset.h"
 #include "util.h"
-#include "metadata.h"
+#include "exceptions.h"
 #include "compose.hpp"
-#include "raw_convert.h"
+#include <libxml++/libxml++.h>
 
 using std::string;
-using boost::shared_ptr;
-using namespace libdcp;
-
-Asset::Asset (boost::filesystem::path directory, boost::filesystem::path file_name)
-       : _directory (directory)
-       , _file_name (file_name)
-       , _uuid (make_uuid ())
-       , _edit_rate (0)
-       , _entry_point (0)
-       , _intrinsic_duration (0)
-       , _duration (0)
+using boost::function;
+using boost::optional;
+using namespace dcp;
+
+/** Create an Asset with a randomly-generated ID */
+Asset::Asset ()
 {
-       if (_file_name.empty ()) {
-               _file_name = _uuid + ".xml";
-       }
+
+}
+
+/** Create an Asset from a given file.  The ID will
+ *  be extracted from the file.
+ *  @param file File name.
+ */
+Asset::Asset (boost::filesystem::path file)
+       : _file (file)
+{
+
+}
+
+/** Create an Asset with a specified ID.
+ *  @param id ID to use.
+ */
+Asset::Asset (string id)
+       : Object (id)
+{
+
 }
 
 void
-Asset::write_to_pkl (xmlpp::Node* node, bool interop) const
+Asset::write_to_pkl (xmlpp::Node* node, Standard standard) const
 {
+       assert (!_file.empty ());
+       
        xmlpp::Node* asset = node->add_child ("Asset");
-       asset->add_child("Id")->add_child_text ("urn:uuid:" + _uuid);
-       asset->add_child("AnnotationText")->add_child_text (_file_name.string ());
-       asset->add_child("Hash")->add_child_text (digest ());
-       asset->add_child("Size")->add_child_text (raw_convert<string> (boost::filesystem::file_size(path())));
-       if (interop) {
-               asset->add_child("Type")->add_child_text (String::compose ("application/x-smpte-mxf;asdcpKind=%1", asdcp_kind ()));
-       } else {
-               asset->add_child("Type")->add_child_text ("application/mxf");
-       }
+       asset->add_child("Id")->add_child_text ("urn:uuid:" + _id);
+       asset->add_child("AnnotationText")->add_child_text (_id);
+       asset->add_child("Hash")->add_child_text (hash ());
+       asset->add_child("Size")->add_child_text (raw_convert<string> (boost::filesystem::file_size (_file)));
+       asset->add_child("Type")->add_child_text (pkl_type (standard));
 }
 
 void
-Asset::write_to_assetmap (xmlpp::Node* node) const
+Asset::write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const
 {
+       assert (!_file.empty ());
+
        xmlpp::Node* asset = node->add_child ("Asset");
-       asset->add_child("Id")->add_child_text ("urn:uuid:" + _uuid);
+       asset->add_child("Id")->add_child_text ("urn:uuid:" + _id);
        xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
        xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
-       chunk->add_child("Path")->add_child_text (_file_name.string ());
+       optional<boost::filesystem::path> path = relative_to_root (root, _file);
+       if (!path) {
+               throw MiscError (String::compose ("Asset %1 is not within the directory %2", _file, root));
+       }
+       chunk->add_child("Path")->add_child_text (path.get().string ());
        chunk->add_child("VolumeIndex")->add_child_text ("1");
        chunk->add_child("Offset")->add_child_text ("0");
-       chunk->add_child("Length")->add_child_text (raw_convert<string> (boost::filesystem::file_size(path())));
-}
-
-boost::filesystem::path
-Asset::path () const
-{
-       boost::filesystem::path p;
-       p /= _directory;
-       p /= _file_name;
-       return p;
+       chunk->add_child("Length")->add_child_text (raw_convert<string> (boost::filesystem::file_size (_file)));
 }
 
 string
-Asset::digest () const
-{
-       if (_digest.empty ()) {
-               _digest = make_digest (path().string(), 0);
-       }
-
-       return _digest;
-}
-
-void
-Asset::compute_digest (boost::function<void (float)> progress)
+Asset::hash (function<void (float)> progress) const
 {
-       if (!_digest.empty ()) {
-               return;
+       assert (!_file.empty ());
+               
+       if (_hash.empty ()) {
+               _hash = make_digest (_file, progress);
        }
 
-       _digest = make_digest (path().string(), &progress);
+       return _hash;
 }
 
 bool
-Asset::equals (shared_ptr<const Asset> other, EqualityOptions, boost::function<void (NoteType, string)> note) const
+Asset::equals (boost::shared_ptr<const Asset> other, EqualityOptions, function<void (NoteType, string)> note) const
 {
-       if (_edit_rate != other->_edit_rate) {
-               note (ERROR, "asset edit rates differ");
+       if (_hash != other->_hash) {
+               note (ERROR, "Asset hashes differ");
                return false;
        }
-       
-       if (_intrinsic_duration != other->_intrinsic_duration) {
-               note (ERROR, "asset intrinsic durations differ");
-       }
-
-       if (_duration != other->_duration) {
-               note (ERROR, "asset durations differ");
-       }
 
        return true;
 }
+
+void
+Asset::set_file (boost::filesystem::path file) const
+{
+       _file = boost::filesystem::absolute (file);
+       _hash.clear ();
+}
+       
index 6db8e5c271761670e0ce20d7187ed34460466650..bad982f1878b2584741ef0955a65253c022ff19f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/asset.h
- *  @brief Parent class for assets of DCPs.
- */
+ *  @brief Asset class.
+ */ 
 
 #ifndef LIBDCP_ASSET_H
 #define LIBDCP_ASSET_H
 
-#include <string>
-#include <list>
+#include "object.h"
+#include "types.h"
 #include <boost/filesystem.hpp>
 #include <boost/function.hpp>
-#include <libxml++/libxml++.h>
-#include "types.h"
-
-namespace ASDCP {
-       struct WriterInfo;
-}
+#include <boost/bind.hpp>
 
 namespace xmlpp {
-       class Element;
+       class Node;
 }
 
-namespace libdcp
-{
+namespace dcp {
 
-/** @brief Parent class for assets of DCPs
+/** @class Asset
+ *  @brief Parent class for DCP assets, i.e. picture/sound/subtitles and CPLs.
  *
- *  These are collections of pictures or sound.
+ *  Note that this class is not used for ReelAssets; those are just for the metadata
+ *  that gets put into &lt;Reel&gt;s.
  */
-class Asset
+class Asset : public Object
 {
 public:
-       /** Construct an Asset.
-        *  @param directory Directory where our XML or MXF file is.
-        *  @param file_name Name of our file within directory, or empty to make one up based on UUID.
+       Asset ();
+       Asset (boost::filesystem::path file);
+       Asset (std::string id);
+
+       virtual bool equals (
+               boost::shared_ptr<const Asset> other,
+               EqualityOptions opt,
+               boost::function<void (NoteType, std::string)> note
+               ) const;
+
+       /** Write details of the asset to a ASSETMAP.
+        *  @param node Parent node.
         */
-       Asset (boost::filesystem::path directory, boost::filesystem::path file_name = "");
-
-       virtual ~Asset() {}
-
-       /** Write details of the asset to a CPL AssetList node.
-        *  @param p Parent element.
-        */
-       virtual void write_to_cpl (xmlpp::Element* p) const = 0;
+       void write_to_assetmap (xmlpp::Node* node, boost::filesystem::path root) const;
 
        /** Write details of the asset to a PKL AssetList node.
-        *  @param p Parent node.
-        */
-       void write_to_pkl (xmlpp::Node *, bool interop) const;
-
-       /** Write details of the asset to a ASSETMAP stream.
-        *  @param s Stream.
+        *  @param node Parent node.
+        *  @param standard Standard to use.
         */
-       void write_to_assetmap (xmlpp::Node *) const;
-
-       /** Compute the digest for this asset.  Calling this is optional: if
-        *  it is not called, the digest will be computed when required.  However,
-        *  calling this method allows the caller to see the progress of the
-        *  computation, which can be long for large assets.
-        *  @param Called with progress between 0 and 1.
-        */
-       void compute_digest (boost::function<void (float)> progress);
-
-       std::string uuid () const {
-               return _uuid;
-       }
+       void write_to_pkl (xmlpp::Node* node, Standard standard) const;
 
-       boost::filesystem::path path () const;
-
-       void set_directory (boost::filesystem::path d) {
-               _directory = d;
-       }
-
-       void set_file_name (boost::filesystem::path f) {
-               _file_name = f;
-       }
-
-       int entry_point () const {
-               return _entry_point;
+       boost::filesystem::path file () const {
+               return _file;
        }
 
-       int duration () const {
-               return _duration;
-       }
-       
-       int intrinsic_duration () const {
-               return _intrinsic_duration;
-       }
-       
-       int edit_rate () const {
-               return _edit_rate;
-       }
-
-       void set_entry_point (int e) {
-               _entry_point = e;
-       }
-       
-       void set_duration (int d) {
-               _duration = d;
-       }
-
-       void set_intrinsic_duration (int d) {
-               _intrinsic_duration = d;
-       }
-
-       void set_edit_rate (int r) {
-               _edit_rate = r;
-       }
+       void set_file (boost::filesystem::path file) const;
 
-       virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)>) const;
+       /** @return the hash of this asset's file */
+       std::string hash (boost::function<void (float)> progress = 0) const;
 
 protected:
+       virtual std::string pkl_type (Standard standard) const = 0;
 
-       /** @return Interop PKL asdcpKind for the &lt;Type&gt; tag e.g. Picture, Sound etc. */
-       virtual std::string asdcp_kind () const = 0;
-       
-       std::string digest () const;
-
-       /** Directory that our MXF or XML file is in */
-       boost::filesystem::path _directory;
-       /** Name of our MXF or XML file */
-       boost::filesystem::path _file_name;
-       /** Our UUID */
-       std::string _uuid;
-       /** The edit rate; this is normally equal to the number of video frames per second */
-       int _edit_rate;
-       /** Start point to present in frames */
-       int _entry_point;
-       /** Total length in frames */
-       int _intrinsic_duration;
-       /** Length to present in frames */
-       int _duration;
-
-private:       
-       /** Digest of our MXF or XML file */
-       mutable std::string _digest;
+       /** The disk file that represents this asset, if one exists */
+       mutable boost::filesystem::path _file;
+       /** Hash of _file, or empty if the hash has not yet been computed */
+       mutable std::string _hash;
 };
 
 }
index 2f5d47fd6699f58579a372cc0912169278bd3cbf..0a0393c652bdd6032c98c4a33d9f37a7eda804b6 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <sstream>
-#include <vector>
-#include <cerrno>
-#include <boost/algorithm/string.hpp>
-#include <openssl/x509.h>
-#include <openssl/ssl.h>
-#include <openssl/asn1.h>
-#include <openssl/err.h>
-#include <libxml++/nodes/element.h>
+/** @file  src/certificates.cc
+ *  @brief Certificate and CertificateChain classes.
+ */
+
 #include "KM_util.h"
 #include "certificates.h"
 #include "compose.hpp"
 #include "exceptions.h"
 #include "util.h"
+#include <libxml++/nodes/element.h>
+#include <openssl/x509.h>
+#include <openssl/ssl.h>
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <boost/algorithm/string.hpp>
+#include <cerrno>
 
 using std::list;
 using std::string;
-using std::stringstream;
-using std::vector;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 /** @param c X509 certificate, which this object will take ownership of */
 Certificate::Certificate (X509* c)
@@ -47,6 +47,9 @@ Certificate::Certificate (X509* c)
        
 }
 
+/** Load an X509 certificate from a file.
+ *  @param filename File to load.
+ */
 Certificate::Certificate (boost::filesystem::path filename)
        : _certificate (0)
        , _public_key (0)
@@ -61,6 +64,9 @@ Certificate::Certificate (boost::filesystem::path filename)
        }
 }
 
+/** Load an X509 certificate from a string.
+ *  @param cert String to read from.
+ */
 Certificate::Certificate (string cert)
        : _certificate (0)
        , _public_key (0)
@@ -68,6 +74,9 @@ Certificate::Certificate (string cert)
        read_string (cert);
 }
 
+/** Copy constructor.
+ *  @param other Certificate to copy.
+ */
 Certificate::Certificate (Certificate const & other)
        : _certificate (0)
        , _public_key (0)
@@ -75,6 +84,9 @@ Certificate::Certificate (Certificate const & other)
        read_string (other.certificate (true));
 }
 
+/** Read a certificate from a string.
+ *  @param cert String to read.
+ */
 void
 Certificate::read_string (string cert)
 {
@@ -91,12 +103,16 @@ Certificate::read_string (string cert)
        BIO_free (bio);
 }
 
+/** Destructor */
 Certificate::~Certificate ()
 {
        X509_free (_certificate);
        RSA_free (_public_key);
 }
 
+/** operator= for Certificate.
+ *  @param other Certificate to read from.
+ */
 Certificate &
 Certificate::operator= (Certificate const & other)
 {
@@ -114,6 +130,10 @@ Certificate::operator= (Certificate const & other)
        return *this;
 }
 
+/** Return the certificate as a string.
+ *  @param with_begin_end true to include the -----BEGIN CERTIFICATE--- / -----END CERTIFICATE----- markers.
+ *  @return Certificate string.
+ */
 string
 Certificate::certificate (bool with_begin_end) const
 {
@@ -143,6 +163,10 @@ Certificate::certificate (bool with_begin_end) const
        return s;
 }
 
+/** @return Certificate's issuer, in the form
+ *  dnqualifier=&lt;dnQualififer&gt;,CN=&lt;commonName&gt;,OU=&lt;organizationalUnitName&gt,O=&lt;organizationName&gt;
+ *  and with + signs escaped to \+
+ */
 string
 Certificate::issuer () const
 {
@@ -244,6 +268,7 @@ Certificate::thumbprint () const
        return Kumu::base64encode (digest, 20, digest_base64, 64);
 }
 
+/** @return RSA public key from this Certificate.  Caller must not free the returned value. */
 RSA *
 Certificate::public_key () const
 {
@@ -266,6 +291,7 @@ Certificate::public_key () const
        return _public_key;
 }
 
+/** @return Root certificate */
 shared_ptr<Certificate>
 CertificateChain::root () const
 {
@@ -273,6 +299,7 @@ CertificateChain::root () const
        return _certificates.front ();
 }
 
+/** @return Leaf certificate */
 shared_ptr<Certificate>
 CertificateChain::leaf () const
 {
@@ -280,6 +307,7 @@ CertificateChain::leaf () const
        return _certificates.back ();
 }
 
+/** @return Certificates in order from leaf to root */
 list<shared_ptr<Certificate> >
 CertificateChain::leaf_to_root () const
 {
@@ -288,6 +316,9 @@ CertificateChain::leaf_to_root () const
        return c;
 }
 
+/** Add a certificate to the end of the chain.
+ *  @param c Certificate to add.
+ */
 void
 CertificateChain::add (shared_ptr<Certificate> c)
 {
index 2bf8d0db2ff4f5eb0233caeeb79b0a22a3ddebdf..96a8cbb7dee6995239cc318cc5ae738c374a4f4b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/certificates.h
+ *  @brief Certificate and CertificateChain classes.
+ */
+
 #ifndef LIBDCP_CERTIFICATES_H
 #define LIBDCP_CERTIFICATES_H
 
-#include <string>
-#include <list>
-#include <boost/noncopyable.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/filesystem.hpp>
 #undef X509_NAME
 #include <openssl/x509.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/filesystem.hpp>
+#include <string>
+#include <list>
 
 class certificates;
 
@@ -34,8 +37,13 @@ namespace xmlpp {
        class Element;
 }
 
-namespace libdcp {
+namespace dcp {
 
+/** @class Certificate
+ *  @brief A wrapper for an X509 certificate.
+ *
+ *  This class can take a Certificate from a file, a string or an OpenSSL X509 object.
+ */
 class Certificate
 {
 public:
@@ -51,16 +59,12 @@ public:
 
        Certificate& operator= (Certificate const &);
 
-       /** @param with_begin_end true to include BEGIN CERTIFICATE / END CERTIFICATE markers
-        *  @return the whole certificate as a string.
-        */
        std::string certificate (bool with_begin_end = false) const;
        std::string issuer () const;
        std::string serial () const;
        std::string subject () const;
        std::string common_name () const;
 
-       /** @return RSA public key from this Certificate.  Caller must not free the returned value. */
        RSA* public_key () const;
 
        std::string thumbprint () const;
@@ -76,12 +80,15 @@ private:
        mutable RSA* _public_key;
 };
 
+/** @class CertificateChain
+ *  @brief A chain of any number of certificates, from root to leaf.
+ */
 class CertificateChain
 {
 public:
        CertificateChain () {}
 
-       void add (boost::shared_ptr<Certificate>);
+       void add (boost::shared_ptr<Certificate> c);
 
        boost::shared_ptr<Certificate> root () const;
        boost::shared_ptr<Certificate> leaf () const;
index b633e3840825d0f569084717d40c391a7896c459..72f952c3fbf888b84336bdeff5948629aeef19fb 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
    EasyDCP player, I think.
 */
 
-double const libdcp::colour_matrix::xyz_to_rgb[3][3] = {
+double const dcp::colour_matrix::xyz_to_rgb[3][3] = {
        {  3.24096989631653,   -1.5373831987381,  -0.498610764741898 },
        { -0.96924364566803,    1.87596750259399,  0.0415550582110882 },
        {  0.0556300804018974, -0.203976958990097, 1.05697154998779 }
 };
 
-double const libdcp::colour_matrix::srgb_to_xyz[3][3] = {
+double const dcp::colour_matrix::srgb_to_xyz[3][3] = {
        {0.4124564, 0.3575761, 0.1804375},
        {0.2126729, 0.7151522, 0.0721750},
        {0.0193339, 0.1191920, 0.9503041}
 };
 
-double const libdcp::colour_matrix::rec709_to_xyz[3][3] = {
+double const dcp::colour_matrix::rec709_to_xyz[3][3] = {
        { 0.412390799265959,  0.357584339383878, 0.180480788401834 },
        { 0.21263900587151,   0.715168678767756, 0.0721923153607337 },
         { 0.0193308187155918, 0.119194779794626, 0.950532152249661 }
index 615e8b38d63ef278ab99b8b55c836d18631ab8b1..ce15051cbbd066545943310897b3bc70a671f1ec 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
 
 */
 
-namespace libdcp {
+namespace dcp {
 
 namespace colour_matrix {
 
diff --git a/src/content.cc b/src/content.cc
new file mode 100644 (file)
index 0000000..50db005
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "content.h"
+#include "util.h"
+#include "metadata.h"
+#include "AS_DCP.h"
+#include "KM_util.h"
+#include <libxml++/nodes/element.h>
+#include <boost/filesystem.hpp>
+#include <boost/function.hpp>
+#include <iostream>
+
+/* We need this here for the #undef ERROR for Windows */
+#include "types.h"
+
+using namespace std;
+using namespace boost;
+using namespace dcp;
+
+Content::Content (boost::filesystem::path file)
+       : Asset (file)
+       , _edit_rate (24, 1)
+       , _intrinsic_duration (0)
+{
+
+}
+
+Content::Content (Fraction edit_rate)
+       : _edit_rate (edit_rate)
+       , _intrinsic_duration (0)
+{
+
+}
+
+bool
+Content::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+       if (!Asset::equals (other, opt, note)) {
+               return false;
+       }
+       
+       if (_edit_rate != other->_edit_rate) {
+               note (ERROR, "content edit rates differ");
+               return false;
+       }
+       
+       if (_intrinsic_duration != other->_intrinsic_duration) {
+               note (ERROR, "asset intrinsic durations differ");
+               return false;
+       }
+
+       return true;
+}
diff --git a/src/content.h b/src/content.h
new file mode 100644 (file)
index 0000000..2059c8f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/content.h
+ *  @brief Content class.
+ */
+
+#ifndef LIBDCP_CONTENT_H
+#define LIBDCP_CONTENT_H
+
+#include "types.h"
+#include "asset.h"
+#include <libxml++/libxml++.h>
+#include <boost/filesystem.hpp>
+#include <boost/function.hpp>
+#include <string>
+#include <list>
+
+namespace ASDCP {
+       class WriterInfo;
+}
+
+namespace xmlpp {
+       class Element;
+}
+
+namespace dcp
+{
+
+/** @class Content
+ *  @brief An asset that represents a piece of content, i.e. picture, sound or subtitle.
+ *
+ *  Such a piece of content will be contained in a file (either MXF or XML) within a DCP.
+ */
+class Content : public Asset
+{
+public:
+       Content (boost::filesystem::path file);
+       Content (Fraction edit_rate);
+       virtual ~Content () {}
+
+       bool equals (
+               boost::shared_ptr<const Content> other,
+               EqualityOptions opt,
+               boost::function<void (NoteType, std::string)>
+               ) const;
+
+       Fraction edit_rate () const {
+               return _edit_rate;
+       }
+
+       int64_t intrinsic_duration () const {
+               return _intrinsic_duration;
+       }
+
+protected:
+       friend class MXFWriter;
+
+       virtual std::string asdcp_kind () const = 0;
+       
+       Fraction _edit_rate;
+       int64_t _intrinsic_duration;
+};
+
+}
+
+#endif
index 5079b4f632b3e95b4484d19564853ff299b43d4d..fcfa9efac37e5006b799ca6672d672d228df2197 100644 (file)
 
 */
 
-#include <libxml/parser.h>
+#include "raw_convert.h"
 #include "cpl.h"
-#include "parse/cpl.h"
 #include "util.h"
-#include "mono_picture_asset.h"
-#include "stereo_picture_asset.h"
-#include "sound_asset.h"
-#include "subtitle_asset.h"
-#include "parse/asset_map.h"
+#include "mono_picture_mxf.h"
+#include "stereo_picture_mxf.h"
+#include "sound_mxf.h"
+#include "subtitle_content.h"
 #include "reel.h"
 #include "metadata.h"
 #include "signer.h"
 #include "exceptions.h"
+#include "xml.h"
 #include "compose.hpp"
-#include "raw_convert.h"
+#include "reel_picture_asset.h"
+#include "reel_sound_asset.h"
+#include "reel_subtitle_asset.h"
+#include "local_time.h"
+#include <libxml/parser.h>
 
 using std::string;
 using std::stringstream;
@@ -40,169 +43,77 @@ using std::list;
 using std::pair;
 using std::make_pair;
 using boost::shared_ptr;
-using boost::lexical_cast;
 using boost::optional;
-using namespace libdcp;
+using namespace dcp;
 
-CPL::CPL (boost::filesystem::path directory, string name, ContentKind content_kind, int length, int frames_per_second)
-       : _directory (directory)
-       , _name (name)
+CPL::CPL (string annotation_text, ContentKind content_kind)
+       : _annotation_text (annotation_text)
+       /* default _content_title_text to _annotation_text */
+       , _content_title_text (annotation_text)
        , _content_kind (content_kind)
-       , _length (length)
-       , _fps (frames_per_second)
+       , _content_version_id ("urn:uuid:" + make_uuid ())
 {
-       _id = make_uuid ();
+       /* default _content_version_id to and _content_version_label to
+          a random ID and the current time.
+       */
+       _content_version_id = "urn:uuid:" + make_uuid() + LocalTime().as_string ();
+       _content_version_label_text = _content_version_id;
 }
 
-/** Construct a CPL object from a XML file.
- *  @param directory The directory containing this CPL's DCP.
- *  @param file The CPL XML filename.
- *  @param asset_maps AssetMaps to look for assets in.
- *  @param require_mxfs true to throw an exception if a required MXF file does not exist.
- */
-CPL::CPL (boost::filesystem::path directory, string file, list<PathAssetMap> asset_maps, bool require_mxfs)
-       : _directory (directory)
+/** Construct a CPL object from a XML file */
+CPL::CPL (boost::filesystem::path file)
+       : Asset (file)
        , _content_kind (FEATURE)
-       , _length (0)
-       , _fps (0)
 {
-       /* Read the XML */
-       shared_ptr<parse::CPL> cpl;
-       try {
-               cpl.reset (new parse::CPL (file));
-       } catch (FileError& e) {
-               boost::throw_exception (FileError ("could not load CPL file", file, e.number ()));
-       }
-       
-       /* Now cherry-pick the required bits into our own data structure */
-       
-       _name = cpl->annotation_text;
-       _content_kind = cpl->content_kind;
-
-       /* Trim urn:uuid: off the front */
-       _id = cpl->id.substr (9);
-
-       for (list<shared_ptr<parse::Reel> >::iterator i = cpl->reels.begin(); i != cpl->reels.end(); ++i) {
-
-               shared_ptr<parse::Picture> p;
-
-               if ((*i)->asset_list->main_picture) {
-                       p = (*i)->asset_list->main_picture;
-               } else {
-                       p = (*i)->asset_list->main_stereoscopic_picture;
-               }
-               
-               _fps = p->edit_rate.numerator;
-               _length += p->duration;
-
-               shared_ptr<PictureAsset> picture;
-               shared_ptr<SoundAsset> sound;
-               shared_ptr<SubtitleAsset> subtitle;
-
-               /* Some rather twisted logic to decide if we are 3D or not;
-                  some DCPs give a MainStereoscopicPicture to indicate 3D, others
-                  just have a FrameRate twice the EditRate and apparently
-                  expect you to divine the fact that they are hence 3D.
-               */
-
-               if (!(*i)->asset_list->main_stereoscopic_picture && p->edit_rate == p->frame_rate) {
+       cxml::Document f ("CompositionPlaylist");
+       f.read_file (file);
 
-                       try {
-                               pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, p->id);
-
-                               picture.reset (new MonoPictureAsset (asset.first, asset.second->chunks.front()->path));
-
-                               picture->read ();
-                               picture->set_edit_rate (_fps);
-                               picture->set_entry_point (p->entry_point);
-                               picture->set_duration (p->duration);
-                               if (p->key_id.length() > 9) {
-                                       /* Trim urn:uuid: */
-                                       picture->set_key_id (p->key_id.substr (9));
-                               }
-                       } catch (MXFFileError) {
-                               if (require_mxfs) {
-                                       throw;
-                               }
-                       }
-                       
-               } else {
-                       try {
-                               pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, p->id);
-
-                               picture.reset (new StereoPictureAsset (asset.first, asset.second->chunks.front()->path));
-
-                               picture->read ();
-                               picture->set_edit_rate (_fps);
-                               picture->set_entry_point (p->entry_point);
-                               picture->set_duration (p->duration);
-                               if (p->key_id.length() > 9) {
-                                       /* Trim urn:uuid: */
-                                       picture->set_key_id (p->key_id.substr (9));
-                               }
-                               
-                       } catch (MXFFileError) {
-                               if (require_mxfs) {
-                                       throw;
-                               }
-                       }
-                       
-               }
-               
-               if ((*i)->asset_list->main_sound) {
-                       
-                       try {
-                               pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, (*i)->asset_list->main_sound->id);
-                       
-                               sound.reset (new SoundAsset (asset.first, asset.second->chunks.front()->path));
-                               shared_ptr<parse::MainSound> s = (*i)->asset_list->main_sound;
-
-                               sound->read ();
-                               sound->set_entry_point (s->entry_point);
-                               sound->set_duration (s->duration);
-                               if (s->key_id.length() > 9) {
-                                       /* Trim urn:uuid: */
-                                       sound->set_key_id (s->key_id.substr (9));
-                               }
-                       } catch (MXFFileError) {
-                               if (require_mxfs) {
-                                       throw;
-                               }
-                       }
-               }
-
-               if ((*i)->asset_list->main_subtitle) {
-                       
-                       pair<string, shared_ptr<const parse::AssetMapAsset> > asset = asset_from_id (asset_maps, (*i)->asset_list->main_subtitle->id);
+       _id = f.string_child ("Id");
+       if (_id.length() > 9) {
+               _id = _id.substr (9);
+       }
+       _annotation_text = f.optional_string_child ("AnnotationText").get_value_or ("");
+       _metadata.issuer = f.optional_string_child ("Issuer").get_value_or ("");
+       _metadata.creator = f.optional_string_child ("Creator").get_value_or ("");
+       _metadata.issue_date = f.string_child ("IssueDate");
+       _content_title_text = f.string_child ("ContentTitleText");
+       _content_kind = content_kind_from_string (f.string_child ("ContentKind"));
+       shared_ptr<cxml::Node> content_version = f.optional_node_child ("ContentVersion");
+       if (content_version) {
+               _content_version_id = content_version->optional_string_child ("Id").get_value_or ("");
+               _content_version_label_text = content_version->string_child ("LabelText");
+               content_version->done ();
+       }
+       f.ignore_child ("RatingList");
+       _reels = type_grand_children<Reel> (f, "ReelList", "Reel");
 
-                       subtitle.reset (new SubtitleAsset (asset.first, asset.second->chunks.front()->path));
+       f.ignore_child ("Issuer");
+       f.ignore_child ("Signer");
+       f.ignore_child ("Signature");
 
-                       subtitle->set_entry_point ((*i)->asset_list->main_subtitle->entry_point);
-                       subtitle->set_duration ((*i)->asset_list->main_subtitle->duration);
-               }
-                       
-               _reels.push_back (shared_ptr<Reel> (new Reel (picture, sound, subtitle)));
-       }
+       f.done ();
 }
 
+/** Add a reel to this CPL.
+ *  @param reel Reel to add.
+ */
 void
-CPL::add_reel (shared_ptr<Reel> reel)
+CPL::add (boost::shared_ptr<Reel> reel)
 {
        _reels.push_back (reel);
 }
 
+/** Write an CompositonPlaylist XML file.
+ *  @param file Filename to write.
+ *  @param standard INTEROP or SMPTE.
+ *  @param signer Signer to sign the CPL, or 0 to add no signature.
+ */
 void
-CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const
+CPL::write_xml (boost::filesystem::path file, Standard standard, shared_ptr<const Signer> signer) const
 {
-       boost::filesystem::path p;
-       p /= _directory;
-       stringstream s;
-       s << _id << "_cpl.xml";
-       p /= s.str();
-
        xmlpp::Document doc;
        xmlpp::Element* root;
-       if (interop) {
+       if (standard == INTEROP) {
                root = doc.create_root_node ("CompositionPlaylist", "http://www.digicine.com/PROTO-ASDCP-CPL-20040511#");
        } else {
                root = doc.create_root_node ("CompositionPlaylist", "http://www.smpte-ra.org/schemas/429-7/2006/CPL");
@@ -213,90 +124,61 @@ CPL::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Sig
        }
        
        root->add_child("Id")->add_child_text ("urn:uuid:" + _id);
-       root->add_child("AnnotationText")->add_child_text (_name);
-       root->add_child("IssueDate")->add_child_text (metadata.issue_date);
-       root->add_child("Issuer")->add_child_text (metadata.issuer);
-       root->add_child("Creator")->add_child_text (metadata.creator);
-       root->add_child("ContentTitleText")->add_child_text (_name);
+       root->add_child("AnnotationText")->add_child_text (_annotation_text);
+       root->add_child("IssueDate")->add_child_text (_metadata.issue_date);
+       root->add_child("Issuer")->add_child_text (_metadata.issuer);
+       root->add_child("Creator")->add_child_text (_metadata.creator);
+       root->add_child("ContentTitleText")->add_child_text (_content_title_text);
        root->add_child("ContentKind")->add_child_text (content_kind_to_string (_content_kind));
        {
                xmlpp::Node* cv = root->add_child ("ContentVersion");
-               cv->add_child ("Id")->add_child_text ("urn:uri:" + _id + "_" + metadata.issue_date);
-               cv->add_child ("LabelText")->add_child_text (_id + "_" + metadata.issue_date);
+               cv->add_child ("Id")->add_child_text (_content_version_id);
+               cv->add_child ("LabelText")->add_child_text (_content_version_label_text);
        }
        root->add_child("RatingList");
 
        xmlpp::Element* reel_list = root->add_child ("ReelList");
        
        for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
-               (*i)->write_to_cpl (reel_list);
+               (*i)->write_to_cpl (reel_list, standard);
        }
 
        if (signer) {
-               signer->sign (root, interop);
+               signer->sign (root, standard);
        }
 
        /* This must not be the _formatted version otherwise signature digests will be wrong */
-       doc.write_to_file (p.string (), "UTF-8");
+       doc.write_to_file (file.string (), "UTF-8");
 
-       _digest = make_digest (p.string (), 0);
-       _length = boost::filesystem::file_size (p.string ());
+       set_file (file);
 }
 
-void
-CPL::write_to_pkl (xmlpp::Node* node, bool interop) const
+list<shared_ptr<const Content> >
+CPL::content () const
 {
-       xmlpp::Node* asset = node->add_child ("Asset");
-       asset->add_child("Id")->add_child_text ("urn:uuid:" + _id);
-       asset->add_child("Hash")->add_child_text (_digest);
-       asset->add_child("Size")->add_child_text (raw_convert<string> (_length));
-       if (interop) {
-               asset->add_child("Type")->add_child_text ("text/xml;asdcpKind=CPL");
-       } else {
-               asset->add_child("Type")->add_child_text ("text/xml");
-       }
-}
+       list<shared_ptr<const Content> > c;
 
-list<shared_ptr<const Asset> >
-CPL::assets () const
-{
-       list<shared_ptr<const Asset> > a;
        for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
                if ((*i)->main_picture ()) {
-                       a.push_back ((*i)->main_picture ());
+                       c.push_back ((*i)->main_picture()->mxf ());
                }
                if ((*i)->main_sound ()) {
-                       a.push_back ((*i)->main_sound ());
+                       c.push_back ((*i)->main_sound()->mxf ());
                }
                if ((*i)->main_subtitle ()) {
-                       a.push_back ((*i)->main_subtitle ());
+                       c.push_back ((*i)->main_subtitle()->subtitle_content ());
                }
        }
 
-       return a;
-}
-
-void
-CPL::write_to_assetmap (xmlpp::Node* node) const
-{
-       xmlpp::Node* asset = node->add_child ("Asset");
-       asset->add_child("Id")->add_child_text ("urn:uuid:" + _id);
-       xmlpp::Node* chunk_list = asset->add_child ("ChunkList");
-       xmlpp::Node* chunk = chunk_list->add_child ("Chunk");
-       chunk->add_child("Path")->add_child_text (_id + "_cpl.xml");
-       chunk->add_child("VolumeIndex")->add_child_text ("1");
-       chunk->add_child("Offset")->add_child_text("0");
-       chunk->add_child("Length")->add_child_text (raw_convert<string> (_length));
+       return c;
 }
        
-       
-       
 bool
 CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
 {
-       if (_name != other._name && !opt.cpl_names_can_differ) {
+       if (_annotation_text != other._annotation_text && !opt.cpl_annotation_texts_can_differ) {
                stringstream s;
-               s << "names differ: " << _name << " vs " << other._name << "\n";
+               s << "annotation texts differ: " << _annotation_text << " vs " << other._annotation_text << "\n";
                note (ERROR, s.str ());
                return false;
        }
@@ -306,16 +188,6 @@ CPL::equals (CPL const & other, EqualityOptions opt, boost::function<void (NoteT
                return false;
        }
 
-       if (_fps != other._fps) {
-               note (ERROR, String::compose ("frames per second differ (%1 vs %2)", _fps, other._fps));
-               return false;
-       }
-
-       if (_length != other._length) {
-               stringstream s;
-               note (ERROR, String::compose ("lengths differ (%1 vs %2)", _length, other._length));
-       }
-
        if (_reels.size() != other._reels.size()) {
                note (ERROR, String::compose ("reel counts differ (%1 vs %2)", _reels.size(), other._reels.size()));
                return false;
@@ -348,31 +220,48 @@ CPL::encrypted () const
        return false;
 }
 
+/** Add a KDM to this CPL.  If the KDM is for any of this CPLs assets it will be used
+ *  to decrypt those assets.
+ *  @param kdm KDM.
+ */
 void
-CPL::add_kdm (KDM const & kdm)
+CPL::add (DecryptedKDM const & kdm)
 {
        for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
-               (*i)->add_kdm (kdm);
+               (*i)->add (kdm);
        }
 }
 
-pair<string, shared_ptr<const parse::AssetMapAsset> >
-CPL::asset_from_id (list<PathAssetMap> asset_maps, string id) const
+/** Set a private key for every MXF referenced by this CPL.  This will allow the data
+ *  to be decrypted or encrypted.
+ *  @param key Key to use.
+ */
+void
+CPL::set_mxf_keys (Key key)
 {
-       for (list<PathAssetMap>::const_iterator i = asset_maps.begin(); i != asset_maps.end(); ++i) {
-               shared_ptr<parse::AssetMapAsset> a = i->second->asset_from_id (id);
-               if (a) {
-                       return make_pair (i->first, a);
-               }
+       for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
+               (*i)->set_mxf_keys (key);
        }
-
-       return make_pair ("", shared_ptr<const parse::AssetMapAsset> ());
 }
 
 void
-CPL::set_mxf_keys (Key key)
+CPL::resolve_refs (list<shared_ptr<Object> > objects)
 {
        for (list<shared_ptr<Reel> >::const_iterator i = _reels.begin(); i != _reels.end(); ++i) {
-               (*i)->set_mxf_keys (key);
+               (*i)->resolve_refs (objects);
        }
 }
+
+string
+CPL::pkl_type (Standard standard) const
+{
+       switch (standard) {
+       case INTEROP:
+               return "text/xml;asdcpKind=CPL";
+       case SMPTE:
+               return "text/xml";
+       default:
+               assert (false);
+       }
+}
+       
index c50d8f903a790a9619c23c99f693c3363b3b3610..164ed734eb7f421e03a52d329466024821bdda0b 100644 (file)
--- a/src/cpl.h
+++ b/src/cpl.h
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/cpl.h
+ *  @brief CPL class.
+ */
+
 #ifndef LIBDCP_CPL_H
 #define LIBDCP_CPL_H
 
-#include <list>
+#include "types.h"
+#include "certificates.h"
+#include "key.h"
+#include "asset.h"
+#include "metadata.h"
+#include <libxml++/libxml++.h>
 #include <boost/shared_ptr.hpp>
 #include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/optional.hpp>
 #include <boost/filesystem.hpp>
-#include <libxml++/libxml++.h>
-#include "types.h"
-#include "certificates.h"
-#include "key.h"
-
-namespace libdcp {
+#include <list>
 
-namespace parse {
-       class AssetMap;
-       class AssetMapAsset;
-}
+namespace dcp {
        
-class Asset;
+class Content;
 class Reel;
 class XMLMetadata;
 class MXFMetadata;
 class Signer;
-class KDM;
+class DecryptedKDM;
        
-/** @brief A CPL within a DCP */
-class CPL
+/** @class CPL
+ *  @brief A Composition Playlist.
+ */
+class CPL : public Asset
 {
 public:
-       CPL (boost::filesystem::path directory, std::string name, ContentKind content_kind, int length, int frames_per_second);
-       CPL (boost::filesystem::path, std::string file, std::list<PathAssetMap> asset_maps, bool require_mxfs = true);
+       CPL (std::string annotation_text, ContentKind content_kind);
+       CPL (boost::filesystem::path file);
 
-       void add_reel (boost::shared_ptr<Reel> reel);
+       bool equals (
+               CPL const & other,
+               EqualityOptions options,
+               boost::function<void (NoteType, std::string)> note
+               ) const;
+
+       void add (boost::shared_ptr<Reel> reel);
+       void add (DecryptedKDM const &);
+
+       /** @return contents of the &lt;AnnotationText&gt; node */
+       std::string annotation_text () const {
+               return _annotation_text;
+       }
        
-       /** @return the length in frames */
-       int length () const {
-               return _length;
+       /** @return contents of the &lt;ContentTitleText&gt; node */
+       std::string content_title_text () const {
+               return _content_title_text;
+       }
+
+       /** @return contents of the &lt;Id&gt; node within &lt;ContentVersion&gt; */
+       void set_content_version_id (std::string id) {
+               _content_version_id = id;
        }
 
+       /** @return contents of the &lt;LabelText&gt; node within &lt;ContentVersion&gt; */
+       void set_content_version_label_text (std::string text) {
+               _content_version_label_text = text;
+       }
+       
        /** @return the type of the content, used by media servers
         *  to categorise things (e.g. feature, trailer, etc.)
         */
@@ -66,59 +90,46 @@ public:
                return _content_kind;
        }
 
+       /** @return the reels in this CPL */
        std::list<boost::shared_ptr<Reel> > reels () const {
                return _reels;
        }
 
-       /** @return the CPL's name, as will be presented on projector
-        *  media servers and theatre management systems.
+       /** @return the Content in this CPL across all its reels
+        *  (Content is picture, sound and subtitles)
         */
-       std::string name () const {
-               return _name;
-       }
-
-       /** @return the number of frames per second */
-       int frames_per_second () const {
-               return _fps;
-       }
-
-       std::list<boost::shared_ptr<const Asset> > assets () const;
+       std::list<boost::shared_ptr<const Content> > content () const;
 
        bool encrypted () const;
 
        void set_mxf_keys (Key);
-
-       std::string id () const {
-               return _id;
+       void set_metadata (XMLMetadata m) {
+               _metadata = m;
        }
-       
-       bool equals (CPL const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const;
-       
-       void write_xml (bool, XMLMetadata const &, boost::shared_ptr<const Signer>) const;
-       void write_to_assetmap (xmlpp::Node *) const;
-       void write_to_pkl (xmlpp::Node *, bool) const;
 
-       void add_kdm (KDM const &);
-       
+       void write_xml (
+               boost::filesystem::path file,
+               Standard standard,
+               boost::shared_ptr<const Signer>
+               ) const;
+
+       void resolve_refs (std::list<boost::shared_ptr<Object> >);
+
+protected:
+       /** @return type string for PKLs for this asset */
+       std::string pkl_type (Standard standard) const;
+
 private:
-       std::pair<std::string, boost::shared_ptr<const parse::AssetMapAsset> > asset_from_id (std::list<PathAssetMap>, std::string id) const;
-       
-       boost::filesystem::path _directory;
-       /** the name of the DCP */
-       std::string _name;
-       /** the content kind of the CPL */
-       ContentKind _content_kind;
-       /** length in frames */
-       mutable int _length;
-       /** frames per second */
-       int _fps;
-       /** reels */
+       std::string _annotation_text;               ///< &lt;AnnotationText&gt;
+       /** &lt;Issuer&gt;, &lt;Creator&gt; and &lt;IssueDate&gt;.  These are grouped
+        *  because they occur together in a few places.
+        */
+       XMLMetadata _metadata;
+       std::string _content_title_text;            ///< &lt;ContentTitleText&gt;
+       ContentKind _content_kind;                  ///< &lt;ContentKind&gt;
+       std::string _content_version_id;            ///< &lt;Id&gt; in &lt;ContentVersion&gt;
+       std::string _content_version_label_text;    ///< &lt;LabelText&gt; in &lt;ContentVersion&gt;
        std::list<boost::shared_ptr<Reel> > _reels;
-
-       /** our UUID */
-       std::string _id;
-       /** a SHA1 digest of our XML */
-       mutable std::string _digest;
 };
 
 }
index 7cce30feb31c77ab44558690310a5965fc434f27..9d3cd72c33e0635ca7c483c9a7e5c94c64196824 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/dcp.cc
- *  @brief A class to create a DCP.
+ *  @brief DCP class.
  */
 
-#include <sstream>
-#include <iomanip>
-#include <cassert>
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <boost/algorithm/string.hpp>
-#include <libxml++/libxml++.h>
-#include <xmlsec/xmldsig.h>
-#include <xmlsec/app.h>
+#include "raw_convert.h"
 #include "dcp.h"
-#include "asset.h"
-#include "sound_asset.h"
-#include "picture_asset.h"
-#include "subtitle_asset.h"
+#include "sound_mxf.h"
+#include "picture_mxf.h"
+#include "subtitle_content.h"
+#include "mono_picture_mxf.h"
+#include "stereo_picture_mxf.h"
 #include "util.h"
 #include "metadata.h"
 #include "exceptions.h"
-#include "parse/pkl.h"
-#include "parse/asset_map.h"
 #include "reel.h"
 #include "cpl.h"
 #include "signer.h"
-#include "kdm.h"
-#include "raw_convert.h"
+#include "compose.hpp"
+#include "AS_DCP.h"
+#include "decrypted_kdm.h"
+#include "decrypted_kdm_key.h"
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/app.h>
+#include <libxml++/libxml++.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <sstream>
+#include <iomanip>
+#include <cassert>
+#include <iostream>
 
 using std::string;
 using std::list;
 using std::stringstream;
 using std::ostream;
-using std::copy;
-using std::back_inserter;
 using std::make_pair;
+using std::map;
+using std::cout;
+using std::exception;
 using boost::shared_ptr;
-using namespace libdcp;
+using boost::algorithm::starts_with;
+using namespace dcp;
 
 DCP::DCP (boost::filesystem::path directory)
        : _directory (directory)
 {
        boost::filesystem::create_directories (directory);
+       _directory = boost::filesystem::canonical (_directory);
 }
 
-void
-DCP::write_xml (bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const
+template<class T> void
+survivable_error (bool keep_going, dcp::DCP::ReadErrors* errors, T const & e)
 {
-       for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               (*i)->write_xml (interop, metadata, signer);
+       if (keep_going) {
+               if (errors) {
+                       errors->push_back (shared_ptr<T> (new T (e)));
+               }
+       } else {
+               throw e;
        }
+}
 
-       string pkl_uuid = make_uuid ();
-       string pkl_path = write_pkl (pkl_uuid, interop, metadata, signer);
+void
+DCP::read (bool keep_going, ReadErrors* errors)
+{
+       /* Read the ASSETMAP */
        
-       write_volindex (interop);
-       write_assetmap (pkl_uuid, boost::filesystem::file_size (pkl_path), interop, metadata);
+       boost::filesystem::path asset_map_file;
+       if (boost::filesystem::exists (_directory / "ASSETMAP")) {
+               asset_map_file = _directory / "ASSETMAP";
+       } else if (boost::filesystem::exists (_directory / "ASSETMAP.xml")) {
+               asset_map_file = _directory / "ASSETMAP.xml";
+       } else {
+               boost::throw_exception (DCPReadError (String::compose ("could not find AssetMap file `%1'", asset_map_file.string())));
+       }
+
+       cxml::Document asset_map ("AssetMap");
+       asset_map.read_file (asset_map_file);
+       list<shared_ptr<cxml::Node> > asset_nodes = asset_map.node_child("AssetList")->node_children ("Asset");
+       map<string, boost::filesystem::path> paths;
+       for (list<shared_ptr<cxml::Node> >::const_iterator i = asset_nodes.begin(); i != asset_nodes.end(); ++i) {
+               if ((*i)->node_child("ChunkList")->node_children("Chunk").size() != 1) {
+                       boost::throw_exception (XMLError ("unsupported asset chunk count"));
+               }
+               string p = (*i)->node_child("ChunkList")->node_child("Chunk")->string_child ("Path");
+               if (starts_with (p, "file://")) {
+                       p = p.substr (7);
+               }
+               paths.insert (make_pair ((*i)->string_child ("Id"), p));
+       }
+
+       /* Read all the assets from the asset map */
+       for (map<string, boost::filesystem::path>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
+               boost::filesystem::path path = _directory / i->second;
+
+               if (!boost::filesystem::exists (path)) {
+                       survivable_error (keep_going, errors, MissingAssetError (path));
+                       continue;
+               }
+               
+               if (boost::algorithm::ends_with (path.string(), ".xml")) {
+                       xmlpp::DomParser* p = new xmlpp::DomParser;
+                       try {
+                               p->parse_file (path.string());
+                       } catch (std::exception& e) {
+                               delete p;
+                               continue;
+                       }
+                       
+                       string const root = p->get_document()->get_root_node()->get_name ();
+                       delete p;
+                       
+                       if (root == "CompositionPlaylist") {
+                               _assets.push_back (shared_ptr<CPL> (new CPL (path)));
+                       } else if (root == "DCSubtitle") {
+                               _assets.push_back (shared_ptr<SubtitleContent> (new SubtitleContent (path, false)));
+                       }
+               } else if (boost::algorithm::ends_with (path.string(), ".mxf")) {
+                       ASDCP::EssenceType_t type;
+                       if (ASDCP::EssenceType (path.string().c_str(), type) != ASDCP::RESULT_OK) {
+                               throw DCPReadError ("Could not find essence type");
+                       }
+                       switch (type) {
+                               case ASDCP::ESS_UNKNOWN:
+                               case ASDCP::ESS_MPEG2_VES:
+                                       throw DCPReadError ("MPEG2 video essences are not supported");
+                               case ASDCP::ESS_JPEG_2000:
+                                       _assets.push_back (shared_ptr<MonoPictureMXF> (new MonoPictureMXF (path)));
+                                       break;
+                               case ASDCP::ESS_PCM_24b_48k:
+                               case ASDCP::ESS_PCM_24b_96k:
+                                       _assets.push_back (shared_ptr<SoundMXF> (new SoundMXF (path)));
+                                       break;
+                               case ASDCP::ESS_JPEG_2000_S:
+                                       _assets.push_back (shared_ptr<StereoPictureMXF> (new StereoPictureMXF (path)));
+                                       break;
+                               case ASDCP::ESS_TIMED_TEXT:
+                                       _assets.push_back (shared_ptr<SubtitleContent> (new SubtitleContent (path, true)));
+                                       break;
+                               default:
+                                       throw DCPReadError ("Unknown MXF essence type");
+                               }
+               }
+       }
+
+       list<shared_ptr<CPL> > cpl = cpls ();
+       for (list<shared_ptr<CPL> >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) {
+               (*i)->resolve_refs (list_of_type<Asset, Object> (assets ()));
+       }
+}
+
+bool
+DCP::equals (DCP const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+       if (_assets.size() != other._assets.size()) {
+               note (ERROR, String::compose ("Asset counts differ: %1 vs %2", _assets.size(), other._assets.size()));
+               return false;
+       }
+
+       list<shared_ptr<Asset> >::const_iterator a = _assets.begin ();
+       list<shared_ptr<Asset> >::const_iterator b = other._assets.begin ();
+
+       while (a != _assets.end ()) {
+               if (!(*a)->equals (*b, opt, note)) {
+                       return false;
+               }
+               ++a;
+               ++b;
+       }
+
+       return true;
 }
 
-std::string
-DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, shared_ptr<const Signer> signer) const
+void
+DCP::add (boost::shared_ptr<Asset> asset)
+{
+       _assets.push_back (asset);
+}
+
+class AssetComparator
+{
+public:
+       bool operator() (shared_ptr<const Content> a, shared_ptr<const Content> b) {
+               return a->id() < b->id();
+       }
+};
+
+bool
+DCP::encrypted () const
 {
-       assert (!_cpls.empty ());
+       list<shared_ptr<CPL> > cpl = cpls ();
+       for (list<shared_ptr<CPL> >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) {
+               if ((*i)->encrypted ()) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+void
+DCP::add (DecryptedKDM const & kdm)
+{
+       list<DecryptedKDMKey> keys = kdm.keys ();
+       list<shared_ptr<CPL> > cpl = cpls ();
        
-       boost::filesystem::path p;
-       p /= _directory;
+       for (list<shared_ptr<CPL> >::iterator i = cpl.begin(); i != cpl.end(); ++i) {
+               for (list<DecryptedKDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) {
+                       if (j->cpl_id() == (*i)->id()) {
+                               (*i)->add (kdm);
+                       }                               
+               }
+       }
+}
+
+boost::filesystem::path
+DCP::write_pkl (Standard standard, string pkl_uuid, XMLMetadata metadata, shared_ptr<const Signer> signer) const
+{
+       boost::filesystem::path p = _directory;
        stringstream s;
        s << pkl_uuid << "_pkl.xml";
        p /= s.str();
 
        xmlpp::Document doc;
        xmlpp::Element* pkl;
-       if (interop) {
+       if (standard == INTEROP) {
                pkl = doc.create_root_node("PackingList", "http://www.digicine.com/PROTO-ASDCP-PKL-20040311#");
        } else {
                pkl = doc.create_root_node("PackingList", "http://www.smpte-ra.org/schemas/429-8/2007/PKL");
@@ -100,83 +253,112 @@ DCP::write_pkl (string pkl_uuid, bool interop, XMLMetadata const & metadata, sha
        }
 
        pkl->add_child("Id")->add_child_text ("urn:uuid:" + pkl_uuid);
+
        /* XXX: this is a bit of a hack */
-       pkl->add_child("AnnotationText")->add_child_text(_cpls.front()->name());
+       assert (cpls().size() > 0);
+       pkl->add_child("AnnotationText")->add_child_text (cpls().front()->annotation_text ());
+       
        pkl->add_child("IssueDate")->add_child_text (metadata.issue_date);
        pkl->add_child("Issuer")->add_child_text (metadata.issuer);
        pkl->add_child("Creator")->add_child_text (metadata.creator);
 
        xmlpp::Element* asset_list = pkl->add_child("AssetList");
-       list<shared_ptr<const Asset> > a = assets ();
-       for (list<shared_ptr<const Asset> >::const_iterator i = a.begin(); i != a.end(); ++i) {
-               (*i)->write_to_pkl (asset_list, interop);
-       }
-       
-       for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               (*i)->write_to_pkl (asset_list, interop);
+       for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) {
+               (*i)->write_to_pkl (asset_list, standard);
        }
 
        if (signer) {
-               signer->sign (pkl, interop);
+               signer->sign (pkl, standard);
        }
                
        doc.write_to_file (p.string (), "UTF-8");
        return p.string ();
 }
 
+/** Write the VOLINDEX file.
+ *  @param standard DCP standard to use (INTEROP or SMPTE)
+ */
 void
-DCP::write_volindex (bool interop) const
+DCP::write_volindex (Standard standard) const
 {
-       boost::filesystem::path p;
-       p /= _directory;
-       if (interop) {
+       boost::filesystem::path p = _directory;
+       switch (standard) {
+       case INTEROP:
                p /= "VOLINDEX";
-       } else {
+               break;
+       case SMPTE:
                p /= "VOLINDEX.xml";
+               break;
+       default:
+               assert (false);
        }
 
        xmlpp::Document doc;
        xmlpp::Element* root;
-       if (interop) {
+
+       switch (standard) {
+       case INTEROP:
                root = doc.create_root_node ("VolumeIndex", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#");
-       } else {
+               break;
+       case SMPTE:
                root = doc.create_root_node ("VolumeIndex", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
+               break;
+       default:
+               assert (false);
        }
+       
        root->add_child("Index")->add_child_text ("1");
        doc.write_to_file (p.string (), "UTF-8");
 }
 
 void
-DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata const & metadata) const
+DCP::write_assetmap (Standard standard, string pkl_uuid, int pkl_length, XMLMetadata metadata) const
 {
-       boost::filesystem::path p;
-       p /= _directory;
-       if (interop) {
+       boost::filesystem::path p = _directory;
+       
+       switch (standard) {
+       case INTEROP:
                p /= "ASSETMAP";
-       } else {
+               break;
+       case SMPTE:
                p /= "ASSETMAP.xml";
+               break;
+       default:
+               assert (false);
        }
 
        xmlpp::Document doc;
        xmlpp::Element* root;
-       if (interop) {
+
+       switch (standard) {
+       case INTEROP:
                root = doc.create_root_node ("AssetMap", "http://www.digicine.com/PROTO-ASDCP-AM-20040311#");
-       } else {
+               break;
+       case SMPTE:
                root = doc.create_root_node ("AssetMap", "http://www.smpte-ra.org/schemas/429-9/2007/AM");
+               break;
+       default:
+               assert (false);
        }
 
        root->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
        root->add_child("AnnotationText")->add_child_text ("Created by " + metadata.creator);
-       if (interop) {
+
+       switch (standard) {
+       case INTEROP:
                root->add_child("VolumeCount")->add_child_text ("1");
                root->add_child("IssueDate")->add_child_text (metadata.issue_date);
                root->add_child("Issuer")->add_child_text (metadata.issuer);
                root->add_child("Creator")->add_child_text (metadata.creator);
-       } else {
+               break;
+       case SMPTE:
                root->add_child("Creator")->add_child_text (metadata.creator);
                root->add_child("VolumeCount")->add_child_text ("1");
                root->add_child("IssueDate")->add_child_text (metadata.issue_date);
                root->add_child("Issuer")->add_child_text (metadata.issuer);
+               break;
+       default:
+               assert (false);
        }
                
        xmlpp::Node* asset_list = root->add_child ("AssetList");
@@ -191,187 +373,41 @@ DCP::write_assetmap (string pkl_uuid, int pkl_length, bool interop, XMLMetadata
        chunk->add_child("Offset")->add_child_text ("0");
        chunk->add_child("Length")->add_child_text (raw_convert<string> (pkl_length));
        
-       for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               (*i)->write_to_assetmap (asset_list);
-       }
-
-       list<shared_ptr<const Asset> > a = assets ();
-       for (list<shared_ptr<const Asset> >::const_iterator i = a.begin(); i != a.end(); ++i) {
-               (*i)->write_to_assetmap (asset_list);
+       for (list<shared_ptr<Asset> >::const_iterator i = _assets.begin(); i != _assets.end(); ++i) {
+               (*i)->write_to_assetmap (asset_list, _directory);
        }
 
        /* This must not be the _formatted version otherwise signature digests will be wrong */
        doc.write_to_file (p.string (), "UTF-8");
 }
 
+/** Write all the XML files for this DCP.
+ *  @param standand INTEROP or SMPTE.
+ *  @param metadata Metadata to use for PKL and asset map files.
+ *  @param signer Signer to use, or 0.
+ */
 void
-DCP::read (bool require_mxfs)
-{
-       read_assets ();
-       read_cpls (require_mxfs);
-}
-
-void
-DCP::read_assets ()
-{
-       shared_ptr<parse::AssetMap> asset_map;
-       try {
-               boost::filesystem::path p = _directory;
-               p /= "ASSETMAP";
-               if (boost::filesystem::exists (p)) {
-                       asset_map.reset (new libdcp::parse::AssetMap (p.string ()));
-               } else {
-                       p = _directory;
-                       p /= "ASSETMAP.xml";
-                       if (boost::filesystem::exists (p)) {
-                               asset_map.reset (new libdcp::parse::AssetMap (p.string ()));
-                       } else {
-                               boost::throw_exception (FileError ("could not find AssetMap file", p, -1));
-                       }
-               }
-               
-       } catch (FileError& e) {
-               boost::throw_exception (FileError ("could not load AssetMap file", e.filename(), e.number ()));
-       }
-
-       for (list<shared_ptr<libdcp::parse::AssetMapAsset> >::const_iterator i = asset_map->assets.begin(); i != asset_map->assets.end(); ++i) {
-               if ((*i)->chunks.size() != 1) {
-                       boost::throw_exception (XMLError ("unsupported asset chunk count"));
-               }
-
-               boost::filesystem::path t = _directory;
-               t /= (*i)->chunks.front()->path;
-               
-               if (boost::algorithm::ends_with (t.string(), ".mxf") || boost::algorithm::ends_with (t.string(), ".ttf")) {
-                       continue;
-               }
-
-               xmlpp::DomParser* p = new xmlpp::DomParser;
-               try {
-                       p->parse_file (t.string());
-               } catch (std::exception& e) {
-                       delete p;
-                       continue;
-               }
-
-               string const root = p->get_document()->get_root_node()->get_name ();
-               delete p;
-
-               if (root == "CompositionPlaylist") {
-                       _files.cpls.push_back (t.string());
-               } else if (root == "PackingList") {
-                       if (_files.pkl.empty ()) {
-                               _files.pkl = t.string();
-                       } else {
-                               boost::throw_exception (DCPReadError ("duplicate PKLs found"));
-                       }
-               }
-       }
-       
-       if (_files.cpls.empty ()) {
-               boost::throw_exception (DCPReadError ("no CPL files found"));
-       }
-
-       if (_files.pkl.empty ()) {
-               boost::throw_exception (DCPReadError ("no PKL file found"));
-       }
-
-       shared_ptr<parse::PKL> pkl;
-       try {
-               pkl.reset (new parse::PKL (_files.pkl));
-       } catch (FileError& e) {
-               boost::throw_exception (FileError ("could not load PKL file", _files.pkl, e.number ()));
-       }
-
-       _asset_maps.push_back (make_pair (boost::filesystem::absolute (_directory).string(), asset_map));
-}
-
-void
-DCP::read_cpls (bool require_mxfs)
-{
-       for (list<string>::iterator i = _files.cpls.begin(); i != _files.cpls.end(); ++i) {
-               _cpls.push_back (shared_ptr<CPL> (new CPL (_directory, *i, _asset_maps, require_mxfs)));
-       }
-}
-
-bool
-DCP::equals (DCP const & other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
-{
-       if (_cpls.size() != other._cpls.size()) {
-               note (ERROR, "CPL counts differ");
-               return false;
-       }
-
-       list<shared_ptr<CPL> >::const_iterator a = _cpls.begin ();
-       list<shared_ptr<CPL> >::const_iterator b = other._cpls.begin ();
-
-       while (a != _cpls.end ()) {
-               if (!(*a)->equals (*b->get(), opt, note)) {
-                       return false;
-               }
-               ++a;
-               ++b;
-       }
-
-       return true;
-}
-
-void
-DCP::add_cpl (shared_ptr<CPL> cpl)
-{
-       _cpls.push_back (cpl);
-}
-
-class AssetComparator
-{
-public:
-       bool operator() (shared_ptr<const Asset> a, shared_ptr<const Asset> b) {
-               return a->uuid() < b->uuid();
-       }
-};
-
-list<shared_ptr<const Asset> >
-DCP::assets () const
-{
-       list<shared_ptr<const Asset> > a;
-       for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               list<shared_ptr<const Asset> > t = (*i)->assets ();
-               a.merge (t);
-       }
-
-       a.sort (AssetComparator ());
-       a.unique ();
-       return a;
-}
-
-bool
-DCP::encrypted () const
+DCP::write_xml (
+       Standard standard,
+       XMLMetadata metadata,
+       shared_ptr<const Signer> signer
+       )
 {
-       for (list<shared_ptr<CPL> >::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               if ((*i)->encrypted ()) {
-                       return true;
-               }
+       list<shared_ptr<CPL> > cpl = cpls ();
+       for (list<shared_ptr<CPL> >::const_iterator i = cpl.begin(); i != cpl.end(); ++i) {
+               string const filename = (*i)->id() + "_cpl.xml";
+               (*i)->write_xml (_directory / filename, standard, signer);
        }
 
-       return false;
-}
-
-void
-DCP::add_kdm (KDM const & kdm)
-{
-       list<KDMKey> keys = kdm.keys ();
+       string const pkl_uuid = make_uuid ();
+       boost::filesystem::path const pkl_path = write_pkl (standard, pkl_uuid, metadata, signer);
        
-       for (list<shared_ptr<CPL> >::iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
-               for (list<KDMKey>::iterator j = keys.begin(); j != keys.end(); ++j) {
-                       if (j->cpl_id() == (*i)->id()) {
-                               (*i)->add_kdm (kdm);
-                       }                               
-               }
-       }
+       write_volindex (standard);
+       write_assetmap (standard, pkl_uuid, boost::filesystem::file_size (pkl_path), metadata);
 }
 
-void
-DCP::add_assets_from (DCP const & ov)
+list<shared_ptr<CPL> >
+DCP::cpls () const
 {
-       copy (ov._asset_maps.begin(), ov._asset_maps.end(), back_inserter (_asset_maps));
+       return list_of_type<Asset, CPL> (_assets);
 }
index 45ccce8875f12aed2c2941391d52d5a37a22fab3..1fa7ae0c65e71740fb1cb82ccbe0700edc9fe1b8 100644 (file)
--- a/src/dcp.h
+++ b/src/dcp.h
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/dcp.h
- *  @brief A class to create or read a DCP.
+ *  @brief DCP class.
  */
 
 #ifndef LIBDCP_DCP_H
 #define LIBDCP_DCP_H
 
-#include <string>
-#include <vector>
-#include <boost/shared_ptr.hpp>
-#include <boost/signals2.hpp>
 #include "types.h"
 #include "certificates.h"
+#include "metadata.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/signals2.hpp>
+#include <string>
+#include <vector>
 
 namespace xmlpp {
        class Document;
@@ -37,18 +38,17 @@ namespace xmlpp {
 }
 
 /** @brief Namespace for everything in libdcp */
-namespace libdcp
+namespace dcp
 {
 
-class Asset;   
-class PictureAsset;
-class SoundAsset;
-class SubtitleAsset;
+class Content; 
 class Reel;
 class CPL;
 class XMLMetadata;
 class Signer;
-class KDM;
+class DecryptedKDM;
+class Asset;
+class DCPReadError;
 
 namespace parse {
        class AssetMap;
@@ -69,91 +69,65 @@ public:
         */
        DCP (boost::filesystem::path directory);
 
-       void read (bool require_mxfs = true);
-
-       /** Read an existing DCP's assets.
-        *
-        *  The DCP's XML metadata will be examined, and you can then look at the contents
-        *  of the DCP.
-        */
-       void read_assets ();
-
-       void read_cpls (bool require_mxfs = true);
-
-       /** Write the required XML files to the directory that was
-        *  passed into the constructor.
+       typedef std::list<boost::shared_ptr<DCPReadError> > ReadErrors;
+       
+       /** Read the DCP's structure into this object.
+        *  @param keep_going true to try to keep going in the face of (some) errors.
+        *  @param errors List of errors that will be added to if keep_going is true.
         */
-       void write_xml (bool interop, XMLMetadata const &, boost::shared_ptr<const Signer> signer = boost::shared_ptr<const Signer> ()) const;
+       void read (bool keep_going = false, ReadErrors* errors = 0);
 
        /** Compare this DCP with another, according to various options.
         *  @param other DCP to compare this one to.
         *  @param options Options to define what "equality" means.
+        *  @param note Functor to handle notes made by the equality operation.
         *  @return true if the DCPs are equal according to EqualityOptions, otherwise false.
         */
        bool equals (DCP const & other, EqualityOptions options, boost::function<void (NoteType, std::string)> note) const;
 
-       /** Add a CPL to this DCP.
-        *  @param cpl CPL to add.
-        */
-       void add_cpl (boost::shared_ptr<CPL> cpl);
+       void add (boost::shared_ptr<Asset> asset);
 
-       /** @return The list of CPLs in this DCP */
-       std::list<boost::shared_ptr<CPL> > cpls () const {
-               return _cpls;
-       }
+       std::list<boost::shared_ptr<CPL> > cpls () const;
 
-       /** Add another DCP as a source of assets for this DCP.  This should be called before
-        *  ::read() on the DCP that needs the extra assets.  For example
-        *
-        *  DCP original_version ("my_dcp_OV");
-        *  DCP supplemental ("my_dcp_VF");
-        *  supplemental.add_assets_from (original_version);
-        *  supplemental.read ();
-        */
-       void add_assets_from (libdcp::DCP const &);
+       /** @return All this DCP's assets (note that CPLs are assets) */
+       std::list<boost::shared_ptr<Asset> > assets () const {
+               return _assets;
+       }
 
        bool encrypted () const;
 
-       void add_kdm (KDM const &);
+       void add (DecryptedKDM const &);
 
-       /** Emitted with a parameter between 0 and 1 to indicate progress
-        *  for long jobs.
-        */
-       boost::signals2::signal<void (float)> Progress;
+       void write_xml (
+               Standard standard,
+               XMLMetadata metadata = XMLMetadata (),
+               boost::shared_ptr<const Signer> signer = boost::shared_ptr<const Signer> ()
+       );
 
 private:
 
        /** Write the PKL file.
         *  @param pkl_uuid UUID to use.
         */
-       std::string write_pkl (std::string pkl_uuid, bool, XMLMetadata const &, boost::shared_ptr<const Signer>) const;
+       boost::filesystem::path write_pkl (
+               Standard standard,
+               std::string pkl_uuid,
+               XMLMetadata metadata,
+               boost::shared_ptr<const Signer> signer
+               ) const;
        
-       /** Write the VOLINDEX file */
-       void write_volindex (bool) const;
+       void write_volindex (Standard standard) const;
 
        /** Write the ASSETMAP file.
         *  @param pkl_uuid UUID of our PKL.
         *  @param pkl_length Length of our PKL in bytes.
         */
-       void write_assetmap (std::string pkl_uuid, int pkl_length, bool, XMLMetadata const &) const;
-
-       /** @return Assets in all the CPLs in this DCP */
-       std::list<boost::shared_ptr<const Asset> > assets () const;
-
-       struct Files {
-               std::list<std::string> cpls;
-               std::string pkl;
-               std::string asset_map;
-       };
-
-       Files _files;
+       void write_assetmap (Standard standard, std::string pkl_uuid, int pkl_length, XMLMetadata metadata) const;
 
        /** the directory that we are writing to */
        boost::filesystem::path _directory;
-       /** our CPLs */
-       std::list<boost::shared_ptr<CPL> > _cpls;
-
-       std::list<PathAssetMap> _asset_maps;
+       /** the assets that make up this DCP */
+       std::list<boost::shared_ptr<Asset> > _assets;
 };
 
 }
index d597e3dc203e765e10e2d20ec9331a7e0de637f1..a3822ed08ea578edf9c704613ccb046d8102829d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/dcp_time.cc
- *  @brief A representation of time within a DCP.
+ *  @brief Time class.
  */
 
+#include "raw_convert.h"
+#include "dcp_time.h"
+#include "exceptions.h"
+#include <boost/algorithm/string.hpp>
 #include <iostream>
 #include <vector>
-#include <boost/algorithm/string.hpp>
 #include <cmath>
-#include "dcp_time.h"
-#include "exceptions.h"
-#include "raw_convert.h"
 
 using namespace std;
 using namespace boost;
-using namespace libdcp;
+using namespace dcp;
 
 Time::Time (int frame, int frames_per_second)
        : h (0)
@@ -85,19 +85,19 @@ Time::Time (string time)
 }
 
 bool
-libdcp::operator== (Time const & a, Time const & b)
+dcp::operator== (Time const & a, Time const & b)
 {
        return (a.h == b.h && a.m == b.m && a.s == b.s && a.t == b.t);
 }
 
 bool
-libdcp::operator!= (Time const & a, Time const & b)
+dcp::operator!= (Time const & a, Time const & b)
 {
        return !(a == b);
 }
 
 bool
-libdcp::operator<= (Time const & a, Time const & b)
+dcp::operator<= (Time const & a, Time const & b)
 {
        if (a.h != b.h) {
                return a.h <= b.h;
@@ -119,7 +119,7 @@ libdcp::operator<= (Time const & a, Time const & b)
 }
 
 bool
-libdcp::operator< (Time const & a, Time const & b)
+dcp::operator< (Time const & a, Time const & b)
 {
        if (a.h != b.h) {
                return a.h < b.h;
@@ -141,7 +141,7 @@ libdcp::operator< (Time const & a, Time const & b)
 }
 
 bool
-libdcp::operator> (Time const & a, Time const & b)
+dcp::operator> (Time const & a, Time const & b)
 {
        if (a.h != b.h) {
                return a.h > b.h;
@@ -163,14 +163,14 @@ libdcp::operator> (Time const & a, Time const & b)
 }
 
 ostream &
-libdcp::operator<< (ostream& s, Time const & t)
+dcp::operator<< (ostream& s, Time const & t)
 {
        s << t.h << ":" << t.m << ":" << t.s << "." << t.t;
        return s;
 }
 
-libdcp::Time
-libdcp::operator+ (Time a, Time const & b)
+dcp::Time
+dcp::operator+ (Time a, Time const & b)
 {
        Time r;
 
@@ -197,8 +197,8 @@ libdcp::operator+ (Time a, Time const & b)
        return r;
 }
 
-libdcp::Time
-libdcp::operator- (Time a, Time const & b)
+dcp::Time
+dcp::operator- (Time a, Time const & b)
 {
        Time r;
 
@@ -226,7 +226,7 @@ libdcp::operator- (Time a, Time const & b)
 }
 
 float
-libdcp::operator/ (Time a, Time const & b)
+dcp::operator/ (Time a, Time const & b)
 {
        int64_t const at = a.h * 3600 * 250 + a.m * 60 * 250 + a.s * 250 + a.t;
        int64_t const bt = b.h * 3600 * 250 + b.m * 60 * 250 + b.s * 250 + b.t;
index 92cee9a0749a67d760a955f7a8ba5d1cff88a982..697ca230745b4cd46f53d65c5cad9a8e34484df0 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/dcp_time.h
- *  @brief A representation of time within a DCP.
+ *  @brief Time class.
  */
 
 #ifndef LIBDCP_TIME_H
 #define LIBDCP_TIME_H
 
+#include <stdint.h>
 #include <string>
 #include <iostream>
-#include <stdint.h>
 
-namespace libdcp {
+namespace dcp {
 
 /** @class Time
  *  @brief A representation of time within a DCP.
diff --git a/src/decrypted_kdm.cc b/src/decrypted_kdm.cc
new file mode 100644 (file)
index 0000000..7e9e146
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "decrypted_kdm.h"
+#include "decrypted_kdm_key.h"
+#include "encrypted_kdm.h"
+#include "util.h"
+#include "exceptions.h"
+#include "cpl.h"
+#include "mxf.h"
+#include "signer.h"
+#include "AS_DCP.h"
+#include "KM_util.h"
+#include "compose.hpp"
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+using std::list;
+using std::string;
+using std::stringstream;
+using std::setw;
+using std::setfill;
+using std::hex;
+using std::pair;
+using boost::shared_ptr;
+using namespace dcp;
+
+static void
+put (uint8_t ** d, string s)
+{
+        memcpy (*d, s.c_str(), s.length());
+        (*d) += s.length();
+}
+
+static void
+put (uint8_t ** d, uint8_t const * s, int N)
+{
+        memcpy (*d, s, N);
+        (*d) += N;
+}
+
+static void
+put_uuid (uint8_t ** d, string id)
+{
+        id.erase (std::remove (id.begin(), id.end(), '-'));
+        for (int i = 0; i < 32; i += 2) {
+                stringstream s;
+                s << id[i] << id[i + 1];
+                int h;
+                s >> hex >> h;
+                **d = h;
+                (*d)++;
+        }
+}
+
+static string
+get_uuid (unsigned char ** p)
+{
+       stringstream g;
+       
+       for (int i = 0; i < 16; ++i) {
+               g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
+               (*p)++;
+               if (i == 3 || i == 5 || i == 7 || i == 9) {
+                       g << '-';
+               }
+       }
+
+       return g.str ();
+}
+
+static string
+get (uint8_t ** p, int N)
+{
+       string g;
+       for (int i = 0; i < N; ++i) {
+               g += **p;
+               (*p)++;
+       }
+
+       return g;
+}
+
+DecryptedKDM::DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key)
+{
+       /* Read the private key */
+          
+       FILE* private_key_file = fopen_boost (private_key, "r");
+       if (!private_key_file) {
+               throw FileError ("could not find RSA private key file", private_key, errno);
+       }
+       
+       RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
+       fclose (private_key_file);      
+       if (!rsa) {
+               throw FileError ("could not read RSA private key file", private_key, errno);
+       }
+
+       /* Use the private key to decrypt the keys */
+
+       list<string> const encrypted_keys = kdm.keys ();
+       for (list<string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
+
+               /* Decode the base-64-encoded cipher value from the KDM */
+               unsigned char cipher_value[256];
+               int const cipher_value_len = base64_decode (*i, cipher_value, sizeof (cipher_value));
+
+               /* Decrypt it */
+               unsigned char * decrypted = new unsigned char[RSA_size(rsa)];
+               int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
+               if (decrypted_len == -1) {
+                       delete[] decrypted;
+                       throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
+               }
+
+               unsigned char* p = decrypted;
+               switch (decrypted_len) {
+               case 134:
+               {
+                       /* Inter-op */
+                       /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
+                       p += 16;
+                       /* 16 is is signer thumbprint [20 bytes] */
+                       p += 20;
+                       /* 36 is CPL id [16 bytes] */
+                       string const cpl_id = get_uuid (&p);
+                       /* 52 is key id [16 bytes] */
+                       string const key_id = get_uuid (&p);
+                       /* 68 is not-valid-before (a string) [25 bytes] */
+                       p += 25;
+                       /* 93 is not-valid-after (a string) [25 bytes] */
+                       p += 25;
+                       /* 118 is the key [ASDCP::KeyLen bytes] */
+                       _keys.push_back (DecryptedKDMKey ("", key_id, Key (p), cpl_id));
+                       break;
+               }
+               case 138:
+               {
+                       /* SMPTE */
+                       /* 0 is structure id (fixed sequence specified by standard) [16 bytes] */
+                       p += 16;
+                       /* 16 is is signer thumbprint [20 bytes] */
+                       p += 20;
+                       /* 36 is CPL id [16 bytes] */
+                       string const cpl_id = get_uuid (&p);
+                       /* 52 is key type [4 bytes] */
+                       string const key_type = get (&p, 4);
+                       /* 56 is key id [16 bytes] */
+                       string const key_id = get_uuid (&p);
+                       /* 72 is not-valid-before (a string) [25 bytes] */
+                       p += 25;
+                       /* 97 is not-valid-after (a string) [25 bytes] */
+                       p += 25;
+                       /* 112 is the key [ASDCP::KeyLen bytes] */
+                       _keys.push_back (DecryptedKDMKey (key_type, key_id, Key (p), cpl_id));
+                       break;
+               }
+               default:
+                       assert (false);
+               }               
+               
+               delete[] decrypted;
+       }
+
+       RSA_free (rsa);
+}
+
+DecryptedKDM::DecryptedKDM (
+       boost::shared_ptr<const CPL> cpl,
+       LocalTime not_valid_before,
+       LocalTime not_valid_after,
+       string annotation_text,
+       string content_title_text,
+       string issue_date
+       )
+       : _not_valid_before (not_valid_before)
+       , _not_valid_after (not_valid_after)
+       , _annotation_text (annotation_text)
+       , _content_title_text (content_title_text)
+       , _issue_date (issue_date)
+{
+       /* Create DecryptedKDMKey objects for each MXF asset */
+       list<shared_ptr<const Content> > content = cpl->content ();
+       for (list<shared_ptr<const Content> >::iterator i = content.begin(); i != content.end(); ++i) {
+               /* XXX: do non-MXF assets need keys? */
+               shared_ptr<const MXF> mxf = boost::dynamic_pointer_cast<const MXF> (*i);
+               if (mxf) {
+                       _keys.push_back (DecryptedKDMKey (mxf->key_type(), mxf->key_id(), mxf->key().get (), cpl->id ()));
+               }
+       }
+}
+
+EncryptedKDM
+DecryptedKDM::encrypt (shared_ptr<const Signer> signer, shared_ptr<const Certificate> recipient) const
+{
+       list<pair<string, string> > key_ids;
+       list<string> keys;
+       for (list<DecryptedKDMKey>::const_iterator i = _keys.begin(); i != _keys.end(); ++i) {
+
+               key_ids.push_back (make_pair (i->type(), i->id ()));
+
+               /* XXX: SMPTE only */
+               uint8_t block[138];
+               uint8_t* p = block;
+
+               /* Magic value specified by SMPTE S430-1-2006 */
+               uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
+               put (&p, structure_id, 16);
+
+               base64_decode (signer->certificates().leaf()->thumbprint (), p, 20);
+               p += 20;
+               
+               put_uuid (&p, i->cpl_id ());
+               put (&p, i->type ());
+               put_uuid (&p, i->id ());
+               put (&p, _not_valid_before.as_string ());
+               put (&p, _not_valid_after.as_string ());
+               put (&p, i->key().value(), ASDCP::KeyLen);
+               
+               /* Encrypt using the projector's public key */
+               RSA* rsa = recipient->public_key ();
+               unsigned char encrypted[RSA_size(rsa)];
+               int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
+               if (encrypted_len == -1) {
+                       throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
+               }
+               
+               /* Lazy overallocation */
+               char out[encrypted_len * 2];
+               Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
+               int const N = strlen (out);
+               stringstream lines;
+               for (int i = 0; i < N; ++i) {
+                       if (i > 0 && (i % 64) == 0) {
+                               lines << "\n";
+                       }
+                       lines << out[i];
+               }
+               
+               keys.push_back (lines.str ());
+       }
+
+       string device_list_description = recipient->common_name ();
+       if (device_list_description.find (".") != string::npos) {
+               device_list_description = device_list_description.substr (device_list_description.find (".") + 1);
+       }
+
+       return EncryptedKDM (
+               signer,
+               recipient,
+               device_list_description,
+               _keys.front().cpl_id (),
+               _content_title_text,
+               _not_valid_before,
+               _not_valid_after,
+               key_ids,
+               keys
+               );
+}
diff --git a/src/decrypted_kdm.h b/src/decrypted_kdm.h
new file mode 100644 (file)
index 0000000..bb50d6a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/decrypted_kdm.h
+ *  @brief DecryptedKDM class.
+ */
+
+#include "key.h"
+#include "local_time.h"
+#include "decrypted_kdm_key.h"
+#include <boost/filesystem.hpp>
+
+namespace dcp {
+
+class DecryptedKDMKey;
+class EncryptedKDM;
+class Signer;
+class Certificate;
+class CPL;
+
+/** @class DecryptedKDM
+ *  @brief A decrypted KDM.
+ *
+ *  This is a KDM that has either been decrypted by a target private key, or one which
+ *  has been created (by some other means) ready for encryption later.
+ *
+ *  A DecryptedKDM object can be created either from an EncryptedKDM and private key file,
+ *  or from the details of the MXFs that the KDM should protect.
+ */
+class DecryptedKDM
+{
+public:
+       /** @param kdm Encrypted KDM.
+        *  @param private_key Private key file name.
+        */
+       DecryptedKDM (EncryptedKDM const & kdm, boost::filesystem::path private_key);
+
+       /** Construct a DecryptedKDM.
+        *  @param cpl CPL that the keys are for.
+        *  @param not_valid_before Start time for the KDM.
+        *  @param not_valid_after End time for the KDM.
+        */
+       DecryptedKDM (
+               boost::shared_ptr<const CPL> cpl,
+               LocalTime not_valid_before,
+               LocalTime not_valid_after,
+               std::string annotation_text,
+               std::string content_title_text,
+               std::string issue_date
+               );
+
+       /** Add a key to this KDM.
+        *  @param type Key type (MDIK, MDAK etc.)
+        *  @param id Key id.
+        *  @param key the key itself (which has been used to encrypt a MXF).
+        */
+       void add_key (std::string type, std::string id, Key key);
+
+       /** Encrypt this KDM's keys and sign the whole KDM.
+        *  @param signer Signer.
+        *  @param recipient Certificate of the projector/server which should receive this KDM's keys.
+        *  @return Encrypted KDM.
+        */
+       EncryptedKDM encrypt (boost::shared_ptr<const Signer> signer, boost::shared_ptr<const Certificate> recipient) const;
+
+       /** @return This KDM's (decrypted) keys, which could be used to decrypt MXFs. */
+       std::list<DecryptedKDMKey> keys () const {
+               return _keys;
+       }
+
+private:
+       LocalTime _not_valid_before;
+       LocalTime _not_valid_after;
+       std::string _annotation_text;
+       std::string _content_title_text;
+       std::string _issue_date;
+       std::list<DecryptedKDMKey> _keys;
+};
+
+}
diff --git a/src/decrypted_kdm_key.cc b/src/decrypted_kdm_key.cc
new file mode 100644 (file)
index 0000000..aac1253
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "decrypted_kdm_key.h"
+
+using namespace dcp;
+
+bool
+dcp::operator== (dcp::DecryptedKDMKey const & a, dcp::DecryptedKDMKey const & b)
+{
+       return a.type() == b.type() && a.id() == b.id() && a.key() == b.key() && a.cpl_id() == b.cpl_id();
+}
diff --git a/src/decrypted_kdm_key.h b/src/decrypted_kdm_key.h
new file mode 100644 (file)
index 0000000..2a06199
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/decrypted_kdm_key.h
+ *  @brief DecryptedKDMKey class
+ */
+
+#ifndef LIBDCP_DECRYPTED_KDM_KEY_H
+#define LIBDCP_DECRYPTED_KDM_KEY_H
+
+#include "key.h"
+
+namespace dcp {
+
+/** @class DecryptedKDMKey
+ *  @brief An un- or de-crypted key from a KDM.
+ */
+class DecryptedKDMKey
+{
+public:
+       DecryptedKDMKey (std::string type, std::string id, Key key, std::string cpl_id)
+               : _type (type)
+               , _id (id)
+               , _key (key)
+               , _cpl_id (cpl_id)
+       {}
+
+       std::string type () const {
+               return _type;
+       }
+
+       std::string id () const {
+               return _id;
+       }
+
+       Key key () const {
+               return _key;
+       }
+
+       std::string cpl_id () const {
+               return _cpl_id;
+       }
+
+private:       
+       std::string _type;
+       std::string _id;
+       Key _key;
+       std::string _cpl_id;
+};
+
+bool operator== (DecryptedKDMKey const &, DecryptedKDMKey const &);
+
+}
+
+#endif
diff --git a/src/encrypted_kdm.cc b/src/encrypted_kdm.cc
new file mode 100644 (file)
index 0000000..5330490
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "encrypted_kdm.h"
+#include "util.h"
+#include "signer.h"
+#include <libcxml/cxml.h>
+#include <libxml++/document.h>
+#include <libxml++/nodes/element.h>
+#include <libxml/parser.h>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+using std::list;
+using std::string;
+using std::map;
+using std::pair;
+using boost::shared_ptr;
+using namespace dcp;
+
+namespace dcp {
+
+/** Namespace for classes used to hold our data; they are internal to this .cc file */ 
+namespace data {
+
+class Signer
+{
+public:
+       Signer () {}
+       
+       Signer (shared_ptr<const cxml::Node> node)
+               : x509_issuer_name (node->string_child ("X509IssuerName"))
+               , x509_serial_number (node->string_child ("X509SerialNumber"))
+       {
+               
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
+               node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
+       }
+       
+       string x509_issuer_name;
+       string x509_serial_number;
+};
+
+class X509Data
+{
+public:
+       X509Data () {}
+       
+       X509Data (boost::shared_ptr<const cxml::Node> node)
+               : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
+               , x509_certificate (node->string_child ("X509Certificate"))
+       {
+               node->done ();
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
+               node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
+       }
+       
+       Signer x509_issuer_serial;
+       std::string x509_certificate;
+};
+       
+class Reference
+{
+public:
+       Reference () {}
+       
+       Reference (string u)
+               : uri (u)
+       {}
+
+       Reference (shared_ptr<const cxml::Node> node)
+               : uri (node->string_attribute ("URI"))
+               , digest_value (node->string_child ("DigestValue"))
+       {
+
+       }
+       
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->set_attribute ("URI", uri);
+               node->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
+               node->add_child("DigestValue", "ds")->add_child_text (digest_value);
+       }
+       
+       string uri;
+       string digest_value;
+};
+
+class SignedInfo
+{
+public:
+       SignedInfo ()
+               : authenticated_public ("#ID_AuthenticatedPublic")
+               , authenticated_private ("#ID_AuthenticatedPrivate")
+       {}
+
+       SignedInfo (shared_ptr<const cxml::Node> node)
+       {
+               list<shared_ptr<cxml::Node> > references = node->node_children ("Reference");
+               for (list<shared_ptr<cxml::Node> >::const_iterator i = references.begin(); i != references.end(); ++i) {
+                       if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPublic") {
+                               authenticated_public = Reference (*i);
+                       } else if ((*i)->string_attribute ("URI") == "#ID_AuthenticatedPrivate") {
+                               authenticated_private = Reference (*i);
+                       }
+
+                       /* XXX: do something if we don't recognise the node */
+               }
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->add_child ("CanonicalizationMethod", "ds")->set_attribute (
+                       "Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"
+                       );
+
+               node->add_child ("SignatureMethod", "ds")->set_attribute (
+                       "Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
+                       );
+               
+               authenticated_public.as_xml (node->add_child ("Reference", "ds"));
+               authenticated_private.as_xml (node->add_child ("Reference", "ds"));
+       }
+               
+private:
+       Reference authenticated_public;
+       Reference authenticated_private;
+};
+       
+class Signature
+{
+public:
+       Signature () {}
+
+       Signature (shared_ptr<const cxml::Node> node)
+               : signed_info (node->node_child ("SignedInfo"))
+               , signature_value (node->string_child ("SignatureValue"))
+       {
+               list<shared_ptr<cxml::Node> > x509_data_nodes = node->node_child("KeyInfo")->node_children ("X509Data");
+               for (list<shared_ptr<cxml::Node> >::const_iterator i = x509_data_nodes.begin(); i != x509_data_nodes.end(); ++i) {
+                       x509_data.push_back (X509Data (*i));
+               }
+       }
+
+       void as_xml (xmlpp::Node* node) const
+       {
+               signed_info.as_xml (node->add_child ("SignedInfo", "ds"));
+               node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
+               
+               xmlpp::Element* key_info_node = node->add_child ("KeyInfo", "ds");
+               for (std::list<X509Data>::const_iterator i = x509_data.begin(); i != x509_data.end(); ++i) {
+                       i->as_xml (key_info_node->add_child ("X509Data", "ds"));
+               }
+       }
+
+       SignedInfo signed_info;
+       string signature_value;
+       list<X509Data> x509_data;
+};
+
+class AuthenticatedPrivate
+{
+public:
+       AuthenticatedPrivate () {}
+       
+       AuthenticatedPrivate (shared_ptr<const cxml::Node> node)
+       {
+               list<shared_ptr<cxml::Node> > encrypted_key_nodes = node->node_children ("EncryptedKey");
+               for (list<shared_ptr<cxml::Node> >::const_iterator i = encrypted_key_nodes.begin(); i != encrypted_key_nodes.end(); ++i) {
+                       encrypted_key.push_back ((*i)->node_child("CipherData")->string_child ("CipherValue"));
+               }
+       }
+
+       void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
+       {
+               references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
+
+               for (list<string>::const_iterator i = encrypted_key.begin(); i != encrypted_key.end(); ++i) {
+                       xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
+                       xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
+                       encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
+                       xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
+                       digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
+                       xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
+                       cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
+               }
+       }
+       
+       list<string> encrypted_key;
+};
+
+class TypedKeyId
+{
+public:
+       TypedKeyId () {}
+       
+       TypedKeyId (shared_ptr<const cxml::Node> node)
+               : key_type (node->string_child ("KeyType"))
+               , key_id (node->string_child ("KeyId").substr (9))
+       {
+
+       }
+
+       TypedKeyId (string type, string id)
+               : key_type (type)
+               , key_id (id)
+       {}
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->add_child("KeyType")->add_child_text (key_type);
+               node->add_child("KeyId")->add_child_text ("urn:uuid:" + key_id);
+       }
+
+       string key_type;
+       string key_id;
+};
+
+class KeyIdList
+{
+public:
+       KeyIdList () {}
+       
+       KeyIdList (shared_ptr<const cxml::Node> node)
+       {
+               list<shared_ptr<cxml::Node> > typed_key_id_nodes = node->node_children ("TypedKeyId");
+               for (list<shared_ptr<cxml::Node> >::const_iterator i = typed_key_id_nodes.begin(); i != typed_key_id_nodes.end(); ++i) {
+                       typed_key_id.push_back (TypedKeyId (*i));
+               }
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               for (list<TypedKeyId>::const_iterator i = typed_key_id.begin(); i != typed_key_id.end(); ++i) {
+                       i->as_xml (node->add_child("TypedKeyId"));
+               }
+       }
+
+       list<TypedKeyId> typed_key_id;
+};
+
+class AuthorizedDeviceInfo
+{
+public:
+       AuthorizedDeviceInfo ()
+               : device_list_identifier (make_uuid ())
+               /* Sometimes digital_cinema_tools uses this magic thumbprint instead of that from an actual
+                  recipient certificate.  KDMs delivered to City Screen appear to use the same thing.
+               */
+               , certificate_thumbprint ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=")
+       {}
+       
+       AuthorizedDeviceInfo (shared_ptr<const cxml::Node> node)
+               : device_list_identifier (node->string_child ("DeviceListIdentifier").substr (9))
+               , device_list_description (node->string_child ("DeviceListDescription"))
+               , certificate_thumbprint (node->node_child("DeviceList")->string_child ("CertificateThumbprint"))
+       {
+
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->add_child ("DeviceListIdentifier")->add_child_text ("urn:uuid:" + device_list_identifier);
+               node->add_child ("DeviceListDescription")->add_child_text (device_list_description);
+               xmlpp::Element* device_list = node->add_child ("DeviceList");
+               device_list->add_child("CertificateThumbprint")->add_child_text (certificate_thumbprint);
+       }
+       
+       string device_list_identifier;
+       string device_list_description;
+       string certificate_thumbprint;
+};
+
+class X509IssuerSerial
+{
+public:
+       X509IssuerSerial () {}
+       
+       X509IssuerSerial (shared_ptr<const cxml::Node> node)
+               : x509_issuer_name (node->string_child ("X509IssuerName"))
+               , x509_serial_number (node->string_child ("X509SerialNumber"))
+       {
+
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
+               node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
+       }
+
+       string x509_issuer_name;
+       string x509_serial_number;
+};
+
+class Recipient
+{
+public:
+       Recipient () {}
+       
+       Recipient (shared_ptr<const cxml::Node> node)
+               : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
+               , x509_subject_name (node->string_child ("X509SubjectName"))
+       {
+
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
+               node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
+       }
+       
+       X509IssuerSerial x509_issuer_serial;
+       string x509_subject_name;
+};
+
+class KDMRequiredExtensions
+{
+public:
+       KDMRequiredExtensions () {}
+       
+       KDMRequiredExtensions (shared_ptr<const cxml::Node> node)
+               : recipient (node->node_child ("Recipient"))
+               , composition_playlist_id (node->string_child ("CompositionPlaylistId").substr (9))
+               , content_title_text (node->string_child ("ContentTitleText"))
+               , not_valid_before (node->string_child ("ContentKeysNotValidBefore"))
+               , not_valid_after (node->string_child ("ContentKeysNotValidAfter"))
+               , authorized_device_info (node->node_child ("AuthorizedDeviceInfo"))
+               , key_id_list (node->node_child ("KeyIdList"))
+       {
+
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               node->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
+               
+               recipient.as_xml (node->add_child ("Recipient"));
+               node->add_child("CompositionPlaylistId")->add_child_text ("urn:uuid:" + composition_playlist_id);
+               /* XXX: no ContentAuthenticator */
+               node->add_child("ContentTitleText")->add_child_text (content_title_text);
+               node->add_child("ContentKeysNotValidBefore")->add_child_text (not_valid_before.as_string ());
+               node->add_child("ContentKeysNotValidAfter")->add_child_text (not_valid_after.as_string ());
+               authorized_device_info.as_xml (node->add_child ("AuthorizedDeviceInfo"));
+               key_id_list.as_xml (node->add_child ("KeyIdList"));
+               
+               xmlpp::Element* forensic_mark_flag_list = node->add_child ("ForensicMarkFlagList");
+               forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
+               forensic_mark_flag_list->add_child("ForensicMarkFlag")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
+       }
+       
+       Recipient recipient;
+       string composition_playlist_id;
+       string content_title_text;
+       LocalTime not_valid_before;
+       LocalTime not_valid_after;
+       AuthorizedDeviceInfo authorized_device_info;
+       KeyIdList key_id_list;
+};
+
+class RequiredExtensions
+{
+public:
+       RequiredExtensions () {}
+       
+       RequiredExtensions (shared_ptr<const cxml::Node> node)
+               : kdm_required_extensions (node->node_child ("KDMRequiredExtensions"))
+       {
+
+       }
+
+       void as_xml (xmlpp::Element* node) const
+       {
+               kdm_required_extensions.as_xml (node->add_child ("KDMRequiredExtensions"));
+       }
+       
+       KDMRequiredExtensions kdm_required_extensions;
+};
+
+class AuthenticatedPublic
+{
+public:
+       AuthenticatedPublic ()
+               : message_id (make_uuid ())
+               , issue_date (LocalTime().as_string ())
+       {}
+       
+       AuthenticatedPublic (shared_ptr<const cxml::Node> node)
+               : message_id (node->string_child ("MessageId").substr (9))
+               , annotation_text (node->string_child ("AnnotationText"))
+               , issue_date (node->string_child ("IssueDate"))
+               , signer (node->node_child ("Signer"))
+               , required_extensions (node->node_child ("RequiredExtensions"))
+       {
+
+       }
+
+       void as_xml (xmlpp::Element* node, map<string, xmlpp::Attribute *>& references) const
+       {
+               references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
+               
+               node->add_child("MessageId")->add_child_text ("urn:uuid:" + message_id);
+               node->add_child("MessageType")->add_child_text ("http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type");
+               node->add_child("AnnotationText")->add_child_text (annotation_text);
+               node->add_child("IssueDate")->add_child_text (issue_date);
+
+               signer.as_xml (node->add_child ("Signer"));
+               required_extensions.as_xml (node->add_child ("RequiredExtensions"));
+
+               node->add_child ("NonCriticalExtensions");
+       }
+
+       string message_id;
+       string annotation_text;
+       string issue_date;
+       Signer signer;
+       RequiredExtensions required_extensions;
+};
+
+/** Class to describe our data.  We use a class hierarchy as it's a bit nicer
+ *  for XML data than a flat description.
+ */
+class EncryptedKDMData
+{
+public:
+       EncryptedKDMData ()
+       {
+
+       }
+       
+       EncryptedKDMData (shared_ptr<const cxml::Node> node)
+               : authenticated_public (node->node_child ("AuthenticatedPublic"))
+               , authenticated_private (node->node_child ("AuthenticatedPrivate"))
+               , signature (node->node_child ("Signature"))
+       {
+               
+       }
+
+       shared_ptr<xmlpp::Document> as_xml () const
+       {
+               shared_ptr<xmlpp::Document> document (new xmlpp::Document ());
+               xmlpp::Element* root = document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
+               root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
+               root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
+               map<string, xmlpp::Attribute *> references;
+               authenticated_public.as_xml (root->add_child ("AuthenticatedPublic"), references);
+               authenticated_private.as_xml (root->add_child ("AuthenticatedPrivate"), references);
+               signature.as_xml (root->add_child ("Signature", "ds"));
+
+               for (map<string, xmlpp::Attribute*>::const_iterator i = references.begin(); i != references.end(); ++i) {
+                       xmlAddID (0, document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
+               }
+
+               return document;
+       }
+
+       AuthenticatedPublic authenticated_public;
+       AuthenticatedPrivate authenticated_private;
+       Signature signature;
+};
+
+}
+}
+
+EncryptedKDM::EncryptedKDM (boost::filesystem::path file)
+       : _data (new data::EncryptedKDMData (shared_ptr<cxml::Node> (new cxml::Document ("DCinemaSecurityMessage", file))))
+{
+       
+}
+
+EncryptedKDM::EncryptedKDM (
+       shared_ptr<const Signer> signer,
+       shared_ptr<const Certificate> recipient,
+       string device_list_description,
+       string cpl_id,
+       string content_title_text,
+       LocalTime not_valid_before,
+       LocalTime not_valid_after,
+       list<pair<string, string> > key_ids,
+       list<string> keys
+       )
+       : _data (new data::EncryptedKDMData)
+{
+       /* Fill our XML-ish description in with the juicy bits that the caller has given */
+       
+       data::AuthenticatedPublic& aup = _data->authenticated_public;
+       aup.signer.x509_issuer_name = signer->certificates().leaf()->issuer ();
+       aup.signer.x509_serial_number = signer->certificates().leaf()->serial ();
+
+       data::KDMRequiredExtensions& kre = _data->authenticated_public.required_extensions.kdm_required_extensions;
+       kre.recipient.x509_issuer_serial.x509_issuer_name = recipient->issuer ();
+       kre.recipient.x509_issuer_serial.x509_serial_number = recipient->serial ();
+       kre.recipient.x509_subject_name = recipient->subject ();
+       kre.authorized_device_info.device_list_description = device_list_description;
+       kre.composition_playlist_id = cpl_id;
+       kre.content_title_text = content_title_text;
+       kre.not_valid_before = not_valid_before;
+       kre.not_valid_after = not_valid_after;
+
+       for (list<pair<string, string> >::const_iterator i = key_ids.begin(); i != key_ids.end(); ++i) {
+               kre.key_id_list.typed_key_id.push_back (data::TypedKeyId (i->first, i->second));
+       }
+
+       _data->authenticated_private.encrypted_key = keys;
+
+       /* Read the XML so far and sign it */
+       shared_ptr<xmlpp::Document> doc = _data->as_xml ();
+       xmlpp::Node::NodeList children = doc->get_root_node()->get_children ();
+       for (xmlpp::Node::NodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
+               if ((*i)->get_name() == "Signature") {
+                       signer->add_signature_value (*i, "ds");
+               }
+       }
+
+       /* Read the bits that add_signature_value did back into our variables */
+       shared_ptr<cxml::Node> signed_doc (new cxml::Node (doc->get_root_node ()));
+       _data->signature = data::Signature (signed_doc->node_child ("Signature"));
+}
+
+EncryptedKDM::EncryptedKDM (EncryptedKDM const & other)
+       : _data (new data::EncryptedKDMData (*other._data))
+{
+
+}
+
+EncryptedKDM &
+EncryptedKDM::operator= (EncryptedKDM const & other)
+{
+       if (this == &other) {
+               return *this;
+       }
+
+       delete _data;
+       _data = new data::EncryptedKDMData (*other._data);
+       return *this;
+}
+
+EncryptedKDM::~EncryptedKDM ()
+{
+       delete _data;
+}
+
+void
+EncryptedKDM::as_xml (boost::filesystem::path path) const
+{
+       FILE* f = fopen_boost (path, "w");
+       string const x = as_xml ();
+       fwrite (x.c_str(), 1, x.length(), f);
+       fclose (f);
+}
+       
+string
+EncryptedKDM::as_xml () const
+{
+       xmlpp::Document document;
+       xmlpp::Element* root = document.create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
+       root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
+       root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
+
+       return _data->as_xml()->write_to_string ("UTF-8");
+}
+
+list<string>
+EncryptedKDM::keys () const
+{
+       return _data->authenticated_private.encrypted_key;
+}
diff --git a/src/encrypted_kdm.h b/src/encrypted_kdm.h
new file mode 100644 (file)
index 0000000..41231c2
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/encrypted_kdm.h
+ *  @brief EncryptedKDM class.
+ */
+
+#ifndef LIBDCP_ENCRYPTED_KDM_H
+#define LIBDCP_ENCRYPTED_KDM_H
+
+#include "local_time.h"
+#include <boost/filesystem.hpp>
+#include <boost/date_time/local_time/local_time.hpp>
+
+namespace cxml {
+       class Node;
+}
+
+namespace dcp {
+
+namespace data {
+       class EncryptedKDMData;
+}
+
+class Signer;  
+class Certificate;
+
+/** @class EncryptedKDM
+ *  @brief An encrypted KDM.
+ *
+ *  This is a KDM whose keys are encrypted using the target projector's private key.
+ *  An EncryptedKDM object can be initialised from a KDM XML file, or created from
+ *  a DecryptedKDM (using DecryptedKDM::encrypt).
+ */
+class EncryptedKDM
+{
+public:
+       /** Read a KDM from an XML file.
+        *  @param file XML file to read.
+        */
+       EncryptedKDM (boost::filesystem::path file);
+
+       EncryptedKDM (EncryptedKDM const & kdm);
+       EncryptedKDM & operator= (EncryptedKDM const &);
+       ~EncryptedKDM ();
+
+       /** Write this KDM as XML to a file.
+        *  @param file File to write to.
+        */
+       void as_xml (boost::filesystem::path file) const;
+
+       /** @return This KDM as XML */
+       std::string as_xml () const;
+
+       /** @return The base64-encoded and encrypted keys that this KDM delivers.
+        *  Note that the returned `keys' contain more than just the asset decryption
+        *  keys (also key id, CPL id etc.)
+        */
+       std::list<std::string> keys () const;
+       
+private:
+
+       friend class DecryptedKDM;
+
+       /** Construct an EncryptedKDM from a set of details */
+       EncryptedKDM (
+               boost::shared_ptr<const Signer> signer,
+               boost::shared_ptr<const Certificate> recipient,
+               std::string device_list_description,
+               std::string cpl_id,
+               std::string cpl_content_title_text,
+               LocalTime _not_valid_before,
+               LocalTime _not_valid_after,
+               std::list<std::pair<std::string, std::string> > key_ids,
+               std::list<std::string> keys
+               );
+       
+       data::EncryptedKDMData* _data;
+};
+
+}
+
+#endif
index 951627a5afe65e55e034696067c7ed75dc7bbde6..28846e1617b0523a43f31b7fcaf0f897ee91f518 100644 (file)
 
 */
 
+/** @file  src/exceptions.cc
+ *  @brief Exceptions thrown by libdcp.
+ */
+
 #include "exceptions.h"
 #include "compose.hpp"
 
 using std::string;
-using namespace libdcp;
+using namespace dcp;
 
-FileError::FileError (std::string const & message, boost::filesystem::path filename, int number)
+FileError::FileError (string message, boost::filesystem::path filename, int number)
        : _message (String::compose ("%1 (%2) (error %3)", message, filename.string(), number))
        , _filename (filename)
        , _number (number)
@@ -31,3 +35,36 @@ FileError::FileError (std::string const & message, boost::filesystem::path filen
 
 }
 
+UnresolvedRefError::UnresolvedRefError (string id)
+       : _message (String::compose ("Unresolved reference to asset id %1", id))
+{
+
+}
+
+TimeFormatError::TimeFormatError (string bad_time)
+       : _message (String::compose ("Bad time string %1", bad_time))
+{
+
+}
+
+MissingAssetError::MissingAssetError (boost::filesystem::path path, AssetType type)
+       : _path (path)
+       , _type (type)
+{
+       string type_name;
+       switch (_type) {
+       case MAIN_PICTURE:
+               type_name = " for main picture";
+               break;
+       case MAIN_SOUND:
+               type_name = " for main sound";
+               break;
+       case MAIN_SUBTITLE:
+               type_name = " for main subtitle";
+               break;
+       case UNKNOWN:
+               break;
+       }
+       
+       _message = String::compose ("Missing asset %1%2", path.string(), type_name);
+}
index 2070dd0fa44d38cb3f2b682790a827d9ad5c3d2f..5d8000d710418db8ebd80ade1de5f70ab0d92244 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
  *  @brief Exceptions thrown by libdcp.
  */
 
-namespace libdcp
+namespace dcp
 {
 
-/** @brief An exception related to a file */
+/** @class FileError
+ *  @brief An exception related to a file
+ */
 class FileError : public std::exception
 {
 public:
-       FileError (std::string const & message, boost::filesystem::path filename, int number);
+       FileError (std::string message, boost::filesystem::path filename, int number);
        ~FileError () throw () {}
 
        /** @return error message */
@@ -59,20 +61,24 @@ private:
        int _number;
 };
 
-/** @brief An exception related to an MXF file */
+/** @class MXFFileError
+ *  @brief An exception related to an MXF file
+ */
 class MXFFileError : public FileError
 {
 public:
-       MXFFileError (std::string const & message, boost::filesystem::path filename, int number)
+       MXFFileError (std::string message, boost::filesystem::path filename, int number)
                : FileError (message, filename, number)
        {}
 };
        
-/** @brief A miscellaneous exception */
+/** @class MiscError
+ *  @brief A miscellaneous exception
+ */
 class MiscError : public std::exception
 {
 public:
-       MiscError (std::string const & message) : _message (message) {}
+       MiscError (std::string message) : _message (message) {}
        ~MiscError () throw () {}
 
        /** @return error message */
@@ -85,11 +91,13 @@ private:
        std::string _message;
 };
 
-/** @brief A DCP read exception */
+/** @class DCPReadError
+ *  @brief A DCP read exception
+ */
 class DCPReadError : public std::exception
 {
 public:
-       DCPReadError (std::string const & message) : _message (message) {}
+       DCPReadError (std::string message) : _message (message) {}
        ~DCPReadError () throw () {}
 
        /** @return error message */
@@ -97,16 +105,41 @@ public:
                return _message.c_str ();
        }
 
-private:
+protected:
+       DCPReadError () {}
+       
        /** error message */
        std::string _message;
 };
 
-/** @brief An XML error */
+/** @class MissingAssetError
+ *  @brief An error of a missing asset.
+ */
+class MissingAssetError : public DCPReadError
+{
+public:
+       enum AssetType {
+               MAIN_PICTURE,  //< main picture is missing
+               MAIN_SOUND,    //< main sound is missing
+               MAIN_SUBTITLE, //< main subtitle is missing
+               UNKNOWN        //< something is missing but we don't know what
+       };
+       
+       MissingAssetError (boost::filesystem::path, AssetType = UNKNOWN);
+       ~MissingAssetError () throw () {}
+
+private:
+       boost::filesystem::path _path;
+       AssetType _type;
+};
+
+/** @class XMLError
+ *  @brief An XML error
+ */
 class XMLError : public std::exception
 {
 public:
-       XMLError (std::string const & message) : _message (message) {}
+       XMLError (std::string message) : _message (message) {}
        ~XMLError () throw () {}
 
        /** @return error message */
@@ -118,7 +151,43 @@ private:
        /** error message */
        std::string _message;
 };
-       
+
+/** @class UnresolvedRefError
+ *  @brief An exception caused by a reference (by UUID) to something which is not known
+ */
+class UnresolvedRefError : public std::exception
+{
+public:
+       UnresolvedRefError (std::string id);
+       ~UnresolvedRefError () throw () {}
+
+       /** @return error message */
+       char const * what () const throw () {
+               return _message.c_str ();
+       }
+
+private:
+       std::string _message;
+};
+
+/** @class TimeFormatError
+ *  @brief A an error with a string passed to LocalTime.
+ */
+class TimeFormatError : public std::exception
+{
+public:
+       TimeFormatError (std::string bad_time);
+       ~TimeFormatError () throw () {}
+
+       /** @return error message */
+       char const * what () const throw () {
+               return _message.c_str ();
+       }
+
+private:
+       std::string _message;
+};
+
 }
 
 #endif
diff --git a/src/file.cc b/src/file.cc
new file mode 100644 (file)
index 0000000..f4a91ad
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/file.cc
+ *  @brief File class.
+ */
+
+#include "file.h"
+#include "util.h"
+#include <stdio.h>
+
+using namespace dcp;
+
+/** Read a file into memory.
+ *  @param file to read.
+ */
+File::File (boost::filesystem::path file)
+{
+       _size = boost::filesystem::file_size (file);
+       _data = new uint8_t[_size];
+       FILE* f = dcp::fopen_boost (file, "r");
+       assert (f);
+       fread (_data, 1, _size, f);
+       fclose (f);
+}
+
+/** File destructor */
+File::~File ()
+{
+       delete[] _data;
+}
diff --git a/src/file.h b/src/file.h
new file mode 100644 (file)
index 0000000..b06cece
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/file.h
+ *  @brief File class.
+ */
+
+#ifndef LIBDCP_FILE_H
+#define LIBDCP_FILE_H
+
+#include <boost/filesystem.hpp>
+
+namespace dcp {
+
+/** @class File
+ *  @brief Helper class which loads a file into memory.
+ */
+class File
+{
+public:
+       File (boost::filesystem::path file);
+       ~File ();
+       
+       uint8_t* data () const {
+               return _data;
+       }
+
+       int64_t size () const {
+               return _size;
+       }
+
+private:
+       uint8_t* _data; ///< file's data
+       int64_t _size;  ///< data size in bytes
+};
+
+}
+
+#endif
diff --git a/src/font.cc b/src/font.cc
new file mode 100644 (file)
index 0000000..51bd866
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "types.h"
+#include "raw_convert.h"
+#include "font.h"
+#include "xml.h"
+#include "text.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+using namespace dcp;
+
+Font::Font (boost::shared_ptr<const cxml::Node> node)
+{
+       text = node->content ();
+       
+       id = node->optional_string_attribute ("Id").get_value_or ("");
+       size = node->optional_number_attribute<int64_t> ("Size").get_value_or (0);
+       italic = node->optional_bool_attribute ("Italic");
+       optional<string> c = node->optional_string_attribute ("Color");
+       if (c) {
+               color = Color (c.get ());
+       }
+       optional<string> const e = node->optional_string_attribute ("Effect");
+       if (e) {
+               effect = string_to_effect (e.get ());
+       }
+       c = node->optional_string_attribute ( "EffectColor");
+       if (c) {
+               effect_color = Color (c.get ());
+       }
+       subtitle_nodes = type_children<Subtitle> (node, "Subtitle");
+       font_nodes = type_children<Font> (node, "Font");
+       text_nodes = type_children<Text> (node, "Text");
+}
+
+Font::Font (std::list<boost::shared_ptr<Font> > const & font_nodes)
+       : size (0)
+       , italic (false)
+       , color ("FFFFFFFF")
+       , effect_color ("FFFFFFFF")
+{
+       for (list<shared_ptr<Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+               if (!(*i)->id.empty ()) {
+                       id = (*i)->id;
+               }
+               if ((*i)->size != 0) {
+                       size = (*i)->size;
+               }
+               if ((*i)->italic) {
+                       italic = (*i)->italic.get ();
+               }
+               if ((*i)->color) {
+                       color = (*i)->color.get ();
+               }
+               if ((*i)->effect) {
+                       effect = (*i)->effect.get ();
+               }
+               if ((*i)->effect_color) {
+                       effect_color = (*i)->effect_color.get ();
+               }
+       }
+}
diff --git a/src/font.h b/src/font.h
new file mode 100644 (file)
index 0000000..0250e99
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/font.h
+ *  @brief Font class
+ */
+
+#include "types.h"
+#include "subtitle.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <list>
+
+namespace cxml {
+       class Node;
+}
+
+namespace dcp {
+
+/** @class Font
+ *  @brief Helper class for parsing subtitle XML.
+ */
+class Font 
+{
+public:
+       Font ()
+               : size (0)
+       {}
+       
+       Font (boost::shared_ptr<const cxml::Node> node);
+       Font (std::list<boost::shared_ptr<Font> > const & font_nodes);
+
+       std::string text;
+       std::string id;
+       int size;
+       boost::optional<bool> italic;
+       boost::optional<Color> color;
+       boost::optional<Effect> effect;
+       boost::optional<Color> effect_color;
+       
+       std::list<boost::shared_ptr<Subtitle> > subtitle_nodes;
+       std::list<boost::shared_ptr<Font> > font_nodes;
+       std::list<boost::shared_ptr<Text> > text_nodes;
+};
+
+}
index 80d9b9021b10d5de83a6a1e7180e114d5b790dbc..abe35b7cf24e13dc051d28a456c12db08adde13a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <cmath>
 #include "gamma_lut.h"
 #include "lut_cache.h"
+#include <cmath>
 
-using namespace libdcp;
+using namespace dcp;
 
 LUTCache<GammaLUT> GammaLUT::cache;
 
-GammaLUT::GammaLUT(int bits, float gamma)
-       : LUT (bits, gamma)
+GammaLUT::GammaLUT (int bit_depth, float gamma, bool linearised)
+       : _bit_depth (bit_depth)
+       , _gamma (gamma)
+       , _linearised (linearised)
 {
-       int const bit_length = pow(2, bits);
-       for (int i = 0; i < bit_length; ++i) {
-               _lut[i] = pow(float(i) / (bit_length - 1), gamma);
+       _lut = new float[int(std::pow(2.0f, _bit_depth))];
+       int const bit_length = pow (2, _bit_depth);
+
+       if (_linearised) {
+               for (int i = 0; i < bit_length; ++i) {
+                       float const p = static_cast<float> (i) / (bit_length - 1);
+                       if (p > 0.04045) {
+                               _lut[i] = pow ((p + 0.055) / 1.055, gamma);
+                       } else {
+                               _lut[i] = p / 12.92;
+                       }
+               }
+       } else {
+               for (int i = 0; i < bit_length; ++i) {
+                       _lut[i] = pow(float(i) / (bit_length - 1), gamma);
+               }
        }
 }
index 32e4af0274f5df237f6c5347863b9b2edacccdd9..76a63ccfe8444665a644df6ec18a7bc23d79e548 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #ifndef LIBDCP_GAMMA_LUT_H
 #define LIBDCP_GAMMA_LUT_H
 
-#include "lut.h"
 #include "lut_cache.h"
 
-namespace libdcp {
+namespace dcp {
 
-class GammaLUT : public LUT
+class GammaLUT
 {
 public:
-       GammaLUT (int bit_length, float gamma);
+       GammaLUT (int bit_depth, float gamma, bool linearised);
+
+       ~GammaLUT () {
+               delete[] _lut;
+       }
+       
+       float const * lut () const {
+               return _lut;
+       }
+
+       int bit_depth () const {
+               return _bit_depth;
+       }
+
+       float gamma () const {
+               return _gamma;
+       }
+
+       bool linearised () const {
+               return _linearised;
+       }
+
        static LUTCache<GammaLUT> cache;
+       
+private:
+       float* _lut;
+       int _bit_depth;
+       float _gamma;
+       bool _linearised;
 };
 
 }
index 24174fca80f3848cac815cccbbc0f0785a7c7357..15ff24dc011a04b175b9a69533afc57454564b63 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
 #include "image.h"
 
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 Image::Image (Size s)
        : _size (s)
@@ -34,7 +34,7 @@ Image::Image (Image const & other)
 
 }
 
-Image::Image (shared_ptr<const Image> other)
+Image::Image (boost::shared_ptr<const Image> other)
        : _size (other->_size)
 {
 
index 8b7fee097d4f038e6c2d175f1ba24d1da86f6b68..069534eacd7296ef5832407206b77f838728aa23 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
 
 #include "util.h"
 
-namespace libdcp {
+namespace dcp {
 
 class Image
 {
diff --git a/src/kdm.cc b/src/kdm.cc
deleted file mode 100644 (file)
index 4132b24..0000000
+++ /dev/null
@@ -1,400 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iomanip>
-#include <algorithm>
-#include <boost/algorithm/string.hpp>
-#include <openssl/rsa.h>
-#include <openssl/pem.h>
-#include <openssl/err.h>
-#include <libcxml/cxml.h>
-#include "AS_DCP.h"
-#include "KM_util.h"
-#include "util.h"
-#include "kdm.h"
-#include "compose.hpp"
-#include "exceptions.h"
-#include "signer.h"
-#include "cpl.h"
-#include "mxf_asset.h"
-#include "xml/kdm_smpte.h"
-
-using std::list;
-using std::string;
-using std::stringstream;
-using std::hex;
-using std::setw;
-using std::setfill;
-using std::cout;
-using boost::shared_ptr;
-using namespace libdcp;
-
-KDM::KDM (boost::filesystem::path kdm, boost::filesystem::path private_key)
-       : _xml_kdm (new xml::DCinemaSecurityMessage (kdm))
-{
-       /* Read the private key */
-          
-       FILE* private_key_file = fopen_boost (private_key, "r");
-       if (!private_key_file) {
-               throw FileError ("could not find RSA private key file", private_key, errno);
-       }
-       
-       RSA* rsa = PEM_read_RSAPrivateKey (private_key_file, 0, 0, 0);
-       fclose (private_key_file);      
-       if (!rsa) {
-               throw FileError ("could not read RSA private key file", private_key, errno);
-       }
-
-       /* Use it to decrypt the keys */
-
-       list<string> encrypted_keys = _xml_kdm->authenticated_private.encrypted_keys;
-
-       for (list<string>::iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
-
-               /* Decode the base-64-encoded cipher value from the KDM */
-               unsigned char cipher_value[256];
-               int const cipher_value_len = base64_decode (*i, cipher_value, sizeof (cipher_value));
-
-               /* Decrypt it */
-               unsigned char* decrypted = new unsigned char[RSA_size(rsa)];
-               int const decrypted_len = RSA_private_decrypt (cipher_value_len, cipher_value, decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
-               if (decrypted_len == -1) {
-                       delete[] decrypted;
-                       throw MiscError (String::compose ("Could not decrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
-               }
-
-               _keys.push_back (KDMKey (decrypted, decrypted_len));
-               delete[] decrypted;
-       }
-
-       RSA_free (rsa);
-}
-
-KDM::KDM (
-       shared_ptr<const CPL> cpl, shared_ptr<const Signer> signer, shared_ptr<const Certificate> recipient_cert,
-       boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
-       string annotation_text, string issue_date
-       )
-       : _xml_kdm (new xml::DCinemaSecurityMessage)
-{
-       xml::AuthenticatedPublic& apu = _xml_kdm->authenticated_public;
-
-       /* AuthenticatedPublic */
-
-       apu.message_id = "urn:uuid:" + make_uuid ();
-       apu.message_type = "http://www.smpte-ra.org/430-1/2006/KDM#kdm-key-type";
-       apu.annotation_text = annotation_text;
-       apu.issue_date = issue_date;
-       apu.signer.x509_issuer_name = signer->certificates().leaf()->issuer ();
-       apu.signer.x509_serial_number = signer->certificates().leaf()->serial ();
-       apu.recipient.x509_issuer_serial.x509_issuer_name = recipient_cert->issuer ();
-       apu.recipient.x509_issuer_serial.x509_serial_number = recipient_cert->serial ();
-       apu.recipient.x509_subject_name = recipient_cert->subject ();
-       apu.composition_playlist_id = "urn:uuid:" + cpl->id ();
-//     apu.content_authenticator = signer->certificates().leaf()->thumbprint ();
-       apu.content_title_text = cpl->name ();
-       apu.content_keys_not_valid_before = ptime_to_string (not_valid_before);
-       apu.content_keys_not_valid_after = ptime_to_string (not_valid_after);
-       apu.authorized_device_info.device_list_identifier = "urn:uuid:" + make_uuid ();
-       string n = recipient_cert->common_name ();
-       if (n.find (".") != string::npos) {
-               n = n.substr (n.find (".") + 1);
-       }
-       apu.authorized_device_info.device_list_description = n;
-//     apu.authorized_device_info.device_list.push_back (recipient_cert->thumbprint ());
-
-       /* Sometimes digital_cinema_tools uses this magic thumbprint instead of that from an actual
-          recipient certificate.  KDMs delivered to City Screen appear to use the same thing.
-       */
-       apu.authorized_device_info.device_list.push_back ("2jmj7l5rSw0yVb/vlWAYkK/YBwk=");
-
-       list<shared_ptr<const Asset> > assets = cpl->assets ();
-       for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
-               /* XXX: non-MXF assets? */
-               shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
-               if (mxf) {
-                       apu.key_id_list.push_back (xml::TypedKeyId (mxf->key_type(), "urn:uuid:" + mxf->key_id()));
-               }
-       }
-
-       apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-picture-disable");
-       apu.forensic_mark_flag_list.push_back ("http://www.smpte-ra.org/430-1/2006/KDM#mrkflg-audio-disable");
-
-       /* AuthenticatedPrivate */
-
-       for (list<shared_ptr<const Asset> >::iterator i = assets.begin(); i != assets.end(); ++i) {
-               /* XXX: non-MXF assets? */
-               shared_ptr<const MXFAsset> mxf = boost::dynamic_pointer_cast<const MXFAsset> (*i);
-               if (mxf) {
-                       KDMKey kkey (
-                                       signer, cpl->id (), mxf->key_type (), mxf->key_id (),
-                                       not_valid_before, not_valid_after, mxf->key().get()
-                               );
-
-                       _keys.push_back (kkey);
-                       _xml_kdm->authenticated_private.encrypted_keys.push_back (kkey.encrypted_base64 (recipient_cert));
-               }
-       }
-
-       /* Signature */
-
-       shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
-       shared_ptr<cxml::Node> root (new cxml::Node (doc->get_root_node ()));
-       xmlpp::Node* signature = root->node_child("Signature")->node();
-       signer->add_signature_value (signature, "ds");
-       _xml_kdm->signature = xml::Signature (shared_ptr<cxml::Node> (new cxml::Node (signature)));
-}
-
-KDM::KDM (KDM const & other)
-       : _keys (other._keys)
-       , _xml_kdm (new xml::DCinemaSecurityMessage (*other._xml_kdm.get()))
-{
-
-}
-
-KDM &
-KDM::operator= (KDM const & other)
-{
-       if (this == &other) {
-               return *this;
-       }
-
-       _keys = other._keys;
-       _xml_kdm.reset (new xml::DCinemaSecurityMessage (*other._xml_kdm.get ()));
-
-       return *this;
-}
-     
-void
-KDM::as_xml (boost::filesystem::path path) const
-{
-       shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
-       /* This must *not* be the _formatted version, otherwise the signature
-          will be wrong.
-       */
-       doc->write_to_file (path.string(), "UTF-8");
-}
-
-string
-KDM::as_xml () const
-{
-       shared_ptr<xmlpp::Document> doc = _xml_kdm->as_xml ();
-       /* This must *not* be the _formatted version, otherwise the signature
-          will be wrong.
-       */
-       return doc->write_to_string ("UTF-8");
-}
-
-KDMKey::KDMKey (
-       shared_ptr<const Signer> signer, string cpl_id, string key_type, string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key
-       )
-       : _cpl_id (cpl_id)
-       , _key_type (key_type)
-       , _key_id (key_id)
-       , _not_valid_before (ptime_to_string (from))
-       , _not_valid_after (ptime_to_string (until))
-       , _key (key)
-{
-       base64_decode (signer->certificates().leaf()->thumbprint (), _signer_thumbprint, 20);
-}
-
-KDMKey::KDMKey (uint8_t const * raw, int len)
-{
-       switch (len) {
-       case 134:
-               /* interop */
-               /* [0-15] is structure id (fixed sequence specified by standard) */
-               raw += 16;
-               get (_signer_thumbprint, &raw, 20);
-               _cpl_id = get_uuid (&raw);
-               _key_id = get_uuid (&raw);
-               _not_valid_before = get (&raw, 25);
-               _not_valid_after = get (&raw, 25);
-               _key = Key (raw);
-               break;
-       case 138:
-               /* SMPTE */
-               /* [0-15] is structure id (fixed sequence specified by standard) */
-               raw += 16;
-               get (_signer_thumbprint, &raw, 20);
-               _cpl_id = get_uuid (&raw);
-               _key_type = get (&raw, 4);
-               _key_id = get_uuid (&raw);
-               _not_valid_before = get (&raw, 25);
-               _not_valid_after = get (&raw, 25);
-               _key = Key (raw);
-               break;
-       default:
-               assert (false);
-       }
-}
-
-KDMKey::KDMKey (KDMKey const & other)
-       : _cpl_id (other._cpl_id)
-       , _key_type (other._key_type)
-       , _key_id (other._key_id)
-       , _not_valid_before (other._not_valid_before)
-       , _not_valid_after (other._not_valid_after)
-       , _key (other._key)
-{
-       memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
-}
-
-KDMKey &
-KDMKey::operator= (KDMKey const & other)
-{
-       if (&other == this) {
-               return *this;
-       }
-
-       _cpl_id = other._cpl_id;
-       _key_type = other._key_type;
-       _key_id = other._key_id;
-       _not_valid_before = other._not_valid_before;
-       _not_valid_after = other._not_valid_after;
-       _key = other._key;
-       memcpy (_signer_thumbprint, other._signer_thumbprint, 20);
-
-       return *this;
-}
-
-string
-KDMKey::encrypted_base64 (shared_ptr<const Certificate> recipient_cert) const
-{
-       assert (_key_type.length() == 4);
-       assert (_not_valid_before.length() == 25);
-       assert (_not_valid_after.length() == 25);
-       
-       /* XXX: SMPTE only */
-       uint8_t block[138];
-       uint8_t* p = block;
-
-       /* Magic value specified by SMPTE S430-1-2006 */
-       uint8_t structure_id[] = { 0xf1, 0xdc, 0x12, 0x44, 0x60, 0x16, 0x9a, 0x0e, 0x85, 0xbc, 0x30, 0x06, 0x42, 0xf8, 0x66, 0xab };
-       put (&p, structure_id, 16);
-       put (&p, _signer_thumbprint, 20);
-       put_uuid (&p, _cpl_id);
-       put (&p, _key_type);
-       put_uuid (&p, _key_id);
-       put (&p, _not_valid_before);
-       put (&p, _not_valid_after);
-       put (&p, _key.value(), ASDCP::KeyLen);
-
-       /* Encrypt using the projector's public key */
-       RSA* rsa = recipient_cert->public_key ();
-       unsigned char encrypted[RSA_size(rsa)];
-       int const encrypted_len = RSA_public_encrypt (p - block, block, encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
-       if (encrypted_len == -1) {
-               throw MiscError (String::compose ("Could not encrypt KDM (%1)", ERR_error_string (ERR_get_error(), 0)));
-       }
-
-       /* Lazy overallocation */
-       char out[encrypted_len * 2];
-       Kumu::base64encode (encrypted, encrypted_len, out, encrypted_len * 2);
-       int const N = strlen (out);
-       stringstream lines;
-       for (int i = 0; i < N; ++i) {
-               if (i > 0 && (i % 64) == 0) {
-                       lines << "\n";
-               }
-               lines << out[i];
-       }
-
-       return lines.str ();
-}
-
-string
-KDMKey::get (uint8_t const ** p, int N) const
-{
-       string g;
-       for (int i = 0; i < N; ++i) {
-               g += **p;
-               (*p)++;
-       }
-
-       return g;
-}
-
-void
-KDMKey::get (uint8_t* o, uint8_t const ** p, int N) const
-{
-       memcpy (o, *p, N);
-       *p += N;
-}
-
-string
-KDMKey::get_uuid (unsigned char const ** p) const
-{
-       stringstream g;
-       
-       for (int i = 0; i < 16; ++i) {
-               g << setw(2) << setfill('0') << hex << static_cast<int> (**p);
-               (*p)++;
-               if (i == 3 || i == 5 || i == 7 || i == 9) {
-                       g << '-';
-               }
-       }
-
-       return g.str ();
-}
-
-void
-KDMKey::put (uint8_t ** d, uint8_t const * s, int N) const
-{
-       memcpy (*d, s, N);
-       (*d) += N;
-}
-
-void
-KDMKey::put (uint8_t ** d, string s) const
-{
-       memcpy (*d, s.c_str(), s.length());
-       (*d) += s.length();
-}
-
-void
-KDMKey::put_uuid (uint8_t ** d, string id) const
-{
-       id.erase (std::remove (id.begin(), id.end(), '-'));
-       for (int i = 0; i < 32; i += 2) {
-               stringstream s;
-               s << id[i] << id[i + 1];
-               int h;
-               s >> hex >> h;
-               **d = h;
-               (*d)++;
-       }
-}
-
-bool
-libdcp::operator== (libdcp::KDMKey const & a, libdcp::KDMKey const & b)
-{
-       if (memcmp (a._signer_thumbprint, b._signer_thumbprint, 20) != 0) {
-               return false;
-       }
-
-       return (
-               a._cpl_id == b._cpl_id &&
-               a._key_type == b._key_type &&
-               a._key_id == b._key_id &&
-               a._not_valid_before == b._not_valid_before &&
-               a._not_valid_after == b._not_valid_after &&
-               a._key == b._key
-               );
-}
diff --git a/src/kdm.h b/src/kdm.h
deleted file mode 100644 (file)
index 4e897ca..0000000
--- a/src/kdm.h
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/kdm.h
- *  @brief Handling of Key Delivery Messages (KDMs).
- */
-
-#ifndef LIBDCP_KDM_H
-#define LIBDCP_KDM_H
-
-#include <boost/filesystem.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
-#include "key.h"
-#include "metadata.h"
-
-class kdm_key_test;
-
-namespace libdcp {
-
-namespace xml {
-       class DCinemaSecurityMessage;
-};
-
-class Signer;
-class Certificate;
-class CPL;
-
-/** @class KDMKey
- *  @brief A single key (and associated metadata) for encrypting or decrypting an MXF.
- *
- *  One or more of these are delivered (themselves encrypted) in a KDM.  The following
- *  data is collected into a block:
- *
- *  A structure ID (a magic value specified by the standard)
- *  The thumbprint of the KDM signer's certificate.
- *  The CPL ID.
- *  The key ID.
- *  Validity start and end times.
- *  The key itself
- *
- *  This data block is then encrypted using the projector's public key, so that
- *  only the target projector can decrypt block.
- */
-class KDMKey
-{
-public:
-       /** Create a KDMKey from the raw block that is encrypted in the KDM's CipherData.
-        *  @param raw Pointer to data block (134 bytes for interop, 138 bytes for SMPTE).
-        *  @param len Length of the data block in bytes.
-        */
-       KDMKey (uint8_t const * raw, int len);
-
-       /** Create a KDMKey from its constituent parts.
-        *  @param signer Signer for the KDM.
-        *  @param cpl_id ID of the CPL that the KDM is for.
-        *  @param key_type Type of data that this key is for (MDIK for image, MDAK for audio, ...)
-        *  @param key_id ID of this key.
-        *  @param from Valid-from time.
-        *  @param until Valid-until time.
-        *  @param key The key itself.
-        */
-       KDMKey (
-               boost::shared_ptr<const Signer> signer,
-               std::string cpl_id, std::string key_type, std::string key_id, boost::posix_time::ptime from, boost::posix_time::ptime until, Key key
-               );
-       
-       KDMKey (KDMKey const &);
-
-       KDMKey& operator= (KDMKey const &);
-
-       /** @return ID of the CPL that the KDM is for */
-       std::string cpl_id () const {
-               return _cpl_id;
-       }
-       
-       /** @return ID of the key */
-       std::string key_id () const {
-               return _key_id;
-       }
-
-       /** @return start of the validity period as a string */
-       std::string not_valid_before () const {
-               return _not_valid_before;
-       }
-
-       /** @return end of the validity period as a string */
-       std::string not_valid_after () const {
-               return _not_valid_after;
-       }
-
-       /** @return the key itself */
-       Key key () const {
-               return _key;
-       }
-
-       /** @param cert Cerfificate.
-        *  @return The data block encrypted with a certificate's public key and converted to base 64.
-        */
-       std::string encrypted_base64 (boost::shared_ptr<const Certificate> cert) const;
-       
-private:
-       friend class ::kdm_key_test;
-       
-       void get (uint8_t *, uint8_t const **, int) const;
-       std::string get (uint8_t const **, int) const;
-       std::string get_uuid (uint8_t const **) const;
-       void put (uint8_t **, uint8_t const *, int) const;
-       void put (uint8_t **, std::string) const;
-       void put_uuid (uint8_t **, std::string) const;
-
-       friend bool operator== (KDMKey const &, KDMKey const &);
-       
-       uint8_t _signer_thumbprint[20];
-       std::string _cpl_id;
-       std::string _key_type;
-       std::string _key_id;
-       std::string _not_valid_before;
-       std::string _not_valid_after;
-       Key _key;
-};
-
-/** @class KDM
- *  @brief A class representing a Key Delivery Message (KDM).
- *
- *  A KDM wraps one or more content keys (which we wrap into KDMKey objects) and various
- *  other metadata.  This class can read and decrypt existing KDMs (provided you have
- *  the private key that the KDM was targeted at).  It can also create new KDMs for
- *  a given CPL.
- */
-class KDM
-{
-public:
-       /** Load and decrypt a KDM.  After this constructor the KDMKeys can be read
-        *  and used to decrypt MXFs.
-        *
-        *  @param kdm KDM file name.
-        *  @param private_key Private key file name.
-        */
-       KDM (boost::filesystem::path kdm, boost::filesystem::path private_key);
-
-       /** Create a new KDM.
-        *  @param cpl CPL that the KDM is for.
-        *  @param signer Certificate chain to sign the KDM with.
-        *  @param recipient_cert Certificate of the projector that this KDM is targeted at.
-        *  @param not_valid_before Start of validity period.
-        *  @param not_valid_after End of validity period.
-        *  @param annotation_text Text for the <AnnotationText> node.
-        *  @param issue_date Text for the <IssueDate> node.
-        */
-       KDM (
-               boost::shared_ptr<const CPL> cpl, boost::shared_ptr<const Signer> signer, boost::shared_ptr<const Certificate> recipient_cert,
-               boost::posix_time::ptime not_valid_before, boost::posix_time::ptime not_valid_after,
-               std::string annotation_text, std::string issue_date
-               );
-
-       KDM (KDM const &);
-       KDM & operator= (KDM const &);
-
-       /** @return The unencrypted content keys from this KDM */
-       std::list<KDMKey> keys () const {
-               return _keys;
-       }
-
-       /** Write this KDM to a file.
-        *  @param file File to write to.
-        */
-       void as_xml (boost::filesystem::path file) const;
-
-       /** Obtain this KDM as an XML string.
-        *  @return XML string.
-        */
-       std::string as_xml () const;
-
-private:
-       /** Unencrypted MXF content keys */
-       std::list<KDMKey> _keys;
-
-       /** The KDM's contents, mapped 1:1-ish to the XML */
-       boost::shared_ptr<xml::DCinemaSecurityMessage> _xml_kdm;
-};
-
-
-}
-
-#endif
index 8cac04a39416357df8e41badacc43215b7d422ea..43e59dfc6de756a078731c5cf87687d8bcb614ea 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <sstream>
-#include <string>
-#include <iomanip>
+/** @file  src/key.cc
+ *  @brief Key class.
+ */
+
+#include "key.h"
 #include "AS_DCP.h"
 #include "KM_prng.h"
 #include "KM_util.h"
-#include "key.h"
+#include <sstream>
+#include <string>
+#include <iomanip>
 
 using std::string;
 using std::stringstream;
 using std::setw;
 using std::setfill;
-using namespace libdcp;
+using namespace dcp;
 
 Key::Key ()
        : _value (new uint8_t[ASDCP::KeyLen])
@@ -86,13 +90,13 @@ Key::hex () const
 }
 
 bool
-libdcp::operator== (Key const & a, Key const & b)
+dcp::operator== (Key const & a, Key const & b)
 {
        return memcmp (a.value(), b.value(), ASDCP::KeyLen) == 0;
 }
 
 bool
-libdcp::operator!= (Key const & a, Key const & b)
+dcp::operator!= (Key const & a, Key const & b)
 {
        return !(a == b);
 }
index d0286627cb92cfc09b2ed8705b4c425d242a5c83..248a2a958193c40c0d92690f381478121f62bf86 100644 (file)
--- a/src/key.h
+++ b/src/key.h
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/key.h
- *  @brief Class to hold a key for encrypting MXFs.
+ *  @brief Key class.
  */
 
 #ifndef LIBDCP_KEY_H
 #define LIBDCP_KEY_H
 
 #include <stdint.h>
+#include <string>
 
-namespace libdcp {
+namespace dcp {
 
 /** @class Key
- *  @brief A key for encrypting MXFs.
+ *  @brief A key for decrypting/encrypting MXFs.
  */
 class Key
 {
diff --git a/src/load_font.cc b/src/load_font.cc
new file mode 100644 (file)
index 0000000..b46569c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "load_font.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using boost::shared_ptr;
+using boost::optional;
+using namespace dcp;
+
+LoadFont::LoadFont (boost::shared_ptr<const cxml::Node> node)
+{
+       optional<string> x = node->optional_string_attribute ("Id");
+       if (!x) {
+               x = node->optional_string_attribute ("ID");
+       }
+       id = x.get_value_or ("");
+       
+       uri = node->optional_string_attribute ("URI");
+}
diff --git a/src/load_font.h b/src/load_font.h
new file mode 100644 (file)
index 0000000..6d52a50
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+namespace cxml {
+       class Node;
+}
+
+namespace dcp {
+       
+class LoadFont 
+{
+public:
+       LoadFont () {}
+       LoadFont (boost::shared_ptr<const cxml::Node> node);
+
+       std::string id;
+       boost::optional<std::string> uri;
+};
+
+}
diff --git a/src/local_time.cc b/src/local_time.cc
new file mode 100644 (file)
index 0000000..2388825
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "local_time.h"
+#include "exceptions.h"
+#include <boost/lexical_cast.hpp>
+#include <cstdio>
+
+using std::string;
+using boost::lexical_cast;
+using namespace dcp;
+
+LocalTime::LocalTime ()
+{
+       time_t now = time (0);
+       struct tm* tm = localtime (&now);
+
+       _year = tm->tm_year + 1900;
+       _month = tm->tm_mon + 1;
+       _day = tm->tm_mday + 1;
+       _hour = tm->tm_hour;
+       _minute = tm->tm_min;
+       _second = tm->tm_sec;
+
+       set_local_time_zone ();
+}
+
+LocalTime::LocalTime (boost::posix_time::ptime t)
+{
+       _year = t.date().year ();
+       _month = t.date().month ();
+       _day = t.date().day ();
+       _hour = t.time_of_day().hours ();
+       _minute = t.time_of_day().minutes ();
+       _second = t.time_of_day().seconds ();
+
+       set_local_time_zone ();
+}
+
+void
+LocalTime::set_local_time_zone ()
+{
+       time_t now = time (0);
+       struct tm* tm = localtime (&now);
+
+       int offset = 0;
+       
+#ifdef LIBDCP_POSIX
+       offset = tm->tm_gmtoff / 60;
+#else
+       TIME_ZONE_INFORMATION tz;
+       GetTimeZoneInformation (&tz);
+       offset = tz.Bias;
+#endif
+
+       bool const negative = offset < 0;
+       offset = negative ? -offset : offset;
+
+       _tz_hour = offset / 60;
+       _tz_minute = offset % 60;
+
+       if (negative) {
+               _tz_hour = -_tz_hour;
+       }
+}
+
+/** @param s A string of the form 2013-01-05T18:06:59+04:00 */
+LocalTime::LocalTime (string s)
+{
+       /* 2013-01-05T18:06:59+04:00 */
+        /* 0123456789012345678901234 */
+       
+       if (s.length() < 25) {
+               throw TimeFormatError (s);
+       }
+
+       /* Check incidental characters */
+       if (s[4] != '-' || s[7] != '-' || s[10] != 'T' || s[13] != ':' || s[16] != ':' || s[22] != ':') {
+               throw TimeFormatError (s);
+       }
+       
+       _year = lexical_cast<int> (s.substr (0, 4));
+       _month = lexical_cast<int> (s.substr (5, 2));
+       _day = lexical_cast<int> (s.substr (8, 2));
+       _hour = lexical_cast<int> (s.substr (11, 2));
+       _minute = lexical_cast<int> (s.substr (14, 2));
+       _second = lexical_cast<int> (s.substr (17, 2));
+       _tz_hour = lexical_cast<int> (s.substr (20, 2));
+       _tz_minute = lexical_cast<int> (s.substr (23, 2));
+
+       if (s[19] == '-') {
+               _tz_hour = -_tz_hour;
+       } else if (s[19] != '+') {
+               throw TimeFormatError (s);
+       }
+}
+
+string
+LocalTime::as_string () const
+{
+       char buffer[32];
+       snprintf (
+               buffer, sizeof (buffer),
+               "%sT%s%s%02d:%02d",
+               date().c_str(), time_of_day().c_str(), (_tz_hour >= 0 ? "+" : "-"), abs (_tz_hour), _tz_minute
+               );
+       return buffer;
+}
+
+string
+LocalTime::date () const
+{
+       char buffer[32];
+       snprintf (buffer, sizeof (buffer), "%04d-%02d-%02d", _year, _month, _day);
+       return buffer;
+}
+
+string
+LocalTime::time_of_day () const
+{
+       char buffer[32];
+       snprintf (buffer, sizeof (buffer), "%02d:%02d:%02d", _hour, _minute, _second);
+       return buffer;
+}
diff --git a/src/local_time.h b/src/local_time.h
new file mode 100644 (file)
index 0000000..00b32f1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_LOCAL_TIME_H
+#define LIBDCP_LOCAL_TIME_H
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <string>
+
+class local_time_test;
+
+namespace dcp {
+
+/** I tried to use boost for this, really I did, but I could not get it
+ *  to parse strings of the required format (those that include time zones).
+ */
+class LocalTime
+{
+public:
+       LocalTime ();
+       LocalTime (boost::posix_time::ptime);
+       LocalTime (std::string);
+
+       std::string as_string () const;
+       std::string date () const;
+       std::string time_of_day () const;
+
+private:
+       friend class ::local_time_test;
+
+       void set_local_time_zone ();
+
+       /* Local time */
+       int _year;
+       int _month;
+       int _day;
+       int _hour;
+       int _minute;
+       int _second;
+
+       /* Amount by which this time is offset from UTC */
+       int _tz_hour;
+       int _tz_minute;
+};
+
+}
+
+#endif
diff --git a/src/lut.h b/src/lut.h
deleted file mode 100644 (file)
index ad29146..0000000
--- a/src/lut.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_LUT_H
-#define LIBDCP_LUT_H
-
-#include <cmath>
-#include <boost/utility.hpp>
-
-namespace libdcp {
-
-class LUT : boost::noncopyable
-{
-public:
-       LUT(int bit_depth, float gamma)
-               : _lut(0)
-               , _bit_depth (bit_depth)
-               , _gamma (gamma)
-       {
-               _lut = new float[int(std::pow(2.0f, _bit_depth))];
-       }
-
-       virtual ~LUT() {
-               delete[] _lut;
-       }
-       
-       float const * lut() const {
-               return _lut;
-       }
-
-       int bit_depth () const {
-               return _bit_depth;
-       }
-
-       float gamma () const {
-               return _gamma;
-       }
-
-protected:
-       float* _lut;
-       int _bit_depth;
-       float _gamma;
-};
-
-}
-
-#endif
index b60ee109009521ee3b7d50db1947ba65d0b4c39e..5c75fe6aacb31fddcf8715fcebcd4ebefc318dfb 100644 (file)
@@ -1,14 +1,34 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
 #ifndef LIBDCP_LUT_CACHE_H
 #define LIBDCP_LUT_CACHE_H
 
 #include <list>
 #include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
 
 template<class T>
-class LUTCache
+class LUTCache : public boost::noncopyable
 {
 public:
-       boost::shared_ptr<T> get (int bit_depth, float gamma)
+       boost::shared_ptr<T> get (int bit_depth, float gamma, bool linearised)
        {
                for (typename std::list<boost::shared_ptr<T> >::iterator i = _cache.begin(); i != _cache.end(); ++i) {
                        if ((*i)->bit_depth() == bit_depth && (*i)->gamma() == gamma) {
@@ -16,7 +36,7 @@ public:
                        }
                }
 
-               boost::shared_ptr<T> lut (new T (bit_depth, gamma));
+               boost::shared_ptr<T> lut (new T (bit_depth, gamma, linearised));
                _cache.push_back (lut);
                return lut;
        }
index acbc3b2ed36fb7bcdc07084d5cd684f28cdb3451..e2e19f3c014b05955a2014107fdf75c566dbacf2 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 /** @file  src/metadata.cc
- *  @brief Metadata for writing to the DCP.
+ *  @brief XMLMetadata and MXFMetadata classes.
  */
 
+#include "metadata.h"
+#include "util.h"
+#include "local_time.h"
 #include <sstream>
 #include <iomanip>
 #include <time.h>
-#ifdef LIBDCP_WINDOWS
-#include <windows.h>
-#endif
-#include "metadata.h"
-#include "util.h"
 
 using namespace std;
-using namespace libdcp;
+using namespace dcp;
 
 MXFMetadata::MXFMetadata ()
        : company_name ("libdcp")
@@ -52,7 +50,5 @@ XMLMetadata::XMLMetadata ()
 void
 XMLMetadata::set_issue_date_now ()
 {
-       time_t now = time (0);
-       struct tm* tm = localtime (&now);
-       issue_date = tm_to_string (tm);
+       issue_date = LocalTime().as_string ();
 }
index b4c0896dab1efe20d2bc2c56dc1e1f9d2cd248ac..b1fcdd8e9936ea89836fdedb37772bf1b7dc3efa 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #define LIBDCP_METADATA_H
 
 /** @file  src/metadata.h
- *  @brief Metadata for writing to the DCP.
+ *  @brief XMLMetadata and MXFMetadata classes.
  */
 
 #include <string>
 
 class utc_offset_to_string_test;
 
-namespace libdcp
+namespace dcp
 {
 
+/** @class MXFMetadata
+ *  @brief Metadata that is written to a MXF file's header
+ */
 class MXFMetadata
 {
 public:
@@ -41,6 +44,9 @@ public:
        std::string product_version;
 };
 
+/** @class XMLMetadata
+ *  @brief Common metadata that is written to a few different XML files
+ */
 class XMLMetadata
 {
 public:
diff --git a/src/mono_picture_asset.cc b/src/mono_picture_asset.cc
deleted file mode 100644 (file)
index f70db2b..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
-    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "mono_picture_asset.h"
-#include "mono_picture_asset_writer.h"
-#include "AS_DCP.h"
-#include "KM_fileio.h"
-#include "exceptions.h"
-#include "mono_picture_frame.h"
-
-using std::string;
-using std::vector;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using boost::lexical_cast;
-using namespace libdcp;
-
-MonoPictureAsset::MonoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name)
-       : PictureAsset (directory, mxf_name)
-{
-
-}
-
-void
-MonoPictureAsset::create (vector<boost::filesystem::path> const & files)
-{
-       create (boost::bind (&MonoPictureAsset::path_from_list, this, _1, files));
-}
-
-void
-MonoPictureAsset::create (boost::function<boost::filesystem::path (int)> get_path)
-{
-       ASDCP::JP2K::CodestreamParser j2k_parser;
-       ASDCP::JP2K::FrameBuffer frame_buffer (4 * Kumu::Megabyte);
-       Kumu::Result_t r = j2k_parser.OpenReadFrame (get_path(0).string().c_str(), frame_buffer);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (FileError ("could not open JPEG2000 file for reading", get_path(0), r));
-       }
-       
-       ASDCP::JP2K::PictureDescriptor picture_desc;
-       j2k_parser.FillPictureDescriptor (picture_desc);
-       picture_desc.EditRate = ASDCP::Rational (_edit_rate, 1);
-       
-       ASDCP::WriterInfo writer_info;
-       fill_writer_info (&writer_info);
-       
-       ASDCP::JP2K::MXFWriter mxf_writer;
-       r = mxf_writer.OpenWrite (path().string().c_str(), writer_info, picture_desc, 16384, false);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for writing", path().string(), r));
-       }
-
-       for (int i = 0; i < _intrinsic_duration; ++i) {
-
-               boost::filesystem::path const path = get_path (i);
-
-               Kumu::Result_t r = j2k_parser.OpenReadFrame (path.string().c_str(), frame_buffer);
-               if (ASDCP_FAILURE (r)) {
-                       boost::throw_exception (FileError ("could not open JPEG2000 file for reading", path, r));
-               }
-
-               r = mxf_writer.WriteFrame (frame_buffer, _encryption_context, 0);
-               if (ASDCP_FAILURE (r)) {
-                       boost::throw_exception (MXFFileError ("error in writing video MXF", this->path().string(), r));
-               }
-
-               if (_progress) {
-                       (*_progress) (0.5 * float (i) / _intrinsic_duration);
-               }
-       }
-       
-       r = mxf_writer.Finalize();
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in finalising video MXF", path().string(), r));
-       }
-}
-
-void
-MonoPictureAsset::read ()
-{
-       ASDCP::JP2K::MXFReader reader;
-       Kumu::Result_t r = reader.OpenRead (path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-       
-       ASDCP::JP2K::PictureDescriptor desc;
-       if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) {
-               boost::throw_exception (DCPReadError ("could not read video MXF information"));
-       }
-
-       _size.width = desc.StoredWidth;
-       _size.height = desc.StoredHeight;
-       _edit_rate = desc.EditRate.Numerator;
-       assert (desc.EditRate.Denominator == 1);
-       _intrinsic_duration = desc.ContainerDuration;
-}
-
-boost::filesystem::path
-MonoPictureAsset::path_from_list (int f, vector<boost::filesystem::path> const & files) const
-{
-       return files[f];
-}
-
-shared_ptr<const MonoPictureFrame>
-MonoPictureAsset::get_frame (int n) const
-{
-       return shared_ptr<const MonoPictureFrame> (new MonoPictureFrame (path(), n, _decryption_context));
-}
-
-bool
-MonoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
-{
-       if (!MXFAsset::equals (other, opt, note)) {
-               return false;
-       }
-
-       ASDCP::JP2K::MXFReader reader_A;
-       Kumu::Result_t r = reader_A.OpenRead (path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-       
-       ASDCP::JP2K::MXFReader reader_B;
-       r = reader_B.OpenRead (other->path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-       
-       ASDCP::JP2K::PictureDescriptor desc_A;
-       if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) {
-               boost::throw_exception (DCPReadError ("could not read video MXF information"));
-       }
-       ASDCP::JP2K::PictureDescriptor desc_B;
-       if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) {
-               boost::throw_exception (DCPReadError ("could not read video MXF information"));
-       }
-       
-       if (!descriptor_equals (desc_A, desc_B, note)) {
-               return false;
-       }
-
-       shared_ptr<const MonoPictureAsset> other_picture = dynamic_pointer_cast<const MonoPictureAsset> (other);
-       assert (other_picture);
-
-       for (int i = 0; i < _intrinsic_duration; ++i) {
-               if (i >= other_picture->intrinsic_duration()) {
-                       return false;
-               }
-               
-               note (PROGRESS, "Comparing video frame " + lexical_cast<string> (i) + " of " + lexical_cast<string> (_intrinsic_duration));
-               shared_ptr<const MonoPictureFrame> frame_A = get_frame (i);
-               shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i);
-               
-               if (!frame_buffer_equals (
-                           i, opt, note,
-                           frame_A->j2k_data(), frame_A->j2k_size(),
-                           frame_B->j2k_data(), frame_B->j2k_size()
-                           )) {
-                       return false;
-               }
-       }
-
-       return true;
-}
-
-shared_ptr<PictureAssetWriter>
-MonoPictureAsset::start_write (bool overwrite)
-{
-       /* XXX: can't we use shared_ptr here? */
-       return shared_ptr<MonoPictureAssetWriter> (new MonoPictureAssetWriter (this, overwrite));
-}
-
-string
-MonoPictureAsset::cpl_node_name () const
-{
-       return "MainPicture";
-}
-
-int
-MonoPictureAsset::edit_rate_factor () const
-{
-       return 1;
-}
diff --git a/src/mono_picture_asset.h b/src/mono_picture_asset.h
deleted file mode 100644 (file)
index ae2df4d..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_MONO_PICTURE_ASSET_H
-#define LIBDCP_MONO_PICTURE_ASSET_H
-
-#include "picture_asset.h"
-
-namespace libdcp {
-
-/** A 2D (monoscopic) picture asset */
-class MonoPictureAsset : public PictureAsset
-{
-public:
-       MonoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name);
-
-       void read ();
-
-       /** The following parameters must be set up (if required) before calling this:
-        *      Interop mode (set_interop)
-        *      Edit rate    (set_edit_rate)
-        *      MXF Metadata (set_metadata)
-        */
-       void create (std::vector<boost::filesystem::path> const & files);
-
-       /** The following parameters must be set up (if required) before calling this:
-        *      Interop mode (set_interop)
-        *      Edit rate    (set_edit_rate)
-        *      MXF Metadata (set_metadata)
-        */
-       void create (boost::function<boost::filesystem::path (int)> get_path);
-
-       /** Start a progressive write to a MonoPictureAsset */
-       boost::shared_ptr<PictureAssetWriter> start_write (bool);
-
-       boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const;
-       bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
-
-private:
-       boost::filesystem::path path_from_list (int f, std::vector<boost::filesystem::path> const & files) const;
-       void construct (boost::function<boost::filesystem::path (int)>, bool, MXFMetadata const &);
-       std::string cpl_node_name () const;
-       int edit_rate_factor () const;
-};
-
-}      
-
-#endif
diff --git a/src/mono_picture_asset_writer.cc b/src/mono_picture_asset_writer.cc
deleted file mode 100644 (file)
index ebf3ec0..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "AS_DCP.h"
-#include "KM_fileio.h"
-#include "mono_picture_asset_writer.h"
-#include "exceptions.h"
-#include "picture_asset.h"
-
-#include "picture_asset_writer_common.cc"
-
-using std::istream;
-using std::ostream;
-using std::string;
-using boost::shared_ptr;
-using namespace libdcp;
-
-struct MonoPictureAssetWriter::ASDCPState : public ASDCPStateBase
-{
-       ASDCP::JP2K::MXFWriter mxf_writer;
-};
-
-/** @param a Asset to write to.  `a' must not be deleted while
- *  this writer class still exists, or bad things will happen.
- */
-MonoPictureAssetWriter::MonoPictureAssetWriter (PictureAsset* asset, bool overwrite)
-       : PictureAssetWriter (asset, overwrite)
-       , _state (new MonoPictureAssetWriter::ASDCPState)
-{
-       _state->encryption_context = asset->encryption_context ();
-}
-
-void
-MonoPictureAssetWriter::start (uint8_t* data, int size)
-{
-       libdcp::start (this, _state, _asset, data, size);
-}
-
-FrameInfo
-MonoPictureAssetWriter::write (uint8_t* data, int size)
-{
-       assert (!_finalized);
-
-       if (!_started) {
-               start (data, size);
-       }
-
-       if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
-               boost::throw_exception (MiscError ("could not parse J2K frame"));
-       }
-
-       uint64_t const before_offset = _state->mxf_writer.Tell ();
-
-       string hash;
-       ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0, &hash);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r));
-       }
-
-       ++_frames_written;
-       return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
-}
-
-void
-MonoPictureAssetWriter::fake_write (int size)
-{
-       assert (_started);
-       assert (!_finalized);
-
-       Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r));
-       }
-
-       ++_frames_written;
-}
-
-void
-MonoPictureAssetWriter::finalize ()
-{
-       assert (!_finalized);
-       
-       Kumu::Result_t r = _state->mxf_writer.Finalize();
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r));
-       }
-
-       _finalized = true;
-       _asset->set_intrinsic_duration (_frames_written);
-       _asset->set_duration (_frames_written);
-}
-
diff --git a/src/mono_picture_asset_writer.h b/src/mono_picture_asset_writer.h
deleted file mode 100644 (file)
index e392262..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <stdint.h>
-#include <string>
-#include <fstream>
-#include <boost/shared_ptr.hpp>
-#include <boost/utility.hpp>
-#include "picture_asset_writer.h"
-
-namespace libdcp {
-
-/** A helper class for writing to MonoPictureAssets progressively (i.e. writing frame-by-frame,
- *  rather than giving libdcp all the frames in one go).
- *
- *  Objects of this class can only be created with MonoPictureAsset::start_write().
- *
- *  Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image
- *  (a verbatim .j2c file).  finalize() must be called after the last frame has been written.
- *  The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may
- *  throw an exception.
- */
-class MonoPictureAssetWriter : public PictureAssetWriter
-{
-public:
-       FrameInfo write (uint8_t *, int);
-       void fake_write (int size);
-       void finalize ();
-
-private:
-       friend class MonoPictureAsset;
-
-       MonoPictureAssetWriter (PictureAsset *, bool);
-       void start (uint8_t *, int);
-
-       /* do this with an opaque pointer so we don't have to include
-          ASDCP headers
-       */
-          
-       struct ASDCPState;
-       boost::shared_ptr<ASDCPState> _state;
-};
-
-}
index 890967d157a0e90c1182918435f87d6eb2ae471a..c2f8abb712f6950703c8c417f40397cc20d68ff1 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <openjpeg.h>
-#include "AS_DCP.h"
-#include "KM_fileio.h"
+/** @file  src/mono_picture_frame.cc
+ *  @brief MonoPictureFrame class.
+ */
+
 #include "mono_picture_frame.h"
 #include "exceptions.h"
 #include "argb_frame.h"
-#include "lut.h"
-#include "util.h"
 #include "gamma_lut.h"
+#include "util.h"
 #include "rgb_xyz.h"
+#include "KM_fileio.h"
+#include "AS_DCP.h"
+#include <openjpeg.h>
 
 #define DCI_GAMMA 2.6
 
 using std::string;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 /** Make a picture frame from a 2D (monoscopic) asset.
  *  @param mxf_path Path to the asset's MXF file.
  *  @param n Frame within the asset, not taking EntryPoint into account.
+ *  @param c Context for decryption, or 0.
  */
 MonoPictureFrame::MonoPictureFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext* c)
 {
@@ -54,17 +58,20 @@ MonoPictureFrame::MonoPictureFrame (boost::filesystem::path mxf_path, int n, ASD
        }
 }
 
+/** MonoPictureFrame destructor */
 MonoPictureFrame::~MonoPictureFrame ()
 {
        delete _buffer;
 }
 
+/** @return Pointer to JPEG2000 data */
 uint8_t const *
 MonoPictureFrame::j2k_data () const
 {
        return _buffer->RoData ();
 }
 
+/** @return Size of JPEG2000 data in bytes */
 int
 MonoPictureFrame::j2k_size () const
 {
@@ -74,17 +81,18 @@ MonoPictureFrame::j2k_size () const
 /** @param reduce a factor by which to reduce the resolution
  *  of the image, expressed as a power of two (pass 0 for no
  *  reduction).
+ *  @param srgb_gamma Reciprocal of output gamma to use after
+ *  the conversion from XYZ to RGB.
  *
  *  @return An ARGB representation of this frame.  This is ARGB in the
  *  Cairo sense, so that each pixel takes up 4 bytes; the first byte
  *  is blue, second green, third red and fourth alpha (always 255).
- *
  */
 shared_ptr<ARGBFrame>
 MonoPictureFrame::argb_frame (int reduce, float srgb_gamma) const
 {
        return xyz_to_rgb (
                decompress_j2k (const_cast<uint8_t*> (_buffer->RoData()), _buffer->Size(), reduce),
-               GammaLUT::cache.get (12, DCI_GAMMA), GammaLUT::cache.get (12, 1 / srgb_gamma)
+               GammaLUT::cache.get (12, DCI_GAMMA, false), GammaLUT::cache.get (12, 1 / srgb_gamma, false)
                );
 }
index 0eedfb95033d1030fbefe504fd0a5dfbf3ac7549..cc6096cd8c705093b8333c48ad989a5bdbe48391 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/mono_picture_frame.h
+ *  @brief MonoPictureFrame class.
+ */
+
+#include "types.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/filesystem.hpp>
 #include <string>
 #include <stdint.h>
-#include <boost/shared_ptr.hpp>
-#include "types.h"
 
 namespace ASDCP {
        namespace JP2K {
@@ -29,12 +35,14 @@ namespace ASDCP {
        class AESDecContext;
 }
 
-namespace libdcp {
+namespace dcp {
 
 class ARGBFrame;
 
-/** A single frame of a 2D (monoscopic) picture asset */       
-class MonoPictureFrame
+/** @class MonoPictureFrame
+ *  @brief A single frame of a 2D (monoscopic) picture asset.
+ */
+class MonoPictureFrame : public boost::noncopyable
 {
 public:
        MonoPictureFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext *);
diff --git a/src/mono_picture_mxf.cc b/src/mono_picture_mxf.cc
new file mode 100644 (file)
index 0000000..4af2400
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "mono_picture_mxf.h"
+#include "mono_picture_mxf_writer.h"
+#include "AS_DCP.h"
+#include "KM_fileio.h"
+#include "exceptions.h"
+#include "mono_picture_frame.h"
+#include "compose.hpp"
+
+using std::string;
+using std::vector;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using namespace dcp;
+
+MonoPictureMXF::MonoPictureMXF (boost::filesystem::path file)
+       : PictureMXF (file)
+{
+       ASDCP::JP2K::MXFReader reader;
+       Kumu::Result_t r = reader.OpenRead (file.string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r));
+       }
+       
+       ASDCP::JP2K::PictureDescriptor desc;
+       if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+
+       read_picture_descriptor (desc);
+       
+       ASDCP::WriterInfo info;
+       if (ASDCP_FAILURE (reader.FillWriterInfo (info))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+
+       read_writer_info (info);
+}
+
+MonoPictureMXF::MonoPictureMXF (Fraction edit_rate)
+       : PictureMXF (edit_rate)
+{
+       
+}
+
+shared_ptr<const MonoPictureFrame>
+MonoPictureMXF::get_frame (int n) const
+{
+       return shared_ptr<const MonoPictureFrame> (new MonoPictureFrame (_file, n, _decryption_context));
+}
+
+bool
+MonoPictureMXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+       if (!MXF::equals (other, opt, note)) {
+               return false;
+       }
+
+       ASDCP::JP2K::MXFReader reader_A;
+       Kumu::Result_t r = reader_A.OpenRead (_file.string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", _file.string(), r));
+       }
+       
+       ASDCP::JP2K::MXFReader reader_B;
+       r = reader_B.OpenRead (other->file().string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", other->file().string(), r));
+       }
+       
+       ASDCP::JP2K::PictureDescriptor desc_A;
+       if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+       ASDCP::JP2K::PictureDescriptor desc_B;
+       if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+       
+       if (!descriptor_equals (desc_A, desc_B, note)) {
+               return false;
+       }
+
+       shared_ptr<const MonoPictureMXF> other_picture = dynamic_pointer_cast<const MonoPictureMXF> (other);
+       assert (other_picture);
+
+       for (int i = 0; i < _intrinsic_duration; ++i) {
+               if (i >= other_picture->intrinsic_duration()) {
+                       return false;
+               }
+               
+               note (PROGRESS, String::compose ("Comparing video frame %1 of %2", i, _intrinsic_duration));
+               shared_ptr<const MonoPictureFrame> frame_A = get_frame (i);
+               shared_ptr<const MonoPictureFrame> frame_B = other_picture->get_frame (i);
+               
+               if (!frame_buffer_equals (
+                           i, opt, note,
+                           frame_A->j2k_data(), frame_A->j2k_size(),
+                           frame_B->j2k_data(), frame_B->j2k_size()
+                           )) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+shared_ptr<PictureMXFWriter>
+MonoPictureMXF::start_write (boost::filesystem::path file, Standard standard, bool overwrite)
+{
+       /* XXX: can't we use shared_ptr here? */
+       return shared_ptr<MonoPictureMXFWriter> (new MonoPictureMXFWriter (this, file, standard, overwrite));
+}
+
+string
+MonoPictureMXF::cpl_node_name () const
+{
+       return "MainPicture";
+}
diff --git a/src/mono_picture_mxf.h b/src/mono_picture_mxf.h
new file mode 100644 (file)
index 0000000..c02a63c
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_MONO_PICTURE_MXF_H
+#define LIBDCP_MONO_PICTURE_MXF_H
+
+#include "picture_mxf.h"
+
+namespace dcp {
+
+class MonoPictureMXFWriter;    
+
+/** @class MonoPictureMXF
+ *  @brief A 2D (monoscopic) picture MXF.
+ */
+class MonoPictureMXF : public PictureMXF
+{
+public:
+       /** Create a MonoPictureMXF by reading a file.
+        *  @param file MXF file to read.
+        */
+       MonoPictureMXF (boost::filesystem::path file);
+
+       /** Create a MonoPictureMXF with a given edit rate.
+        *  @param edit_rate Edit rate (i.e. frame rate) in frames per second.
+        */
+       MonoPictureMXF (Fraction edit_rate);
+
+       /** Start a progressive write to a MonoPictureMXF */
+       boost::shared_ptr<PictureMXFWriter> start_write (boost::filesystem::path, Standard standard, bool);
+
+       bool equals (
+               boost::shared_ptr<const Content> other,
+               EqualityOptions opt,
+               boost::function<void (NoteType, std::string)> note
+               ) const;
+       
+       boost::shared_ptr<const MonoPictureFrame> get_frame (int n) const;
+
+private:
+       std::string cpl_node_name () const;
+};
+
+}      
+
+#endif
diff --git a/src/mono_picture_mxf_writer.cc b/src/mono_picture_mxf_writer.cc
new file mode 100644 (file)
index 0000000..5cc424e
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/mono_picture_mxf_writer.cc
+ *  @brief MonoPictureMXFWriter class
+ */
+
+#include "AS_DCP.h"
+#include "KM_fileio.h"
+#include "mono_picture_mxf_writer.h"
+#include "exceptions.h"
+#include "picture_mxf.h"
+
+#include "picture_mxf_writer_common.cc"
+
+using std::istream;
+using std::ostream;
+using std::string;
+using boost::shared_ptr;
+using namespace dcp;
+
+struct MonoPictureMXFWriter::ASDCPState : public ASDCPStateBase
+{
+       ASDCP::JP2K::MXFWriter mxf_writer;
+};
+
+/** @param a Asset to write to.  `a' must not be deleted while
+ *  this writer class still exists, or bad things will happen.
+ */
+MonoPictureMXFWriter::MonoPictureMXFWriter (PictureMXF* asset, boost::filesystem::path file, Standard standard, bool overwrite)
+       : PictureMXFWriter (asset, file, standard, overwrite)
+       , _state (new MonoPictureMXFWriter::ASDCPState)
+{
+       _state->encryption_context = asset->encryption_context ();
+}
+
+void
+MonoPictureMXFWriter::start (uint8_t* data, int size)
+{
+       dcp::start (this, _state, _standard, _picture_mxf, data, size);
+       _picture_mxf->set_frame_rate (_picture_mxf->edit_rate());
+}
+
+FrameInfo
+MonoPictureMXFWriter::write (uint8_t* data, int size)
+{
+       assert (!_finalized);
+
+       if (!_started) {
+               start (data, size);
+       }
+
+       if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
+               boost::throw_exception (MiscError ("could not parse J2K frame"));
+       }
+
+       uint64_t const before_offset = _state->mxf_writer.Tell ();
+
+       string hash;
+       ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0, &hash);
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("error in writing video MXF", _file.string(), r));
+       }
+
+       ++_frames_written;
+       return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
+}
+
+void
+MonoPictureMXFWriter::fake_write (int size)
+{
+       assert (_started);
+       assert (!_finalized);
+
+       Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size);
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("error in writing video MXF", _mxf->file().string(), r));
+       }
+
+       ++_frames_written;
+}
+
+void
+MonoPictureMXFWriter::finalize ()
+{
+       Kumu::Result_t r = _state->mxf_writer.Finalize();
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("error in finalizing video MXF", _mxf->file().string(), r));
+       }
+
+       PictureMXFWriter::finalize ();
+}
+
diff --git a/src/mono_picture_mxf_writer.h b/src/mono_picture_mxf_writer.h
new file mode 100644 (file)
index 0000000..065171c
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/mono_picture_mxf_writer.h
+ *  @brief MonoPictureMXFWriter class
+ */
+
+#ifndef LIBDCP_MONO_PICTURE_MXF_WRITER_H
+#define LIBDCP_MONO_PICTURE_MXF_WRITER_H
+
+#include "picture_mxf_writer.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+
+namespace dcp {
+
+/** @class MonoPictureMXFWriter
+ *  @brief A helper class for writing to MonoPictureMXFs
+ *
+ *  Objects of this class can only be created with MonoPictureMXF::start_write().
+ *
+ *  Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image
+ *  (a verbatim .j2c file).  finalize() must be called after the last frame has been written.
+ *  The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may
+ *  throw an exception.
+ */
+class MonoPictureMXFWriter : public PictureMXFWriter
+{
+public:
+       FrameInfo write (uint8_t *, int);
+       void fake_write (int size);
+       void finalize ();
+
+private:
+       friend class MonoPictureMXF;
+
+       MonoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, Standard standard, bool);
+       void start (uint8_t *, int);
+
+       /* do this with an opaque pointer so we don't have to include
+          ASDCP headers
+       */
+          
+       struct ASDCPState;
+       boost::shared_ptr<ASDCPState> _state;
+};
+
+}
+
+#endif
diff --git a/src/mxf.cc b/src/mxf.cc
new file mode 100644 (file)
index 0000000..dd4d2ef
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/asset.cc
+ *  @brief Parent class for assets of DCPs made up of MXF files.
+ */
+
+#include "raw_convert.h"
+#include "AS_DCP.h"
+#include "KM_prng.h"
+#include "KM_util.h"
+#include "mxf.h"
+#include "util.h"
+#include "metadata.h"
+#include "exceptions.h"
+#include "compose.hpp"
+#include <libxml++/nodes/element.h>
+#include <boost/filesystem.hpp>
+#include <iostream>
+
+using std::string;
+using std::list;
+using std::pair;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using namespace dcp;
+
+MXF::MXF (Fraction edit_rate)
+       : Content (edit_rate)
+       , _encryption_context (0)
+       , _decryption_context (0)
+{
+
+}
+
+MXF::MXF (boost::filesystem::path file)
+       : Content (file)
+       , _encryption_context (0)
+       , _decryption_context (0)
+{
+
+}
+
+MXF::~MXF ()
+{
+       delete _encryption_context;
+       delete _decryption_context;
+}
+
+void
+MXF::fill_writer_info (ASDCP::WriterInfo* writer_info, Standard standard)
+{
+       writer_info->ProductVersion = _metadata.product_version;
+       writer_info->CompanyName = _metadata.company_name;
+       writer_info->ProductName = _metadata.product_name.c_str();
+
+       if (standard == INTEROP) {
+               writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP;
+       } else {
+               writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE;
+       }
+       unsigned int c;
+       Kumu::hex2bin (_id.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c);
+       assert (c == Kumu::UUID_Length);
+
+       if (_key) {
+               Kumu::GenRandomUUID (writer_info->ContextID);
+               writer_info->EncryptedEssence = true;
+
+               unsigned int c;
+               Kumu::hex2bin (_key_id.c_str(), writer_info->CryptographicKeyID, Kumu::UUID_Length, &c);
+               assert (c == Kumu::UUID_Length);
+       }
+}
+
+bool
+MXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+       if (!Content::equals (other, opt, note)) {
+               return false;
+       }
+       
+       shared_ptr<const MXF> other_mxf = dynamic_pointer_cast<const MXF> (other);
+       if (!other_mxf) {
+               note (ERROR, "comparing an MXF asset with a non-MXF asset");
+               return false;
+       }
+       
+       if (_file != other_mxf->file ()) {
+               note (ERROR, "MXF names differ");
+               if (!opt.mxf_names_can_differ) {
+                       return false;
+               }
+       }
+       
+       return true;
+}
+
+/** Set the (private) key that will be used to encrypt or decrypt this MXF's content.
+ *  This is the top-secret key that is distributed (itself encrypted) to cinemas
+ *  via Key Delivery Messages (KDMs).
+ *  @param key Key to use.
+ */
+void
+MXF::set_key (Key key)
+{
+       _key = key;
+
+       if (_key_id.empty ()) {
+               /* No key ID so far; we now need one */
+               _key_id = make_uuid ();
+       }
+       
+       _decryption_context = new ASDCP::AESDecContext;
+       if (ASDCP_FAILURE (_decryption_context->InitKey (_key->value ()))) {
+               throw MiscError ("could not set up decryption context");
+       }
+
+       _encryption_context = new ASDCP::AESEncContext;
+       if (ASDCP_FAILURE (_encryption_context->InitKey (_key->value ()))) {
+               throw MiscError ("could not set up encryption context");
+       }
+       
+       uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE];
+       
+       Kumu::FortunaRNG rng;
+       if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) {
+               throw MiscError ("could not set up CBC initialization vector");
+       }
+}
+
+void
+MXF::read_writer_info (ASDCP::WriterInfo const & info)
+{
+       char buffer[64];
+       Kumu::bin2UUIDhex (info.AssetUUID, ASDCP::UUIDlen, buffer, sizeof (buffer));
+       _id = buffer;
+}
+
+string
+MXF::pkl_type (Standard standard) const
+{
+       switch (standard) {
+       case INTEROP:
+               return String::compose ("application/x-smpte-mxf;asdcpKind=%1", asdcp_kind ());
+       case SMPTE:
+               return "application/mxf";
+       default:
+               assert (false);
+       }
+}
diff --git a/src/mxf.h b/src/mxf.h
new file mode 100644 (file)
index 0000000..c5ecd1b
--- /dev/null
+++ b/src/mxf.h
@@ -0,0 +1,123 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_MXF_H
+#define LIBDCP_MXF_H
+
+#include "content.h"
+#include "key.h"
+#include "metadata.h"
+#include <boost/signals2.hpp>
+
+namespace ASDCP {
+       class AESEncContext;
+       class AESDecContext;
+}
+
+/* Undefine some stuff that the OS X 10.5 SDK defines */
+#undef Key
+#undef set_key
+
+namespace dcp
+{
+
+class MXFMetadata;     
+
+/** @class MXF
+ *  @brief Parent class for classes which represent MXF files.
+ */
+class MXF : public Content
+{
+public:
+       MXF (Fraction edit_rate);
+       MXF (boost::filesystem::path file);
+       ~MXF ();
+
+       /** @return the 4-character key type for this MXF (MDIK, MDAK, etc.) */
+       virtual std::string key_type () const = 0;
+       
+       bool equals (
+               boost::shared_ptr<const Content> other,
+               EqualityOptions opt,
+               boost::function<void (NoteType, std::string)> note
+               ) const;
+
+       /** Fill in a ADSCP::WriteInfo struct.
+        *  @param w struct to fill in.
+        *  @param standard INTEROP or SMPTE.
+        */
+       void fill_writer_info (ASDCP::WriterInfo* w, Standard standard);
+
+       /** @return true if the data is encrypted */
+       bool encrypted () const {
+               return !_key_id.empty ();
+       }
+
+       /** Set the ID of the key that is used for encryption/decryption.
+        *  @param i key ID.
+        */
+       void set_key_id (std::string i) {
+               _key_id = i;
+       }
+
+       /** @return the ID of the key used for encryption/decryption, or an empty string */
+       std::string key_id () const {
+               return _key_id;
+       }
+
+       void set_key (Key);
+
+       /** @return encryption/decryption key, if one has been set */
+       boost::optional<Key> key () const {
+               return _key;
+       }
+
+       /** @return encryption context, set up with any key that has been passed to set_key() */
+       ASDCP::AESEncContext* encryption_context () const {
+               return _encryption_context;
+       }
+
+       /** Set the metadata that is written to the MXF file.
+        *  @param m Metadata.
+        */
+       void set_metadata (MXFMetadata m) {
+               _metadata = m;
+       }
+
+       /** @return metadata from the MXF file */
+       MXFMetadata metadata () const {
+               return _metadata;
+       }
+
+protected:
+       std::string pkl_type (Standard standard) const;
+       void read_writer_info (ASDCP::WriterInfo const &);
+       
+       ASDCP::AESEncContext* _encryption_context;
+       ASDCP::AESDecContext* _decryption_context;
+       /** ID of the key used for encryption/decryption, or an empty string */
+       std::string _key_id;
+       /** Key used for encryption/decryption, if there is one */
+       boost::optional<Key> _key;
+       MXFMetadata _metadata;
+};
+
+}
+
+#endif
diff --git a/src/mxf_asset.cc b/src/mxf_asset.cc
deleted file mode 100644 (file)
index f16fd35..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/asset.cc
- *  @brief Parent class for assets of DCPs made up of MXF files.
- */
-
-#include <iostream>
-#include <boost/filesystem.hpp>
-#include <libxml++/nodes/element.h>
-#include "AS_DCP.h"
-#include "KM_prng.h"
-#include "KM_util.h"
-#include "mxf_asset.h"
-#include "util.h"
-#include "metadata.h"
-#include "exceptions.h"
-#include "kdm.h"
-#include "raw_convert.h"
-
-using std::string;
-using std::list;
-using std::pair;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using namespace libdcp;
-
-MXFAsset::MXFAsset (boost::filesystem::path directory, boost::filesystem::path file_name)
-       : Asset (directory, file_name)
-       , _progress (0)
-       , _encryption_context (0)
-       , _decryption_context (0)
-       , _interop (false)
-{
-
-}
-
-MXFAsset::~MXFAsset ()
-{
-       delete _encryption_context;
-       delete _decryption_context;
-}
-
-void
-MXFAsset::fill_writer_info (ASDCP::WriterInfo* writer_info)
-{
-       writer_info->ProductVersion = _metadata.product_version;
-       writer_info->CompanyName = _metadata.company_name;
-       writer_info->ProductName = _metadata.product_name.c_str();
-
-       if (_interop) {
-               writer_info->LabelSetType = ASDCP::LS_MXF_INTEROP;
-       } else {
-               writer_info->LabelSetType = ASDCP::LS_MXF_SMPTE;
-       }
-       unsigned int c;
-       Kumu::hex2bin (_uuid.c_str(), writer_info->AssetUUID, Kumu::UUID_Length, &c);
-       assert (c == Kumu::UUID_Length);
-
-       if (_key) {
-               Kumu::GenRandomUUID (writer_info->ContextID);
-               writer_info->EncryptedEssence = true;
-
-               unsigned int c;
-               Kumu::hex2bin (_key_id.c_str(), writer_info->CryptographicKeyID, Kumu::UUID_Length, &c);
-               assert (c == Kumu::UUID_Length);
-       }
-}
-
-bool
-MXFAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
-{
-       if (!Asset::equals (other, opt, note)) {
-               return false;
-       }
-       
-       shared_ptr<const MXFAsset> other_mxf = dynamic_pointer_cast<const MXFAsset> (other);
-       if (!other_mxf) {
-               note (ERROR, "comparing an MXF asset with a non-MXF asset");
-               return false;
-       }
-       
-       if (_file_name != other_mxf->_file_name) {
-               note (ERROR, "MXF names differ");
-               if (!opt.mxf_names_can_differ) {
-                       return false;
-               }
-       }
-       
-       return true;
-}
-
-void
-MXFAsset::write_to_cpl (xmlpp::Element* node) const
-{
-       pair<string, string> const attr = cpl_node_attribute ();
-       xmlpp::Element* a = node->add_child (cpl_node_name ());
-       if (!attr.first.empty ()) {
-               a->set_attribute (attr.first, attr.second);
-       }
-       a->add_child ("Id")->add_child_text ("urn:uuid:" + _uuid);
-       a->add_child ("AnnotationText")->add_child_text (_file_name.string ());
-       a->add_child ("EditRate")->add_child_text (raw_convert<string> (_edit_rate) + " 1");
-       a->add_child ("IntrinsicDuration")->add_child_text (raw_convert<string> (_intrinsic_duration));
-       a->add_child ("EntryPoint")->add_child_text (raw_convert<string> (_entry_point));
-       a->add_child ("Duration")->add_child_text (raw_convert<string> (_duration));
-       if (!_key_id.empty ()) {
-               a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id);
-       }
-}
-
-void
-MXFAsset::set_key (Key key)
-{
-       _key = key;
-
-       if (_key_id.empty ()) {
-               /* No key ID so far; we now need one */
-               _key_id = make_uuid ();
-       }
-       
-       _decryption_context = new ASDCP::AESDecContext;
-       if (ASDCP_FAILURE (_decryption_context->InitKey (_key->value ()))) {
-               throw MiscError ("could not set up decryption context");
-       }
-
-       _encryption_context = new ASDCP::AESEncContext;
-       if (ASDCP_FAILURE (_encryption_context->InitKey (_key->value ()))) {
-               throw MiscError ("could not set up encryption context");
-       }
-       
-       uint8_t cbc_buffer[ASDCP::CBC_BLOCK_SIZE];
-       
-       Kumu::FortunaRNG rng;
-       if (ASDCP_FAILURE (_encryption_context->SetIVec (rng.FillRandom (cbc_buffer, ASDCP::CBC_BLOCK_SIZE)))) {
-               throw MiscError ("could not set up CBC initialization vector");
-       }
-}
diff --git a/src/mxf_asset.h b/src/mxf_asset.h
deleted file mode 100644 (file)
index 4eafdcb..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_MXF_ASSET_H
-#define LIBDCP_MXF_ASSET_H
-
-#include <boost/signals2.hpp>
-#include "asset.h"
-#include "key.h"
-#include "metadata.h"
-
-namespace ASDCP {
-       class AESEncContext;
-       class AESDecContext;
-}
-
-/* Undefine some stuff that the OS X 10.5 SDK defines */
-#undef Key
-#undef set_key
-
-namespace libdcp
-{
-
-class MXFMetadata;     
-
-/** @brief Parent class for assets which have MXF files */     
-class MXFAsset : public Asset
-{
-public:
-       /** Construct an MXFAsset.
-        *  This class will not write anything to disk in this constructor, but subclasses may.
-        *
-        *  @param directory Directory where MXF file is.
-        *  @param file_name Name of MXF file.
-        */
-       MXFAsset (boost::filesystem::path directory, boost::filesystem::path file_name);
-       
-       ~MXFAsset ();
-
-       virtual bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
-       virtual void write_to_cpl (xmlpp::Element *) const;
-       virtual std::string key_type () const = 0;
-       
-       /** Fill in a ADSCP::WriteInfo struct.
-        *  @param w struct to fill in.
-        */
-       void fill_writer_info (ASDCP::WriterInfo* w);
-
-       void set_progress (boost::signals2::signal<void (float)>* progress) {
-               _progress = progress;
-       }
-
-       bool encrypted () const {
-               return !_key_id.empty ();
-       }
-
-       void set_key_id (std::string i) {
-               _key_id = i;
-       }
-
-       std::string key_id () const {
-               return _key_id;
-       }
-       
-       void set_key (Key);
-
-       boost::optional<Key> key () const {
-               return _key;
-       }
-
-       ASDCP::AESEncContext* encryption_context () const {
-               return _encryption_context;
-       }
-
-       void set_metadata (MXFMetadata m) {
-               _metadata = m;
-       }
-
-       MXFMetadata metadata () const {
-               return _metadata;
-       }
-
-       /** Set whether or not the asset should be written in Interop mode.
-        *  @param i true to use interop.
-        */
-       void set_interop (bool i) {
-               _interop = i;
-       }
-
-       bool interop () const {
-               return _interop;
-       }
-
-protected:
-       virtual std::string cpl_node_name () const = 0;
-       virtual std::pair<std::string, std::string> cpl_node_attribute () const {
-               return std::make_pair ("", "");
-       }
-       
-       /** Signal to emit to report progress, or 0 */
-       boost::signals2::signal<void (float)>* _progress;
-       ASDCP::AESEncContext* _encryption_context;
-       ASDCP::AESDecContext* _decryption_context;
-       std::string _key_id;
-       boost::optional<Key> _key;
-       MXFMetadata _metadata;
-       bool _interop;
-};
-
-}
-
-#endif
diff --git a/src/mxf_writer.cc b/src/mxf_writer.cc
new file mode 100644 (file)
index 0000000..374e659
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/mxf_writer.h
+ *  @brief MXFWriter class.
+ */
+
+#include "mxf_writer.h"
+#include "mxf.h"
+
+using namespace dcp;
+
+/** Create an MXFWriter.
+ *  @param mxf MXF that we are writing.
+ *  @param file File to write to.
+ */
+MXFWriter::MXFWriter (MXF* mxf, boost::filesystem::path file)
+       : _mxf (mxf)
+       , _file (file)
+       , _frames_written (0)
+       , _finalized (false)
+{
+       mxf->set_file (file);
+}
+
+MXFWriter::~MXFWriter ()
+{
+       assert (_finalized);
+}
+
+void
+MXFWriter::finalize ()
+{
+       assert (!_finalized);
+       _finalized = true;
+       _mxf->_intrinsic_duration = _frames_written;
+}
diff --git a/src/mxf_writer.h b/src/mxf_writer.h
new file mode 100644 (file)
index 0000000..b4e20f3
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/mxf_writer.h
+ *  @brief MXFWriter class.
+ */
+
+#ifndef LIBDCP_MXF_WRITER_H
+#define LIBDCP_MXF_WRITER_H
+
+#include <boost/filesystem.hpp>
+
+namespace dcp {
+
+class MXF;
+
+/** @class MXFWriter
+ *  @brief Parent class for classes which can write MXF files.
+ *
+ *  The MXFWriter lasts for the duration of the write and is then discarded.
+ *  They can only be created by calling start_write() on an MXF object.
+ */
+class MXFWriter : public boost::noncopyable
+{
+public:
+       virtual ~MXFWriter ();
+       virtual void finalize ();
+
+protected:
+       MXFWriter (MXF* mxf, boost::filesystem::path file);
+
+       /** MXF that we are writing */
+       MXF* _mxf;
+       /** File that we are writing to */
+       boost::filesystem::path _file;
+       /** Number of `frames' written so far; the definition of a frame
+        *  varies depending on the subclass.
+        */
+       int64_t _frames_written;
+       /** true if finalize() has been called on this object */
+       bool _finalized;
+};
+
+}
+
+#endif
diff --git a/src/object.cc b/src/object.cc
new file mode 100644 (file)
index 0000000..315e501
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/object.cc
+ *  @brief Object class.
+ */
+
+#include "object.h"
+#include "util.h"
+
+using std::string;
+using namespace dcp;
+
+/** Create an Object with a random ID. */
+Object::Object ()
+       : _id (make_uuid ())
+{
+
+}
+
+/** Create an Object with a given ID.
+ *  @param id ID to use.
+ */
+Object::Object (string id)
+       : _id (id)
+{
+
+}
diff --git a/src/object.h b/src/object.h
new file mode 100644 (file)
index 0000000..82598c5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/object.h
+ *  @brief Object class.
+ */
+
+#ifndef LIBDCP_OBJECT_H
+#define LIBDCP_OBJECT_H
+
+#include <boost/noncopyable.hpp>
+#include <string>
+
+namespace dcp {
+
+/** @class Object
+ *  @brief Some part of a DCP that has a UUID.
+ */
+class Object : public boost::noncopyable
+{
+public:
+       Object ();
+       Object (std::string id);
+       virtual ~Object () {}
+
+       /** @return ID */
+       std::string id () const {
+               return _id;
+       }
+
+protected:
+       /** ID */
+       std::string _id;
+};
+       
+}
+
+#endif
diff --git a/src/parse/asset_map.cc b/src/parse/asset_map.cc
deleted file mode 100644 (file)
index 484c271..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/asset_map.cc
- *  @brief Classes used to parse a AssetMap.
- */
-
-#include <boost/algorithm/string.hpp>
-#include "asset_map.h"
-#include "../util.h"
-#include "../xml.h"
-
-using std::string;
-using std::list;
-using boost::shared_ptr;
-using namespace libdcp::parse;
-
-AssetMap::AssetMap (string file)
-{
-       cxml::Document f ("AssetMap");
-       f.read_file (file);
-
-       id = f.string_child ("Id");
-       creator = f.string_child ("Creator");
-       volume_count = f.number_child<int64_t> ("VolumeCount");
-       issue_date = f.string_child ("IssueDate");
-       issuer = f.string_child ("Issuer");
-       assets = type_grand_children<AssetMapAsset> (f, "AssetList", "Asset");
-}
-
-AssetMapAsset::AssetMapAsset (shared_ptr<const cxml::Node> node)
-{
-       id = node->string_child ("Id");
-       packing_list = node->optional_string_child ("PackingList").get_value_or ("");
-       chunks = type_grand_children<Chunk> (node, "ChunkList", "Chunk");
-}
-
-Chunk::Chunk (shared_ptr<const cxml::Node> node)
-{
-       path = node->string_child ("Path");
-
-       string const prefix = "file://";
-
-       if (boost::algorithm::starts_with (path, prefix)) {
-               path = path.substr (prefix.length());
-       }
-       
-       volume_index = node->optional_number_child<int64_t> ("VolumeIndex").get_value_or (0);
-       offset = node->optional_number_child<int64_t> ("Offset").get_value_or (0);
-       length = node->optional_number_child<int64_t> ("Length").get_value_or (0);
-}
-
-shared_ptr<AssetMapAsset>
-AssetMap::asset_from_id (string id) const
-{
-       for (list<shared_ptr<AssetMapAsset> >::const_iterator i = assets.begin (); i != assets.end(); ++i) {
-               if ((*i)->id == id) {
-                       return *i;
-               }
-       }
-
-       return shared_ptr<AssetMapAsset> ();
-}
diff --git a/src/parse/asset_map.h b/src/parse/asset_map.h
deleted file mode 100644 (file)
index af3e891..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/asset_map.h
- *  @brief Classes used to parse a AssetMap.
- */
-
-#include <stdint.h>
-#include <boost/shared_ptr.hpp>
-#include <libcxml/cxml.h>
-
-namespace libdcp {
-
-namespace parse {
-
-/** @class Chunk
- *  @brief A simple parser for and representation of a \<Chunk\> node within an asset map.
- */
-class Chunk
-{
-public:
-       Chunk ();
-       Chunk (boost::shared_ptr<const cxml::Node> node);
-
-       std::string path;
-       int64_t volume_index;
-       int64_t offset;
-       int64_t length;
-};
-
-/** @class AssetMapAsset
- *  @brief A simple parser for and representation of an \<AssetMap\> node within an asset map.
- */
-class AssetMapAsset
-{
-public:
-       AssetMapAsset ();
-       AssetMapAsset (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       std::string packing_list;
-       std::list<boost::shared_ptr<Chunk> > chunks;
-};
-
-/** @class AssetMap
- *  @brief A simple parser for and representation of an asset map file.
- */
-class AssetMap
-{
-public:
-       AssetMap (std::string file);
-
-       boost::shared_ptr<AssetMapAsset> asset_from_id (std::string id) const;
-       
-       std::string id;
-       std::string creator;
-       int64_t volume_count;
-       std::string issue_date;
-       std::string issuer;
-       std::list<boost::shared_ptr<AssetMapAsset> > assets;
-};
-
-}
-
-}
diff --git a/src/parse/cpl.cc b/src/parse/cpl.cc
deleted file mode 100644 (file)
index f6ce434..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/cpl_file.cc
- *  @brief Classes used to parse a CPL.
- */
-
-#include <iostream>
-#include "cpl.h"
-#include "../xml.h"
-#include "../util.h"
-
-using std::string;
-using std::bad_cast;
-using boost::shared_ptr;
-using namespace libdcp::parse;
-
-CPL::CPL (string file)
-{
-       cxml::Document f ("CompositionPlaylist");
-       f.read_file (file);
-       
-       id = f.string_child ("Id");
-       annotation_text = f.optional_string_child ("AnnotationText").get_value_or ("");
-       issue_date = f.string_child ("IssueDate");
-       creator = f.optional_string_child ("Creator").get_value_or ("");
-       content_title_text = f.string_child ("ContentTitleText");
-       content_kind = content_kind_from_string (f.string_child ("ContentKind"));
-       content_version = optional_type_child<ContentVersion> (f, "ContentVersion");
-       f.ignore_child ("RatingList");
-       reels = type_grand_children<Reel> (f, "ReelList", "Reel");
-
-       f.ignore_child ("Issuer");
-       f.ignore_child ("Signer");
-       f.ignore_child ("Signature");
-
-       f.done ();
-}
-
-ContentVersion::ContentVersion (shared_ptr<const cxml::Node> node)
-{
-       id = node->optional_string_child ("Id").get_value_or ("");
-       label_text = node->string_child ("LabelText");
-       node->done ();
-}
-
-Reel::Reel (shared_ptr<const cxml::Node> node)
-{
-       id = node->string_child ("Id");
-       asset_list = type_child<CPLAssetList> (node, "AssetList");
-
-       node->ignore_child ("AnnotationText");
-       node->done ();
-}
-
-CPLAssetList::CPLAssetList (shared_ptr<const cxml::Node> node)
-{
-       main_picture = optional_type_child<MainPicture> (node, "MainPicture");
-       main_stereoscopic_picture = optional_type_child<MainStereoscopicPicture> (node, "MainStereoscopicPicture");
-       main_sound = optional_type_child<MainSound> (node, "MainSound");
-       main_subtitle = optional_type_child<MainSubtitle> (node, "MainSubtitle");
-
-       node->done ();
-}
-
-MainPicture::MainPicture (shared_ptr<const cxml::Node> node)
-       : Picture (node)
-{
-
-}
-
-MainStereoscopicPicture::MainStereoscopicPicture (shared_ptr<const cxml::Node> node)
-       : Picture (node)
-{
-
-}
-
-Picture::Picture (shared_ptr<const cxml::Node> node)
-{
-       id = node->string_child ("Id");
-       annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
-       edit_rate = Fraction (node->string_child ("EditRate"));
-       intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration");
-       entry_point = node->number_child<int64_t> ("EntryPoint");
-       duration = node->number_child<int64_t> ("Duration");
-       frame_rate = Fraction (node->string_child ("FrameRate"));
-       try {
-               screen_aspect_ratio = Fraction (node->string_child ("ScreenAspectRatio"));
-       } catch (XMLError& e) {
-               /* Maybe it's not a fraction */
-       }
-       try {
-               float f = node->number_child<float> ("ScreenAspectRatio");
-               screen_aspect_ratio = Fraction (f * 1000, 1000);
-       } catch (bad_cast& e) {
-
-       }
-
-       key_id = node->optional_string_child ("KeyId").get_value_or ("");
-
-       node->ignore_child ("Hash");
-
-       node->done ();
-}
-
-MainSound::MainSound (shared_ptr<const cxml::Node> node)
-{
-       id = node->string_child ("Id");
-       annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
-       edit_rate = Fraction (node->string_child ("EditRate"));
-       intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration");
-       entry_point = node->number_child<int64_t> ("EntryPoint");
-       duration = node->number_child<int64_t> ("Duration");
-       key_id = node->optional_string_child ("KeyId").get_value_or ("");
-       
-       node->ignore_child ("Hash");
-       node->ignore_child ("Language");
-       
-       node->done ();
-}
-
-MainSubtitle::MainSubtitle (shared_ptr<const cxml::Node> node)
-{
-       id = node->string_child ("Id");
-       annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
-       edit_rate = Fraction (node->string_child ("EditRate"));
-       intrinsic_duration = node->number_child<int64_t> ("IntrinsicDuration");
-       entry_point = node->number_child<int64_t> ("EntryPoint");
-       duration = node->number_child<int64_t> ("Duration");
-
-       node->ignore_child ("Hash");
-       node->ignore_child ("Language");
-       
-       node->done ();
-}
diff --git a/src/parse/cpl.h b/src/parse/cpl.h
deleted file mode 100644 (file)
index 04bf935..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/parse/cpl.h
- *  @brief Classes used to parse a CPL.
- */
-
-#include <stdint.h>
-#include <boost/shared_ptr.hpp>
-#include <libcxml/cxml.h>
-#include "../types.h"
-
-namespace libdcp {
-
-namespace parse        {
-
-/** @brief A simple representation of a CPL \<Picture\> node */
-class Picture
-{
-public:
-       Picture () {}
-       Picture (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       std::string annotation_text;
-       Fraction edit_rate;
-       /** Duration of the whole thing */
-       int64_t intrinsic_duration;
-       /** Start point in frames */
-       int64_t entry_point;
-       /** Duration that will actually play */
-       int64_t duration;
-       Fraction frame_rate;
-       Fraction screen_aspect_ratio;
-       std::string key_id;
-};
-
-
-/** @brief A simple parser for and representation of a CPL \<MainPicture\> node */
-class MainPicture : public Picture
-{
-public:
-       MainPicture () {}
-       MainPicture (boost::shared_ptr<const cxml::Node> node);
-};
-
-/** @brief A simple parser for and representation of a CPL \<MainStereoscopicPicture\> node */
-class MainStereoscopicPicture : public Picture
-{
-public:
-       MainStereoscopicPicture () {}
-       MainStereoscopicPicture (boost::shared_ptr<const cxml::Node> node);
-};
-
-/** @brief A simple parser for and representation of a CPL \<MainSound\> node */
-class MainSound
-{
-public:
-       MainSound () {}
-       MainSound (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       std::string annotation_text;
-       Fraction edit_rate;
-       int64_t intrinsic_duration;
-       int64_t entry_point;
-       int64_t duration;
-       std::string key_id;
-};
-
-/** @brief A simple parser for and representation of a CPL \<MainSubtitle\> node */
-class MainSubtitle
-{
-public:
-       MainSubtitle () {}
-       MainSubtitle (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       std::string annotation_text;
-       Fraction edit_rate;
-       int64_t intrinsic_duration;
-       int64_t entry_point;
-       int64_t duration;
-};
-
-/** @brief A simple parser for and representation of a CPL \<AssetList\> node */
-class CPLAssetList
-{
-public:
-       CPLAssetList () {}
-       CPLAssetList (boost::shared_ptr<const cxml::Node> node);
-
-       boost::shared_ptr<MainPicture> main_picture;
-       boost::shared_ptr<MainStereoscopicPicture> main_stereoscopic_picture;
-       boost::shared_ptr<MainSound> main_sound;
-       boost::shared_ptr<MainSubtitle> main_subtitle;
-};
-
-/** @brief A simple parser for and representation of a CPL \<Reel\> node */
-class Reel
-{
-public:
-       Reel () {}
-       Reel (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       boost::shared_ptr<CPLAssetList> asset_list;
-};
-
-
-/** @brief A simple parser for and representation of a CPL \<ContentVersion\> node */
-class ContentVersion
-{
-public:
-       ContentVersion () {}
-       ContentVersion (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       std::string label_text;
-};
-
-/** @class CPL
- *  @brief Class to parse a CPL
- *
- *  This class is used to parse XML CPL files.  It is rarely necessary
- *  for the caller to use it outside libdcp.
- */
-class CPL
-{
-public:
-       /** Parse a CPL XML file into our member variables */
-       CPL (std::string file);
-
-       std::string id;
-       std::string annotation_text;
-       std::string issue_date;
-       std::string creator;
-       std::string content_title_text;
-       ContentKind content_kind;
-       boost::shared_ptr<ContentVersion> content_version;
-       std::list<boost::shared_ptr<Reel> > reels;
-};
-
-}
-
-}
-
diff --git a/src/parse/pkl.cc b/src/parse/pkl.cc
deleted file mode 100644 (file)
index bbf070a..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/pkl_file.cc
- *  @brief Classes used to parse a PKL.
- */
-
-#include <iostream>
-#include "pkl.h"
-
-using namespace std;
-using namespace boost;
-using namespace libdcp::parse;
-
-PKL::PKL (string file)
-{
-       cxml::Document f ("PackingList");
-       f.read_file (file);
-       
-       id = f.string_child ("Id");
-       annotation_text = f.optional_string_child ("AnnotationText").get_value_or ("");
-       issue_date = f.string_child ("IssueDate");
-       issuer = f.string_child ("Issuer");
-       creator = f.string_child ("Creator");
-       assets = type_grand_children<PKLAsset> (f, "AssetList", "Asset");
-}
-
-PKLAsset::PKLAsset (boost::shared_ptr<const cxml::Node> node)
-{
-       id = node->string_child ("Id");
-       annotation_text = node->optional_string_child ("AnnotationText").get_value_or ("");
-       hash = node->string_child ("Hash");
-       size = node->number_child<int64_t> ("Size");
-       type = node->string_child ("Type");
-       original_file_name = node->optional_string_child ("OriginalFileName").get_value_or ("");
-}
diff --git a/src/parse/pkl.h b/src/parse/pkl.h
deleted file mode 100644 (file)
index 13d87fa..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/parse/pkl.h
- *  @brief Classes used to parse a PKL
- */
-
-#include <boost/shared_ptr.hpp>
-#include "../xml.h"
-
-namespace libdcp {
-
-namespace parse {
-
-class PKLAsset
-{
-public:
-       PKLAsset () {}
-       PKLAsset (boost::shared_ptr<const cxml::Node>);
-
-       std::string id;
-       std::string annotation_text;
-       std::string hash;
-       int64_t size;
-       std::string type;
-       std::string original_file_name;
-};
-
-class PKL
-{
-public:
-       PKL (std::string file);
-
-       std::string id;
-       std::string annotation_text;
-       std::string issue_date;
-       std::string issuer;
-       std::string creator;
-       std::list<boost::shared_ptr<PKLAsset> > assets;
-};
-       
-}
-
-}
diff --git a/src/parse/subtitle.cc b/src/parse/subtitle.cc
deleted file mode 100644 (file)
index 831fef5..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
-    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
-#include "subtitle.h"
-#include "../raw_convert.h"
-#include "../types.h"
-
-using std::string;
-using std::list;
-using boost::shared_ptr;
-using boost::optional;
-using namespace libdcp;
-using namespace libdcp::parse;
-
-Font::Font (shared_ptr<const cxml::Node> node)
-{
-       text = node->content ();
-       
-       id = node->optional_string_attribute ("Id").get_value_or ("");
-       size = node->optional_number_attribute<int64_t> ("Size").get_value_or (0);
-       italic = node->optional_bool_attribute ("Italic");
-       optional<string> c = node->optional_string_attribute ("Color");
-       if (c) {
-               color = Color (c.get ());
-       }
-       optional<string> const e = node->optional_string_attribute ("Effect");
-       if (e) {
-               effect = string_to_effect (e.get ());
-       }
-       c = node->optional_string_attribute ( "EffectColor");
-       if (c) {
-               effect_color = Color (c.get ());
-       }
-       subtitle_nodes = type_children<Subtitle> (node, "Subtitle");
-       font_nodes = type_children<Font> (node, "Font");
-       text_nodes = type_children<Text> (node, "Text");
-}
-
-Font::Font (list<shared_ptr<Font> > const & font_nodes)
-       : size (0)
-       , italic (false)
-       , color ("FFFFFFFF")
-       , effect_color ("FFFFFFFF")
-{
-       for (list<shared_ptr<Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
-               if (!(*i)->id.empty ()) {
-                       id = (*i)->id;
-               }
-               if ((*i)->size != 0) {
-                       size = (*i)->size;
-               }
-               if ((*i)->italic) {
-                       italic = (*i)->italic.get ();
-               }
-               if ((*i)->color) {
-                       color = (*i)->color.get ();
-               }
-               if ((*i)->effect) {
-                       effect = (*i)->effect.get ();
-               }
-               if ((*i)->effect_color) {
-                       effect_color = (*i)->effect_color.get ();
-               }
-       }
-}
-
-LoadFont::LoadFont (shared_ptr<const cxml::Node> node)
-{
-       optional<string> x = node->optional_string_attribute ("Id");
-       if (!x) {
-               x = node->optional_string_attribute ("ID");
-       }
-       id = x.get_value_or ("");
-               
-       uri = node->optional_string_attribute ("URI");
-}
-
-Subtitle::Subtitle (shared_ptr<const cxml::Node> node)
-{
-       in = Time (node->string_attribute ("TimeIn"));
-       out = Time (node->string_attribute ("TimeOut"));
-       font_nodes = type_children<Font> (node, "Font");
-       text_nodes = type_children<Text> (node, "Text");
-       fade_up_time = fade_time (node, "FadeUpTime");
-       fade_down_time = fade_time (node, "FadeDownTime");
-}
-
-Time
-Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name)
-{
-       string const u = node->optional_string_attribute (name).get_value_or ("");
-       Time t;
-       
-       if (u.empty ()) {
-               t = Time (0, 0, 0, 20);
-       } else if (u.find (":") != string::npos) {
-               t = Time (u);
-       } else {
-               t = Time (0, 0, 0, raw_convert<int> (u));
-       }
-
-       if (t > Time (0, 0, 8, 0)) {
-               t = Time (0, 0, 8, 0);
-       }
-
-       return t;
-}
-
-Text::Text (shared_ptr<const cxml::Node> node)
-       : v_align (CENTER)
-{
-       /* Vertical position */
-       text = node->content ();
-       optional<float> x = node->optional_number_attribute<float> ("VPosition");
-       if (!x) {
-               x = node->number_attribute<float> ("Vposition");
-       }
-       v_position = x.get ();
-
-       /* Vertical alignment */
-       optional<string> v = node->optional_string_attribute ("VAlign");
-       if (!v) {
-               v = node->optional_string_attribute ("Valign");
-       }
-       if (v) {
-               v_align = string_to_valign (v.get ());
-       }
-
-       font_nodes = type_children<Font> (node, "Font");
-}
-
diff --git a/src/parse/subtitle.h b/src/parse/subtitle.h
deleted file mode 100644 (file)
index 3d99d9b..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "../xml.h"
-#include "../dcp_time.h"
-#include "../types.h"
-
-namespace libdcp
-{
-
-namespace parse
-{
-
-class Font;
-
-class Text
-{
-public:
-       Text ()
-               : v_position (0)
-               , v_align (TOP)
-       {}
-       
-       Text (boost::shared_ptr<const cxml::Node> node);
-
-       float v_position;
-       VAlign v_align;
-       std::string text;
-       std::list<boost::shared_ptr<Font> > font_nodes;
-};
-
-class Subtitle 
-{
-public:
-       Subtitle () {}
-       Subtitle (boost::shared_ptr<const cxml::Node> node);
-
-       Time in;
-       Time out;
-       Time fade_up_time;
-       Time fade_down_time;
-       std::list<boost::shared_ptr<Font> > font_nodes;
-       std::list<boost::shared_ptr<Text> > text_nodes;
-
-private:
-       Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name);
-};
-
-class Font 
-{
-public:
-       Font ()
-               : size (0)
-       {}
-       
-       Font (boost::shared_ptr<const cxml::Node> node);
-       Font (std::list<boost::shared_ptr<Font> > const & font_nodes);
-
-       std::string text;
-       std::string id;
-       int size;
-       boost::optional<bool> italic;
-       boost::optional<Color> color;
-       boost::optional<Effect> effect;
-       boost::optional<Color> effect_color;
-       
-       std::list<boost::shared_ptr<Subtitle> > subtitle_nodes;
-       std::list<boost::shared_ptr<Font> > font_nodes;
-       std::list<boost::shared_ptr<Text> > text_nodes;
-};
-
-class LoadFont 
-{
-public:
-       LoadFont () {}
-       LoadFont (boost::shared_ptr<const cxml::Node> node);
-
-       std::string id;
-       boost::optional<std::string> uri;
-};
-
-}
-
-}
diff --git a/src/picture_asset.cc b/src/picture_asset.cc
deleted file mode 100644 (file)
index 8d3b034..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
-    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/picture_asset.cc
- *  @brief An asset made up of JPEG2000 files
- */
-
-#include <list>
-#include <stdexcept>
-#include <iostream>
-#include <sstream>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <openjpeg.h>
-#include <libxml++/nodes/element.h>
-#include "AS_DCP.h"
-#include "KM_fileio.h"
-#include "picture_asset.h"
-#include "util.h"
-#include "exceptions.h"
-#include "xyz_frame.h"
-#include "picture_asset_writer.h"
-#include "raw_convert.h"
-
-using std::string;
-using std::ostream;
-using std::list;
-using std::vector;
-using std::max;
-using std::stringstream;
-using std::pair;
-using std::make_pair;
-using std::istream;
-using std::cout;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using boost::lexical_cast;
-using namespace libdcp;
-
-PictureAsset::PictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name)
-       : MXFAsset (directory, mxf_name)
-{
-
-}
-
-void
-PictureAsset::write_to_cpl (xmlpp::Element* node) const
-{
-       MXFAsset::write_to_cpl (node);
-       
-       xmlpp::Node::NodeList c = node->get_children ();
-       xmlpp::Node::NodeList::iterator i = c.begin();
-       while (i != c.end() && (*i)->get_name() != cpl_node_name ()) {
-               ++i;
-       }
-
-       assert (i != c.end ());
-
-       (*i)->add_child ("FrameRate")->add_child_text (raw_convert<string> (_edit_rate * edit_rate_factor ()) + " 1");
-       if (_interop) {
-               stringstream s;
-               s << std::fixed << std::setprecision (2) << (float (_size.width) / _size.height);
-               (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ());
-       } else {
-               (*i)->add_child ("ScreenAspectRatio")->add_child_text (raw_convert<string> (_size.width) + " " + raw_convert<string> (_size.height));
-       }
-}
-
-bool
-PictureAsset::descriptor_equals (
-       ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function<void (NoteType, string)> note
-       ) const
-{
-       if (
-               a.EditRate != b.EditRate ||
-               a.SampleRate != b.SampleRate ||
-               a.StoredWidth != b.StoredWidth ||
-               a.StoredHeight != b.StoredHeight ||
-               a.AspectRatio != b.AspectRatio ||
-               a.Rsize != b.Rsize ||
-               a.Xsize != b.Xsize ||
-               a.Ysize != b.Ysize ||
-               a.XOsize != b.XOsize ||
-               a.YOsize != b.YOsize ||
-               a.XTsize != b.XTsize ||
-               a.YTsize != b.YTsize ||
-               a.XTOsize != b.XTOsize ||
-               a.YTOsize != b.YTOsize ||
-               a.Csize != b.Csize
-//             a.CodingStyleDefault != b.CodingStyleDefault ||
-//             a.QuantizationDefault != b.QuantizationDefault
-               ) {
-               
-               note (ERROR, "video MXF picture descriptors differ");
-               return false;
-       }
-
-       if (a.ContainerDuration != b.ContainerDuration) {
-               note (ERROR, "video container durations differ");
-       }
-       
-//             for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) {
-//                     if (a.ImageComponents[j] != b.ImageComponents[j]) {
-//                             notes.pack_start ("video MXF picture descriptors differ");
-//                     }
-//             }
-
-       return true;
-}
-
-bool
-PictureAsset::frame_buffer_equals (
-       int frame, EqualityOptions opt, boost::function<void (NoteType, string)> note,
-       uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
-       ) const
-{
-       if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) {
-               note (NOTE, "J2K identical");
-               /* Easy result; the J2K data is identical */
-               return true;
-       }
-               
-       /* Decompress the images to bitmaps */
-       shared_ptr<XYZFrame> image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0);
-       shared_ptr<XYZFrame> image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0);
-       
-       /* Compare them */
-       
-       vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3);
-       int d = 0;
-       int max_diff = 0;
-       
-       for (int c = 0; c < 3; ++c) {
-               
-               if (image_A->size() != image_B->size()) {
-                       note (ERROR, "image sizes for frame " + lexical_cast<string>(frame) + " differ");
-                       return false;
-               }
-               
-               int const pixels = image_A->size().width * image_A->size().height;
-               for (int j = 0; j < pixels; ++j) {
-                       int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]);
-                       abs_diffs[d++] = t;
-                       max_diff = max (max_diff, t);
-               }
-       }
-               
-       uint64_t total = 0;
-       for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
-               total += *j;
-       }
-       
-       double const mean = double (total) / abs_diffs.size ();
-       
-       uint64_t total_squared_deviation = 0;
-       for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
-               total_squared_deviation += pow (*j - mean, 2);
-       }
-       
-       double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
-       
-       note (NOTE, "mean difference " + lexical_cast<string> (mean) + ", deviation " + lexical_cast<string> (std_dev));
-       
-       if (mean > opt.max_mean_pixel_error) {
-               note (
-                       ERROR,
-                       "mean " + lexical_cast<string>(mean) +
-                       " out of range " + lexical_cast<string>(opt.max_mean_pixel_error) +
-                       " in frame " + lexical_cast<string>(frame)
-                       );
-               
-               return false;
-       }
-
-       if (std_dev > opt.max_std_dev_pixel_error) {
-               note (
-                       ERROR,
-                       "standard deviation " + lexical_cast<string>(std_dev) +
-                       " out of range " + lexical_cast<string>(opt.max_std_dev_pixel_error) +
-                       " in frame " + lexical_cast<string>(frame)
-                       );
-               
-               return false;
-       }
-
-       return true;
-}
-
-string
-PictureAsset::key_type () const
-{
-       return "MDIK";
-}
-
-
-
diff --git a/src/picture_asset.h b/src/picture_asset.h
deleted file mode 100644 (file)
index b404abd..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_PICTURE_ASSET_H
-#define LIBDCP_PICTURE_ASSET_H
-
-/** @file  src/picture_asset.h
- *  @brief An asset made up of JPEG2000 data
- */
-
-#include <openjpeg.h>
-#include "mxf_asset.h"
-#include "util.h"
-#include "metadata.h"
-
-namespace ASDCP {
-       namespace JP2K {
-               struct PictureDescriptor;
-       }
-}
-
-namespace libdcp
-{
-
-class MonoPictureFrame;        
-class StereoPictureFrame;
-class PictureAssetWriter;
-
-/** @brief An asset made up of JPEG2000 data */
-class PictureAsset : public MXFAsset
-{
-public:
-       /** Construct a PictureAsset.
-        *  
-        *  @param directory Directory where MXF file is.
-        *  @param mxf_name Name of MXF file.
-        */
-       PictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name);
-
-       /** Start a progressive write to this asset.
-        *  The following parameters must be set up (if required) before calling this:
-        *      Interop mode (set_interop)
-        *      Edit rate    (set_edit_rate)
-        *      MXF Metadata (set_metadata)
-        *      
-        *  @param overwrite true to overwrite an existing MXF file; in this mode, writing can be resumed to a partially-written MXF; false if the
-        *  MXF file does not exist.
-        */
-       virtual boost::shared_ptr<PictureAssetWriter> start_write (bool overwrite) = 0;
-
-       virtual void read () = 0;
-       virtual void create (std::vector<boost::filesystem::path> const &) {}
-       virtual void create (boost::function<boost::filesystem::path (int)>) {}
-       
-       Size size () const {
-               return _size;
-       }
-
-       void set_size (Size s) {
-               _size = s;
-       }
-
-       void write_to_cpl (xmlpp::Element *) const;
-
-protected:
-
-       std::string asdcp_kind () const {
-               return "Picture";
-       }
-
-       bool frame_buffer_equals (
-               int frame, EqualityOptions opt, boost::function<void (NoteType, std::string)> note,
-               uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
-               ) const;
-
-       bool descriptor_equals (
-               ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function<void (NoteType, std::string)>
-               ) const;
-
-       /** picture size in pixels */
-       Size _size;
-
-private:
-       std::string key_type () const;
-       virtual int edit_rate_factor () const = 0;
-};
-       
-
-}
-
-#endif
diff --git a/src/picture_asset_writer.cc b/src/picture_asset_writer.cc
deleted file mode 100644 (file)
index 92b384d..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <inttypes.h>
-#include <stdint.h>
-#include "AS_DCP.h"
-#include "KM_fileio.h"
-#include "picture_asset_writer.h"
-#include "exceptions.h"
-#include "picture_asset.h"
-
-using std::istream;
-using std::ostream;
-using std::string;
-using boost::shared_ptr;
-using namespace libdcp;
-
-FrameInfo::FrameInfo (istream& s)
-       : offset (0)
-       , size (0)
-{
-       s >> offset >> size;
-
-       if (!s.good ()) {
-               /* Make sure we zero these if something bad happened, otherwise
-                  the caller might try to alloc lots of RAM.
-               */
-               offset = size = 0;
-       }
-
-       s >> hash;
-}
-
-FrameInfo::FrameInfo (FILE* f)
-{
-#ifdef LIBDCP_WINDOWS
-       fscanf (f, "%I64u", &offset);
-       fscanf (f, "%I64u", &size);
-#else  
-       fscanf (f, "%" SCNu64, &offset);
-       fscanf (f, "%" SCNu64, &size);
-#endif 
-
-       if (ferror (f)) {
-               offset = size = 0;
-       }
-
-       char hash_buffer[128];
-       fscanf (f, "%s", hash_buffer);
-       hash = hash_buffer;
-}
-
-void
-FrameInfo::write (ostream& s) const
-{
-       s << offset << " " << size << " " << hash;
-}
-
-void
-FrameInfo::write (FILE* f) const
-{
-#ifdef LIBDCP_WINDOWS  
-       fprintf (f, "%I64u %I64u %s", offset, size, hash.c_str ());
-#else  
-       fprintf (f, "%" PRIu64 " %" PRIu64 " %s", offset, size, hash.c_str ());
-#endif 
-}
-
-
-PictureAssetWriter::PictureAssetWriter (PictureAsset* asset, bool overwrite)
-       : _asset (asset)
-       , _frames_written (0)
-       , _started (false)
-       , _finalized (false)
-       , _overwrite (overwrite)
-{
-
-}
diff --git a/src/picture_asset_writer.h b/src/picture_asset_writer.h
deleted file mode 100644 (file)
index a62b20b..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <stdint.h>
-#include <string>
-#include <fstream>
-#include <boost/shared_ptr.hpp>
-#include <boost/utility.hpp>
-#include "metadata.h"
-#include "types.h"
-
-namespace libdcp {
-
-class PictureAsset;    
-
-/** Information about a single frame (either a monoscopic frame or a left *or* right eye stereoscopic frame) */        
-struct FrameInfo
-{
-       FrameInfo (uint64_t o, uint64_t s, std::string h)
-               : offset (o)
-               , size (s)
-               , hash (h)
-       {}
-
-       FrameInfo (std::istream& s);
-       FrameInfo (FILE *);
-
-       void write (std::ostream& s) const;
-       void write (FILE *) const;
-       
-       uint64_t offset;
-       uint64_t size;
-       std::string hash;
-};
-
-class PictureAssetWriter : public boost::noncopyable
-{
-public:
-       virtual ~PictureAssetWriter () {}
-       virtual FrameInfo write (uint8_t *, int) = 0;
-       virtual void finalize () = 0;
-       virtual void fake_write (int) = 0;
-       
-protected:
-       template <class P, class Q>
-       friend void start (PictureAssetWriter *, boost::shared_ptr<P>, Q *, uint8_t *, int);
-
-       PictureAssetWriter (PictureAsset *, bool);
-
-       PictureAsset* _asset;
-       
-       /** Number of picture frames written to the asset so far.  For stereo assets
-        *  this will be incremented for each eye (i.e. there will be twice the number
-        *  of frames as in a mono asset).
-        */
-       int _frames_written;
-       bool _started;
-       /** true if finalize() has been called */
-       bool _finalized;
-       bool _overwrite;
-};
-
-}
diff --git a/src/picture_asset_writer_common.cc b/src/picture_asset_writer_common.cc
deleted file mode 100644 (file)
index 5284874..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-using boost::shared_ptr;
-
-struct ASDCPStateBase
-{
-       ASDCPStateBase ()
-               : frame_buffer (4 * Kumu::Megabyte)
-       {}
-       
-       ASDCP::JP2K::CodestreamParser j2k_parser;
-       ASDCP::JP2K::FrameBuffer frame_buffer;
-       ASDCP::WriterInfo writer_info;
-       ASDCP::JP2K::PictureDescriptor picture_descriptor;
-       ASDCP::AESEncContext* encryption_context;
-};
-
-template <class P, class Q>
-void libdcp::start (PictureAssetWriter* writer, shared_ptr<P> state, Q* asset, uint8_t* data, int size)
-{
-       if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) {
-               boost::throw_exception (MiscError ("could not parse J2K frame"));
-       }
-
-       state->j2k_parser.FillPictureDescriptor (state->picture_descriptor);
-       state->picture_descriptor.EditRate = ASDCP::Rational (asset->edit_rate(), 1);
-       
-       asset->fill_writer_info (&state->writer_info);
-       
-       Kumu::Result_t r = state->mxf_writer.OpenWrite (
-               asset->path().string().c_str(),
-               state->writer_info,
-               state->picture_descriptor,
-               16384,
-               writer->_overwrite
-               );
-
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for writing", asset->path().string(), r));
-       }
-
-       writer->_started = true;
-}
diff --git a/src/picture_mxf.cc b/src/picture_mxf.cc
new file mode 100644 (file)
index 0000000..4a8e477
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "picture_mxf.h"
+#include "util.h"
+#include "exceptions.h"
+#include "xyz_frame.h"
+#include "picture_mxf_writer.h"
+#include "compose.hpp"
+#include "AS_DCP.h"
+#include "KM_fileio.h"
+#include <libxml++/nodes/element.h>
+#include <openjpeg.h>
+#include <boost/filesystem.hpp>
+#include <list>
+#include <stdexcept>
+#include <iostream>
+#include <sstream>
+
+using std::string;
+using std::ostream;
+using std::list;
+using std::vector;
+using std::max;
+using std::stringstream;
+using std::pair;
+using std::make_pair;
+using std::istream;
+using std::cout;
+using boost::shared_ptr;
+using namespace dcp;
+
+PictureMXF::PictureMXF (boost::filesystem::path file)
+       : MXF (file)
+{
+
+}
+
+PictureMXF::PictureMXF (Fraction edit_rate)
+       : MXF (edit_rate)
+{
+
+}
+
+void
+PictureMXF::read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const & desc)
+{
+       _size.width = desc.StoredWidth;
+       _size.height = desc.StoredHeight;
+       _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
+       _intrinsic_duration = desc.ContainerDuration;
+       _frame_rate = Fraction (desc.SampleRate.Numerator, desc.SampleRate.Denominator);
+       _screen_aspect_ratio = Fraction (desc.AspectRatio.Numerator, desc.AspectRatio.Denominator);
+}
+
+bool
+PictureMXF::descriptor_equals (
+       ASDCP::JP2K::PictureDescriptor const & a, ASDCP::JP2K::PictureDescriptor const & b, boost::function<void (NoteType, string)> note
+       ) const
+{
+       if (
+               a.EditRate != b.EditRate ||
+               a.SampleRate != b.SampleRate ||
+               a.StoredWidth != b.StoredWidth ||
+               a.StoredHeight != b.StoredHeight ||
+               a.AspectRatio != b.AspectRatio ||
+               a.Rsize != b.Rsize ||
+               a.Xsize != b.Xsize ||
+               a.Ysize != b.Ysize ||
+               a.XOsize != b.XOsize ||
+               a.YOsize != b.YOsize ||
+               a.XTsize != b.XTsize ||
+               a.YTsize != b.YTsize ||
+               a.XTOsize != b.XTOsize ||
+               a.YTOsize != b.YTOsize ||
+               a.Csize != b.Csize
+//             a.CodingStyleDefault != b.CodingStyleDefault ||
+//             a.QuantizationDefault != b.QuantizationDefault
+               ) {
+               
+               note (ERROR, "video MXF picture descriptors differ");
+               return false;
+       }
+
+       if (a.ContainerDuration != b.ContainerDuration) {
+               note (ERROR, "video container durations differ");
+       }
+       
+//             for (unsigned int j = 0; j < ASDCP::JP2K::MaxComponents; ++j) {
+//                     if (a.ImageComponents[j] != b.ImageComponents[j]) {
+//                             notes.pack_start ("video MXF picture descriptors differ");
+//                     }
+//             }
+
+       return true;
+}
+
+bool
+PictureMXF::frame_buffer_equals (
+       int frame, EqualityOptions opt, boost::function<void (NoteType, string)> note,
+       uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
+       ) const
+{
+       if (size_A == size_B && memcmp (data_A, data_B, size_A) == 0) {
+               note (NOTE, "J2K identical");
+               /* Easy result; the J2K data is identical */
+               return true;
+       }
+               
+       /* Decompress the images to bitmaps */
+       shared_ptr<XYZFrame> image_A = decompress_j2k (const_cast<uint8_t*> (data_A), size_A, 0);
+       shared_ptr<XYZFrame> image_B = decompress_j2k (const_cast<uint8_t*> (data_B), size_B, 0);
+       
+       /* Compare them */
+       
+       vector<int> abs_diffs (image_A->size().width * image_A->size().height * 3);
+       int d = 0;
+       int max_diff = 0;
+       
+       for (int c = 0; c < 3; ++c) {
+               
+               if (image_A->size() != image_B->size()) {
+                       note (ERROR, String::compose ("image sizes for frame %1 differ", frame));
+                       return false;
+               }
+               
+               int const pixels = image_A->size().width * image_A->size().height;
+               for (int j = 0; j < pixels; ++j) {
+                       int const t = abs (image_A->data(c)[j] - image_B->data(c)[j]);
+                       abs_diffs[d++] = t;
+                       max_diff = max (max_diff, t);
+               }
+       }
+               
+       uint64_t total = 0;
+       for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
+               total += *j;
+       }
+       
+       double const mean = double (total) / abs_diffs.size ();
+       
+       uint64_t total_squared_deviation = 0;
+       for (vector<int>::iterator j = abs_diffs.begin(); j != abs_diffs.end(); ++j) {
+               total_squared_deviation += pow (*j - mean, 2);
+       }
+       
+       double const std_dev = sqrt (double (total_squared_deviation) / abs_diffs.size());
+       
+       note (NOTE, String::compose ("mean difference %1, deviation %2", mean, std_dev));
+       
+       if (mean > opt.max_mean_pixel_error) {
+               note (
+                       ERROR,
+                       String::compose ("mean %1 out of range %2 in frame %3", mean, opt.max_mean_pixel_error, frame)
+                       );
+               
+               return false;
+       }
+
+       if (std_dev > opt.max_std_dev_pixel_error) {
+               note (
+                       ERROR,
+                       String::compose ("standard deviation %1 out of range %2 in frame %3", std_dev, opt.max_std_dev_pixel_error, frame)
+                       );
+               
+               return false;
+       }
+
+       return true;
+}
+
+string
+PictureMXF::key_type () const
+{
+       return "MDIK";
+}
diff --git a/src/picture_mxf.h b/src/picture_mxf.h
new file mode 100644 (file)
index 0000000..1ce2e7d
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_PICTURE_MXF_H
+#define LIBDCP_PICTURE_MXF_H
+
+/** @file  src/picture_mxf.h
+ *  @brief PictureMXF class.
+ */
+
+#include "mxf.h"
+#include "util.h"
+#include "metadata.h"
+#include <openjpeg.h>
+
+namespace ASDCP {
+       namespace JP2K {
+               class PictureDescriptor;
+       }
+}
+
+namespace dcp
+{
+
+class MonoPictureFrame;        
+class StereoPictureFrame;
+class PictureMXFWriter;
+
+/** @class PictureMXF
+ *  @brief An asset made up of JPEG2000 data.
+ */
+class PictureMXF : public MXF
+{
+public:
+       PictureMXF (boost::filesystem::path file);
+       PictureMXF (Fraction edit_rate);
+
+       virtual boost::shared_ptr<PictureMXFWriter> start_write (
+               boost::filesystem::path file,
+               Standard standard,
+               bool overwrite
+               ) = 0;
+
+       Size size () const {
+               return _size;
+       }
+
+       void set_size (Size s) {
+               _size = s;
+       }
+
+       Fraction frame_rate () const {
+               return _frame_rate;
+       }
+
+       void set_frame_rate (Fraction r) {
+               _frame_rate = r;
+       }
+
+       Fraction screen_aspect_ratio () const {
+               return _screen_aspect_ratio;
+       }
+
+       void set_screen_aspect_ratio (Fraction r) {
+               _screen_aspect_ratio = r;
+       }
+
+protected:
+
+       bool frame_buffer_equals (
+               int frame, EqualityOptions opt, boost::function<void (NoteType, std::string)> note,
+               uint8_t const * data_A, unsigned int size_A, uint8_t const * data_B, unsigned int size_B
+               ) const;
+
+       bool descriptor_equals (
+               ASDCP::JP2K::PictureDescriptor const & a,
+               ASDCP::JP2K::PictureDescriptor const & b,
+               boost::function<void (NoteType, std::string)>
+               ) const;
+
+       void read_picture_descriptor (ASDCP::JP2K::PictureDescriptor const &);
+
+       /** picture size in pixels */
+       Size _size;
+       Fraction _frame_rate;
+       Fraction _screen_aspect_ratio;
+
+private:
+       std::string key_type () const;
+       std::string asdcp_kind () const {
+               return "Picture";
+       }
+};
+       
+
+}
+
+#endif
diff --git a/src/picture_mxf_writer.cc b/src/picture_mxf_writer.cc
new file mode 100644 (file)
index 0000000..4642640
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "KM_fileio.h"
+#include "picture_mxf_writer.h"
+#include "exceptions.h"
+#include "picture_mxf.h"
+#include "AS_DCP.h"
+#include <inttypes.h>
+#include <stdint.h>
+
+using std::istream;
+using std::ostream;
+using std::string;
+using boost::shared_ptr;
+using namespace dcp;
+
+FrameInfo::FrameInfo (istream& s)
+       : offset (0)
+       , size (0)
+{
+       s >> offset >> size;
+
+       if (!s.good ()) {
+               /* Make sure we zero these if something bad happened, otherwise
+                  the caller might try to alloc lots of RAM.
+               */
+               offset = size = 0;
+       }
+
+       s >> hash;
+}
+
+FrameInfo::FrameInfo (FILE* f)
+{
+#ifdef LIBDCP_WINDOWS
+       fscanf (f, "%I64u", &offset);
+       fscanf (f, "%I64u", &size);
+#else  
+       fscanf (f, "%" SCNu64, &offset);
+       fscanf (f, "%" SCNu64, &size);
+#endif 
+
+       if (ferror (f)) {
+               offset = size = 0;
+       }
+
+       char hash_buffer[128];
+       fscanf (f, "%s", hash_buffer);
+       hash = hash_buffer;
+}
+
+void
+FrameInfo::write (ostream& s) const
+{
+       s << offset << " " << size << " " << hash;
+}
+
+void
+FrameInfo::write (FILE* f) const
+{
+#ifdef LIBDCP_WINDOWS  
+       fprintf (f, "%I64u %I64u %s", offset, size, hash.c_str ());
+#else  
+       fprintf (f, "%" PRIu64 " %" PRIu64 " %s", offset, size, hash.c_str ());
+#endif 
+}
+
+PictureMXFWriter::PictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, Standard standard, bool overwrite)
+       : MXFWriter (mxf, file)
+       , _picture_mxf (mxf)
+       , _started (false)
+       , _standard (standard)
+       , _overwrite (overwrite)
+{
+       
+}
diff --git a/src/picture_mxf_writer.h b/src/picture_mxf_writer.h
new file mode 100644 (file)
index 0000000..d1baa7a
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/picture_mxf_writer.h
+ *  @brief PictureMXFWriter and FrameInfo classes.
+ */
+
+#ifndef LIBDCP_PICTURE_MXF_WRITER_H
+#define LIBDCP_PICTURE_MXF_WRITER_H
+
+#include "metadata.h"
+#include "types.h"
+#include "mxf_writer.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+
+namespace dcp {
+
+class PictureMXF;      
+
+/** @class FrameInfo
+ *  @brief Information about a single frame (either a monoscopic frame or a left *or* right eye stereoscopic frame)
+ */
+struct FrameInfo
+{
+       FrameInfo (uint64_t o, uint64_t s, std::string h)
+               : offset (o)
+               , size (s)
+               , hash (h)
+       {}
+
+       FrameInfo (std::istream& s);
+       FrameInfo (FILE *);
+
+       void write (std::ostream& s) const;
+       void write (FILE *) const;
+       
+       uint64_t offset;
+       uint64_t size;
+       std::string hash;
+};
+
+/** @class PictureMXFWriter
+ *  @brief Parent class for classes which write picture MXF files.
+ */
+class PictureMXFWriter : public MXFWriter
+{
+public:
+       virtual FrameInfo write (uint8_t *, int) = 0;
+       virtual void fake_write (int) = 0;
+
+protected:
+       template <class P, class Q>
+       friend void start (PictureMXFWriter *, boost::shared_ptr<P>, Standard, Q *, uint8_t *, int);
+
+       PictureMXFWriter (PictureMXF *, boost::filesystem::path, Standard standard, bool);
+
+       PictureMXF* _picture_mxf;
+       bool _started;
+       Standard _standard;
+       bool _overwrite;
+};
+
+}
+
+#endif
diff --git a/src/picture_mxf_writer_common.cc b/src/picture_mxf_writer_common.cc
new file mode 100644 (file)
index 0000000..d8d1589
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+using boost::shared_ptr;
+
+struct ASDCPStateBase
+{
+       ASDCPStateBase ()
+               : frame_buffer (4 * Kumu::Megabyte)
+       {}
+       
+       ASDCP::JP2K::CodestreamParser j2k_parser;
+       ASDCP::JP2K::FrameBuffer frame_buffer;
+       ASDCP::WriterInfo writer_info;
+       ASDCP::JP2K::PictureDescriptor picture_descriptor;
+       ASDCP::AESEncContext* encryption_context;
+};
+
+template <class P, class Q>
+void dcp::start (PictureMXFWriter* writer, shared_ptr<P> state, Standard standard, Q* mxf, uint8_t* data, int size)
+{
+       mxf->set_file (writer->_file);
+       
+       if (ASDCP_FAILURE (state->j2k_parser.OpenReadFrame (data, size, state->frame_buffer))) {
+               boost::throw_exception (MiscError ("could not parse J2K frame"));
+       }
+
+       state->j2k_parser.FillPictureDescriptor (state->picture_descriptor);
+       state->picture_descriptor.EditRate = ASDCP::Rational (mxf->edit_rate().numerator, mxf->edit_rate().denominator);
+
+       mxf->set_size (Size (state->picture_descriptor.StoredWidth, state->picture_descriptor.StoredHeight));
+       mxf->set_screen_aspect_ratio (Fraction (state->picture_descriptor.AspectRatio.Numerator, state->picture_descriptor.AspectRatio.Denominator));
+       
+       mxf->fill_writer_info (&state->writer_info, standard);
+       
+       Kumu::Result_t r = state->mxf_writer.OpenWrite (
+               mxf->file().string().c_str(),
+               state->writer_info,
+               state->picture_descriptor,
+               16384,
+               writer->_overwrite
+               );
+
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for writing", mxf->file().string(), r));
+       }
+
+       writer->_started = true;
+}
index 507af15223c49e5887b10b2bfa6b7c0bdfd66c1f..68bbaf7a13d61a2f2a16e2ae3ab65df9148a1cd2 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <sstream>
 
-namespace libdcp {
+namespace dcp {
 
 /** A sort-of version of boost::lexical_cast that does uses the "C"
  *  locale (i.e. no thousands separators and a . for the decimal separator).
diff --git a/src/rec709_linearised_gamma_lut.cc b/src/rec709_linearised_gamma_lut.cc
deleted file mode 100644 (file)
index f14e447..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "rec709_linearised_gamma_lut.h"
-
-using namespace libdcp;
-
-LUTCache<Rec709LinearisedGammaLUT> Rec709LinearisedGammaLUT::cache;
-
-Rec709LinearisedGammaLUT::Rec709LinearisedGammaLUT (int bits, float gamma)
-       : LUT (bits, gamma)
-{
-       int const bit_length = pow (2, bits);
-       for (int i = 0; i < bit_length; ++i) {
-               float const p = static_cast<float> (i) / (bit_length - 1);
-               if (p > 0.08125) {
-                       _lut[i] = pow ((p + 0.099) / 1.099, gamma);
-               } else {
-                       _lut[i] = p / 4.5;
-               }
-       }
-}
diff --git a/src/rec709_linearised_gamma_lut.h b/src/rec709_linearised_gamma_lut.h
deleted file mode 100644 (file)
index af0158a..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "lut.h"
-#include "lut_cache.h"
-
-namespace libdcp {
-
-class Rec709LinearisedGammaLUT : public LUT
-{
-public:
-       Rec709LinearisedGammaLUT (int bit_length, float gamma);
-       static LUTCache<Rec709LinearisedGammaLUT> cache;
-};
-
-}
index ff51c025b78dd664ebd9a47e827e77b042bf42c0..34b520b49365b08db4047ff46dc686f167f5545f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <libxml++/nodes/element.h>
 #include "reel.h"
 #include "util.h"
-#include "picture_asset.h"
-#include "mono_picture_asset.h"
-#include "stereo_picture_asset.h"
-#include "sound_asset.h"
-#include "subtitle_asset.h"
-#include "kdm.h"
+#include "picture_mxf.h"
+#include "mono_picture_mxf.h"
+#include "stereo_picture_mxf.h"
+#include "sound_mxf.h"
+#include "subtitle_content.h"
+#include "reel_mono_picture_asset.h"
+#include "reel_stereo_picture_asset.h"
+#include "reel_sound_asset.h"
+#include "reel_subtitle_asset.h"
+#include "decrypted_kdm_key.h"
+#include "decrypted_kdm.h"
+#include <libxml++/nodes/element.h>
 
 using std::string;
 using std::list;
 using std::cout;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
-using namespace libdcp;
+using namespace dcp;
+
+Reel::Reel (boost::shared_ptr<const cxml::Node> node)
+       : Object (node->string_child ("Id"))
+{
+       shared_ptr<cxml::Node> asset_list = node->node_child ("AssetList");
+
+       shared_ptr<cxml::Node> main_picture = asset_list->optional_node_child ("MainPicture");
+       if (main_picture) {
+               _main_picture.reset (new ReelMonoPictureAsset (main_picture));
+       }
+       
+       shared_ptr<cxml::Node> main_stereoscopic_picture = asset_list->optional_node_child ("MainStereoscopicPicture");
+       if (main_stereoscopic_picture) {
+               _main_picture.reset (new ReelStereoPictureAsset (main_stereoscopic_picture));
+       }
+       
+       shared_ptr<cxml::Node> main_sound = asset_list->optional_node_child ("MainSound");
+       if (main_sound) {
+               _main_sound.reset (new ReelSoundAsset (main_sound));
+       }
+       
+       shared_ptr<cxml::Node> main_subtitle = asset_list->optional_node_child ("MainSubtitle");
+       if (main_subtitle) {
+               _main_subtitle.reset (new ReelSubtitleAsset (main_subtitle));
+       }
+
+       node->ignore_child ("AnnotationText");
+       node->done ();
+}
 
 void
-Reel::write_to_cpl (xmlpp::Element* node) const
+Reel::write_to_cpl (xmlpp::Element* node, Standard standard) const
 {
        xmlpp::Element* reel = node->add_child ("Reel");
        reel->add_child("Id")->add_child_text ("urn:uuid:" + make_uuid());
        xmlpp::Element* asset_list = reel->add_child ("AssetList");
        
-       if (_main_picture && dynamic_pointer_cast<MonoPictureAsset> (_main_picture)) {
+       if (_main_picture && dynamic_pointer_cast<ReelMonoPictureAsset> (_main_picture)) {
                /* Mono pictures come before other stuff... */
-               _main_picture->write_to_cpl (asset_list);
+               _main_picture->write_to_cpl (asset_list, standard);
        }
 
        if (_main_sound) {
-               _main_sound->write_to_cpl (asset_list);
+               _main_sound->write_to_cpl (asset_list, standard);
        }
 
        if (_main_subtitle) {
-               _main_subtitle->write_to_cpl (asset_list);
+               _main_subtitle->write_to_cpl (asset_list, standard);
        }
 
-       if (_main_picture && dynamic_pointer_cast<StereoPictureAsset> (_main_picture)) {
+       if (_main_picture && dynamic_pointer_cast<ReelStereoPictureAsset> (_main_picture)) {
                /* ... but stereo pictures must come after */
-               _main_picture->write_to_cpl (asset_list);
+               _main_picture->write_to_cpl (asset_list, standard);
        }
 }
        
@@ -100,16 +134,16 @@ Reel::encrypted () const
 }
 
 void
-Reel::add_kdm (KDM const & kdm)
+Reel::add (DecryptedKDM const & kdm)
 {
-       list<KDMKey> keys = kdm.keys ();
+       list<DecryptedKDMKey> keys = kdm.keys ();
        
-       for (list<KDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
-               if (i->key_id() == _main_picture->key_id()) {
-                       _main_picture->set_key (i->key ());
+       for (list<DecryptedKDMKey>::iterator i = keys.begin(); i != keys.end(); ++i) {
+               if (i->id() == _main_picture->key_id()) {
+                       _main_picture->mxf()->set_key (i->key ());
                }
-               if (i->key_id() == _main_sound->key_id()) {
-                       _main_sound->set_key (i->key ());
+               if (i->id() == _main_sound->key_id()) {
+                       _main_sound->mxf()->set_key (i->key ());
                }
        }
 }
@@ -117,10 +151,41 @@ Reel::add_kdm (KDM const & kdm)
 void
 Reel::set_mxf_keys (Key key)
 {
-       _main_picture->set_key (key);
+       _main_picture->mxf()->set_key (key);
        if (_main_sound) {
-               _main_sound->set_key (key);
+               _main_sound->mxf()->set_key (key);
        }
 
        /* XXX: subtitle asset? */
 }
+
+void
+Reel::add (shared_ptr<ReelAsset> asset)
+{
+       shared_ptr<ReelPictureAsset> p = dynamic_pointer_cast<ReelPictureAsset> (asset);
+       shared_ptr<ReelSoundAsset> so = dynamic_pointer_cast<ReelSoundAsset> (asset);
+       shared_ptr<ReelSubtitleAsset> su = dynamic_pointer_cast<ReelSubtitleAsset> (asset);
+       if (p) {
+               _main_picture = p;
+       } else if (so) {
+               _main_sound = so;
+       } else if (su) {
+               _main_subtitle = su;
+       }
+}
+
+void
+Reel::resolve_refs (list<shared_ptr<Object> > objects)
+{
+       if (_main_picture) {
+               _main_picture->content().resolve (objects);
+       }
+
+       if (_main_sound) {
+               _main_sound->content().resolve (objects);
+       }
+
+       if (_main_subtitle) {
+               _main_subtitle->content().resolve (objects);
+       }
+}
index 35302f17d96cdd0baf52a69314953b1adce07a73..5bb19eb01020f18ba158d78ca257804d89850a87 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #ifndef LIBDCP_REEL_H
 #define LIBDCP_REEL_H
 
-#include <list>
-#include <boost/shared_ptr.hpp>
-#include <boost/function.hpp>
-#include <libxml++/libxml++.h>
 #include "key.h"
 #include "types.h"
+#include "ref.h"
+#include <libxml++/libxml++.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <list>
 
-namespace xmlpp {
+namespace cxml {
        class Node;
 }
 
-namespace libdcp {
+namespace dcp {
 
-class PictureAsset;
-class SoundAsset;
-class SubtitleAsset;
-class KDM;     
+class DecryptedKDM;
+class ReelAsset;
+class ReelPictureAsset;
+class ReelSoundAsset;
+class ReelSubtitleAsset;
+class Content;
 
-/** @brief A reel within a DCP; the part which actually contains picture, sound and subtitle data */   
-class Reel
+/** @brief A reel within a DCP; the part which actually refers to picture, sound and subtitle data */  
+class Reel : public Object
 {
 public:
+       Reel () {}
+       
        Reel (
-               boost::shared_ptr<PictureAsset> picture,
-               boost::shared_ptr<SoundAsset> sound,
-               boost::shared_ptr<SubtitleAsset> subtitle
+               boost::shared_ptr<ReelPictureAsset> picture,
+               boost::shared_ptr<ReelSoundAsset> sound,
+               boost::shared_ptr<ReelSubtitleAsset> subtitle
                )
                : _main_picture (picture)
                , _main_sound (sound)
                , _main_subtitle (subtitle)
        {}
+
+       Reel (boost::shared_ptr<const cxml::Node>);
        
-       boost::shared_ptr<const PictureAsset> main_picture () const {
+       boost::shared_ptr<ReelPictureAsset> main_picture () const {
                return _main_picture;
        }
 
-       boost::shared_ptr<const SoundAsset> main_sound () const {
+       boost::shared_ptr<ReelSoundAsset> main_sound () const {
                return _main_sound;
        }
        
-       boost::shared_ptr<const SubtitleAsset> main_subtitle () const {
+       boost::shared_ptr<ReelSubtitleAsset> main_subtitle () const {
                return _main_subtitle;
        }
 
-       void write_to_cpl (xmlpp::Element *) const;
+       void add (boost::shared_ptr<ReelAsset> asset);
+
+       void write_to_cpl (xmlpp::Element* node, Standard standard) const;
 
        bool encrypted () const;
 
-       void set_mxf_keys (libdcp::Key);
+       void set_mxf_keys (dcp::Key);
        
        bool equals (boost::shared_ptr<const Reel> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> notes) const;
 
-       void add_kdm (KDM const &);
+       void add (DecryptedKDM const &);
+
+       void resolve_refs (std::list<boost::shared_ptr<Object> >);
 
 private:
-       boost::shared_ptr<PictureAsset> _main_picture;
-       boost::shared_ptr<SoundAsset> _main_sound;
-       boost::shared_ptr<SubtitleAsset> _main_subtitle;
+       boost::shared_ptr<ReelPictureAsset> _main_picture;
+       boost::shared_ptr<ReelSoundAsset> _main_sound;
+       boost::shared_ptr<ReelSubtitleAsset> _main_subtitle;
 };
 
 }
diff --git a/src/reel_asset.cc b/src/reel_asset.cc
new file mode 100644 (file)
index 0000000..7987a15
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "raw_convert.h"
+#include "reel_asset.h"
+#include "content.h"
+#include "compose.hpp"
+#include <libcxml/cxml.h>
+
+using std::pair;
+using std::string;
+using std::make_pair;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelAsset::ReelAsset ()
+       : Object (make_uuid ())
+       , _content (_id)
+       , _edit_rate (Fraction (24, 1))
+       , _intrinsic_duration (0)
+       , _entry_point (0)
+       , _duration (0)
+{
+
+}
+
+/** Construct a ReelAsset.
+ *  @param content Content that this asset refers to.
+ *  @param entry_point Entry point to use in that content.
+ */
+ReelAsset::ReelAsset (boost::shared_ptr<Content> content, int64_t entry_point)
+       : Object (content->id ())
+       , _content (content)
+       , _edit_rate (content->edit_rate ())
+       , _intrinsic_duration (content->intrinsic_duration ())
+       , _entry_point (entry_point)
+       , _duration (_intrinsic_duration - _entry_point)
+       , _hash (make_digest (content->file (), 0))
+{
+       /* default _annotation_text to the leaf name of our file */
+        _annotation_text = content->file().leaf().string ();
+}
+
+ReelAsset::ReelAsset (boost::shared_ptr<const cxml::Node> node)
+       : Object (node->string_child ("Id"))
+       , _content (_id)
+       , _annotation_text (node->optional_string_child ("AnnotationText").get_value_or (""))
+       , _edit_rate (Fraction (node->string_child ("EditRate")))
+       , _intrinsic_duration (node->number_child<int64_t> ("IntrinsicDuration"))
+       , _entry_point (node->number_child<int64_t> ("EntryPoint"))
+       , _duration (node->number_child<int64_t> ("Duration"))
+       , _hash (node->optional_string_child ("Hash").get_value_or (""))
+       , _key_id (node->optional_string_child ("KeyId").get_value_or (""))
+{
+       if (_id.length() > 9) {
+               _id = _id.substr (9);
+               _content.set_id (_id);
+       }
+       
+       if (_key_id.length() > 9) {
+               _key_id = _key_id.substr (9);
+       }
+}
+
+void
+ReelAsset::write_to_cpl (xmlpp::Node* node, Standard) const
+{
+        pair<string, string> const attr = cpl_node_attribute ();
+        xmlpp::Element* a = node->add_child (cpl_node_name ());
+        if (!attr.first.empty ()) {
+                a->set_attribute (attr.first, attr.second);
+        }
+        a->add_child("Id")->add_child_text ("urn:uuid:" + _id);
+        a->add_child("AnnotationText")->add_child_text (_annotation_text);
+        a->add_child("EditRate")->add_child_text (String::compose ("%1 %2", _edit_rate.numerator, _edit_rate.denominator));
+        a->add_child("IntrinsicDuration")->add_child_text (raw_convert<string> (_intrinsic_duration));
+        a->add_child("EntryPoint")->add_child_text (raw_convert<string> (_entry_point));
+        a->add_child("Duration")->add_child_text (raw_convert<string> (_duration));
+        if (!_key_id.empty ()) {
+                a->add_child("KeyId")->add_child_text ("urn:uuid:" + _key_id);
+        }
+}
+
+pair<string, string>
+ReelAsset::cpl_node_attribute () const
+{
+       return make_pair ("", "");
+}
diff --git a/src/reel_asset.h b/src/reel_asset.h
new file mode 100644 (file)
index 0000000..a71290a
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_REEL_ASSET_H
+#define LIBDCP_REEL_ASSET_H
+
+#include "object.h"
+#include "util.h"
+#include "ref.h"
+#include <boost/shared_ptr.hpp>
+
+namespace cxml {
+       class Node;
+}
+
+namespace xmlpp {
+       class Node;
+}
+
+namespace dcp {
+
+class Content;
+
+/** @class ReelAsset
+ *  @brief An entry in a &lt;Reel&gt; which refers to a use of a piece of content.
+ *
+ *  This class encapsulates the XML that exists in a &lt;Reel&gt; to say
+ *  that a piece of content is used in this reel.  It does not
+ *  describe the content itself (but links to a Content object which does).
+ */
+class ReelAsset : public Object
+{
+public:
+       ReelAsset ();
+       ReelAsset (boost::shared_ptr<Content> content, int64_t entry_point);
+       ReelAsset (boost::shared_ptr<const cxml::Node>);
+
+       virtual void write_to_cpl (xmlpp::Node* node, Standard standard) const;
+
+       virtual bool equals (
+               boost::shared_ptr<const ReelAsset>,
+               EqualityOptions,
+               boost::function<void (NoteType, std::string)>)
+               const {
+               
+               return false;
+       }
+
+       /** @return a Ref to our actual content */
+       Ref<Content>& content () {
+               return _content;
+       }
+
+       /** @return true if a KeyId is specified for this asset, implying
+        *  that its content is encrypted.
+        */
+       bool encrypted () const {
+               return !_key_id.empty ();
+       }
+
+       /** @return Key ID to describe the key that encrypts this asset's;
+        *  content.
+        */
+       std::string key_id () const {
+               return _key_id;
+       }
+
+protected:
+       /** @return the node name that this asset uses in the CPL's &lt;Reel&gt; node
+        *  e.g. MainPicture, MainSound etc.
+        */
+       virtual std::string cpl_node_name () const = 0;
+
+       /** @return Any attribute that should be used on the asset's node in the
+        *  CPL.
+        */
+       virtual std::pair<std::string, std::string> cpl_node_attribute () const;
+
+       /** Reference to the content (MXF or XML file) that this reel entry
+        *  applies to.
+        */
+       Ref<Content> _content;
+
+private:
+       
+       std::string _annotation_text; ///< The <AnnotationText> from the reel's entry for this asset
+       Fraction _edit_rate;          ///< The <EditRate> from the reel's entry for this asset
+       int64_t _intrinsic_duration;  ///< The <IntrinsicDuration> from the reel's entry for this asset
+       int64_t _entry_point;         ///< The <EntryPoint> from the reel's entry for this asset
+       int64_t _duration;            ///< The <Duration> from the reel's entry for this asset
+       std::string _hash;            ///< The <Hash> from the reel's entry for this asset
+       std::string _key_id;          ///< The <KeyId> from the reel's entry for this asset, or empty if there isn't one
+};
+
+}
+
+#endif
diff --git a/src/reel_mono_picture_asset.cc b/src/reel_mono_picture_asset.cc
new file mode 100644 (file)
index 0000000..b4ed630
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_mono_picture_asset.cc
+ *  @brief ReelMonoPictureAsset class.
+ */
+
+#include "reel_mono_picture_asset.h"
+#include "mono_picture_mxf.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelMonoPictureAsset::ReelMonoPictureAsset ()
+{
+
+}
+
+ReelMonoPictureAsset::ReelMonoPictureAsset (boost::shared_ptr<MonoPictureMXF> mxf, int64_t entry_point)
+       : ReelPictureAsset (mxf, entry_point)
+{
+
+}
+
+ReelMonoPictureAsset::ReelMonoPictureAsset (boost::shared_ptr<const cxml::Node> node)
+       : ReelPictureAsset (node)
+{
+       node->done ();
+}
+
+string
+ReelMonoPictureAsset::cpl_node_name () const
+{
+       return "MainPicture";
+}
+
diff --git a/src/reel_mono_picture_asset.h b/src/reel_mono_picture_asset.h
new file mode 100644 (file)
index 0000000..a25550a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_mono_picture_asset.h
+ *  @brief ReelMonoPictureAsset class.
+ */
+
+#ifndef LIBDCP_REEL_MONO_PICTURE_ASSET_H
+#define LIBDCP_REEL_MONO_PICTURE_ASSET_H
+
+#include "reel_picture_asset.h"
+
+namespace dcp {
+
+class MonoPictureMXF;  
+
+/** @class ReelMonoPictureAsset
+ *  @brief Part of a Reel's description which refers to a monoscopic picture MXF.
+ */
+class ReelMonoPictureAsset : public ReelPictureAsset
+{
+public:
+       ReelMonoPictureAsset ();
+       ReelMonoPictureAsset (boost::shared_ptr<MonoPictureMXF> content, int64_t entry_point);
+       ReelMonoPictureAsset (boost::shared_ptr<const cxml::Node>);
+
+private:
+       std::string cpl_node_name () const;
+};
+
+}
+
+#endif
diff --git a/src/reel_picture_asset.cc b/src/reel_picture_asset.cc
new file mode 100644 (file)
index 0000000..1a3d47b
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_picture_asset.h
+ *  @brief ReelPictureAsset class.
+ */
+
+#include "content.h"
+#include "reel_picture_asset.h"
+#include "picture_mxf.h"
+#include "compose.hpp"
+#include <libcxml/cxml.h>
+#include <iomanip>
+
+using std::bad_cast;
+using std::string;
+using std::stringstream;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelPictureAsset::ReelPictureAsset ()
+       : _frame_rate (Fraction (24, 1))
+       , _screen_aspect_ratio (Fraction (1998, 1080))
+{
+
+}
+
+ReelPictureAsset::ReelPictureAsset (boost::shared_ptr<PictureMXF> content, int64_t entry_point)
+       : ReelAsset (content, entry_point)
+       , _frame_rate (content->frame_rate ())
+       , _screen_aspect_ratio (content->screen_aspect_ratio ())
+{
+       
+}
+
+ReelPictureAsset::ReelPictureAsset (boost::shared_ptr<const cxml::Node> node)
+       : ReelAsset (node)
+{
+       _frame_rate = Fraction (node->string_child ("FrameRate"));
+       try {
+               _screen_aspect_ratio = Fraction (node->string_child ("ScreenAspectRatio"));
+       } catch (XMLError& e) {
+               /* Maybe it's not a fraction */
+       }
+       try {
+               float f = node->number_child<float> ("ScreenAspectRatio");
+               _screen_aspect_ratio = Fraction (f * 1000, 1000);
+       } catch (bad_cast& e) {
+
+       }
+}
+
+void
+ReelPictureAsset::write_to_cpl (xmlpp::Node* node, Standard standard) const
+{
+       ReelAsset::write_to_cpl (node, standard);
+
+       xmlpp::Node::NodeList c = node->get_children ();
+       xmlpp::Node::NodeList::iterator i = c.begin();
+       while (i != c.end() && (*i)->get_name() != cpl_node_name ()) {
+               ++i;
+       }
+
+       assert (i != c.end ());
+       
+       (*i)->add_child ("FrameRate")->add_child_text (String::compose ("%1 %2", _frame_rate.numerator, _frame_rate.denominator));
+       if (standard == INTEROP) {
+               stringstream s;
+               s << std::fixed << std::setprecision (2) << (float (_screen_aspect_ratio.numerator) / _screen_aspect_ratio.denominator);
+               (*i)->add_child ("ScreenAspectRatio")->add_child_text (s.str ());
+       } else {
+               (*i)->add_child ("ScreenAspectRatio")->add_child_text (
+                       String::compose ("%1 %2", _screen_aspect_ratio.numerator, _screen_aspect_ratio.denominator)
+                       );
+       }
+}
diff --git a/src/reel_picture_asset.h b/src/reel_picture_asset.h
new file mode 100644 (file)
index 0000000..547f3ad
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_picture_asset.h
+ *  @brief ReelPictureAsset class.
+ */
+
+#ifndef LIBDCP_REEL_PICTURE_ASSET_H
+#define LIBDCP_REEL_PICTURE_ASSET_H
+
+#include "reel_asset.h"
+#include "picture_mxf.h"
+
+namespace dcp {
+
+/** @class ReelPictureAsset
+ *  @brief Part of a Reel's description which refers to a picture MXF.
+ */
+class ReelPictureAsset : public ReelAsset
+{
+public:
+       ReelPictureAsset ();
+       ReelPictureAsset (boost::shared_ptr<PictureMXF> content, int64_t entry_point);
+       ReelPictureAsset (boost::shared_ptr<const cxml::Node>);
+
+       virtual void write_to_cpl (xmlpp::Node* node, Standard standard) const;
+
+       boost::shared_ptr<PictureMXF> mxf () {
+               return boost::dynamic_pointer_cast<PictureMXF> (_content.object ());
+       }
+
+       void set_screen_aspect_ratio (Fraction a) {
+               _screen_aspect_ratio = a;
+       }
+
+private:
+       Fraction _frame_rate;
+       Fraction _screen_aspect_ratio;
+};
+
+}
+
+#endif
diff --git a/src/reel_sound_asset.cc b/src/reel_sound_asset.cc
new file mode 100644 (file)
index 0000000..984434d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_sound_asset.cc
+ *  @brief ReelSoundAsset class.
+ */
+
+#include "reel_sound_asset.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelSoundAsset::ReelSoundAsset (boost::shared_ptr<Content> content, int64_t entry_point)
+       : ReelAsset (content, entry_point)
+{
+
+}
+
+ReelSoundAsset::ReelSoundAsset (boost::shared_ptr<const cxml::Node> node)
+       : ReelAsset (node)
+{
+       node->ignore_child ("Language");
+       node->done ();
+}
+
+string
+ReelSoundAsset::cpl_node_name () const
+{
+       return "MainSound";
+}
diff --git a/src/reel_sound_asset.h b/src/reel_sound_asset.h
new file mode 100644 (file)
index 0000000..42835c8
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_sound_asset.h
+ *  @brief ReelSoundAsset class.
+ */
+
+#include "reel_asset.h"
+#include "sound_mxf.h"
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace dcp {
+
+/** @class ReelSoundAsset
+ *  @brief Part of a Reel's description which refers to a sound MXF.
+ */
+class ReelSoundAsset : public ReelAsset
+{
+public:
+       ReelSoundAsset (boost::shared_ptr<Content> content, int64_t entry_point);
+       ReelSoundAsset (boost::shared_ptr<const cxml::Node>);
+
+       boost::shared_ptr<SoundMXF> mxf () {
+               return boost::dynamic_pointer_cast<SoundMXF> (_content.object ());
+       }
+
+       boost::shared_ptr<const SoundMXF> mxf () const {
+               return boost::dynamic_pointer_cast<const SoundMXF> (_content.object ());
+       }
+       
+private:
+       std::string cpl_node_name () const;
+};
+
+}
+
diff --git a/src/reel_stereo_picture_asset.cc b/src/reel_stereo_picture_asset.cc
new file mode 100644 (file)
index 0000000..09178dc
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_stereo_picture_asset.cc
+ *  @brief ReelStereoPictureAsset class.
+ */
+
+#include "reel_stereo_picture_asset.h"
+#include "stereo_picture_mxf.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using std::pair;
+using std::make_pair;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelStereoPictureAsset::ReelStereoPictureAsset ()
+{
+
+}
+
+ReelStereoPictureAsset::ReelStereoPictureAsset (boost::shared_ptr<StereoPictureMXF> mxf, int64_t entry_point)
+       : ReelPictureAsset (mxf, entry_point)
+{
+
+}
+
+ReelStereoPictureAsset::ReelStereoPictureAsset (boost::shared_ptr<const cxml::Node> node)
+       : ReelPictureAsset (node)
+{
+       node->done ();
+}
+
+string
+ReelStereoPictureAsset::cpl_node_name () const
+{
+       return "msp-cpl:MainStereoscopicPicture";
+}
+
+pair<string, string>
+ReelStereoPictureAsset::cpl_node_attribute (Standard standard) const
+{
+       if (standard == INTEROP) {
+               return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL");
+       } else {
+               return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL");
+       }
+
+       return make_pair ("", "");
+}
diff --git a/src/reel_stereo_picture_asset.h b/src/reel_stereo_picture_asset.h
new file mode 100644 (file)
index 0000000..57cc4da
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_stereo_picture_asset.h
+ *  @brief ReelStereoPictureAsset class.
+ */
+
+#ifndef LIBDCP_REEL_STEREO_PICTURE_ASSET_H
+#define LIBDCP_REEL_STEREO_PICTURE_ASSET_H
+
+#include "reel_picture_asset.h"
+
+namespace dcp {
+
+class StereoPictureMXF;        
+
+/** @class ReelStereoPictureAsset
+ *  @brief Part of a Reel's description which refers to a stereoscopic picture MXF.
+ */
+class ReelStereoPictureAsset : public ReelPictureAsset
+{
+public:
+       ReelStereoPictureAsset ();
+       ReelStereoPictureAsset (boost::shared_ptr<StereoPictureMXF> content, int64_t entry_point);
+       ReelStereoPictureAsset (boost::shared_ptr<const cxml::Node>);
+
+private:
+       std::string cpl_node_name () const;
+       std::pair<std::string, std::string> cpl_node_attribute (Standard standard) const;
+};
+
+}
+
+#endif
+
diff --git a/src/reel_subtitle_asset.cc b/src/reel_subtitle_asset.cc
new file mode 100644 (file)
index 0000000..1c94820
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_subtitle_asset.cc
+ *  @brief ReelSubtitleAsset class.
+ */
+
+#include "subtitle_content.h"
+#include "reel_subtitle_asset.h"
+
+using std::string;
+using boost::shared_ptr;
+using namespace dcp;
+
+ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<SubtitleContent> content, int64_t entry_point)
+       : ReelAsset (content, entry_point)
+{
+
+}
+
+ReelSubtitleAsset::ReelSubtitleAsset (boost::shared_ptr<const cxml::Node> node)
+       : ReelAsset (node)
+{
+       node->ignore_child ("Language");
+       node->done ();
+}
+
+string
+ReelSubtitleAsset::cpl_node_name () const
+{
+       return "MainSubtitle";
+}
diff --git a/src/reel_subtitle_asset.h b/src/reel_subtitle_asset.h
new file mode 100644 (file)
index 0000000..5cdfac1
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/reel_subtitle_asset.h
+ *  @brief ReelSubtitleAsset class.
+ */
+
+#ifndef LIBDCP_REEL_SUBTITLE_ASSET_H
+#define LIBDCP_REEL_SUBTITLE_ASSET_H
+
+#include "reel_asset.h"
+
+namespace dcp {
+
+class SubtitleContent;
+
+/** @class ReelSubtitleAsset
+ *  @brief Part of a Reel's description which refers to a subtitle XML file.
+ */
+class ReelSubtitleAsset : public ReelAsset
+{
+public:
+       ReelSubtitleAsset (boost::shared_ptr<SubtitleContent> content, int64_t entry_point);
+       ReelSubtitleAsset (boost::shared_ptr<const cxml::Node>);
+
+       boost::shared_ptr<SubtitleContent> subtitle_content () const {
+               return boost::dynamic_pointer_cast<SubtitleContent> (_content.object ());
+       }
+
+private:       
+       std::string cpl_node_name () const;
+};
+
+}
+
+#endif
diff --git a/src/ref.h b/src/ref.h
new file mode 100644 (file)
index 0000000..287d508
--- /dev/null
+++ b/src/ref.h
@@ -0,0 +1,121 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/ref.h
+ *  @brief Ref class.
+ */
+
+#ifndef LIBDCP_REF_H
+#define LIBDCP_REF_H
+
+#include "exceptions.h"
+#include "object.h"
+#include "util.h"
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace dcp {
+
+/** @class Ref
+ *  @brief A reference to an object which is identified by a universally-unique identifier (UUID).
+ *
+ *  This class is a `pointer' to a thing.  It will always know the
+ *  UUID of the thing, and it may have a shared_ptr to the C++ object
+ *  which represents the thing.
+ *
+ *  If the Ref does not have a shared_ptr it may be given one by
+ *  calling resolve() with a list of objects.  The shared_ptr will be
+ *  set up using any object on the list which has a matching ID.
+ */
+template<class T>
+class Ref
+{
+public:
+       /** Initialise a Ref with an ID but no shared_ptr */
+       Ref (std::string id)
+               : _id (id)
+       {}
+
+       /** Initialise a Ref with a shared_ptr to an object */
+       Ref (boost::shared_ptr<T> object)
+               : _id (object->id ())
+               , _object (object)
+       {}
+
+       /** Set the ID of this Ref */
+       void set_id (std::string id)
+       {
+               _id = id;
+       }
+
+       /** Look through a list of objects and copy a shared_ptr to any object
+        *  which matches the ID of this one.
+        */
+       void resolve (std::list<boost::shared_ptr<Object> > objects)
+       {
+               typename std::list<boost::shared_ptr<Object> >::iterator i = objects.begin();
+               while (i != objects.end() && !ids_equal ((*i)->id(), _id)) {
+                       ++i;
+               }
+
+               if (i != objects.end ()) {
+                       _object = boost::dynamic_pointer_cast<T> (*i);
+               }
+       }
+
+       /** @return the ID of the thing that we are pointing to */
+       std::string id () const {
+               return _id;
+       }
+
+       /** @return a shared_ptr to the thing; an UnresolvedRefError is thrown
+        *  if the shared_ptr is not known.
+        */
+       boost::shared_ptr<T> object () const {
+               if (!_object) {
+                       throw UnresolvedRefError (_id);
+               }
+               
+               return _object;
+       }
+
+       /** operator-> to access the shared_ptr; an UnresolvedRefError is thrown
+        *  if the shared_ptr is not known.
+        */
+       T * operator->() const {
+               if (!_object) {
+                       throw UnresolvedRefError (_id);
+               }
+               
+               return _object.get ();
+       }
+
+       /** @return true if a shared_ptr is known for this Ref */
+       bool resolved () const {
+               return _object;
+       }
+
+private:
+       std::string _id;              ///< ID; will always be known
+       boost::shared_ptr<T> _object; ///< shared_ptr to the thing, may be null.
+};
+
+}
+
+#endif
index dea2106c0665d02ddf0b1f09e48d7c14a21b02e2..72ee38c1c5802a91d0ea5a5d3aae392c78779d7b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "gamma_lut.h"
 #include "image.h"
 #include "colour_matrix.h"
+#include <cmath>
 
 using std::min;
 using std::max;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 #define DCI_COEFFICIENT (48.0 / 52.37)
 
 /** Convert an openjpeg XYZ image to RGB.
  *  @param xyz_frame Frame in XYZ.
+ *  @param lut_in Input Gamma LUT to use.
+ *  @param lut_out Output Gamma LUT to use.
  *  @return RGB image.
  */
 shared_ptr<ARGBFrame>
-libdcp::xyz_to_rgb (shared_ptr<const XYZFrame> xyz_frame, shared_ptr<const LUT> lut_in, shared_ptr<const LUT> lut_out)
+dcp::xyz_to_rgb (
+       boost::shared_ptr<const XYZFrame> xyz_frame,
+       boost::shared_ptr<const GammaLUT> lut_in,
+       boost::shared_ptr<const GammaLUT> lut_out
+       )
 {
        int const max_colour = pow (2, lut_out->bit_depth()) - 1;
 
@@ -99,8 +106,13 @@ libdcp::xyz_to_rgb (shared_ptr<const XYZFrame> xyz_frame, shared_ptr<const LUT>
        return argb_frame;
 }
 
-shared_ptr<libdcp::XYZFrame>
-libdcp::rgb_to_xyz (shared_ptr<const Image> rgb, shared_ptr<const LUT> lut_in, shared_ptr<const LUT> lut_out, double const colour_matrix[3][3])
+shared_ptr<dcp::XYZFrame>
+dcp::rgb_to_xyz (
+       boost::shared_ptr<const Image> rgb,
+       boost::shared_ptr<const GammaLUT> lut_in,
+       boost::shared_ptr<const GammaLUT> lut_out,
+       double const colour_matrix[3][3]
+       )
 {
        assert (lut_in->bit_depth() == 12);
        assert (lut_out->bit_depth() == 16);
index 87959cb38eff1705996c5909fc0f8619e4871320..28e09230abe1a912ad03e6f2d4927794a507ccc8 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <boost/shared_ptr.hpp>
 
-namespace libdcp {
+namespace dcp {
 
 class ARGBFrame;       
 class XYZFrame;
-class LUT;
+class GammaLUT;
 class Image;
        
 extern boost::shared_ptr<ARGBFrame> xyz_to_rgb (
-       boost::shared_ptr<const XYZFrame>, boost::shared_ptr<const LUT>, boost::shared_ptr<const LUT>
+       boost::shared_ptr<const XYZFrame>, boost::shared_ptr<const GammaLUT>, boost::shared_ptr<const GammaLUT>
        );
 
 extern boost::shared_ptr<XYZFrame> rgb_to_xyz (
-       boost::shared_ptr<const Image>, boost::shared_ptr<const LUT>, boost::shared_ptr<const LUT>, double const colour_matrix[3][3]
+       boost::shared_ptr<const Image>, boost::shared_ptr<const GammaLUT>, boost::shared_ptr<const GammaLUT>, double const colour_matrix[3][3]
        );
        
 }
index 11cf5eb874b73093c4c45f00aea2a7b8b9e7a846..8f0114a2b0e0a9c6c944345b60e707e002130890 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/signer.cc
+ *  @brief Signer class.
+ */
+
+#include "signer.h"
+#include "exceptions.h"
+#include <libcxml/cxml.h>
 #include <libxml++/libxml++.h>
 #include <xmlsec/xmldsig.h>
 #include <xmlsec/dl.h>
 #include <xmlsec/app.h>
 #include <xmlsec/crypto.h>
-#include <libcxml/cxml.h>
-#include "signer.h"
-#include "exceptions.h"
 #include "compose.hpp"
 
 using std::string;
 using std::list;
 using std::cout;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
-/** @param signer_key Filename of private key to sign with */
+/** Add a &lt;Signer&gt; and &lt;ds:Signature&gt; nodes to an XML node.
+ *  @param parent XML node to add to.
+ *  @param standard INTEROP or SMPTE.
+ */
 void
-Signer::sign (xmlpp::Element* parent, bool interop) const
+Signer::sign (xmlpp::Element* parent, Standard standard) const
 {
-       add_signer (parent, "dsig");
+       /* <Signer> */
+       
+       xmlpp::Element* signer = parent->add_child("Signer");
+       xmlpp::Element* data = signer->add_child("X509Data", "dsig");
+       xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", "dsig");
+       serial_element->add_child("X509IssuerName", "dsig")->add_child_text (_certificates.leaf()->issuer());
+       serial_element->add_child("X509SerialNumber", "dsig")->add_child_text (_certificates.leaf()->serial());
+       data->add_child("X509SubjectName", "dsig")->add_child_text (_certificates.leaf()->subject());
 
+       /* <Signature> */
+       
        xmlpp::Element* signature = parent->add_child("Signature", "dsig");
        
        xmlpp::Element* signed_info = signature->add_child ("SignedInfo", "dsig");
        signed_info->add_child("CanonicalizationMethod", "dsig")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
        
-       if (interop) {
+       if (standard == INTEROP) {
                signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2000/09/xmldsig#rsa-sha1");
        } else {
                signed_info->add_child("SignatureMethod", "dsig")->set_attribute("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
@@ -115,21 +131,3 @@ Signer::add_signature_value (xmlpp::Node* parent, string ns) const
 
        xmlSecDSigCtxDestroy (signature_context);
 }
-
-void
-Signer::add_signer (xmlpp::Element* parent, string ns) const
-{
-       xmlpp::Element* signer = parent->add_child("Signer");
-
-       {
-               xmlpp::Element* data = signer->add_child("X509Data", ns);
-               
-               {
-                       xmlpp::Element* serial_element = data->add_child("X509IssuerSerial", ns);
-                       serial_element->add_child("X509IssuerName", ns)->add_child_text (_certificates.leaf()->issuer());
-                       serial_element->add_child("X509SerialNumber", ns)->add_child_text (_certificates.leaf()->serial());
-               }
-               
-               data->add_child("X509SubjectName", ns)->add_child_text (_certificates.leaf()->subject());
-       }
-}
index efb8099bcbbd86b5f3402a4b41eedc1f5e44e905..6e258f8d5dba8bd87aef6f4e2742f22b3da10a0d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <boost/filesystem.hpp>
+#ifndef LIBDCP_SIGNER_H
+#define LIBDCP_SIGNER_H
+
+/** @file  src/signer.h
+ *  @brief Signer class.
+ */
+
 #include "certificates.h"
+#include "types.h"
+#include <boost/filesystem.hpp>
 
 namespace xmlpp {
        class Element;
        class Node;
 }
 
-namespace libdcp {
+namespace dcp {
 
-class Signer
+/** @class Signer
+ *  @brief A class which can sign XML files.
+ */
+class Signer : public boost::noncopyable
 {
 public:
+       /** @param c Certificate chain to sign with.
+        *  @param k Key to sign with.
+        */
        Signer (CertificateChain c, boost::filesystem::path k)
                : _certificates (c)
                , _key (k)
        {}
 
-       void sign (xmlpp::Element* parent, bool interop) const;
+       void sign (xmlpp::Element* parent, Standard standard) const;
        void add_signature_value (xmlpp::Node* parent, std::string ns) const;
 
        CertificateChain const & certificates () const {
@@ -44,11 +58,12 @@ public:
        
 private:       
 
-       void add_signer (xmlpp::Element* parent, std::string ns) const;
-       
+       /** Certificate chain to sign with */
        CertificateChain _certificates;
        /** Filename of signer key */
        boost::filesystem::path _key;
 };
 
 }
+
+#endif
index f35e4ff61732c8f8d957455144d26f1e4ab2ab9b..3b75b06ccae717939c46fea3bf52a6b2417682fa 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <fstream>
-#include <sstream>
-#include <boost/filesystem.hpp>
-#include <boost/algorithm/string.hpp>
-#include <openssl/sha.h>
-#include <openssl/bio.h>
-#include <openssl/evp.h>
-#include "KM_util.h"
+/** @file  src/signer_chain.cc
+ *  @brief Functions to make signer chains.
+ */
+
 #include "signer_chain.h"
 #include "exceptions.h"
 #include "util.h"
+#include "KM_util.h"
+#include <openssl/sha.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <fstream>
+#include <sstream>
 
 using std::string;
 using std::ofstream;
@@ -35,7 +39,11 @@ using std::ifstream;
 using std::stringstream;
 using std::cout;
 
-static void command (string cmd)
+/** Run a shell command.
+ *  @param cmd Command to run (UTF8-encoded).
+ */
+static void
+command (string cmd)
 {
 #ifdef LIBDCP_WINDOWS
        /* We need to use CreateProcessW on Windows so that the UTF-8/16 mess
@@ -77,16 +85,15 @@ static void command (string cmd)
        if (code) {
                stringstream s;
                s << "error " << code << " in " << cmd << " within " << boost::filesystem::current_path();
-               throw libdcp::MiscError (s.str());
+               throw dcp::MiscError (s.str());
        }
 }
 
 /** Extract a public key from a private key and create a SHA1 digest of it.
- *  @param key Private key
+ *  @param private_key Private key
  *  @param openssl openssl binary name (or full path if openssl is not on the system path).
  *  @return SHA1 digest of corresponding public key, with escaped / characters.
  */
-       
 static string
 public_key_digest (boost::filesystem::path private_key, boost::filesystem::path openssl)
 {
@@ -102,7 +109,7 @@ public_key_digest (boost::filesystem::path private_key, boost::filesystem::path
        string pub;
        ifstream f (public_name.string().c_str ());
        if (!f.good ()) {
-               throw libdcp::MiscError ("public key not found");
+               throw dcp::MiscError ("public key not found");
        }
 
        bool read = false;
@@ -121,22 +128,22 @@ public_key_digest (boost::filesystem::path private_key, boost::filesystem::path
        /* Decode the base64 of the public key */
                
        unsigned char buffer[512];
-       int const N = libdcp::base64_decode (pub, buffer, 1024);
+       int const N = dcp::base64_decode (pub, buffer, 1024);
 
        /* Hash it with SHA1 (without the first 24 bytes, for reasons that are not entirely clear) */
 
        SHA_CTX context;
        if (!SHA1_Init (&context)) {
-               throw libdcp::MiscError ("could not init SHA1 context");
+               throw dcp::MiscError ("could not init SHA1 context");
        }
 
        if (!SHA1_Update (&context, buffer + 24, N - 24)) {
-               throw libdcp::MiscError ("could not update SHA1 digest");
+               throw dcp::MiscError ("could not update SHA1 digest");
        }
 
        unsigned char digest[SHA_DIGEST_LENGTH];
        if (!SHA1_Final (digest, &context)) {
-               throw libdcp::MiscError ("could not finish SHA1 digest");
+               throw dcp::MiscError ("could not finish SHA1 digest");
        }
 
        char digest_base64[64];
@@ -149,8 +156,12 @@ public_key_digest (boost::filesystem::path private_key, boost::filesystem::path
        return dig;
 }
 
+/** Generate a chain of root, intermediate and leaf keys by running an OpenSSL binary.
+ *  @param directory Directory to write the files to.
+ *  @param openssl openssl binary path.
+ */
 void
-libdcp::make_signer_chain (boost::filesystem::path directory, boost::filesystem::path openssl)
+dcp::make_signer_chain (boost::filesystem::path directory, boost::filesystem::path openssl)
 {
        boost::filesystem::path const cwd = boost::filesystem::current_path ();
 
index 50f56ed9d93a2f88b2c98d298fa38681ce1fa27f..f039caf31a2ad6e9bcc2c2076e95d9596646fc62 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/signer_chain.h
+ *  @brief Functions to make signer chains.
+ */
+
 #include <boost/filesystem.hpp>
 
-namespace libdcp {
+namespace dcp {
 
 /** Create a chain of certificates for signing things.
  *  @param directory Directory to write files to.
diff --git a/src/sound_asset.cc b/src/sound_asset.cc
deleted file mode 100644 (file)
index 54fbdb8..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
-    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/sound_asset.cc
- *  @brief An asset made up of WAV files
- */
-
-#include <iostream>
-#include <stdexcept>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <libxml++/nodes/element.h>
-#include "KM_fileio.h"
-#include "AS_DCP.h"
-#include "sound_asset.h"
-#include "util.h"
-#include "exceptions.h"
-#include "sound_frame.h"
-
-using std::string;
-using std::stringstream;
-using std::ostream;
-using std::vector;
-using std::list;
-using boost::shared_ptr;
-using boost::lexical_cast;
-using namespace libdcp;
-
-SoundAsset::SoundAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name)
-       : MXFAsset (directory, mxf_name)
-       , _channels (0)
-       , _sampling_rate (0)
-{
-
-}
-
-void
-SoundAsset::create (vector<boost::filesystem::path> const & files)
-{
-       create (boost::bind (&SoundAsset::path_from_channel, this, _1, files));
-}
-
-void
-SoundAsset::read ()
-{
-       ASDCP::PCM::MXFReader reader;
-       Kumu::Result_t r = reader.OpenRead (path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-
-       ASDCP::PCM::AudioDescriptor desc;
-       if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) {
-               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
-       }
-
-       _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator;
-       _channels = desc.ChannelCount;
-       _edit_rate = desc.EditRate.Numerator;
-       assert (desc.EditRate.Denominator == 1);
-       _intrinsic_duration = desc.ContainerDuration;
-}
-
-boost::filesystem::path
-SoundAsset::path_from_channel (Channel channel, vector<boost::filesystem::path> const & files)
-{
-       unsigned int const c = int (channel);
-       assert (c < files.size ());
-       return files[c];
-}
-
-void
-SoundAsset::create (boost::function<boost::filesystem::path (Channel)> get_path)
-{
-       ASDCP::Rational asdcp_edit_rate (_edit_rate, 1);
-
-       assert (_channels > 0);
-       ASDCP::PCM::WAVParser* pcm_parser_channel[_channels];
-       for (int i = 0; i < _channels; ++i) {
-               pcm_parser_channel[i] = new ASDCP::PCM::WAVParser ();
-       }
-
-       Kumu::Result_t r = pcm_parser_channel[0]->OpenRead (get_path(LEFT).string().c_str(), asdcp_edit_rate);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (FileError ("could not open WAV file for reading", get_path(LEFT), r));
-       }
-       
-       ASDCP::PCM::AudioDescriptor audio_desc;
-       pcm_parser_channel[0]->FillAudioDescriptor (audio_desc);
-       audio_desc.ChannelCount = 0;
-       audio_desc.BlockAlign = 0;
-       audio_desc.EditRate = asdcp_edit_rate;
-       audio_desc.AvgBps = audio_desc.AvgBps * _channels;
-
-       Channel channels[] = {
-               LEFT,
-               RIGHT,
-               CENTRE,
-               LFE,
-               LS,
-               RS,
-               /* XXX: not quite sure what these should be yet */
-               CHANNEL_7,
-               CHANNEL_8
-       };
-
-       assert (int(_channels) <= int(sizeof(channels) / sizeof(Channel)));
-
-       ASDCP::PCM::FrameBuffer* frame_buffer_channel[_channels];
-       ASDCP::PCM::AudioDescriptor* audio_desc_channel[_channels];
-       for (int i = 0; i < _channels; ++i) {
-               frame_buffer_channel[i] = new ASDCP::PCM::FrameBuffer ();
-               audio_desc_channel[i] = new ASDCP::PCM::AudioDescriptor ();
-       }
-
-       for (int i = 0; i < _channels; ++i) {
-
-               boost::filesystem::path const path = get_path (channels[i]);
-               
-               Kumu::Result_t r = pcm_parser_channel[i]->OpenRead (path.string().c_str(), asdcp_edit_rate);
-               if (ASDCP_FAILURE (r)) {
-                       boost::throw_exception (FileError ("could not open WAV file for reading", path, r));
-               }
-
-               pcm_parser_channel[i]->FillAudioDescriptor (*audio_desc_channel[i]);
-               frame_buffer_channel[i]->Capacity (ASDCP::PCM::CalcFrameBufferSize (*audio_desc_channel[i]));
-
-               audio_desc.ChannelCount += audio_desc_channel[i]->ChannelCount;
-               audio_desc.BlockAlign += audio_desc_channel[i]->BlockAlign;
-       }
-
-       ASDCP::PCM::FrameBuffer frame_buffer;
-       frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
-       frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (audio_desc));
-
-       ASDCP::WriterInfo writer_info;
-       MXFAsset::fill_writer_info (&writer_info);
-
-       ASDCP::PCM::MXFWriter mxf_writer;
-       r = mxf_writer.OpenWrite (path().string().c_str(), writer_info, audio_desc);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (FileError ("could not open audio MXF for writing", path().string(), r));
-       }
-       
-       for (int i = 0; i < _intrinsic_duration; ++i) {
-
-               for (int j = 0; j < _channels; ++j) {
-                       memset (frame_buffer_channel[j]->Data(), 0, frame_buffer_channel[j]->Capacity());
-                       if (ASDCP_FAILURE (pcm_parser_channel[j]->ReadFrame (*frame_buffer_channel[j]))) {
-                               boost::throw_exception (MiscError ("could not read audio frame"));
-                       }
-               }
-
-               byte_t *data_s = frame_buffer.Data();
-               byte_t *data_e = data_s + frame_buffer.Capacity();
-               byte_t sample_size = ASDCP::PCM::CalcSampleSize (*audio_desc_channel[0]);
-               int offset = 0;
-
-               while (data_s < data_e) {
-                       for (int j = 0; j < _channels; ++j) {
-                               byte_t* frame = frame_buffer_channel[j]->Data() + offset;
-                               memcpy (data_s, frame, sample_size);
-                               data_s += sample_size;
-                       }
-                       offset += sample_size;
-               }
-
-               if (ASDCP_FAILURE (mxf_writer.WriteFrame (frame_buffer, _encryption_context, 0))) {
-                       boost::throw_exception (MiscError ("could not write audio MXF frame"));
-               }
-
-               if (_progress) {
-                       (*_progress) (0.5 * float (i) / _intrinsic_duration);
-               }
-       }
-
-       bool const failed = ASDCP_FAILURE (mxf_writer.Finalize());
-
-       for (int i = 0; i < _channels; ++i) {
-               delete pcm_parser_channel[i];
-               delete frame_buffer_channel[i];
-               delete audio_desc_channel[i];
-       }
-
-       if (failed) {
-               boost::throw_exception (MiscError ("could not finalise audio MXF"));
-       }
-}
-
-string
-SoundAsset::cpl_node_name () const
-{
-       return "MainSound";
-}
-
-bool
-SoundAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
-{
-       if (!MXFAsset::equals (other, opt, note)) {
-               return false;
-       }
-                    
-       ASDCP::PCM::MXFReader reader_A;
-       Kumu::Result_t r = reader_A.OpenRead (path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-
-       ASDCP::PCM::MXFReader reader_B;
-       r = reader_B.OpenRead (other->path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-
-       ASDCP::PCM::AudioDescriptor desc_A;
-       if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) {
-               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
-       }
-       ASDCP::PCM::AudioDescriptor desc_B;
-       if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) {
-               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
-       }
-       
-       if (
-               desc_A.EditRate != desc_B.EditRate ||
-               desc_A.AudioSamplingRate != desc_B.AudioSamplingRate ||
-               desc_A.Locked != desc_B.Locked ||
-               desc_A.ChannelCount != desc_B.ChannelCount ||
-               desc_A.QuantizationBits != desc_B.QuantizationBits ||
-               desc_A.BlockAlign != desc_B.BlockAlign ||
-               desc_A.AvgBps != desc_B.AvgBps ||
-               desc_A.LinkedTrackID != desc_B.LinkedTrackID ||
-               desc_A.ContainerDuration != desc_B.ContainerDuration
-//             desc_A.ChannelFormat != desc_B.ChannelFormat ||
-               ) {
-               
-               note (ERROR, "audio MXF picture descriptors differ");
-               return false;
-       }
-       
-       ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte);
-       ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte);
-       
-       for (int i = 0; i < _intrinsic_duration; ++i) {
-               if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) {
-                       boost::throw_exception (DCPReadError ("could not read audio frame"));
-               }
-               
-               if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) {
-                       boost::throw_exception (DCPReadError ("could not read audio frame"));
-               }
-               
-               if (buffer_A.Size() != buffer_B.Size()) {
-                       note (ERROR, "sizes of audio data for frame " + lexical_cast<string>(i) + " differ");
-                       return false;
-               }
-               
-               if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) {
-                       for (uint32_t i = 0; i < buffer_A.Size(); ++i) {
-                               int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]);
-                               if (d > opt.max_audio_sample_error) {
-                                       note (ERROR, "PCM data difference of " + lexical_cast<string> (d));
-                                       return false;
-                               }
-                       }
-               }
-       }
-
-       return true;
-}
-
-shared_ptr<const SoundFrame>
-SoundAsset::get_frame (int n) const
-{
-       /* XXX: should add on entry point here? */
-       return shared_ptr<const SoundFrame> (new SoundFrame (path().string(), n, _decryption_context));
-}
-
-shared_ptr<SoundAssetWriter>
-SoundAsset::start_write ()
-{
-       /* XXX: can't we use a shared_ptr here? */
-       return shared_ptr<SoundAssetWriter> (new SoundAssetWriter (this));
-}
-
-struct SoundAssetWriter::ASDCPState
-{
-       ASDCP::PCM::MXFWriter mxf_writer;
-       ASDCP::PCM::FrameBuffer frame_buffer;
-       ASDCP::WriterInfo writer_info;
-       ASDCP::PCM::AudioDescriptor audio_desc;
-       ASDCP::AESEncContext* encryption_context;
-};
-
-SoundAssetWriter::SoundAssetWriter (SoundAsset* a)
-       : _state (new SoundAssetWriter::ASDCPState)
-       , _asset (a)
-       , _finalized (false)
-       , _frames_written (0)
-       , _frame_buffer_offset (0)
-{
-       _state->encryption_context = a->encryption_context ();
-       
-       /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
-       _state->audio_desc.EditRate = ASDCP::Rational (_asset->edit_rate(), 1);
-       _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_asset->sampling_rate(), 1);
-       _state->audio_desc.Locked = 0;
-       _state->audio_desc.ChannelCount = _asset->channels ();
-       _state->audio_desc.QuantizationBits = 24;
-       _state->audio_desc.BlockAlign = 3 * _asset->channels();
-       _state->audio_desc.AvgBps = _asset->sampling_rate() * _state->audio_desc.BlockAlign;
-       _state->audio_desc.LinkedTrackID = 0;
-       _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE;
-       
-       _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc));
-       _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc));
-       memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
-       
-       _asset->fill_writer_info (&_state->writer_info);
-       
-       Kumu::Result_t r = _state->mxf_writer.OpenWrite (_asset->path().string().c_str(), _state->writer_info, _state->audio_desc);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (FileError ("could not open audio MXF for writing", _asset->path().string(), r));
-       }
-}
-
-void
-SoundAssetWriter::write (float const * const * data, int frames)
-{
-       for (int i = 0; i < frames; ++i) {
-
-               byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
-
-               /* Write one sample per channel */
-               for (int j = 0; j < _asset->channels(); ++j) {
-                       int32_t const s = data[j][i] * (1 << 23);
-                       *out++ = (s & 0xff);
-                       *out++ = (s & 0xff00) >> 8;
-                       *out++ = (s & 0xff0000) >> 16;
-               }
-               _frame_buffer_offset += 3 * _asset->channels();
-
-               assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity()));
-
-               /* Finish the MXF frame if required */
-               if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
-                       write_current_frame ();
-                       _frame_buffer_offset = 0;
-                       memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
-               }
-       }
-}
-
-void
-SoundAssetWriter::write_current_frame ()
-{
-       ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MiscError ("could not write audio MXF frame (" + lexical_cast<string> (int (r)) + ")"));
-       }
-
-       ++_frames_written;
-}
-
-void
-SoundAssetWriter::finalize ()
-{
-       if (_frame_buffer_offset > 0) {
-               write_current_frame ();
-       }
-       
-       if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
-               boost::throw_exception (MiscError ("could not finalise audio MXF"));
-       }
-
-       _finalized = true;
-       _asset->set_intrinsic_duration (_frames_written);
-       _asset->set_duration (_frames_written);
-}
-
-string
-SoundAsset::key_type () const
-{
-       return "MDAK";
-}
diff --git a/src/sound_asset.h b/src/sound_asset.h
deleted file mode 100644 (file)
index c52a543..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_SOUND_ASSET_H
-#define LIBDCP_SOUND_ASSET_H
-
-/** @file  src/sound_asset.h
- *  @brief An asset made up of PCM audio data files
- */
-
-#include "mxf_asset.h"
-#include "types.h"
-#include "metadata.h"
-
-namespace libdcp
-{
-
-class SoundFrame;
-class SoundAsset;
-
-class SoundAssetWriter
-{
-public:
-       void write (float const * const *, int);
-       void finalize ();
-
-private:
-       friend class SoundAsset;
-
-       SoundAssetWriter (SoundAsset *);
-
-       /* no copy construction */
-       SoundAssetWriter (SoundAssetWriter const &);
-       SoundAssetWriter& operator= (SoundAssetWriter const &);
-       
-       void write_current_frame ();
-
-       /* do this with an opaque pointer so we don't have to include
-          ASDCP headers
-       */
-          
-       struct ASDCPState;
-       boost::shared_ptr<ASDCPState> _state;
-
-       SoundAsset* _asset;
-       bool _finalized;
-       int _frames_written;
-       int _frame_buffer_offset;
-};
-
-/** @brief An asset made up of WAV files */
-class SoundAsset : public MXFAsset
-{
-public:
-       SoundAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name);
-
-       void read ();
-
-       /** The following parameters must be set up (if required) before calling this:
-        *      Interop mode (set_interop)
-        *      Edit rate    (set_edit_rate)
-        *      MXF Metadata (set_metadata)
-        *      Channels     (set_channels)
-        *      Intrinsic duration (set_intrinsic_duration)
-        */
-       void create (std::vector<boost::filesystem::path> const & files);
-
-       /** The following parameters must be set up (if required) before calling this:
-        *      Interop mode (set_interop)
-        *      Edit rate    (set_edit_rate)
-        *      MXF Metadata (set_metadata)
-        *      Channels     (set_channels)
-        *      Intrinsic duration (set_intrinsic_duration)
-        */
-       void create (boost::function<boost::filesystem::path (Channel)> get_path);
-
-       boost::shared_ptr<SoundAssetWriter> start_write ();
-       
-       bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
-
-       boost::shared_ptr<const SoundFrame> get_frame (int n) const;
-
-       void set_channels (int c) {
-               _channels = c;
-       }
-       
-       int channels () const {
-               return _channels;
-       }
-
-       void set_sampling_rate (int s) {
-               _sampling_rate = s;
-       }
-
-       int sampling_rate () const {
-               return _sampling_rate;
-       }
-
-protected:
-
-       std::string asdcp_kind () const {
-               return "Sound";
-       }
-       
-private:
-       std::string key_type () const;
-       void construct (boost::function<boost::filesystem::path (Channel)> get_path);
-       boost::filesystem::path path_from_channel (Channel channel, std::vector<boost::filesystem::path> const & files);
-       std::string cpl_node_name () const;
-
-       /** Number of channels in the asset */
-       int _channels;
-       int _sampling_rate;
-};
-
-}
-
-#endif
index 6bc52c1a54f73b84fafcd9b8f073bccbc00c5c16..c79b29b36a576117600348e97c7a70073e922315 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include "AS_DCP.h"
-#include "KM_fileio.h"
+/** @file  src/sound_frame.cc
+ *  @brief SoundFrame class.
+ */
+
 #include "sound_frame.h"
 #include "exceptions.h"
+#include "AS_DCP.h"
+#include "KM_fileio.h"
 
 using namespace std;
-using namespace libdcp;
+using namespace dcp;
 
-SoundFrame::SoundFrame (string mxf_path, int n, ASDCP::AESDecContext* c)
+SoundFrame::SoundFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext* c)
 {
        ASDCP::PCM::MXFReader reader;
-       Kumu::Result_t r = reader.OpenRead (mxf_path.c_str());
+       Kumu::Result_t r = reader.OpenRead (mxf_path.string().c_str());
        if (ASDCP_FAILURE (r)) {
                boost::throw_exception (FileError ("could not open MXF file for reading", mxf_path, r));
        }
index 154ff84b4b40d1c472464633cece2bbb2d353ebd..26f738d3524c72156a51221864b3d0d6bb20d44c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/sound_frame.h
+ *  @brief SoundFrame class.
+ */
+
 #ifndef LIBDCP_SOUND_FRAME_H
 #define LIBDCP_SOUND_FRAME_H
 
-#include <string>
+#include <boost/noncopyable.hpp>
+#include <boost/filesystem.hpp>
 #include <stdint.h>
+#include <string>
 
 namespace ASDCP {
        namespace PCM {
@@ -30,18 +36,22 @@ namespace ASDCP {
        class AESDecContext;
 }
 
-namespace libdcp {
+namespace dcp {
 
-class SoundFrame
+/** @class SoundFrame
+ *  @brief One &lsquo;frame&rsquo; of sound data from a MXF.
+ */
+class SoundFrame : public boost::noncopyable
 {
 public:
-       SoundFrame (std::string mxf_path, int n, ASDCP::AESDecContext *);
+       SoundFrame (boost::filesystem::path mxf_path, int n, ASDCP::AESDecContext *);
        ~SoundFrame ();
 
        uint8_t const * data () const;
        int size () const;
 
 private:
+       /** a buffer to hold the frame */
        ASDCP::PCM::FrameBuffer* _buffer;
 };
 
diff --git a/src/sound_mxf.cc b/src/sound_mxf.cc
new file mode 100644 (file)
index 0000000..fa48a6c
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/sound_mxf.cc
+ *  @brief SoundMXF class.
+ */
+
+#include "sound_mxf.h"
+#include "util.h"
+#include "exceptions.h"
+#include "sound_frame.h"
+#include "sound_mxf_writer.h"
+#include "compose.hpp"
+#include "KM_fileio.h"
+#include "AS_DCP.h"
+#include <libxml++/nodes/element.h>
+#include <boost/filesystem.hpp>
+#include <iostream>
+#include <stdexcept>
+
+using std::string;
+using std::stringstream;
+using std::ostream;
+using std::vector;
+using std::list;
+using boost::shared_ptr;
+using namespace dcp;
+
+SoundMXF::SoundMXF (boost::filesystem::path file)
+       : MXF (file)
+       , _channels (0)
+       , _sampling_rate (0)
+{
+       ASDCP::PCM::MXFReader reader;
+       Kumu::Result_t r = reader.OpenRead (file.string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r));
+       }
+
+       ASDCP::PCM::AudioDescriptor desc;
+       if (ASDCP_FAILURE (reader.FillAudioDescriptor (desc))) {
+               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
+       }
+
+       _sampling_rate = desc.AudioSamplingRate.Numerator / desc.AudioSamplingRate.Denominator;
+       _channels = desc.ChannelCount;
+       _edit_rate = Fraction (desc.EditRate.Numerator, desc.EditRate.Denominator);
+
+       _intrinsic_duration = desc.ContainerDuration;
+
+       ASDCP::WriterInfo info;
+       if (ASDCP_FAILURE (reader.FillWriterInfo (info))) {
+               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
+       }
+
+       read_writer_info (info);
+
+}
+
+SoundMXF::SoundMXF (Fraction edit_rate, int sampling_rate, int channels)
+       : MXF (edit_rate)
+       , _channels (channels)
+       , _sampling_rate (sampling_rate)
+{
+
+}
+
+bool
+SoundMXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+       if (!MXF::equals (other, opt, note)) {
+               return false;
+       }
+                    
+       ASDCP::PCM::MXFReader reader_A;
+       Kumu::Result_t r = reader_A.OpenRead (file().string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r));
+       }
+
+       ASDCP::PCM::MXFReader reader_B;
+       r = reader_B.OpenRead (other->file().string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r));
+       }
+
+       ASDCP::PCM::AudioDescriptor desc_A;
+       if (ASDCP_FAILURE (reader_A.FillAudioDescriptor (desc_A))) {
+               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
+       }
+       ASDCP::PCM::AudioDescriptor desc_B;
+       if (ASDCP_FAILURE (reader_B.FillAudioDescriptor (desc_B))) {
+               boost::throw_exception (DCPReadError ("could not read audio MXF information"));
+       }
+       
+       if (
+               desc_A.EditRate != desc_B.EditRate ||
+               desc_A.AudioSamplingRate != desc_B.AudioSamplingRate ||
+               desc_A.Locked != desc_B.Locked ||
+               desc_A.ChannelCount != desc_B.ChannelCount ||
+               desc_A.QuantizationBits != desc_B.QuantizationBits ||
+               desc_A.BlockAlign != desc_B.BlockAlign ||
+               desc_A.AvgBps != desc_B.AvgBps ||
+               desc_A.LinkedTrackID != desc_B.LinkedTrackID ||
+               desc_A.ContainerDuration != desc_B.ContainerDuration
+//             desc_A.ChannelFormat != desc_B.ChannelFormat ||
+               ) {
+               
+               note (ERROR, "audio MXF picture descriptors differ");
+               return false;
+       }
+       
+       ASDCP::PCM::FrameBuffer buffer_A (1 * Kumu::Megabyte);
+       ASDCP::PCM::FrameBuffer buffer_B (1 * Kumu::Megabyte);
+       
+       for (int i = 0; i < _intrinsic_duration; ++i) {
+               if (ASDCP_FAILURE (reader_A.ReadFrame (i, buffer_A))) {
+                       boost::throw_exception (DCPReadError ("could not read audio frame"));
+               }
+               
+               if (ASDCP_FAILURE (reader_B.ReadFrame (i, buffer_B))) {
+                       boost::throw_exception (DCPReadError ("could not read audio frame"));
+               }
+               
+               if (buffer_A.Size() != buffer_B.Size()) {
+                       note (ERROR, String::compose ("sizes of audio data for frame %1 differ", i));
+                       return false;
+               }
+               
+               if (memcmp (buffer_A.RoData(), buffer_B.RoData(), buffer_A.Size()) != 0) {
+                       for (uint32_t i = 0; i < buffer_A.Size(); ++i) {
+                               int const d = abs (buffer_A.RoData()[i] - buffer_B.RoData()[i]);
+                               if (d > opt.max_audio_sample_error) {
+                                       note (ERROR, String::compose ("PCM data difference of %1", d));
+                                       return false;
+                               }
+                       }
+               }
+       }
+
+       return true;
+}
+
+shared_ptr<const SoundFrame>
+SoundMXF::get_frame (int n) const
+{
+       /* XXX: should add on entry point here? */
+       return shared_ptr<const SoundFrame> (new SoundFrame (file(), n, _decryption_context));
+}
+
+shared_ptr<SoundMXFWriter>
+SoundMXF::start_write (boost::filesystem::path file, Standard standard)
+{
+       /* XXX: can't we use a shared_ptr here? */
+       return shared_ptr<SoundMXFWriter> (new SoundMXFWriter (this, file, standard));
+}
+
+string
+SoundMXF::key_type () const
+{
+       return "MDAK";
+}
diff --git a/src/sound_mxf.h b/src/sound_mxf.h
new file mode 100644 (file)
index 0000000..50d1095
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/sound_mxf.h
+ *  @brief SoundMXF class
+ */
+
+#ifndef LIBDCP_SOUND_MXF_H
+#define LIBDCP_SOUND_MXF_H
+
+#include "mxf.h"
+#include "types.h"
+#include "metadata.h"
+
+namespace dcp
+{
+
+class SoundFrame;
+class SoundMXFWriter;
+
+/** @class SoundMXF
+ *  @brief Representation of a MXF file containing sound
+ */
+class SoundMXF : public MXF
+{
+public:
+       SoundMXF (boost::filesystem::path file);
+       SoundMXF (Fraction edit_rate, int sampling_rate, int channels);
+
+       boost::shared_ptr<SoundMXFWriter> start_write (boost::filesystem::path file, Standard standard);
+       
+       bool equals (
+               boost::shared_ptr<const Content> other,
+               EqualityOptions opt,
+               boost::function<void (NoteType, std::string)> note
+               ) const;
+
+       boost::shared_ptr<const SoundFrame> get_frame (int n) const;
+
+       /** @return number of channels */
+       int channels () const {
+               return _channels;
+       }
+
+       /** @return sampling rate in Hz */
+       int sampling_rate () const {
+               return _sampling_rate;
+       }
+
+private:
+       std::string key_type () const;
+       std::string asdcp_kind () const {
+               return "Sound";
+       }
+
+       int _channels;      ///< number of channels
+       int _sampling_rate; ///< sampling rate in Hz
+};
+
+}
+
+#endif
diff --git a/src/sound_mxf_writer.cc b/src/sound_mxf_writer.cc
new file mode 100644 (file)
index 0000000..51ba7e4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "sound_mxf_writer.h"
+#include "sound_mxf.h"
+#include "exceptions.h"
+#include "compose.hpp"
+#include "AS_DCP.h"
+
+using namespace dcp;
+
+struct SoundMXFWriter::ASDCPState
+{
+       ASDCP::PCM::MXFWriter mxf_writer;
+       ASDCP::PCM::FrameBuffer frame_buffer;
+       ASDCP::WriterInfo writer_info;
+       ASDCP::PCM::AudioDescriptor audio_desc;
+       ASDCP::AESEncContext* encryption_context;
+};
+
+SoundMXFWriter::SoundMXFWriter (SoundMXF* m, boost::filesystem::path file, Standard standard)
+       : MXFWriter (m, file)
+       , _state (new SoundMXFWriter::ASDCPState)
+       , _sound_mxf (m)
+       , _frame_buffer_offset (0)
+{
+       _state->encryption_context = m->encryption_context ();
+       
+       /* Derived from ASDCP::Wav::SimpleWaveHeader::FillADesc */
+       _state->audio_desc.EditRate = ASDCP::Rational (_sound_mxf->edit_rate().numerator, _sound_mxf->edit_rate().denominator);
+       _state->audio_desc.AudioSamplingRate = ASDCP::Rational (_sound_mxf->sampling_rate(), 1);
+       _state->audio_desc.Locked = 0;
+       _state->audio_desc.ChannelCount = _sound_mxf->channels ();
+       _state->audio_desc.QuantizationBits = 24;
+       _state->audio_desc.BlockAlign = 3 * _sound_mxf->channels();
+       _state->audio_desc.AvgBps = _sound_mxf->sampling_rate() * _state->audio_desc.BlockAlign;
+       _state->audio_desc.LinkedTrackID = 0;
+       _state->audio_desc.ChannelFormat = ASDCP::PCM::CF_NONE;
+       
+       _state->frame_buffer.Capacity (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc));
+       _state->frame_buffer.Size (ASDCP::PCM::CalcFrameBufferSize (_state->audio_desc));
+       memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
+       
+       _sound_mxf->fill_writer_info (&_state->writer_info, standard);
+       
+       Kumu::Result_t r = _state->mxf_writer.OpenWrite (file.string().c_str(), _state->writer_info, _state->audio_desc);
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (FileError ("could not open audio MXF for writing", file.string(), r));
+       }
+}
+
+void
+SoundMXFWriter::write (float const * const * data, int frames)
+{
+       assert (!_finalized);
+       
+       for (int i = 0; i < frames; ++i) {
+
+               byte_t* out = _state->frame_buffer.Data() + _frame_buffer_offset;
+
+               /* Write one sample per channel */
+               for (int j = 0; j < _sound_mxf->channels(); ++j) {
+                       int32_t const s = data[j][i] * (1 << 23);
+                       *out++ = (s & 0xff);
+                       *out++ = (s & 0xff00) >> 8;
+                       *out++ = (s & 0xff0000) >> 16;
+               }
+               _frame_buffer_offset += 3 * _sound_mxf->channels();
+
+               assert (_frame_buffer_offset <= int (_state->frame_buffer.Capacity()));
+
+               /* Finish the MXF frame if required */
+               if (_frame_buffer_offset == int (_state->frame_buffer.Capacity())) {
+                       write_current_frame ();
+                       _frame_buffer_offset = 0;
+                       memset (_state->frame_buffer.Data(), 0, _state->frame_buffer.Capacity());
+               }
+       }
+}
+
+void
+SoundMXFWriter::write_current_frame ()
+{
+       ASDCP::Result_t const r = _state->mxf_writer.WriteFrame (_state->frame_buffer, _state->encryption_context, 0);
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MiscError (String::compose ("could not write audio MXF frame (%1)", int (r))));
+       }
+
+       ++_frames_written;
+}
+
+void
+SoundMXFWriter::finalize ()
+{
+       if (_frame_buffer_offset > 0) {
+               write_current_frame ();
+       }
+       
+       if (ASDCP_FAILURE (_state->mxf_writer.Finalize())) {
+               boost::throw_exception (MiscError ("could not finalise audio MXF"));
+       }
+
+       MXFWriter::finalize ();
+}
diff --git a/src/sound_mxf_writer.h b/src/sound_mxf_writer.h
new file mode 100644 (file)
index 0000000..4f60074
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "mxf_writer.h"
+#include "types.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/filesystem.hpp>
+
+namespace dcp {
+
+class SoundFrame;
+class SoundMXF;
+
+class SoundMXFWriter : public MXFWriter
+{
+public:
+       void write (float const * const *, int);
+       void finalize ();
+
+private:
+       friend class SoundMXF;
+
+       SoundMXFWriter (SoundMXF *, boost::filesystem::path, Standard standard);
+
+       void write_current_frame ();
+
+       /* do this with an opaque pointer so we don't have to include
+          ASDCP headers
+       */
+          
+       struct ASDCPState;
+       boost::shared_ptr<ASDCPState> _state;
+
+       SoundMXF* _sound_mxf;
+       int _frame_buffer_offset;
+};
+
+}
+
diff --git a/src/srgb_linearised_gamma_lut.cc b/src/srgb_linearised_gamma_lut.cc
deleted file mode 100644 (file)
index 0d3650f..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "srgb_linearised_gamma_lut.h"
-
-using namespace libdcp;
-
-LUTCache<SRGBLinearisedGammaLUT> SRGBLinearisedGammaLUT::cache;
-
-SRGBLinearisedGammaLUT::SRGBLinearisedGammaLUT (int bits, float gamma)
-       : LUT (bits, gamma)
-{
-       int const bit_length = pow (2, bits);
-       for (int i = 0; i < bit_length; ++i) {
-               float const p = static_cast<float> (i) / (bit_length - 1);
-               if (p > 0.04045) {
-                       _lut[i] = pow ((p + 0.055) / 1.055, gamma);
-               } else {
-                       _lut[i] = p / 12.92;
-               }
-       }
-}
diff --git a/src/srgb_linearised_gamma_lut.h b/src/srgb_linearised_gamma_lut.h
deleted file mode 100644 (file)
index 34800c6..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "lut.h"
-#include "lut_cache.h"
-
-namespace libdcp {
-
-class SRGBLinearisedGammaLUT : public LUT
-{
-public:
-       SRGBLinearisedGammaLUT (int bit_length, float gamma);
-       static LUTCache<SRGBLinearisedGammaLUT> cache;
-};
-
-}
diff --git a/src/stereo_picture_asset.cc b/src/stereo_picture_asset.cc
deleted file mode 100644 (file)
index 47a41cc..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "AS_DCP.h"
-#include "stereo_picture_asset.h"
-#include "stereo_picture_frame.h"
-#include "exceptions.h"
-#include "stereo_picture_asset_writer.h"
-
-using std::string;
-using std::pair;
-using std::make_pair;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-using namespace libdcp;
-
-bool
-StereoPictureAsset::equals (shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
-{
-       if (!MXFAsset::equals (other, opt, note)) {
-               return false;
-       }
-
-       ASDCP::JP2K::MXFSReader reader_A;
-       Kumu::Result_t r = reader_A.OpenRead (path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-       
-       ASDCP::JP2K::MXFSReader reader_B;
-       r = reader_B.OpenRead (other->path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-       
-       ASDCP::JP2K::PictureDescriptor desc_A;
-       if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) {
-               boost::throw_exception (DCPReadError ("could not read video MXF information"));
-       }
-       ASDCP::JP2K::PictureDescriptor desc_B;
-       if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) {
-               boost::throw_exception (DCPReadError ("could not read video MXF information"));
-       }
-       
-       if (!descriptor_equals (desc_A, desc_B, note)) {
-               return false;
-       }
-       
-       shared_ptr<const StereoPictureAsset> other_picture = dynamic_pointer_cast<const StereoPictureAsset> (other);
-       assert (other_picture);
-
-       for (int i = 0; i < _intrinsic_duration; ++i) {
-               shared_ptr<const StereoPictureFrame> frame_A = get_frame (i);
-               shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i);
-               
-               if (!frame_buffer_equals (
-                           i, opt, note,
-                           frame_A->left_j2k_data(), frame_A->left_j2k_size(),
-                           frame_B->left_j2k_data(), frame_B->left_j2k_size()
-                           )) {
-                       return false;
-               }
-               
-               if (!frame_buffer_equals (
-                           i, opt, note,
-                           frame_A->right_j2k_data(), frame_A->right_j2k_size(),
-                           frame_B->right_j2k_data(), frame_B->right_j2k_size()
-                           )) {
-                       return false;
-               }
-       }
-
-       return true;
-}
-
-StereoPictureAsset::StereoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name)
-       : PictureAsset (directory, mxf_name)
-{
-       
-}
-
-void
-StereoPictureAsset::read ()
-{
-       ASDCP::JP2K::MXFSReader reader;
-       Kumu::Result_t r = reader.OpenRead (path().string().c_str());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", path().string(), r));
-       }
-       
-       ASDCP::JP2K::PictureDescriptor desc;
-       if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) {
-               boost::throw_exception (DCPReadError ("could not read video MXF information"));
-       }
-
-       _size.width = desc.StoredWidth;
-       _size.height = desc.StoredHeight;
-}
-
-shared_ptr<const StereoPictureFrame>
-StereoPictureAsset::get_frame (int n) const
-{
-       return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (path().string(), n));
-}
-
-shared_ptr<PictureAssetWriter>
-StereoPictureAsset::start_write (bool overwrite)
-{
-       return shared_ptr<StereoPictureAssetWriter> (new StereoPictureAssetWriter (this, overwrite));
-}
-
-string
-StereoPictureAsset::cpl_node_name () const
-{
-       return "msp-cpl:MainStereoscopicPicture";
-}
-
-pair<string, string>
-StereoPictureAsset::cpl_node_attribute () const
-{
-       if (_interop) {
-               return make_pair ("xmlns:msp-cpl", "http://www.digicine.com/schemas/437-Y/2007/Main-Stereo-Picture-CPL");
-       } else {
-               return make_pair ("xmlns:msp-cpl", "http://www.smpte-ra.org/schemas/429-10/2008/Main-Stereo-Picture-CPL");
-       }
-
-       return make_pair ("", "");
-}
-
-int
-StereoPictureAsset::edit_rate_factor () const
-{
-       return 2;
-}
diff --git a/src/stereo_picture_asset.h b/src/stereo_picture_asset.h
deleted file mode 100644 (file)
index 64cdb6b..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_STEREO_PICTURE_ASSET_H
-#define LIBDCP_STEREO_PICTURE_ASSET_H
-
-#include "picture_asset.h"
-
-namespace libdcp {
-       
-/** A 3D (stereoscopic) picture asset */       
-class StereoPictureAsset : public PictureAsset
-{
-public:
-       StereoPictureAsset (boost::filesystem::path directory, boost::filesystem::path mxf_name);
-
-       void read ();
-       
-       /** Start a progressive write to a StereoPictureAsset */
-       boost::shared_ptr<PictureAssetWriter> start_write (bool);
-
-       boost::shared_ptr<const StereoPictureFrame> get_frame (int n) const;
-       bool equals (boost::shared_ptr<const Asset> other, EqualityOptions opt, boost::function<void (NoteType, std::string)> note) const;
-
-private:
-       std::string cpl_node_name () const;
-       std::pair<std::string, std::string> cpl_node_attribute () const;
-       int edit_rate_factor () const;
-};
-
-}
-
-#endif
diff --git a/src/stereo_picture_asset_writer.cc b/src/stereo_picture_asset_writer.cc
deleted file mode 100644 (file)
index b4b0ad5..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "AS_DCP.h"
-#include "KM_fileio.h"
-#include "stereo_picture_asset_writer.h"
-#include "exceptions.h"
-#include "picture_asset.h"
-
-#include "picture_asset_writer_common.cc"
-
-using std::istream;
-using std::ostream;
-using std::string;
-using boost::shared_ptr;
-using namespace libdcp;
-
-struct StereoPictureAssetWriter::ASDCPState : public ASDCPStateBase
-{
-       ASDCP::JP2K::MXFSWriter mxf_writer;
-};
-
-StereoPictureAssetWriter::StereoPictureAssetWriter (PictureAsset* asset, bool overwrite)
-       : PictureAssetWriter (asset, overwrite)
-       , _state (new StereoPictureAssetWriter::ASDCPState)
-       , _next_eye (EYE_LEFT)
-{
-       _state->encryption_context = asset->encryption_context ();
-}
-
-void
-StereoPictureAssetWriter::start (uint8_t* data, int size)
-{
-       libdcp::start (this, _state, _asset, data, size);
-}
-
-/** Write a frame for one eye.  Frames must be written left, then right, then left etc.
- *  @param data JPEG2000 data.
- *  @param size Size of data.
- */
-FrameInfo
-StereoPictureAssetWriter::write (uint8_t* data, int size)
-{
-       assert (!_finalized);
-
-       if (!_started) {
-               start (data, size);
-       }
-
-       if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
-               boost::throw_exception (MiscError ("could not parse J2K frame"));
-       }
-
-       uint64_t const before_offset = _state->mxf_writer.Tell ();
-
-       string hash;
-       Kumu::Result_t r = _state->mxf_writer.WriteFrame (
-               _state->frame_buffer,
-               _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT,
-               _state->encryption_context,
-               0,
-               &hash
-               );
-
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r));
-       }
-
-       _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
-
-       ++_frames_written;
-       return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
-}
-
-void
-StereoPictureAssetWriter::fake_write (int size)
-{
-       assert (_started);
-       assert (!_finalized);
-
-       Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size, _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT);
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in writing video MXF", _asset->path().string(), r));
-       }
-
-       _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
-       ++_frames_written;
-}
-
-void
-StereoPictureAssetWriter::finalize ()
-{
-       assert (!_finalized);
-       
-       Kumu::Result_t r = _state->mxf_writer.Finalize();
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("error in finalizing video MXF", _asset->path().string(), r));
-       }
-
-       _finalized = true;
-       _asset->set_intrinsic_duration (_frames_written / 2);
-       _asset->set_duration (_frames_written / 2);
-}
diff --git a/src/stereo_picture_asset_writer.h b/src/stereo_picture_asset_writer.h
deleted file mode 100644 (file)
index 771524c..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <stdint.h>
-#include <string>
-#include <fstream>
-#include <boost/shared_ptr.hpp>
-#include <boost/utility.hpp>
-#include "picture_asset_writer.h"
-
-namespace libdcp {
-
-/** A helper class for writing to StereoPictureAssets progressively (i.e. writing frame-by-frame,
- *  rather than giving libdcp all the frames in one go).
- *
- *  Objects of this class can only be created with StereoPictureAsset::start_write().
- *
- *  Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image
- *  (a verbatim .j2c file).  finalize() must be called after the last frame has been written.
- *  The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may
- *  throw an exception.
- */
-class StereoPictureAssetWriter : public PictureAssetWriter
-{
-public:
-       FrameInfo write (uint8_t *, int);
-       void fake_write (int size);
-       void finalize ();
-
-private:
-       friend class StereoPictureAsset;
-
-       StereoPictureAssetWriter (PictureAsset *, bool);
-       void start (uint8_t *, int);
-
-       /* do this with an opaque pointer so we don't have to include
-          ASDCP headers
-       */
-          
-       struct ASDCPState;
-       boost::shared_ptr<ASDCPState> _state;
-
-       libdcp::Eye _next_eye;
-};
-
-}
index dce1f106dc57e8cce6584cc94cac8bd566bd62c1..0b56b21632c5b444131042aa80d26f7ef68bac98 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <openjpeg.h>
-#include "AS_DCP.h"
-#include "KM_fileio.h"
 #include "stereo_picture_frame.h"
 #include "exceptions.h"
 #include "argb_frame.h"
-#include "lut.h"
 #include "util.h"
 #include "gamma_lut.h"
 #include "rgb_xyz.h"
+#include "AS_DCP.h"
+#include "KM_fileio.h"
+#include <openjpeg.h>
 
 #define DCI_GAMMA 2.6
 
 using std::string;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 /** Make a picture frame from a 3D (stereoscopic) asset.
  *  @param mxf_path Path to the asset's MXF file.
@@ -59,11 +58,12 @@ StereoPictureFrame::~StereoPictureFrame ()
        delete _buffer;
 }
 
-/** @param reduce a factor by which to reduce the resolution
+/** @param eye Eye to return (EYE_LEFT or EYE_RIGHT).
+ *  @param reduce a factor by which to reduce the resolution
  *  of the image, expressed as a power of two (pass 0 for no
  *  reduction).
- *
- *  @param eye Eye to return (EYE_LEFT or EYE_RIGHT).
+ *  @param srgb_gamma Reciprocal of gamma to use when doing the
+ *  output gamma correction (after conversion from XYZ to RGB).
  *
  *  @return An ARGB representation of one of the eyes (left or right)
  *  of this frame.  This is ARGB in the Cairo sense, so that each
@@ -84,7 +84,7 @@ StereoPictureFrame::argb_frame (Eye eye, int reduce, float srgb_gamma) const
                break;
        }
        
-       return xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA), GammaLUT::cache.get (12, 1 / srgb_gamma));
+       return xyz_to_rgb (xyz_frame, GammaLUT::cache.get (12, DCI_GAMMA, false), GammaLUT::cache.get (12, 1 / srgb_gamma, false));
 }
 
 uint8_t const *
index 878f6ab4c40e21858357cadf1a13f10e3ff06502..fe363da5b64131940fd50618d3d9554375d6d142 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <string>
-#include <stdint.h>
-#include <boost/shared_ptr.hpp>
 #include "types.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/filesystem.hpp>
+#include <stdint.h>
+#include <string>
 
 namespace ASDCP {
        namespace JP2K {
@@ -29,12 +31,12 @@ namespace ASDCP {
        class AESDecContext;
 }
 
-namespace libdcp {
+namespace dcp {
 
 class ARGBFrame;
 
 /** A single frame of a 3D (stereoscopic) picture asset */     
-class StereoPictureFrame
+class StereoPictureFrame : public boost::noncopyable
 {
 public:
        StereoPictureFrame (boost::filesystem::path mxf_path, int n);
diff --git a/src/stereo_picture_mxf.cc b/src/stereo_picture_mxf.cc
new file mode 100644 (file)
index 0000000..0188e4e
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "AS_DCP.h"
+#include "stereo_picture_mxf.h"
+#include "stereo_picture_frame.h"
+#include "exceptions.h"
+#include "stereo_picture_mxf_writer.h"
+
+using std::string;
+using std::pair;
+using std::make_pair;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using namespace dcp;
+
+StereoPictureMXF::StereoPictureMXF (boost::filesystem::path file)
+       : PictureMXF (file)
+{
+       ASDCP::JP2K::MXFSReader reader;
+       Kumu::Result_t r = reader.OpenRead (file.string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file.string(), r));
+       }
+       
+       ASDCP::JP2K::PictureDescriptor desc;
+       if (ASDCP_FAILURE (reader.FillPictureDescriptor (desc))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+
+       read_picture_descriptor (desc);
+
+       ASDCP::WriterInfo info;
+       if (ASDCP_FAILURE (reader.FillWriterInfo (info))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+
+       read_writer_info (info);
+}
+
+StereoPictureMXF::StereoPictureMXF (Fraction edit_rate)
+       : PictureMXF
+         (edit_rate)
+{
+
+}
+
+shared_ptr<const StereoPictureFrame>
+StereoPictureMXF::get_frame (int n) const
+{
+       return shared_ptr<const StereoPictureFrame> (new StereoPictureFrame (file().string(), n));
+}
+
+shared_ptr<PictureMXFWriter>
+StereoPictureMXF::start_write (boost::filesystem::path file, Standard standard, bool overwrite)
+{
+       return shared_ptr<StereoPictureMXFWriter> (new StereoPictureMXFWriter (this, file, standard, overwrite));
+}
+
+bool
+StereoPictureMXF::equals (shared_ptr<const Content> other, EqualityOptions opt, boost::function<void (NoteType, string)> note) const
+{
+       if (!MXF::equals (other, opt, note)) {
+               return false;
+       }
+
+       ASDCP::JP2K::MXFSReader reader_A;
+       Kumu::Result_t r = reader_A.OpenRead (file().string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r));
+       }
+       
+       ASDCP::JP2K::MXFSReader reader_B;
+       r = reader_B.OpenRead (other->file().string().c_str());
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("could not open MXF file for reading", file().string(), r));
+       }
+       
+       ASDCP::JP2K::PictureDescriptor desc_A;
+       if (ASDCP_FAILURE (reader_A.FillPictureDescriptor (desc_A))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+       ASDCP::JP2K::PictureDescriptor desc_B;
+       if (ASDCP_FAILURE (reader_B.FillPictureDescriptor (desc_B))) {
+               boost::throw_exception (DCPReadError ("could not read video MXF information"));
+       }
+       
+       if (!descriptor_equals (desc_A, desc_B, note)) {
+               return false;
+       }
+       
+       shared_ptr<const StereoPictureMXF> other_picture = dynamic_pointer_cast<const StereoPictureMXF> (other);
+       assert (other_picture);
+
+       for (int i = 0; i < _intrinsic_duration; ++i) {
+               shared_ptr<const StereoPictureFrame> frame_A = get_frame (i);
+               shared_ptr<const StereoPictureFrame> frame_B = other_picture->get_frame (i);
+               
+               if (!frame_buffer_equals (
+                           i, opt, note,
+                           frame_A->left_j2k_data(), frame_A->left_j2k_size(),
+                           frame_B->left_j2k_data(), frame_B->left_j2k_size()
+                           )) {
+                       return false;
+               }
+               
+               if (!frame_buffer_equals (
+                           i, opt, note,
+                           frame_A->right_j2k_data(), frame_A->right_j2k_size(),
+                           frame_B->right_j2k_data(), frame_B->right_j2k_size()
+                           )) {
+                       return false;
+               }
+       }
+
+       return true;
+}
diff --git a/src/stereo_picture_mxf.h b/src/stereo_picture_mxf.h
new file mode 100644 (file)
index 0000000..dc4605c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_STEREO_PICTURE_MXF_H
+#define LIBDCP_STEREO_PICTURE_MXF_H
+
+#include "picture_mxf.h"
+
+namespace dcp {
+       
+/** A 3D (stereoscopic) picture asset */       
+class StereoPictureMXF : public PictureMXF
+{
+public:
+       StereoPictureMXF (boost::filesystem::path file);
+       StereoPictureMXF (Fraction edit_rate);
+
+       /** Start a progressive write to a StereoPictureMXF */
+       boost::shared_ptr<PictureMXFWriter> start_write (boost::filesystem::path file, Standard, bool);
+
+       bool equals (
+               boost::shared_ptr<const Content> other,
+               EqualityOptions opt,
+               boost::function<void (NoteType, std::string)> note
+               ) const;
+       
+       boost::shared_ptr<const StereoPictureFrame> get_frame (int n) const;
+};
+
+}
+
+#endif
diff --git a/src/stereo_picture_mxf_writer.cc b/src/stereo_picture_mxf_writer.cc
new file mode 100644 (file)
index 0000000..4f98c60
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "stereo_picture_mxf_writer.h"
+#include "exceptions.h"
+#include "picture_mxf.h"
+#include "AS_DCP.h"
+#include "KM_fileio.h"
+
+#include "picture_mxf_writer_common.cc"
+
+using std::istream;
+using std::ostream;
+using std::string;
+using boost::shared_ptr;
+using namespace dcp;
+
+struct StereoPictureMXFWriter::ASDCPState : public ASDCPStateBase
+{
+       ASDCP::JP2K::MXFSWriter mxf_writer;
+};
+
+StereoPictureMXFWriter::StereoPictureMXFWriter (PictureMXF* mxf, boost::filesystem::path file, Standard standard, bool overwrite)
+       : PictureMXFWriter (mxf, file, standard, overwrite)
+       , _state (new StereoPictureMXFWriter::ASDCPState)
+       , _next_eye (EYE_LEFT)
+{
+       _state->encryption_context = mxf->encryption_context ();
+}
+
+void
+StereoPictureMXFWriter::start (uint8_t* data, int size)
+{
+       dcp::start (this, _state, _standard, _picture_mxf, data, size);
+       _picture_mxf->set_frame_rate (Fraction (_picture_mxf->edit_rate().numerator * 2, _picture_mxf->edit_rate().denominator));
+}
+
+/** Write a frame for one eye.  Frames must be written left, then right, then left etc.
+ *  @param data JPEG2000 data.
+ *  @param size Size of data.
+ */
+FrameInfo
+StereoPictureMXFWriter::write (uint8_t* data, int size)
+{
+       assert (!_finalized);
+
+       if (!_started) {
+               start (data, size);
+       }
+
+       if (ASDCP_FAILURE (_state->j2k_parser.OpenReadFrame (data, size, _state->frame_buffer))) {
+               boost::throw_exception (MiscError ("could not parse J2K frame"));
+       }
+
+       uint64_t const before_offset = _state->mxf_writer.Tell ();
+
+       string hash;
+       Kumu::Result_t r = _state->mxf_writer.WriteFrame (
+               _state->frame_buffer,
+               _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT,
+               _state->encryption_context,
+               0,
+               &hash
+               );
+
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("error in writing video MXF", _mxf->file().string(), r));
+       }
+
+       _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
+
+       if (_next_eye == EYE_LEFT) {
+               ++_frames_written;
+       }
+                       
+       return FrameInfo (before_offset, _state->mxf_writer.Tell() - before_offset, hash);
+}
+
+void
+StereoPictureMXFWriter::fake_write (int size)
+{
+       assert (_started);
+       assert (!_finalized);
+
+       Kumu::Result_t r = _state->mxf_writer.FakeWriteFrame (size, _next_eye == EYE_LEFT ? ASDCP::JP2K::SP_LEFT : ASDCP::JP2K::SP_RIGHT);
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("error in writing video MXF", _mxf->file().string(), r));
+       }
+
+       _next_eye = _next_eye == EYE_LEFT ? EYE_RIGHT : EYE_LEFT;
+       if (_next_eye == EYE_LEFT) {
+               ++_frames_written;
+       }
+}
+
+void
+StereoPictureMXFWriter::finalize ()
+{
+       Kumu::Result_t r = _state->mxf_writer.Finalize();
+       if (ASDCP_FAILURE (r)) {
+               boost::throw_exception (MXFFileError ("error in finalizing video MXF", _mxf->file().string(), r));
+       }
+
+       PictureMXFWriter::finalize ();
+}
diff --git a/src/stereo_picture_mxf_writer.h b/src/stereo_picture_mxf_writer.h
new file mode 100644 (file)
index 0000000..ebd9dbe
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "picture_mxf_writer.h"
+#include "types.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <stdint.h>
+#include <string>
+#include <fstream>
+
+namespace dcp {
+
+/** A helper class for writing to StereoPictureAssets progressively (i.e. writing frame-by-frame,
+ *  rather than giving libdcp all the frames in one go).
+ *
+ *  Objects of this class can only be created with StereoPictureAsset::start_write().
+ *
+ *  Frames can be written to the MonoPictureAsset by calling write() with a JPEG2000 image
+ *  (a verbatim .j2c file).  finalize() must be called after the last frame has been written.
+ *  The action of finalize() can't be done in MonoPictureAssetWriter's destructor as it may
+ *  throw an exception.
+ */
+class StereoPictureMXFWriter : public PictureMXFWriter
+{
+public:
+       FrameInfo write (uint8_t *, int);
+       void fake_write (int size);
+       void finalize ();
+
+private:
+       friend class StereoPictureMXF;
+
+       StereoPictureMXFWriter (PictureMXF *, boost::filesystem::path file, Standard, bool);
+       void start (uint8_t *, int);
+
+       /* do this with an opaque pointer so we don't have to include
+          ASDCP headers
+       */
+          
+       struct ASDCPState;
+       boost::shared_ptr<ASDCPState> _state;
+
+       dcp::Eye _next_eye;
+};
+
+}
diff --git a/src/subtitle.cc b/src/subtitle.cc
new file mode 100644 (file)
index 0000000..1271496
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "subtitle.h"
+#include "xml.h"
+#include "font.h"
+#include "text.h"
+#include <libcxml/cxml.h>
+#include <boost/lexical_cast.hpp>
+
+using std::string;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using namespace dcp;
+
+Subtitle::Subtitle (boost::shared_ptr<const cxml::Node> node)
+{
+       in = Time (node->string_attribute ("TimeIn"));
+       out = Time (node->string_attribute ("TimeOut"));
+       font_nodes = type_children<Font> (node, "Font");
+       text_nodes = type_children<Text> (node, "Text");
+       fade_up_time = fade_time (node, "FadeUpTime");
+       fade_down_time = fade_time (node, "FadeDownTime");
+}
+
+Time
+Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name)
+{
+       string const u = node->optional_string_attribute (name).get_value_or ("");
+       Time t;
+       
+       if (u.empty ()) {
+               t = Time (0, 0, 0, 20);
+       } else if (u.find (":") != string::npos) {
+               t = Time (u);
+       } else {
+               t = Time (0, 0, 0, lexical_cast<int> (u));
+       }
+
+       if (t > Time (0, 0, 8, 0)) {
+               t = Time (0, 0, 8, 0);
+       }
+
+       return t;
+}
diff --git a/src/subtitle.h b/src/subtitle.h
new file mode 100644 (file)
index 0000000..073bfb0
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_SUBTITLE_H
+#define LIBDCP_SUBTITLE_H
+
+#include "dcp_time.h"
+#include <boost/shared_ptr.hpp>
+#include <list>
+
+namespace cxml {
+       class Node;
+}
+
+namespace dcp {
+
+class Font;    
+class Text;
+
+class Subtitle 
+{
+public:
+       Subtitle () {}
+       Subtitle (boost::shared_ptr<const cxml::Node> node);
+
+       Time in;
+       Time out;
+       Time fade_up_time;
+       Time fade_down_time;
+       std::list<boost::shared_ptr<Font> > font_nodes;
+       std::list<boost::shared_ptr<Text> > text_nodes;
+
+private:
+       Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name);
+};
+
+}
+
+#endif
diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc
deleted file mode 100644 (file)
index 89269fb..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <fstream>
-#include <cerrno>
-#include <boost/algorithm/string.hpp>
-#include <libxml++/nodes/element.h>
-#include "AS_DCP.h"
-#include "KM_util.h"
-#include "subtitle_asset.h"
-#include "parse/subtitle.h"
-#include "util.h"
-#include "xml.h"
-#include "raw_convert.h"
-
-using std::string;
-using std::list;
-using std::ostream;
-using std::ofstream;
-using std::stringstream;
-using std::cout;
-using boost::shared_ptr;
-using boost::optional;
-using namespace libdcp;
-
-SubtitleAsset::SubtitleAsset (string directory, string file)
-       : Asset (directory, file)
-       , _need_sort (false)
-{
-       /* Grotesque hack: we should look in the PKL to see what type this file is;
-          instead we'll look at the first character to decide what to do.
-          I think this is easily fixable (properly) in 1.0.
-       */
-
-       FILE* f = fopen_boost (path(), "r");
-       if (!f) {
-               throw FileError ("Could not open file for reading", file, errno);
-       }
-       unsigned char test[1];
-       fread (test, 1, 1, f);
-       fclose (f);
-
-       if (test[0] == '<' || test[0] == 0xef) {
-               read_xml (path().string());
-       } else {
-               read_mxf (path().string());
-       }
-}
-
-SubtitleAsset::SubtitleAsset (string directory, string movie_title, string language)
-       : Asset (directory)
-       , _movie_title (movie_title)
-       , _reel_number ("1")
-       , _language (language)
-       , _need_sort (false)
-{
-
-}
-
-void
-SubtitleAsset::read_mxf (string mxf_file)
-{
-       ASDCP::TimedText::MXFReader reader;
-       Kumu::Result_t r = reader.OpenRead (mxf_file.c_str ());
-       if (ASDCP_FAILURE (r)) {
-               boost::throw_exception (MXFFileError ("could not open MXF file for reading", mxf_file, r));
-       }
-
-       string s;
-       reader.ReadTimedTextResource (s, 0, 0);
-       shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
-       stringstream t;
-       t << s;
-       xml->read_stream (t);
-       read_xml (xml);
-}
-
-void
-SubtitleAsset::read_xml (string xml_file)
-{
-       shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle"));
-       xml->read_file (xml_file);
-       read_xml (xml);
-}
-
-void
-SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml)
-{
-       /* XXX: hacks aplenty in here; need separate parsers for DCSubtitle and SubtitleReel */
-       
-       /* DCSubtitle */
-       optional<string> x = xml->optional_string_child ("SubtitleID");
-       if (!x) {
-               /* SubtitleReel */
-               x = xml->optional_string_child ("Id");
-       }
-       _uuid = x.get_value_or ("");
-
-       _movie_title = xml->optional_string_child ("MovieTitle");
-       _reel_number = xml->string_child ("ReelNumber");
-       _language = xml->string_child ("Language");
-
-       xml->ignore_child ("LoadFont");
-
-       list<shared_ptr<libdcp::parse::Font> > font_nodes = type_children<libdcp::parse::Font> (xml, "Font");
-       _load_font_nodes = type_children<libdcp::parse::LoadFont> (xml, "LoadFont");
-
-       /* Now make Subtitle objects to represent the raw XML nodes
-          in a sane way.
-       */
-
-       shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList");
-       if (subtitle_list) {
-               list<shared_ptr<libdcp::parse::Font> > font = type_children<libdcp::parse::Font> (subtitle_list, "Font");
-               copy (font.begin(), font.end(), back_inserter (font_nodes));
-       }
-       
-       ParseState parse_state;
-       examine_font_nodes (xml, font_nodes, parse_state);
-}
-
-void
-SubtitleAsset::examine_font_nodes (
-       shared_ptr<const cxml::Node> xml,
-       list<shared_ptr<libdcp::parse::Font> > const & font_nodes,
-       ParseState& parse_state
-       )
-{
-       for (list<shared_ptr<libdcp::parse::Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
-
-               parse_state.font_nodes.push_back (*i);
-               maybe_add_subtitle ((*i)->text, parse_state);
-
-               for (list<shared_ptr<libdcp::parse::Subtitle> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) {
-                       parse_state.subtitle_nodes.push_back (*j);
-                       examine_text_nodes (xml, (*j)->text_nodes, parse_state);
-                       examine_font_nodes (xml, (*j)->font_nodes, parse_state);
-                       parse_state.subtitle_nodes.pop_back ();
-               }
-       
-               examine_font_nodes (xml, (*i)->font_nodes, parse_state);
-               examine_text_nodes (xml, (*i)->text_nodes, parse_state);
-               
-               parse_state.font_nodes.pop_back ();
-       }
-}
-
-void
-SubtitleAsset::examine_text_nodes (
-       shared_ptr<const cxml::Node> xml,
-       list<shared_ptr<libdcp::parse::Text> > const & text_nodes,
-       ParseState& parse_state
-       )
-{
-       for (list<shared_ptr<libdcp::parse::Text> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) {
-               parse_state.text_nodes.push_back (*i);
-               maybe_add_subtitle ((*i)->text, parse_state);
-               examine_font_nodes (xml, (*i)->font_nodes, parse_state);
-               parse_state.text_nodes.pop_back ();
-       }
-}
-
-void
-SubtitleAsset::maybe_add_subtitle (string text, ParseState const & parse_state)
-{
-       if (empty_or_white_space (text)) {
-               return;
-       }
-       
-       if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) {
-               return;
-       }
-
-       assert (!parse_state.text_nodes.empty ());
-       assert (!parse_state.subtitle_nodes.empty ());
-       
-       libdcp::parse::Font effective_font (parse_state.font_nodes);
-       libdcp::parse::Text effective_text (*parse_state.text_nodes.back ());
-       libdcp::parse::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ());
-
-       _subtitles.push_back (
-               shared_ptr<Subtitle> (
-                       new Subtitle (
-                               font_id_to_name (effective_font.id),
-                               effective_font.italic.get(),
-                               effective_font.color.get(),
-                               effective_font.size,
-                               effective_subtitle.in,
-                               effective_subtitle.out,
-                               effective_text.v_position,
-                               effective_text.v_align,
-                               text,
-                               effective_font.effect ? effective_font.effect.get() : NONE,
-                               effective_font.effect_color.get(),
-                               effective_subtitle.fade_up_time,
-                               effective_subtitle.fade_down_time
-                               )
-                       )
-               );
-}
-
-list<shared_ptr<Subtitle> >
-SubtitleAsset::subtitles_at (Time t) const
-{
-       list<shared_ptr<Subtitle> > s;
-       for (list<shared_ptr<Subtitle> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
-               if ((*i)->in() <= t && t <= (*i)->out ()) {
-                       s.push_back (*i);
-               }
-       }
-
-       return s;
-}
-
-std::string
-SubtitleAsset::font_id_to_name (string id) const
-{
-       list<shared_ptr<libdcp::parse::LoadFont> >::const_iterator i = _load_font_nodes.begin();
-       while (i != _load_font_nodes.end() && (*i)->id != id) {
-               ++i;
-       }
-
-       if (i == _load_font_nodes.end ()) {
-               return "";
-       }
-
-       if ((*i)->uri && (*i)->uri.get() == "arial.ttf") {
-               return "Arial";
-       }
-
-       return "";
-}
-
-Subtitle::Subtitle (
-       string font,
-       bool italic,
-       Color color,
-       int size,
-       Time in,
-       Time out,
-       float v_position,
-       VAlign v_align,
-       string text,
-       Effect effect,
-       Color effect_color,
-       Time fade_up_time,
-       Time fade_down_time
-       )
-       : _font (font)
-       , _italic (italic)
-       , _color (color)
-       , _size (size)
-       , _in (in)
-       , _out (out)
-       , _v_position (v_position)
-       , _v_align (v_align)
-       , _text (text)
-       , _effect (effect)
-       , _effect_color (effect_color)
-       , _fade_up_time (fade_up_time)
-       , _fade_down_time (fade_down_time)
-{
-
-}
-
-int
-Subtitle::size_in_pixels (int screen_height) const
-{
-       /* Size in the subtitle file is given in points as if the screen
-          height is 11 inches, so a 72pt font would be 1/11th of the screen
-          height.
-       */
-       
-       return _size * screen_height / (11 * 72);
-}
-
-bool
-libdcp::operator== (Subtitle const & a, Subtitle const & b)
-{
-       return (
-               a.font() == b.font() &&
-               a.italic() == b.italic() &&
-               a.color() == b.color() &&
-               a.size() == b.size() &&
-               a.in() == b.in() &&
-               a.out() == b.out() &&
-               a.v_position() == b.v_position() &&
-               a.v_align() == b.v_align() &&
-               a.text() == b.text() &&
-               a.effect() == b.effect() &&
-               a.effect_color() == b.effect_color() &&
-               a.fade_up_time() == b.fade_up_time() &&
-               a.fade_down_time() == b.fade_down_time()
-               );
-}
-
-ostream&
-libdcp::operator<< (ostream& s, Subtitle const & sub)
-{
-       s << "\n`" << sub.text() << "' from " << sub.in() << " to " << sub.out() << ";\n"
-         << "fade up " << sub.fade_up_time() << ", fade down " << sub.fade_down_time() << ";\n"
-         << "font " << sub.font() << ", ";
-
-       if (sub.italic()) {
-               s << "italic";
-       } else {
-               s << "non-italic";
-       }
-       
-       s << ", size " << sub.size() << ", color " << sub.color() << ", vpos " << sub.v_position() << ", valign " << ((int) sub.v_align()) << ";\n"
-         << "effect " << ((int) sub.effect()) << ", effect color " << sub.effect_color();
-
-       return s;
-}
-
-void
-SubtitleAsset::add (shared_ptr<Subtitle> s)
-{
-       _subtitles.push_back (s);
-       _need_sort = true;
-}
-
-void
-SubtitleAsset::write_to_cpl (xmlpp::Element* node) const
-{
-       /* XXX: should EditRate, Duration and IntrinsicDuration be in here? */
-
-       xmlpp::Node* ms = node->add_child ("MainSubtitle");
-       ms->add_child("Id")->add_child_text("urn:uuid:" + _uuid);
-       ms->add_child("AnnotationText")->add_child_text (_file_name.string ());
-       /* XXX */
-       ms->add_child("EntryPoint")->add_child_text ("0");
-}
-
-struct SubtitleSorter {
-       bool operator() (shared_ptr<Subtitle> a, shared_ptr<Subtitle> b) {
-               if (a->in() != b->in()) {
-                       return a->in() < b->in();
-               }
-               return a->v_position() < b->v_position();
-       }
-};
-
-void
-SubtitleAsset::write_xml () const
-{
-       FILE* f = fopen_boost (path (), "r");
-       Glib::ustring const s = xml_as_string ();
-       fwrite (s.c_str(), 1, s.length(), f);
-       fclose (f);
-}
-
-Glib::ustring
-SubtitleAsset::xml_as_string () const
-{
-       xmlpp::Document doc;
-       xmlpp::Element* root = doc.create_root_node ("DCSubtitle");
-       root->set_attribute ("Version", "1.0");
-
-       root->add_child("SubtitleID")->add_child_text (_uuid);
-       if (_movie_title) {
-               root->add_child("MovieTitle")->add_child_text (_movie_title.get ());
-       }
-       root->add_child("ReelNumber")->add_child_text (raw_convert<string> (_reel_number));
-       root->add_child("Language")->add_child_text (_language);
-
-       if (_load_font_nodes.size() > 1) {
-               boost::throw_exception (MiscError ("multiple LoadFont nodes not supported"));
-       }
-
-       if (!_load_font_nodes.empty ()) {
-               xmlpp::Element* load_font = root->add_child("LoadFont");
-               load_font->set_attribute("Id", _load_font_nodes.front()->id);
-               if (_load_font_nodes.front()->uri) {
-                       load_font->set_attribute("URI",  _load_font_nodes.front()->uri.get ());
-               }
-       }
-
-       list<shared_ptr<Subtitle> > sorted = _subtitles;
-       if (_need_sort) {
-               sorted.sort (SubtitleSorter ());
-       }
-
-       /* XXX: multiple fonts not supported */
-       /* XXX: script, underlined, weight not supported */
-
-       bool italic = false;
-       Color color;
-       int size = 0;
-       Effect effect = NONE;
-       Color effect_color;
-       int spot_number = 1;
-       Time last_in;
-       Time last_out;
-       Time last_fade_up_time;
-       Time last_fade_down_time;
-
-       xmlpp::Element* font = 0;
-       xmlpp::Element* subtitle = 0;
-
-       for (list<shared_ptr<Subtitle> >::iterator i = sorted.begin(); i != sorted.end(); ++i) {
-
-               /* We will start a new <Font>...</Font> whenever some font property changes.
-                  I suppose we should really make an optimal hierarchy of <Font> tags, but
-                  that seems hard.
-               */
-
-               bool const font_changed =
-                       italic       != (*i)->italic()       ||
-                       color        != (*i)->color()        ||
-                       size         != (*i)->size()         ||
-                       effect       != (*i)->effect()       ||
-                       effect_color != (*i)->effect_color();
-
-               if (font_changed) {
-                       italic = (*i)->italic ();
-                       color = (*i)->color ();
-                       size = (*i)->size ();
-                       effect = (*i)->effect ();
-                       effect_color = (*i)->effect_color ();
-               }
-
-               if (!font || font_changed) {
-                       font = root->add_child ("Font");
-                       string id = "theFontId";
-                       if (!_load_font_nodes.empty()) {
-                               id = _load_font_nodes.front()->id;
-                       }
-                       font->set_attribute ("Id", id);
-                       font->set_attribute ("Italic", italic ? "yes" : "no");
-                       font->set_attribute ("Color", color.to_argb_string());
-                       font->set_attribute ("Size", raw_convert<string> (size));
-                       font->set_attribute ("Effect", effect_to_string (effect));
-                       font->set_attribute ("EffectColor", effect_color.to_argb_string());
-                       font->set_attribute ("Script", "normal");
-                       font->set_attribute ("Underlined", "no");
-                       font->set_attribute ("Weight", "normal");
-               }
-
-               if (!subtitle || font_changed ||
-                   (last_in != (*i)->in() ||
-                    last_out != (*i)->out() ||
-                    last_fade_up_time != (*i)->fade_up_time() ||
-                    last_fade_down_time != (*i)->fade_down_time()
-                           )) {
-
-                       subtitle = font->add_child ("Subtitle");
-                       subtitle->set_attribute ("SpotNumber", raw_convert<string> (spot_number++));
-                       subtitle->set_attribute ("TimeIn", (*i)->in().to_string());
-                       subtitle->set_attribute ("TimeOut", (*i)->out().to_string());
-                       subtitle->set_attribute ("FadeUpTime", raw_convert<string> ((*i)->fade_up_time().to_ticks()));
-                       subtitle->set_attribute ("FadeDownTime", raw_convert<string> ((*i)->fade_down_time().to_ticks()));
-
-                       last_in = (*i)->in ();
-                       last_out = (*i)->out ();
-                       last_fade_up_time = (*i)->fade_up_time ();
-                       last_fade_down_time = (*i)->fade_down_time ();
-               }
-
-               xmlpp::Element* text = subtitle->add_child ("Text");
-               text->set_attribute ("VAlign", valign_to_string ((*i)->v_align()));             
-               text->set_attribute ("VPosition", raw_convert<string> ((*i)->v_position()));
-               text->add_child_text ((*i)->text());
-       }
-
-       return doc.write_to_string_formatted ("UTF-8");
-}
-
diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h
deleted file mode 100644 (file)
index a7ec641..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBDCP_SUBTITLE_ASSET_H
-#define LIBDCP_SUBTITLE_ASSET_H
-
-#include <libcxml/cxml.h>
-#include "asset.h"
-#include "dcp_time.h"
-
-namespace libdcp
-{
-
-namespace parse
-{
-       class Font;
-       class Text;
-       class Subtitle;
-       class LoadFont;
-}
-
-class Subtitle
-{
-public:
-       Subtitle (
-               std::string font,
-               bool italic,
-               Color color,
-               int size,
-               Time in,
-               Time out,
-               float v_position,
-               VAlign v_align,
-               std::string text,
-               Effect effect,
-               Color effect_color,
-               Time fade_up_time,
-               Time fade_down_time
-               );
-
-       std::string font () const {
-               return _font;
-       }
-
-       bool italic () const {
-               return _italic;
-       }
-
-       Color color () const {
-               return _color;
-       }
-
-       Time in () const {
-               return _in;
-       }
-
-       Time out () const {
-               return _out;
-       }
-
-       std::string text () const {
-               return _text;
-       }
-
-       float v_position () const {
-               return _v_position;
-       }
-
-       VAlign v_align () const {
-               return _v_align;
-       }
-
-       Effect effect () const {
-               return _effect;
-       }
-
-       Color effect_color () const {
-               return _effect_color;
-       }
-
-       Time fade_up_time () const {
-               return _fade_up_time;
-       }
-
-       Time fade_down_time () const {
-               return _fade_down_time;
-       }
-
-       int size () const {
-               return _size;
-       }
-       
-       int size_in_pixels (int screen_height) const;
-
-private:
-       std::string _font;
-       bool _italic;
-       Color _color;
-       /** Size in points as if the screen height is 11 inches, so a 72pt font
-        *  would be 1/11th of the screen height.
-        */ 
-       int _size;
-       Time _in;
-       Time _out;
-       /** Vertical position as a proportion of the screen height from the top
-        *  (between 0 and 1)
-        */
-       float _v_position;
-       VAlign _v_align;
-       std::string _text;
-       Effect _effect;
-       Color _effect_color;
-       Time _fade_up_time;
-       Time _fade_down_time;
-};
-
-bool operator== (Subtitle const & a, Subtitle const & b);
-std::ostream& operator<< (std::ostream& s, Subtitle const & sub);
-
-class SubtitleAsset : public Asset
-{
-public:
-       SubtitleAsset (std::string directory, std::string xml_file);
-       SubtitleAsset (std::string directory, std::string movie_title, std::string language);
-
-       void write_to_cpl (xmlpp::Element *) const;
-       virtual bool equals (boost::shared_ptr<const Asset>, EqualityOptions, boost::function<void (NoteType, std::string)> note) const {
-               /* XXX */
-               note (ERROR, "subtitle assets not compared yet");
-               return true;
-       }
-
-       std::string language () const {
-               return _language;
-       }
-
-       std::list<boost::shared_ptr<Subtitle> > subtitles_at (Time t) const;
-       std::list<boost::shared_ptr<Subtitle> > const & subtitles () const {
-               return _subtitles;
-       }
-
-       void add (boost::shared_ptr<Subtitle>);
-
-       void read_xml (std::string);
-       void write_xml () const;
-       Glib::ustring xml_as_string () const;
-
-protected:
-
-       std::string asdcp_kind () const {
-               return "Subtitle";
-       }
-
-private:
-       std::string font_id_to_name (std::string id) const;
-       void read_mxf (std::string);
-       void read_xml (boost::shared_ptr<cxml::Document>);
-
-       struct ParseState {
-               std::list<boost::shared_ptr<parse::Font> > font_nodes;
-               std::list<boost::shared_ptr<parse::Text> > text_nodes;
-               std::list<boost::shared_ptr<parse::Subtitle> > subtitle_nodes;
-       };
-
-       void maybe_add_subtitle (std::string text, ParseState const & parse_state);
-       
-       void examine_font_nodes (
-               boost::shared_ptr<const cxml::Node> xml,
-               std::list<boost::shared_ptr<parse::Font> > const & font_nodes,
-               ParseState& parse_state
-               );
-       
-       void examine_text_nodes (
-               boost::shared_ptr<const cxml::Node> xml,
-               std::list<boost::shared_ptr<parse::Text> > const & text_nodes,
-               ParseState& parse_state
-               );
-
-       boost::optional<std::string> _movie_title;
-       /* strangely, this is sometimes a string */
-       std::string _reel_number;
-       std::string _language;
-       std::list<boost::shared_ptr<parse::LoadFont> > _load_font_nodes;
-
-       std::list<boost::shared_ptr<Subtitle> > _subtitles;
-       bool _need_sort;
-};
-
-}
-
-#endif
diff --git a/src/subtitle_content.cc b/src/subtitle_content.cc
new file mode 100644 (file)
index 0000000..a622e7b
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "raw_convert.h"
+#include "subtitle_content.h"
+#include "util.h"
+#include "xml.h"
+#include "font.h"
+#include "text.h"
+#include "load_font.h"
+#include "subtitle_string.h"
+#include "AS_DCP.h"
+#include "KM_util.h"
+#include <libxml++/nodes/element.h>
+#include <boost/algorithm/string.hpp>
+#include <fstream>
+
+using std::string;
+using std::list;
+using std::ostream;
+using std::ofstream;
+using std::stringstream;
+using std::cout;
+using boost::shared_ptr;
+using boost::optional;
+using namespace dcp;
+
+SubtitleContent::SubtitleContent (boost::filesystem::path file, bool mxf)
+       : Content (file)
+       , _need_sort (false)
+{
+       shared_ptr<cxml::Document> xml;
+       
+       if (mxf) {
+               ASDCP::TimedText::MXFReader reader;
+               Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
+               if (ASDCP_FAILURE (r)) {
+                       boost::throw_exception (MXFFileError ("could not open MXF file for reading", file, r));
+               }
+               
+               string s;
+               reader.ReadTimedTextResource (s, 0, 0);
+               xml.reset (new cxml::Document ("SubtitleReel"));
+               stringstream t;
+               t << s;
+               xml->read_stream (t);
+
+               ASDCP::WriterInfo info;
+               reader.FillWriterInfo (info);
+               
+               char buffer[64];
+               Kumu::bin2UUIDhex (info.AssetUUID, ASDCP::UUIDlen, buffer, sizeof (buffer));
+               _id = buffer;
+
+       } else {
+               xml.reset (new cxml::Document ("DCSubtitle"));
+               xml->read_file (file);
+               _id = xml->string_child ("SubtitleID");
+       }
+
+       /* XXX: hacks aplenty in here; probably need separate parsers for DCSubtitle and SubtitleReel */
+
+       _movie_title = xml->optional_string_child ("MovieTitle");
+       _reel_number = xml->string_child ("ReelNumber");
+       _language = xml->string_child ("Language");
+
+       xml->ignore_child ("LoadFont");
+
+       list<shared_ptr<dcp::Font> > font_nodes = type_children<dcp::Font> (xml, "Font");
+       _load_font_nodes = type_children<dcp::LoadFont> (xml, "LoadFont");
+
+       /* Now make Subtitle objects to represent the raw XML nodes
+          in a sane way.
+       */
+
+       shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList");
+       if (subtitle_list) {
+               list<shared_ptr<dcp::Font> > font = type_children<dcp::Font> (subtitle_list, "Font");
+               copy (font.begin(), font.end(), back_inserter (font_nodes));
+       }
+       
+       ParseState parse_state;
+       examine_font_nodes (xml, font_nodes, parse_state);
+}
+
+SubtitleContent::SubtitleContent (Fraction edit_rate, string movie_title, string language)
+       : Content (edit_rate)
+       , _movie_title (movie_title)
+       , _reel_number ("1")
+       , _language (language)
+       , _need_sort (false)
+{
+
+}
+
+void
+SubtitleContent::examine_font_nodes (
+       shared_ptr<const cxml::Node> xml,
+       list<shared_ptr<dcp::Font> > const & font_nodes,
+       ParseState& parse_state
+       )
+{
+       for (list<shared_ptr<dcp::Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+
+               parse_state.font_nodes.push_back (*i);
+               maybe_add_subtitle ((*i)->text, parse_state);
+
+               for (list<shared_ptr<dcp::Subtitle> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) {
+                       parse_state.subtitle_nodes.push_back (*j);
+                       examine_text_nodes (xml, (*j)->text_nodes, parse_state);
+                       examine_font_nodes (xml, (*j)->font_nodes, parse_state);
+                       parse_state.subtitle_nodes.pop_back ();
+               }
+       
+               examine_font_nodes (xml, (*i)->font_nodes, parse_state);
+               examine_text_nodes (xml, (*i)->text_nodes, parse_state);
+               
+               parse_state.font_nodes.pop_back ();
+       }
+}
+
+void
+SubtitleContent::examine_text_nodes (
+       shared_ptr<const cxml::Node> xml,
+       list<shared_ptr<dcp::Text> > const & text_nodes,
+       ParseState& parse_state
+       )
+{
+       for (list<shared_ptr<dcp::Text> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) {
+               parse_state.text_nodes.push_back (*i);
+               maybe_add_subtitle ((*i)->text, parse_state);
+               examine_font_nodes (xml, (*i)->font_nodes, parse_state);
+               parse_state.text_nodes.pop_back ();
+       }
+}
+
+void
+SubtitleContent::maybe_add_subtitle (string text, ParseState const & parse_state)
+{
+       if (empty_or_white_space (text)) {
+               return;
+       }
+       
+       if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) {
+               return;
+       }
+
+       assert (!parse_state.text_nodes.empty ());
+       assert (!parse_state.subtitle_nodes.empty ());
+       
+       dcp::Font effective_font (parse_state.font_nodes);
+       dcp::Text effective_text (*parse_state.text_nodes.back ());
+       dcp::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ());
+
+       _subtitles.push_back (
+               shared_ptr<SubtitleString> (
+                       new SubtitleString (
+                               font_id_to_name (effective_font.id),
+                               effective_font.italic.get(),
+                               effective_font.color.get(),
+                               effective_font.size,
+                               effective_subtitle.in,
+                               effective_subtitle.out,
+                               effective_text.v_position,
+                               effective_text.v_align,
+                               text,
+                               effective_font.effect ? effective_font.effect.get() : NONE,
+                               effective_font.effect_color.get(),
+                               effective_subtitle.fade_up_time,
+                               effective_subtitle.fade_down_time
+                               )
+                       )
+               );
+}
+
+list<shared_ptr<SubtitleString> >
+SubtitleContent::subtitles_at (Time t) const
+{
+       list<shared_ptr<SubtitleString> > s;
+       for (list<shared_ptr<SubtitleString> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+               if ((*i)->in() <= t && t <= (*i)->out ()) {
+                       s.push_back (*i);
+               }
+       }
+
+       return s;
+}
+
+std::string
+SubtitleContent::font_id_to_name (string id) const
+{
+       list<shared_ptr<dcp::LoadFont> >::const_iterator i = _load_font_nodes.begin();
+       while (i != _load_font_nodes.end() && (*i)->id != id) {
+               ++i;
+       }
+
+       if (i == _load_font_nodes.end ()) {
+               return "";
+       }
+
+       if ((*i)->uri && (*i)->uri.get() == "arial.ttf") {
+               return "Arial";
+       }
+
+       return "";
+}
+
+void
+SubtitleContent::add (shared_ptr<SubtitleString> s)
+{
+       _subtitles.push_back (s);
+       _need_sort = true;
+}
+
+struct SubtitleSorter {
+       bool operator() (shared_ptr<SubtitleString> a, shared_ptr<SubtitleString> b) {
+               if (a->in() != b->in()) {
+                       return a->in() < b->in();
+               }
+               return a->v_position() < b->v_position();
+       }
+};
+
+void
+SubtitleContent::write_xml () const
+{
+       FILE* f = fopen_boost (file (), "r");
+       Glib::ustring const s = xml_as_string ();
+       fwrite (s.c_str(), 1, s.length(), f);
+       fclose (f);
+}
+
+Glib::ustring
+SubtitleContent::xml_as_string () const
+{
+       xmlpp::Document doc;
+       xmlpp::Element* root = doc.create_root_node ("DCSubtitle");
+       root->set_attribute ("Version", "1.0");
+
+       root->add_child("SubtitleID")->add_child_text (_id);
+       if (_movie_title) {
+               root->add_child("MovieTitle")->add_child_text (_movie_title.get ());
+       }
+       root->add_child("ReelNumber")->add_child_text (raw_convert<string> (_reel_number));
+       root->add_child("Language")->add_child_text (_language);
+
+       if (_load_font_nodes.size() > 1) {
+               boost::throw_exception (MiscError ("multiple LoadFont nodes not supported"));
+       }
+
+       if (!_load_font_nodes.empty ()) {
+               xmlpp::Element* load_font = root->add_child("LoadFont");
+               load_font->set_attribute ("Id", _load_font_nodes.front()->id);
+               if (_load_font_nodes.front()->uri) {
+                       load_font->set_attribute ("URI", _load_font_nodes.front()->uri.get ());
+               }
+       }
+
+       list<shared_ptr<SubtitleString> > sorted = _subtitles;
+       if (_need_sort) {
+               sorted.sort (SubtitleSorter ());
+       }
+
+       /* XXX: multiple fonts not supported */
+       /* XXX: script, underlined, weight not supported */
+
+       bool italic = false;
+       Color color;
+       int size = 0;
+       Effect effect = NONE;
+       Color effect_color;
+       int spot_number = 1;
+       Time last_in;
+       Time last_out;
+       Time last_fade_up_time;
+       Time last_fade_down_time;
+
+       xmlpp::Element* font = 0;
+       xmlpp::Element* subtitle = 0;
+
+       for (list<shared_ptr<SubtitleString> >::iterator i = sorted.begin(); i != sorted.end(); ++i) {
+
+               /* We will start a new <Font>...</Font> whenever some font property changes.
+                  I suppose we should really make an optimal hierarchy of <Font> tags, but
+                  that seems hard.
+               */
+
+               bool const font_changed =
+                       italic       != (*i)->italic()       ||
+                       color        != (*i)->color()        ||
+                       size         != (*i)->size()         ||
+                       effect       != (*i)->effect()       ||
+                       effect_color != (*i)->effect_color();
+
+               if (font_changed) {
+                       italic = (*i)->italic ();
+                       color = (*i)->color ();
+                       size = (*i)->size ();
+                       effect = (*i)->effect ();
+                       effect_color = (*i)->effect_color ();
+               }
+
+               if (!font || font_changed) {
+                       font = root->add_child ("Font");
+                       string id = "theFontId";
+                       if (!_load_font_nodes.empty()) {
+                               id = _load_font_nodes.front()->id;
+                       }
+                       font->set_attribute ("Id", id);
+                       font->set_attribute ("Italic", italic ? "yes" : "no");
+                       font->set_attribute ("Color", color.to_argb_string());
+                       font->set_attribute ("Size", raw_convert<string> (size));
+                       font->set_attribute ("Effect", effect_to_string (effect));
+                       font->set_attribute ("EffectColor", effect_color.to_argb_string());
+                       font->set_attribute ("Script", "normal");
+                       font->set_attribute ("Underlined", "no");
+                       font->set_attribute ("Weight", "normal");
+               }
+
+               if (!subtitle || font_changed ||
+                   (last_in != (*i)->in() ||
+                    last_out != (*i)->out() ||
+                    last_fade_up_time != (*i)->fade_up_time() ||
+                    last_fade_down_time != (*i)->fade_down_time()
+                           )) {
+
+                       subtitle = font->add_child ("Subtitle");
+                       subtitle->set_attribute ("SpotNumber", raw_convert<string> (spot_number++));
+                       subtitle->set_attribute ("TimeIn", (*i)->in().to_string());
+                       subtitle->set_attribute ("TimeOut", (*i)->out().to_string());
+                       subtitle->set_attribute ("FadeUpTime", raw_convert<string> ((*i)->fade_up_time().to_ticks()));
+                       subtitle->set_attribute ("FadeDownTime", raw_convert<string> ((*i)->fade_down_time().to_ticks()));
+
+                       last_in = (*i)->in ();
+                       last_out = (*i)->out ();
+                       last_fade_up_time = (*i)->fade_up_time ();
+                       last_fade_down_time = (*i)->fade_down_time ();
+               }
+
+               xmlpp::Element* text = subtitle->add_child ("Text");
+               text->set_attribute ("VAlign", valign_to_string ((*i)->v_align()));             
+               text->set_attribute ("VPosition", raw_convert<string> ((*i)->v_position()));
+               text->add_child_text ((*i)->text());
+       }
+
+       return doc.write_to_string_formatted ("UTF-8");
+}
+
diff --git a/src/subtitle_content.h b/src/subtitle_content.h
new file mode 100644 (file)
index 0000000..2c60692
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBDCP_SUBTITLE_CONTENT_H
+#define LIBDCP_SUBTITLE_CONTENT_H
+
+#include "content.h"
+#include "dcp_time.h"
+#include <libcxml/cxml.h>
+
+namespace dcp
+{
+
+class SubtitleString;  
+class Font;
+class Text;
+class Subtitle;
+class LoadFont;
+
+/** @class SubtitleContent
+ *  @brief A representation of an XML or MXF file containing subtitles.
+ *
+ *  XXX: perhaps this should inhert from MXF, or there should be different
+ *  classes for XML and MXF subs.
+ */
+class SubtitleContent : public Content
+{
+public:
+       /** Construct a SubtitleContent.
+        *  @param file Filename.
+        *  @param mxf true if the file is an MXF file, false for XML.
+        */
+       SubtitleContent (boost::filesystem::path file, bool mxf);
+       SubtitleContent (Fraction edit_rate, std::string movie_title, std::string language);
+
+       bool equals (
+               boost::shared_ptr<const Content>,
+               EqualityOptions,
+               boost::function<void (NoteType, std::string)> note
+               ) const {
+               /* XXX */
+               note (ERROR, "subtitle content not compared yet");
+               return true;
+       }
+
+       std::string language () const {
+               return _language;
+       }
+
+       std::list<boost::shared_ptr<SubtitleString> > subtitles_at (Time t) const;
+       std::list<boost::shared_ptr<SubtitleString> > const & subtitles () const {
+               return _subtitles;
+       }
+
+       void add (boost::shared_ptr<SubtitleString>);
+
+       void write_xml () const;
+       Glib::ustring xml_as_string () const;
+
+protected:
+       std::string pkl_type (Standard) const {
+               return "text/xml";
+       }
+
+       std::string asdcp_kind () const {
+               return "Subtitle";
+       }
+       
+private:
+       std::string font_id_to_name (std::string id) const;
+
+       struct ParseState {
+               std::list<boost::shared_ptr<Font> > font_nodes;
+               std::list<boost::shared_ptr<Text> > text_nodes;
+               std::list<boost::shared_ptr<Subtitle> > subtitle_nodes;
+       };
+
+       void maybe_add_subtitle (std::string text, ParseState const & parse_state);
+       
+       void examine_font_nodes (
+               boost::shared_ptr<const cxml::Node> xml,
+               std::list<boost::shared_ptr<Font> > const & font_nodes,
+               ParseState& parse_state
+               );
+       
+       void examine_text_nodes (
+               boost::shared_ptr<const cxml::Node> xml,
+               std::list<boost::shared_ptr<Text> > const & text_nodes,
+               ParseState& parse_state
+               );
+
+       boost::optional<std::string> _movie_title;
+       /* strangely, this is sometimes a string */
+       std::string _reel_number;
+       std::string _language;
+       std::list<boost::shared_ptr<LoadFont> > _load_font_nodes;
+
+       std::list<boost::shared_ptr<SubtitleString> > _subtitles;
+       bool _need_sort;
+};
+
+}
+
+#endif
diff --git a/src/subtitle_string.cc b/src/subtitle_string.cc
new file mode 100644 (file)
index 0000000..66869fd
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "subtitle_string.h"
+#include "xml.h"
+
+using std::string;
+using std::ostream;
+using namespace dcp;
+
+SubtitleString::SubtitleString (
+       string font,
+       bool italic,
+       Color color,
+       int size,
+       Time in,
+       Time out,
+       float v_position,
+       VAlign v_align,
+       string text,
+       Effect effect,
+       Color effect_color,
+       Time fade_up_time,
+       Time fade_down_time
+       )
+       : _font (font)
+       , _italic (italic)
+       , _color (color)
+       , _size (size)
+       , _in (in)
+       , _out (out)
+       , _v_position (v_position)
+       , _v_align (v_align)
+       , _text (text)
+       , _effect (effect)
+       , _effect_color (effect_color)
+       , _fade_up_time (fade_up_time)
+       , _fade_down_time (fade_down_time)
+{
+
+}
+
+int
+SubtitleString::size_in_pixels (int screen_height) const
+{
+       /* Size in the subtitle file is given in points as if the screen
+          height is 11 inches, so a 72pt font would be 1/11th of the screen
+          height.
+       */
+       
+       return _size * screen_height / (11 * 72);
+}
+
+bool
+dcp::operator== (SubtitleString const & a, SubtitleString const & b)
+{
+       return (
+               a.font() == b.font() &&
+               a.italic() == b.italic() &&
+               a.color() == b.color() &&
+               a.size() == b.size() &&
+               a.in() == b.in() &&
+               a.out() == b.out() &&
+               a.v_position() == b.v_position() &&
+               a.v_align() == b.v_align() &&
+               a.text() == b.text() &&
+               a.effect() == b.effect() &&
+               a.effect_color() == b.effect_color() &&
+               a.fade_up_time() == b.fade_up_time() &&
+               a.fade_down_time() == b.fade_down_time()
+               );
+}
+
+ostream&
+dcp::operator<< (ostream& s, SubtitleString const & sub)
+{
+       s << "\n`" << sub.text() << "' from " << sub.in() << " to " << sub.out() << ";\n"
+         << "fade up " << sub.fade_up_time() << ", fade down " << sub.fade_down_time() << ";\n"
+         << "font " << sub.font() << ", ";
+
+       if (sub.italic()) {
+               s << "italic";
+       } else {
+               s << "non-italic";
+       }
+       
+       s << ", size " << sub.size() << ", color " << sub.color() << ", vpos " << sub.v_position() << ", valign " << ((int) sub.v_align()) << ";\n"
+         << "effect " << ((int) sub.effect()) << ", effect color " << sub.effect_color();
+
+       return s;
+}
diff --git a/src/subtitle_string.h b/src/subtitle_string.h
new file mode 100644 (file)
index 0000000..03d3d4f
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/subtitle_string.h
+ *  @brief SubtitleString class.
+ */
+
+#ifndef LIBDCP_SUBTITLE_STRING_H
+#define LIBDCP_SUBTITLE_STRING_H
+
+#include "types.h"
+#include "dcp_time.h"
+#include <string>
+
+namespace dcp {
+
+/** @class SubtitleString
+ *  @brief A single line of subtitle text with all the associated attributes.
+ */
+class SubtitleString
+{
+public:
+       SubtitleString (
+               std::string font,
+               bool italic,
+               Color color,
+               int size,
+               Time in,
+               Time out,
+               float v_position,
+               VAlign v_align,
+               std::string text,
+               Effect effect,
+               Color effect_color,
+               Time fade_up_time,
+               Time fade_down_time
+               );
+
+       std::string font () const {
+               return _font;
+       }
+
+       bool italic () const {
+               return _italic;
+       }
+
+       Color color () const {
+               return _color;
+       }
+
+       Time in () const {
+               return _in;
+       }
+
+       Time out () const {
+               return _out;
+       }
+
+       std::string text () const {
+               return _text;
+       }
+
+       float v_position () const {
+               return _v_position;
+       }
+
+       VAlign v_align () const {
+               return _v_align;
+       }
+
+       Effect effect () const {
+               return _effect;
+       }
+
+       Color effect_color () const {
+               return _effect_color;
+       }
+
+       Time fade_up_time () const {
+               return _fade_up_time;
+       }
+
+       Time fade_down_time () const {
+               return _fade_down_time;
+       }
+
+       int size () const {
+               return _size;
+       }
+       
+       int size_in_pixels (int screen_height) const;
+
+private:
+       /** font name */
+       std::string _font;
+       /** true if the text is italic */
+       bool _italic;
+       /** text colour */
+       Color _color;
+       /** Size in points as if the screen height is 11 inches, so a 72pt font
+        *  would be 1/11th of the screen height.
+        */ 
+       int _size;
+       Time _in;
+       Time _out;
+       /** Vertical position as a proportion of the screen height from the top
+        *  (between 0 and 1)
+        */
+       float _v_position;
+       VAlign _v_align;
+       std::string _text;
+       Effect _effect;
+       Color _effect_color;
+       Time _fade_up_time;
+       Time _fade_down_time;
+};
+
+bool operator== (SubtitleString const & a, SubtitleString const & b);
+std::ostream& operator<< (std::ostream& s, SubtitleString const & sub);
+
+}
+
+#endif
diff --git a/src/text.cc b/src/text.cc
new file mode 100644 (file)
index 0000000..6fbdbe6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/text.cc
+ *  @brief Text class for parsing subtitle XML.
+ */
+
+#include "text.h"
+#include "xml.h"
+#include "font.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using boost::shared_ptr;
+using boost::optional;
+using namespace dcp;
+
+/** Read a &lt;Text&gt; node from a subtitle XML file, noting its contents
+ *  in this object's member variables.
+ *  @param node Node to read.
+ */
+Text::Text (boost::shared_ptr<const cxml::Node> node)
+       : v_align (CENTER)
+{
+       text = node->content ();
+       optional<float> x = node->optional_number_attribute<float> ("VPosition");
+       if (!x) {
+               x = node->number_attribute<float> ("Vposition");
+       }
+       v_position = x.get ();
+       
+       optional<string> v = node->optional_string_attribute ("VAlign");
+       if (v) {
+               v_align = string_to_valign (v.get ());
+       }
+
+       font_nodes = type_children<Font> (node, "Font");
+}
diff --git a/src/text.h b/src/text.h
new file mode 100644 (file)
index 0000000..268d146
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/text.h
+ *  @brief Text class for parsing subtitle XML.
+ */
+
+#include "types.h"
+#include <boost/shared_ptr.hpp>
+#include <list>
+
+namespace cxml {
+       class Node;
+}
+
+namespace dcp {
+
+class Font;
+
+/** @class Text
+ *  @brief Parser for Text nodes from subtitle XML.
+ */
+class Text
+{
+public:
+       /** Construct a default text node */
+       Text ()
+               : v_position (0)
+               , v_align (TOP)
+       {}
+       
+       Text (boost::shared_ptr<const cxml::Node> node);
+
+       float v_position;
+       VAlign v_align;
+       std::string text;
+       std::list<boost::shared_ptr<Font> > font_nodes;
+};
+
+}
index f45e3345b8b5894c62a14dbf5ed44de98333b7aa..fc6d50f3e0dec5c56953061ed6cd6e12b07b3f97 100644 (file)
 
 */
 
+#include "raw_convert.h"
+#include "types.h"
+#include "exceptions.h"
+#include <boost/algorithm/string.hpp>
 #include <vector>
 #include <cstdio>
 #include <iomanip>
-#include <boost/lexical_cast.hpp>
-#include <boost/algorithm/string.hpp>
-#include "types.h"
-#include "exceptions.h"
-#include "raw_convert.h"
 
 using namespace std;
-using namespace libdcp;
+using namespace dcp;
 using namespace boost;
 
+/** Construct a Fraction from a string of the form <numerator> <denominator>
+ *  e.g. "1 3".
+ */
 Fraction::Fraction (string s)
 {
        vector<string> b;
@@ -42,17 +44,18 @@ Fraction::Fraction (string s)
 }
 
 bool
-libdcp::operator== (Fraction const & a, Fraction const & b)
+dcp::operator== (Fraction const & a, Fraction const & b)
 {
        return (a.numerator == b.numerator && a.denominator == b.denominator);
 }
 
 bool
-libdcp::operator!= (Fraction const & a, Fraction const & b)
+dcp::operator!= (Fraction const & a, Fraction const & b)
 {
        return (a.numerator != b.numerator || a.denominator != b.denominator);
 }
 
+/** Construct a Color, initialising it to black. */
 Color::Color ()
        : r (0)
        , g (0)
@@ -61,6 +64,9 @@ Color::Color ()
 
 }
 
+/** Construct a Color from R, G and B.  The values run between
+ *  0 and 255.
+ */
 Color::Color (int r_, int g_, int b_)
        : r (r_)
        , g (g_)
@@ -104,7 +110,7 @@ Color::to_argb_string () const
  *  @param b Second color to compare.
  */
 bool
-libdcp::operator== (Color const & a, Color const & b)
+dcp::operator== (Color const & a, Color const & b)
 {
        return (a.r == b.r && a.g == b.g && a.b == b.b);
 }
@@ -114,20 +120,20 @@ libdcp::operator== (Color const & a, Color const & b)
  *  @param b Second color to compare.
  */
 bool
-libdcp::operator!= (Color const & a, Color const & b)
+dcp::operator!= (Color const & a, Color const & b)
 {
        return !(a == b);
 }
 
 ostream &
-libdcp::operator<< (ostream& s, Color const & c)
+dcp::operator<< (ostream& s, Color const & c)
 {
        s << "(" << c.r << ", " << c.g << ", " << c.b << ")";
        return s;
 }
 
 string
-libdcp::effect_to_string (Effect e)
+dcp::effect_to_string (Effect e)
 {
        switch (e) {
        case NONE:
@@ -142,7 +148,7 @@ libdcp::effect_to_string (Effect e)
 }
 
 Effect
-libdcp::string_to_effect (string s)
+dcp::string_to_effect (string s)
 {
        if (s == "none") {
                return NONE;
@@ -156,7 +162,7 @@ libdcp::string_to_effect (string s)
 }
 
 string
-libdcp::valign_to_string (VAlign v)
+dcp::valign_to_string (VAlign v)
 {
        switch (v) {
        case TOP:
@@ -171,7 +177,7 @@ libdcp::valign_to_string (VAlign v)
 }
 
 VAlign
-libdcp::string_to_valign (string s)
+dcp::string_to_valign (string s)
 {
        if (s == "top") {
                return TOP;
index 013c18630847bc02d5c9930466753b906542ecb2..ece7d44c284bb547ee18764103947b887c7d8aa0 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #ifndef LIBDCP_TYPES_H
 #define LIBDCP_TYPES_H
 
-#include <string>
 #include <boost/shared_ptr.hpp>
+#include <string>
 
-namespace libdcp
+namespace dcp
 {
 
 namespace parse {
@@ -85,12 +85,20 @@ enum Eye
        EYE_LEFT,
        EYE_RIGHT
 };
-       
+
+/** @class Fraction
+ *  @brief A fraction (i.e. a thing with an integer numerator and an integer denominator).
+ */
 class Fraction
 {
 public:
+       /** Construct a fraction of 0/0 */
        Fraction () : numerator (0), denominator (0) {}
        Fraction (std::string s);
+       /** Construct a fraction with a specified numerator and denominator.
+        *  @param n Numerator.
+        *  @param d Denominator.
+        */
        Fraction (int n, int d) : numerator (n), denominator (d) {}
 
        int numerator;
@@ -99,20 +107,33 @@ public:
 
 extern bool operator== (Fraction const & a, Fraction const & b);
 extern bool operator!= (Fraction const & a, Fraction const & b);
-       
-struct EqualityOptions {
+
+/** @struct EqualityOptions
+ *  @brief  A class to describe what "equality" means for a particular test.
+ *
+ *  When comparing things, we want to be able to ignore some differences;
+ *  this class expresses those differences.
+ */
+struct EqualityOptions
+{
+       /** Construct an EqualityOptions where nothing at all can differ */
        EqualityOptions () 
                : max_mean_pixel_error (0)
                , max_std_dev_pixel_error (0)
                , max_audio_sample_error (0)
-               , cpl_names_can_differ (false)
+               , cpl_annotation_texts_can_differ (false)
                , mxf_names_can_differ (false)
        {}
 
+       /** The maximum allowable mean difference in pixel value between two images */
        double max_mean_pixel_error;
+       /** The maximum standard deviation of the differences in pixel value between two images */
        double max_std_dev_pixel_error;
+       /** The maximum difference in audio sample value between two soundtracks */
        int max_audio_sample_error;
-       bool cpl_names_can_differ;
+       /** true if the <AnnotationText> nodes of CPLs are allowed to differ */
+       bool cpl_annotation_texts_can_differ;
+       /** true if MXF filenames are allowed to differ */
        bool mxf_names_can_differ;
 };
 
@@ -125,6 +146,11 @@ enum NoteType {
        NOTE
 };
 
+enum Standard {
+       INTEROP,
+       SMPTE
+};
+
 /** @class Color
  *  @brief An RGB color (aka colour).
  */
index a668b7fc9a5304d587b0ebeedb6094d8e9cc2e8c..3d37454f58a8b0061d7c69972ae06fef4ac4c450 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
  *  @brief Utility methods.
  */
 
-#include <stdexcept>
-#include <sstream>
-#include <iostream>
-#include <iomanip>
-#include <boost/filesystem.hpp>
-#include <boost/lexical_cast.hpp>
-#include <openssl/sha.h>
-#include <libxml++/nodes/element.h>
-#include <libxml++/document.h>
-#include <xmlsec/xmldsig.h>
-#include <xmlsec/dl.h>
-#include <xmlsec/app.h>
-#include <xmlsec/crypto.h>
-#include "KM_util.h"
-#include "KM_fileio.h"
-#include "AS_DCP.h"
 #include "util.h"
 #include "exceptions.h"
 #include "types.h"
 #include "certificates.h"
 #include "gamma_lut.h"
 #include "xyz_frame.h"
+#include "compose.hpp"
+#include "KM_util.h"
+#include "KM_fileio.h"
+#include "AS_DCP.h"
+#include <xmlsec/xmldsig.h>
+#include <xmlsec/dl.h>
+#include <xmlsec/app.h>
+#include <xmlsec/crypto.h>
+#include <libxml++/nodes/element.h>
+#include <libxml++/document.h>
+#include <openssl/sha.h>
+#include <boost/filesystem.hpp>
+#include <boost/algorithm/string.hpp>
+#include <stdexcept>
+#include <sstream>
+#include <iostream>
+#include <iomanip>
 
 using std::string;
 using std::wstring;
@@ -54,15 +55,18 @@ using std::max;
 using std::list;
 using std::setw;
 using std::setfill;
+using std::ostream;
 using boost::shared_ptr;
-using boost::lexical_cast;
-using namespace libdcp;
+using boost::optional;
+using boost::function;
+using boost::algorithm::trim;
+using namespace dcp;
 
 /** Create a UUID.
  *  @return UUID.
  */
 string
-libdcp::make_uuid ()
+dcp::make_uuid ()
 {
        char buffer[64];
        Kumu::UUID id;
@@ -74,15 +78,15 @@ libdcp::make_uuid ()
 
 /** Create a digest for a file.
  *  @param filename File name.
- *  @param progress Pointer to a progress reporting function, or 0.  The function will be called
+ *  @param progress Optional progress reporting function.  The function will be called
  *  with a progress value between 0 and 1.
  *  @return Digest.
  */
 string
-libdcp::make_digest (string filename, boost::function<void (float)>* progress)
+dcp::make_digest (boost::filesystem::path filename, function<void (float)> progress)
 {
        Kumu::FileReader reader;
-       Kumu::Result_t r = reader.OpenRead (filename.c_str ());
+       Kumu::Result_t r = reader.OpenRead (filename.string().c_str ());
        if (ASDCP_FAILURE (r)) {
                boost::throw_exception (FileError ("could not open file to compute digest", filename, r));
        }
@@ -108,7 +112,7 @@ libdcp::make_digest (string filename, boost::function<void (float)>* progress)
                SHA1_Update (&sha, read_buffer.Data(), read);
 
                if (progress) {
-                       (*progress) (float (done) / size);
+                       progress (float (done) / size);
                        done += read;
                }
        }
@@ -121,12 +125,12 @@ libdcp::make_digest (string filename, boost::function<void (float)>* progress)
 }
 
 /** Convert a content kind to a string which can be used in a
- *  <ContentKind> node.
+ *  &lt;ContentKind&gt; node.
  *  @param kind ContentKind.
  *  @return string.
  */
 string
-libdcp::content_kind_to_string (ContentKind kind)
+dcp::content_kind_to_string (ContentKind kind)
 {
        switch (kind) {
        case FEATURE:
@@ -154,35 +158,35 @@ libdcp::content_kind_to_string (ContentKind kind)
        assert (false);
 }
 
-/** Convert a string from a <ContentKind> node to a libdcp ContentKind.
+/** Convert a string from a &lt;ContentKind&gt; node to a libdcp ContentKind.
  *  Reasonably tolerant about varying case.
- *  @param type Content kind string.
+ *  @param kind Content kind string.
  *  @return libdcp ContentKind.
  */
-libdcp::ContentKind
-libdcp::content_kind_from_string (string type)
+dcp::ContentKind
+dcp::content_kind_from_string (string kind)
 {
-       transform (type.begin(), type.end(), type.begin(), ::tolower);
+       transform (kind.begin(), kind.end(), kind.begin(), ::tolower);
        
-       if (type == "feature") {
+       if (kind == "feature") {
                return FEATURE;
-       } else if (type == "short") {
+       } else if (kind == "short") {
                return SHORT;
-       } else if (type == "trailer") {
+       } else if (kind == "trailer") {
                return TRAILER;
-       } else if (type == "test") {
+       } else if (kind == "test") {
                return TEST;
-       } else if (type == "transitional") {
+       } else if (kind == "transitional") {
                return TRANSITIONAL;
-       } else if (type == "rating") {
+       } else if (kind == "rating") {
                return RATING;
-       } else if (type == "teaser") {
+       } else if (kind == "teaser") {
                return TEASER;
-       } else if (type == "policy") {
+       } else if (kind == "policy") {
                return POLICY;
-       } else if (type == "psa") {
+       } else if (kind == "psa") {
                return PUBLIC_SERVICE_ANNOUNCEMENT;
-       } else if (type == "advertisement") {
+       } else if (kind == "advertisement") {
                return ADVERTISEMENT;
        }
 
@@ -198,8 +202,8 @@ libdcp::content_kind_from_string (string type)
  *  This is useful for scaling 4K DCP images down to 2K.
  *  @return XYZ image.
  */
-shared_ptr<libdcp::XYZFrame>
-libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
+shared_ptr<dcp::XYZFrame>
+dcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
 {
        opj_dinfo_t* decoder = opj_create_decompress (CODEC_J2K);
        opj_dparameters_t parameters;
@@ -211,7 +215,7 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
        if (!image) {
                opj_destroy_decompress (decoder);
                opj_cio_close (cio);
-               boost::throw_exception (DCPReadError ("could not decode JPEG2000 codestream of " + lexical_cast<string> (size) + " bytes."));
+               boost::throw_exception (DCPReadError (String::compose ("could not decode JPEG2000 codestream of %1 bytes.", size)));
        }
 
        opj_destroy_decompress (decoder);
@@ -226,7 +230,7 @@ libdcp::decompress_j2k (uint8_t* data, int64_t size, int reduce)
  *  @return true if the string contains only space, newline or tab characters, or is empty.
  */
 bool
-libdcp::empty_or_white_space (string s)
+dcp::empty_or_white_space (string s)
 {
        for (size_t i = 0; i < s.length(); ++i) {
                if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
@@ -237,8 +241,11 @@ libdcp::empty_or_white_space (string s)
        return true;
 }
 
+/** Set up various bits that the library needs.  Should be called one
+ *  by client applications.
+ */
 void
-libdcp::init ()
+dcp::init ()
 {
        if (xmlSecInit() < 0) {
                throw MiscError ("could not initialise xmlsec");
@@ -259,21 +266,33 @@ libdcp::init ()
        }
 }
 
-bool libdcp::operator== (libdcp::Size const & a, libdcp::Size const & b)
+bool dcp::operator== (dcp::Size const & a, dcp::Size const & b)
 {
        return (a.width == b.width && a.height == b.height);
 }
 
-bool libdcp::operator!= (libdcp::Size const & a, libdcp::Size const & b)
+bool dcp::operator!= (dcp::Size const & a, dcp::Size const & b)
 {
        return !(a == b);
 }
 
-/** The base64 decode routine in KM_util.cpp gives different values to both
- *  this and the command-line base64 for some inputs.  Not sure why.
+ostream& dcp::operator<< (ostream& s, dcp::Size const & a)
+{
+       s << a.width << "x" << a.height;
+       return s;
+}
+
+/** Decode a base64 string.  The base64 decode routine in KM_util.cpp
+ *  gives different values to both this and the command-line base64
+ *  for some inputs.  Not sure why.
+ *
+ *  @param in base64-encoded string.
+ *  @param out Output buffer.
+ *  @param out_length Length of output buffer.
+ *  @return Number of characters written to the output buffer.
  */
 int
-libdcp::base64_decode (string const & in, unsigned char* out, int out_length)
+dcp::base64_decode (string const & in, unsigned char* out, int out_length)
 {
        BIO* b64 = BIO_new (BIO_f_base64 ());
 
@@ -297,68 +316,55 @@ libdcp::base64_decode (string const & in, unsigned char* out, int out_length)
        return N;
 }
 
-string
-libdcp::tm_to_string (struct tm* tm)
+/** @param p Path to open.
+ *  @param t mode flags, as for fopen(3).
+ *  @return FILE pointer or 0 on error.
+ *
+ *  Apparently there is no way to create an ofstream using a UTF-8
+ *  filename under Windows.  We are hence reduced to using fopen
+ *  with this wrapper.
+ */
+FILE *
+dcp::fopen_boost (boost::filesystem::path p, string t)
 {
-       char buffer[64];
-       strftime (buffer, 64, "%Y-%m-%dT%H:%M:%S", tm);
-
-       int offset = 0;
-
-#ifdef LIBDCP_POSIX
-       offset = tm->tm_gmtoff / 60;
+#ifdef LIBDCP_WINDOWS
+        wstring w (t.begin(), t.end());
+       /* c_str() here should give a UTF-16 string */
+        return _wfopen (p.c_str(), w.c_str ());
 #else
-       TIME_ZONE_INFORMATION tz;
-       GetTimeZoneInformation (&tz);
-       offset = tz.Bias;
+        return fopen (p.c_str(), t.c_str ());
 #endif
-       
-       return string (buffer) + utc_offset_to_string (offset);
 }
 
-/** @param b Offset from UTC to local time in minutes.
- *  @return string of the form e.g. -01:00.
- */
-string
-libdcp::utc_offset_to_string (int b)
+optional<boost::filesystem::path>
+dcp::relative_to_root (boost::filesystem::path root, boost::filesystem::path file)
 {
-       bool const negative = (b < 0);
-       b = negative ? -b : b;
+       boost::filesystem::path::const_iterator i = root.begin ();
+       boost::filesystem::path::const_iterator j = file.begin ();
 
-       int const hours = b / 60;
-       int const minutes = b % 60;
+       while (i != root.end() && j != file.end() && *i == *j) {
+               ++i;
+               ++j;
+       }
 
-       stringstream o;
-       if (negative) {
-               o << "-";
-       } else {
-               o << "+";
+       if (i != root.end ()) {
+               return optional<boost::filesystem::path> ();
        }
 
-       o << setw(2) << setfill('0') << hours << ":" << setw(2) << setfill('0') << minutes;
-       return o.str ();
-}
+       boost::filesystem::path rel;
+       while (j != file.end ()) {
+               rel /= *j++;
+       }
 
-string
-libdcp::ptime_to_string (boost::posix_time::ptime t)
-{
-       struct tm t_tm = boost::posix_time::to_tm (t);
-       return tm_to_string (&t_tm);
+       return rel;
 }
 
-
-/* Apparently there is no way to create an ofstream using a UTF-8
-   filename under Windows.  We are hence reduced to using fopen
-   with this wrapper.
-*/
-FILE *
-libdcp::fopen_boost (boost::filesystem::path p, string t)
+bool
+dcp::ids_equal (string a, string b)
 {
-#ifdef LIBDCP_WINDOWS
-        wstring w (t.begin(), t.end());
-       /* c_str() here should give a UTF-16 string */
-        return _wfopen (p.c_str(), w.c_str ());
-#else
-        return fopen (p.c_str(), t.c_str ());
-#endif
+       transform (a.begin(), a.end(), a.begin(), ::tolower);
+       transform (b.begin(), b.end(), b.begin(), ::tolower);
+       trim (a);
+       trim (b);
+       return a == b;
 }
index 2a6aae1b004de20443ab631e34ad8c4fa2c415ad..d3f212c7531fbd51785e1856e40eaa309a5dfc96 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
  *  @brief Utility methods.
  */
 
-#include <string>
-#include <stdint.h>
+#include "types.h"
 #include <boost/shared_ptr.hpp>
 #include <boost/function.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/filesystem.hpp>
+#include <boost/optional.hpp>
 #include <openjpeg.h>
-#include "types.h"
+#include <string>
+#include <stdint.h>
 
 namespace xmlpp {
        class Element;
 }
 
-namespace libdcp {
+namespace dcp {
 
 class ARGBFrame;
 class CertificateChain;
 class GammaLUT;
 class XYZFrame;
 
-struct Size {
+/** @struct Size
+ *  @brief The integer, two-dimensional size of something.
+ */
+struct Size
+{
        Size ()
                : width (0)
                , height (0)
@@ -65,26 +69,40 @@ struct Size {
        
 extern bool operator== (Size const & a, Size const & b);
 extern bool operator!= (Size const & a, Size const & b);
+extern std::ostream& operator<< (std::ostream& s, Size const & a);
 
 extern std::string make_uuid ();
-extern std::string make_digest (std::string filename, boost::function<void (float)> *);
+extern std::string make_digest (boost::filesystem::path filename, boost::function<void (float)>);
 extern std::string content_kind_to_string (ContentKind kind);
 extern ContentKind content_kind_from_string (std::string kind);
 extern bool empty_or_white_space (std::string s);
 extern boost::shared_ptr<XYZFrame> decompress_j2k (uint8_t* data, int64_t size, int reduce);
+extern bool ids_equal (std::string a, std::string b);
 
 extern void init ();
 
-extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, bool interop);
+extern void sign (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, Standard standard);
 extern void add_signature_value (xmlpp::Element* parent, CertificateChain const & certificates, boost::filesystem::path signer_key, std::string const & ns);
 extern void add_signer (xmlpp::Element* parent, CertificateChain const & certificates, std::string const & ns);
 
 extern int base64_decode (std::string const & in, unsigned char* out, int out_length);
-
-extern std::string tm_to_string (struct tm *);
-extern std::string utc_offset_to_string (int);
-extern std::string ptime_to_string (boost::posix_time::ptime);
+extern boost::optional<boost::filesystem::path> relative_to_root (boost::filesystem::path root, boost::filesystem::path file);
 extern FILE * fopen_boost (boost::filesystem::path, std::string);
+
+template <class F, class T>
+std::list<boost::shared_ptr<T> >
+list_of_type (std::list<boost::shared_ptr<F> > const & from)
+{
+       std::list<boost::shared_ptr<T> > out;
+       for (typename std::list<boost::shared_ptr<F> >::const_iterator i = from.begin(); i != from.end(); ++i) {
+               boost::shared_ptr<T> check = boost::dynamic_pointer_cast<T> (*i);
+               if (check) {
+                       out.push_back (check);
+               }
+       }
+
+       return out;
+}
        
 }
 
index 52abd13a746e3864daa886ba1d1101ac6c3d4092..9b6dc65ff633d423be9cd38f0ff3c5edc7c1ba80 100644 (file)
@@ -1,5 +1,5 @@
 
-namespace libdcp {
+namespace dcp {
 
 extern char const * version;
 extern char const * git_commit;
index 8923455f1dffa044539ecd68a138d8b404cc803e..e5f0c2a51aa8a0460d2fd90bd03c761637e935b8 100644 (file)
@@ -6,51 +6,64 @@ def build(bld):
     else:
         obj = bld(features = 'cxx cxxshlib')
 
-    obj.name = 'libdcp'
-    obj.target = 'dcp'
+    obj.name = 'libdcp%s' % bld.env.API_VERSION
+    obj.target = 'dcp%s' % bld.env.API_VERSION
     obj.export_includes = ['.']
     obj.uselib = 'BOOST_FILESYSTEM BOOST_SIGNALS2 BOOST_DATETIME OPENSSL SIGC++ LIBXML++ OPENJPEG CXML XMLSEC1'
-    obj.use = 'libkumu-libdcp libasdcp-libdcp'
+    obj.use = 'libkumu-libdcp%s libasdcp-libdcp%s' % (bld.env.API_VERSION, bld.env.API_VERSION)
     obj.source = """
                  argb_frame.cc
                  asset.cc
                  certificates.cc
                  colour_matrix.cc
+                 content.cc
                  cpl.cc
                  dcp.cc        
                  dcp_time.cc
+                 decrypted_kdm.cc
+                 decrypted_kdm_key.cc
+                 encrypted_kdm.cc
                  exceptions.cc
+                 file.cc
+                 font.cc
                  gamma_lut.cc
                  image.cc
-                 kdm.cc
                  key.cc
+                 load_font.cc
+                 local_time.cc
                  metadata.cc
-                 mono_picture_asset.cc
-                 mono_picture_asset_writer.cc
+                 mono_picture_mxf.cc
+                 mono_picture_mxf_writer.cc
                  mono_picture_frame.cc
-                 mxf_asset.cc
-                 picture_asset.cc
-                 picture_asset_writer.cc
-                 rec709_linearised_gamma_lut.cc
+                 mxf.cc
+                 mxf_writer.cc
+                 object.cc
+                 picture_mxf.cc
+                 picture_mxf_writer.cc
                  reel.cc
+                 reel_asset.cc
+                 reel_mono_picture_asset.cc
+                 reel_picture_asset.cc
+                 reel_sound_asset.cc
+                 reel_stereo_picture_asset.cc
+                 reel_subtitle_asset.cc
                  rgb_xyz.cc
                  signer.cc
                  signer_chain.cc
-                 sound_asset.cc
+                 sound_mxf.cc
+                 sound_mxf_writer.cc
                  sound_frame.cc
-                 srgb_linearised_gamma_lut.cc
-                 stereo_picture_asset.cc
-                 stereo_picture_asset_writer.cc
+                 stereo_picture_mxf.cc
+                 stereo_picture_mxf_writer.cc
                  stereo_picture_frame.cc
-                 subtitle_asset.cc
+                 subtitle.cc
+                 subtitle_content.cc
+                 subtitle_string.cc
+                 text.cc
                  types.cc
                  util.cc
                  version.cc
                  xyz_frame.cc
-                 parse/asset_map.cc
-                 parse/cpl.cc
-                 parse/pkl.cc
-                 parse/subtitle.cc
                  """
 
     headers = """
@@ -58,40 +71,52 @@ def build(bld):
               certificates.h
               colour_matrix.h
               cpl.h
+              content.h
               dcp.h
               dcp_time.h
+              decrypted_kdm.h
+              decrypted_kdm_key.h
+              encrypted_kdm.h
               exceptions.h
               gamma_lut.h
               image.h
-              kdm.h
               key.h
-              lut.h
+              local_time.h
               lut_cache.h
               metadata.h
-              mono_picture_asset.h
+              mono_picture_mxf.h
               mono_picture_frame.h
-              mxf_asset.h
-              picture_asset.h
-              picture_asset_writer.h
+              mxf.h
+              mxf_writer.h
+              object.h
+              picture_mxf.h
+              picture_mxf_writer.h
               raw_convert.h
               rgb_xyz.h
-              rec709_linearised_gamma_lut.h
               reel.h
+              reel_asset.h
+              reel_mono_picture_asset.h
+              reel_picture_asset.h
+              reel_sound_asset.h
+              reel_stereo_picture_asset.h
+              ref.h
               argb_frame.h
               signer.h
               signer_chain.h
-              sound_asset.h
               sound_frame.h
-              srgb_linearised_gamma_lut.h
-              stereo_picture_asset.h
+              sound_mxf.h
+              sound_mxf_writer.h
+              stereo_picture_mxf.h
               stereo_picture_frame.h
-              subtitle_asset.h
+              subtitle.h
+              subtitle_content.h
+              subtitle_string.h
               types.h
               util.h
               version.h
               xyz_frame.h
               """
 
-    bld.install_files('${PREFIX}/include/libdcp', headers)
+    bld.install_files('${PREFIX}/include/libdcp%s/dcp' % bld.env.API_VERSION, headers)
     if bld.env.STATIC:
-        bld.install_files('${PREFIX}/lib', 'libdcp.a')
+        bld.install_files('${PREFIX}/lib', 'libdcp%s.a' % bld.env.API_VERSION)
index 5978ff7ec10d7346a941d5ebbb71297c3fc5b33c..b89d8ccdfb7db41b28d6c72f638c4537a1ac7992 100644 (file)
--- a/src/xml.h
+++ b/src/xml.h
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #ifndef LIBDCP_XML_H
 #define LIBDCP_XML_H
 
-#include <libcxml/cxml.h>
 #include "exceptions.h"
+#include <libcxml/cxml.h>
 
-namespace libdcp
+namespace dcp
 {
 
 template <class T>
diff --git a/src/xml/kdm_smpte.h b/src/xml/kdm_smpte.h
deleted file mode 100644 (file)
index 32a297f..0000000
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/xml/kdm_smpte.h
- *  @brief 1:1ish C++ representations of the XML schema for a SMPTE KDM.
- *
- *  This file contains classes which map pretty-much 1:1 to the elements in a SMPTE KDM
- *  (Key Delivery Message).  The `main' KDM class contains a pointer to a DCinemaSecurityMessage
- *  from this file.
- *
- *  This should probably have been automatically generated from the XSD,
- *  but I think it's too much trouble considering that the XSD does not
- *  change very often.
- */
-
-#ifndef LIBDCP_XML_KDM_SMPTE_H
-#define LIBDCP_XML_KDM_SMPTE_H
-
-#include <string>
-#include <list>
-#include <boost/optional.hpp>
-#include <boost/filesystem.hpp>
-#include <libxml/parser.h>
-#include <libxml++/libxml++.h>
-#include <libcxml/cxml.h>
-#include "../exceptions.h"
-
-namespace libdcp {
-namespace xml {
-
-class Writer
-{
-public:
-       Writer ()
-               : document (new xmlpp::Document)
-       {}
-       
-       boost::shared_ptr<xmlpp::Document> document;
-       std::map<std::string, xmlpp::Attribute *> references;
-};
-
-class Signer
-{
-public:
-       Signer () {}
-       Signer (boost::shared_ptr<const cxml::Node> node)
-               : x509_issuer_name (node->string_child ("X509IssuerName"))
-               , x509_serial_number (node->string_child ("X509SerialNumber"))
-       {
-               node->done ();
-       }
-
-       void as_xml (xmlpp::Element* node) const
-       {
-               node->add_child("X509IssuerName", "ds")->add_child_text (x509_issuer_name);
-               node->add_child("X509SerialNumber", "ds")->add_child_text (x509_serial_number);
-       }
-       
-       std::string x509_issuer_name;
-       std::string x509_serial_number;
-};
-
-class Recipient
-{
-public:
-       Recipient () {}
-       Recipient (boost::shared_ptr<const cxml::Node> node)
-               : x509_issuer_serial (node->node_child ("X509IssuerSerial"))
-               , x509_subject_name (node->string_child ("X509SubjectName"))
-       {
-               node->done ();
-       }
-
-       void as_xml (xmlpp::Element* node) const
-       {
-               x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial"));
-               node->add_child("X509SubjectName")->add_child_text (x509_subject_name);
-       }
-       
-       Signer x509_issuer_serial;
-       std::string x509_subject_name;
-};
-
-class AuthorizedDeviceInfo
-{
-public:
-       AuthorizedDeviceInfo () {}
-       AuthorizedDeviceInfo (boost::shared_ptr<const cxml::Node> node)
-               : device_list_identifier (node->string_child ("DeviceListIdentifier"))
-               , device_list_description (node->string_child ("DeviceListDescription"))
-       {
-               std::list<boost::shared_ptr<cxml::Node> > ct = node->node_child("DeviceList")->node_children("CertificateThumbprint");
-               for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = ct.begin(); i != ct.end(); ++i) {
-                       device_list.push_back ((*i)->content ());
-               }
-
-               node->done ();
-       }
-       
-       void as_xml (xmlpp::Element* node) const
-       {
-               node->add_child ("DeviceListIdentifier")->add_child_text (device_list_identifier);
-               node->add_child ("DeviceListDescription")->add_child_text (device_list_description);
-               xmlpp::Element* dl = node->add_child ("DeviceList");
-               for (std::list<std::string>::const_iterator i = device_list.begin(); i != device_list.end(); ++i) {
-                       dl->add_child("CertificateThumbprint")->add_child_text (*i);
-               }
-       }
-       
-       std::string device_list_identifier;
-       std::string device_list_description;
-       std::list<std::string> device_list;
-};
-
-class TypedKeyId
-{
-public:
-       TypedKeyId () {}
-
-       TypedKeyId (std::string t, std::string i)
-               : key_type (t)
-               , key_id (i)
-       {}
-       
-       TypedKeyId (boost::shared_ptr<const cxml::Node> node)
-               : key_type (node->string_child ("KeyType"))
-               , key_id (node->string_child ("KeyId"))
-       {
-               node->done ();
-       }
-       
-       void as_xml (xmlpp::Element* node) const
-       {
-               node->add_child("KeyType")->add_child_text (key_type);
-               node->add_child("KeyId")->add_child_text (key_id);
-       }
-
-       std::string key_type;
-       std::string key_id;
-};
-
-class AuthenticatedPublic
-{
-public:
-       AuthenticatedPublic () {}
-       AuthenticatedPublic (boost::shared_ptr<const cxml::Node> node)
-               : message_id (node->string_child ("MessageId"))
-               , message_type (node->string_child ("MessageType"))
-               , annotation_text (node->optional_string_child ("AnnotationText"))
-               , issue_date (node->string_child ("IssueDate"))
-               , signer (node->node_child ("Signer"))
-       {
-               boost::shared_ptr<const cxml::Node> c = node->node_child ("RequiredExtensions");
-               c = c->node_child ("KDMRequiredExtensions");
-               recipient = Recipient (c->node_child ("Recipient"));
-               composition_playlist_id = c->string_child ("CompositionPlaylistId");
-               content_authenticator = c->optional_string_child ("ContentAuthenticator");
-               content_title_text = c->string_child ("ContentTitleText");
-               content_keys_not_valid_before = c->string_child ("ContentKeysNotValidBefore");
-               content_keys_not_valid_after = c->string_child ("ContentKeysNotValidAfter");
-               authorized_device_info = AuthorizedDeviceInfo (c->node_child ("AuthorizedDeviceInfo"));
-               
-               std::list<boost::shared_ptr<cxml::Node> > kil = c->node_child("KeyIdList")->node_children("TypedKeyId");
-               for (std::list<boost::shared_ptr<cxml::Node> >::iterator i = kil.begin(); i != kil.end(); ++i) {
-                       key_id_list.push_back (TypedKeyId (*i));
-               }
-               
-               boost::shared_ptr<cxml::Node> fmfl = c->optional_node_child("ForensicMarkFlagList");
-               if (fmfl) {
-                       std::list<boost::shared_ptr<cxml::Node> > fmf = fmfl->node_children("ForensicMarkFlag");
-                       for (std::list<boost::shared_ptr<cxml::Node> >::iterator i = fmf.begin(); i != fmf.end(); ++i) {
-                               forensic_mark_flag_list.push_back ((*i)->content ());
-                       }
-               }
-               
-               node->ignore_child ("NonCriticalExtensions");
-               node->done ();
-       }
-
-       void as_xml (Writer& writer, xmlpp::Element* node) const
-       {
-               writer.references["ID_AuthenticatedPublic"] = node->set_attribute ("Id", "ID_AuthenticatedPublic");
-               
-               node->add_child("MessageId")->add_child_text (message_id);
-               node->add_child("MessageType")->add_child_text (message_type);
-               if (annotation_text) {
-                       node->add_child("AnnotationText")->add_child_text (annotation_text.get ());
-               }
-               node->add_child("IssueDate")->add_child_text (issue_date);
-               signer.as_xml (node->add_child("Signer"));
-               
-               xmlpp::Element* kdm_required_extensions = node->add_child("RequiredExtensions")->add_child("KDMRequiredExtensions");
-               kdm_required_extensions->set_attribute ("xmlns", "http://www.smpte-ra.org/schemas/430-1/2006/KDM");
-               recipient.as_xml (kdm_required_extensions->add_child ("Recipient"));
-               
-               kdm_required_extensions->add_child("CompositionPlaylistId")->add_child_text (composition_playlist_id);
-               if (content_authenticator) {
-                       kdm_required_extensions->add_child("ContentAuthenticator")->add_child_text (content_authenticator.get ());
-               }
-               kdm_required_extensions->add_child("ContentTitleText")->add_child_text (content_title_text);
-               kdm_required_extensions->add_child("ContentKeysNotValidBefore")->add_child_text (content_keys_not_valid_before);
-               kdm_required_extensions->add_child("ContentKeysNotValidAfter")->add_child_text (content_keys_not_valid_after);
-               authorized_device_info.as_xml (kdm_required_extensions->add_child("AuthorizedDeviceInfo"));
-               
-               xmlpp::Element* kil = kdm_required_extensions->add_child("KeyIdList");
-               for (std::list<TypedKeyId>::const_iterator i = key_id_list.begin(); i != key_id_list.end(); ++i) {
-                       i->as_xml (kil->add_child ("TypedKeyId"));
-               }
-               
-               xmlpp::Element* fmfl = kdm_required_extensions->add_child ("ForensicMarkFlagList");
-               for (std::list<std::string>::const_iterator i = forensic_mark_flag_list.begin(); i != forensic_mark_flag_list.end(); ++i) {
-                       fmfl->add_child("ForensicMarkFlag")->add_child_text (*i);
-               }
-               
-               node->add_child ("NonCriticalExtensions");
-       }
-       
-       std::string message_id;
-       std::string message_type;
-       boost::optional<std::string> annotation_text;
-       std::string issue_date;
-       Signer signer;
-       Recipient recipient;
-       std::string composition_playlist_id;
-       boost::optional<std::string> content_authenticator;
-       std::string content_title_text;
-       std::string content_keys_not_valid_before;
-       std::string content_keys_not_valid_after;
-       AuthorizedDeviceInfo authorized_device_info;
-       std::list<TypedKeyId> key_id_list;
-       std::list<std::string> forensic_mark_flag_list;
-};
-
-class AuthenticatedPrivate
-{
-public:
-       AuthenticatedPrivate () {}
-       
-       AuthenticatedPrivate (boost::shared_ptr<const cxml::Node> node)
-       {
-               std::list<boost::shared_ptr<cxml::Node> > ek = node->node_children ("EncryptedKey");
-               for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = ek.begin(); i != ek.end(); ++i) {
-                       encrypted_keys.push_back ((*i)->node_child("CipherData")->string_child("CipherValue"));
-               }
-               
-               node->done ();
-       }
-       
-       void as_xml (Writer& writer, xmlpp::Element* node) const
-       {
-               writer.references["ID_AuthenticatedPrivate"] = node->set_attribute ("Id", "ID_AuthenticatedPrivate");
-               
-               for (std::list<std::string>::const_iterator i = encrypted_keys.begin(); i != encrypted_keys.end(); ++i) {
-                       xmlpp::Element* encrypted_key = node->add_child ("EncryptedKey", "enc");
-                       xmlpp::Element* encryption_method = encrypted_key->add_child ("EncryptionMethod", "enc");
-                       encryption_method->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p");
-                       xmlpp::Element* digest_method = encryption_method->add_child ("DigestMethod", "ds");
-                       digest_method->set_attribute ("Algorithm", "http://www.w3.org/2000/09/xmldsig#sha1");
-                       xmlpp::Element* cipher_data = encrypted_key->add_child ("CipherData", "enc");
-                       cipher_data->add_child("CipherValue", "enc")->add_child_text (*i);
-               }
-       }       
-       
-       std::list<std::string> encrypted_keys;
-};
-
-class X509Data
-{
-public:
-       X509Data () {}
-       X509Data (boost::shared_ptr<const cxml::Node> node)
-               : x509_issuer_serial (Signer (node->node_child ("X509IssuerSerial")))
-               , x509_certificate (node->string_child ("X509Certificate"))
-       {
-               node->done ();
-       }
-
-       void as_xml (xmlpp::Element* node) const
-       {
-               x509_issuer_serial.as_xml (node->add_child ("X509IssuerSerial", "ds"));
-               node->add_child("X509Certificate", "ds")->add_child_text (x509_certificate);
-       }
-       
-       Signer x509_issuer_serial;
-       std::string x509_certificate;
-};
-
-class Reference
-{
-public:
-       Reference () {}
-       Reference (std::string u)
-               : uri (u)
-       {}
-                 
-       Reference (boost::shared_ptr<const cxml::Node> node)
-               : uri (node->string_attribute ("URI"))
-               , digest_value (node->string_child ("DigestValue"))
-       {
-               node->ignore_child ("DigestMethod");
-               node->done ();
-       }
-
-       void as_xml (xmlpp::Element* node) const
-       {
-               xmlpp::Element* reference = node->add_child ("Reference", "ds");
-               reference->set_attribute ("URI", uri);
-               reference->add_child("DigestMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmlenc#sha256");
-               reference->add_child("DigestValue", "ds")->add_child_text (digest_value);
-       }
-       
-       std::string uri;
-       std::string digest_value;
-};
-
-class Signature
-{
-public:
-       Signature ()
-               : authenticated_public ("#ID_AuthenticatedPublic")
-               , authenticated_private ("#ID_AuthenticatedPrivate")
-       {}
-       
-       Signature (boost::shared_ptr<const cxml::Node> node)
-       {
-               std::list<boost::shared_ptr<cxml::Node> > refs = node->node_child("SignedInfo")->node_children ("Reference");
-               for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = refs.begin(); i != refs.end(); ++i) {
-                       if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPublic") {
-                               authenticated_public = Reference (*i);
-                       } else if ((*i)->string_attribute("URI") == "#ID_AuthenticatedPrivate") {
-                               authenticated_private = Reference (*i);
-                       } else {
-                               throw XMLError ("unrecognised reference URI");
-                       }
-               }
-               
-               std::list<boost::shared_ptr<cxml::Node> > data = node->node_child("KeyInfo")->node_children ("X509Data");
-               for (std::list<boost::shared_ptr<cxml::Node> >::const_iterator i = data.begin(); i != data.end(); ++i) {
-                       key_info.push_back (X509Data (*i));
-               }
-
-               signature_value = node->string_child ("SignatureValue");
-               
-               node->done ();
-       }
-       
-       void as_xml (xmlpp::Element* node) const
-       {
-               xmlpp::Element* si = node->add_child ("SignedInfo", "ds");
-               si->add_child ("CanonicalizationMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments");
-               si->add_child ("SignatureMethod", "ds")->set_attribute ("Algorithm", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256");
-               
-               authenticated_public.as_xml (si);
-               authenticated_private.as_xml (si);
-               
-               node->add_child("SignatureValue", "ds")->add_child_text (signature_value);
-               
-               xmlpp::Element* ki = node->add_child ("KeyInfo", "ds");
-               for (std::list<X509Data>::const_iterator i = key_info.begin(); i != key_info.end(); ++i) {
-                       i->as_xml (ki->add_child ("X509Data", "ds"));
-               }
-       }
-
-       Reference authenticated_public;
-       Reference authenticated_private;
-       std::string signature_value;
-       std::list<X509Data> key_info;
-};
-
-class DCinemaSecurityMessage
-{
-public:
-       DCinemaSecurityMessage () {}
-       DCinemaSecurityMessage (boost::filesystem::path file)
-       {
-               cxml::Document f ("DCinemaSecurityMessage");
-               f.read_file (file.string ());
-               
-               authenticated_public = AuthenticatedPublic (f.node_child ("AuthenticatedPublic"));
-               authenticated_private = AuthenticatedPrivate (f.node_child ("AuthenticatedPrivate"));
-               signature = Signature (f.node_child ("Signature"));
-               
-               f.done ();
-       }
-       
-       boost::shared_ptr<xmlpp::Document> as_xml () const
-       {
-               Writer writer;
-               
-               xmlpp::Element* root = writer.document->create_root_node ("DCinemaSecurityMessage", "http://www.smpte-ra.org/schemas/430-3/2006/ETM");
-               root->set_namespace_declaration ("http://www.w3.org/2000/09/xmldsig#", "ds");
-               root->set_namespace_declaration ("http://www.w3.org/2001/04/xmlenc#", "enc");
-               
-               authenticated_public.as_xml (writer, root->add_child ("AuthenticatedPublic"));
-               authenticated_private.as_xml (writer, root->add_child ("AuthenticatedPrivate"));
-               signature.as_xml (root->add_child ("Signature", "ds"));
-
-               for (std::map<std::string, xmlpp::Attribute*>::const_iterator i = writer.references.begin(); i != writer.references.end(); ++i) {
-                       xmlAddID (0, writer.document->cobj(), (const xmlChar *) i->first.c_str(), i->second->cobj ());
-               }
-               
-               return writer.document;
-       }
-
-       AuthenticatedPublic authenticated_public;
-       AuthenticatedPrivate authenticated_private;
-       Signature signature;
-};
-
-}
-}
-
-#endif
-       
index f5b0ee86cfb1113ba574b92297e88493736f8ec1..cba908836ad0b5126d884756cf370a9b84285cf4 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/xyz_frame.cc
+ *  @brief XZYFrame class.
+ */
+
+#include "xyz_frame.h"
 #include <cassert>
 #include <stdexcept>
-#include "xyz_frame.h"
 
-using namespace libdcp;
+using namespace dcp;
 
 /** Construct an XYZFrame, taking ownership of the opj_image_t */
 XYZFrame::XYZFrame (opj_image_t* image)
@@ -30,6 +34,9 @@ XYZFrame::XYZFrame (opj_image_t* image)
        assert (_opj_image->numcomps == 3);
 }
 
+/** Construct a new XYZFrame with undefined contents.
+ *  @param size Size for the frame in pixels.
+ */
 XYZFrame::XYZFrame (Size size)
 {
        opj_image_cmptparm_t cmptparm[3];
@@ -58,11 +65,15 @@ XYZFrame::XYZFrame (Size size)
        _opj_image->y1 = size.height;
 }
 
+/** XYZFrame destructor */
 XYZFrame::~XYZFrame ()
 {
        opj_image_destroy (_opj_image);
 }
 
+/** @param c Component index (0, 1 or 2)
+ *  @return Pointer to the data for component c.
+ */
 int *
 XYZFrame::data (int c) const
 {
@@ -70,9 +81,10 @@ XYZFrame::data (int c) const
        return _opj_image->comps[c].data;
 }
 
-libdcp::Size
+/** @return Size of the image in pixels */
+dcp::Size
 XYZFrame::size () const
 {
        /* XXX: this may not be right; x0 and y0 can presumably be non-zero */
-       return libdcp::Size (_opj_image->x1, _opj_image->y1);
+       return dcp::Size (_opj_image->x1, _opj_image->y1);
 }
index a4dcb2c028c0d0c9b25b434783d2686656c66b79..8ba829b2c6302ecde5e08ca3bc6c2b07ae6409f3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <openjpeg.h>
+/** @file  src/xyz_frame.h
+ *  @brief XZYFrame class.
+ */
+
 #include "util.h"
+#include <openjpeg.h>
 
-namespace libdcp {
+namespace dcp {
 
-class XYZFrame
+/** @class XYZFrame
+ *  @brief An image in XYZ colour.
+ *
+ *  This class is a thin wrapper of libopenjpeg's opj_image_t.
+ */
+class XYZFrame : public boost::noncopyable
 {
 public:
        XYZFrame (opj_image_t *);
@@ -30,14 +39,17 @@ public:
        ~XYZFrame ();
 
        int* data (int) const;
-       libdcp::Size size () const;
+       dcp::Size size () const;
 
+       /** @return Pointer to opj_image_t struct.  The caller
+        *  must not delete this.
+        */
        opj_image_t* opj_image () const {
                return _opj_image;
        }
 
 private:
-       opj_image_t* _opj_image;
+       opj_image_t* _opj_image; ///< opj_image_t that we are managing
 };
 
 }
index 0a66bc66fc26d6c91b50b7eea850a1de0e5915fa..690afa774a6c6c19f6e64667b218c88c1516562c 100644 (file)
@@ -25,15 +25,15 @@ using boost::shared_ptr;
 
 BOOST_AUTO_TEST_CASE (certificates)
 {
-       libdcp::CertificateChain c;
+       dcp::CertificateChain c;
 
-       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
-       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
-       c.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
+       c.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
+       c.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
+       c.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
 
-       list<shared_ptr<libdcp::Certificate> > leaf_to_root = c.leaf_to_root ();
+       list<shared_ptr<dcp::Certificate> > leaf_to_root = c.leaf_to_root ();
 
-       list<shared_ptr<libdcp::Certificate> >::iterator i = leaf_to_root.begin ();
+       list<shared_ptr<dcp::Certificate> >::iterator i = leaf_to_root.begin ();
 
        /* Leaf */
        BOOST_CHECK_EQUAL (*i, c.leaf ());
@@ -78,6 +78,6 @@ BOOST_AUTO_TEST_CASE (certificates)
                );
 
        /* Check that reconstruction from a string works */
-       libdcp::Certificate test (c.root()->certificate (true));
+       dcp::Certificate test (c.root()->certificate (true));
        BOOST_CHECK_EQUAL (test.certificate(), c.root()->certificate());
 }
index 30a96a0dd78369ac8403a747fbeb6e5882c73b0f..d68f59d43a8670b8ec32807921c0974ae15623db 100644 (file)
 
 #include "util.h"
 
-/* Check that libdcp::Color works */
+/* Check that dcp::Color works */
 BOOST_AUTO_TEST_CASE (color)
 {
-       libdcp::Color c ("FFFF0000");
+       dcp::Color c ("FFFF0000");
 
        BOOST_CHECK_EQUAL (c.r, 255);
        BOOST_CHECK_EQUAL (c.g, 0);
        BOOST_CHECK_EQUAL (c.b, 0);
        BOOST_CHECK_EQUAL (c.to_argb_string(), "FFFF0000");
 
-       c = libdcp::Color ("FF00FF00");
+       c = dcp::Color ("FF00FF00");
 
        BOOST_CHECK_EQUAL (c.r, 0);
        BOOST_CHECK_EQUAL (c.g, 255);
        BOOST_CHECK_EQUAL (c.b, 0);
        BOOST_CHECK_EQUAL (c.to_argb_string(), "FF00FF00");
 
-       c = libdcp::Color ("FF0000FF");
+       c = dcp::Color ("FF0000FF");
 
        BOOST_CHECK_EQUAL (c.r, 0);
        BOOST_CHECK_EQUAL (c.g, 0);
diff --git a/test/cpl_sar.cc b/test/cpl_sar.cc
deleted file mode 100644 (file)
index c91ce28..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include <libcxml/cxml.h>
-#include "cpl.h"
-#include "mono_picture_asset.h"
-
-using boost::shared_ptr;
-
-/* Test for a reported bug where <ScreenAspectRatio> in Interop files uses
-   excessive decimal places and (sometimes) the wrong decimal point character.
-*/
-BOOST_AUTO_TEST_CASE (cpl_sar)
-{
-       shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/foo", "video.mxf"));
-       mp->set_interop (true);
-
-       {
-               mp->set_size (libdcp::Size (1998, 1080));
-               xmlpp::Document doc;
-               xmlpp::Element* el = doc.create_root_node ("Test");
-               mp->write_to_cpl (el);
-               
-               cxml::Node node (el);
-               BOOST_CHECK_EQUAL (node.node_child("MainPicture")->string_child ("ScreenAspectRatio"), "1.85");
-       }
-
-       {
-               mp->set_size (libdcp::Size (2048, 858));
-               xmlpp::Document doc;
-               xmlpp::Element* el = doc.create_root_node ("Test");
-               mp->write_to_cpl (el);
-               
-               cxml::Node node (el);
-               BOOST_CHECK_EQUAL (node.node_child("MainPicture")->string_child ("ScreenAspectRatio"), "2.39");
-       }
-}
diff --git a/test/cpl_sar_test.cc b/test/cpl_sar_test.cc
new file mode 100644 (file)
index 0000000..0c62ea8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "cpl.h"
+#include "reel_mono_picture_asset.h"
+#include "mono_picture_mxf.h"
+#include <libcxml/cxml.h>
+#include <boost/test/unit_test.hpp>
+
+using boost::shared_ptr;
+
+/* Test for a reported bug where <ScreenAspectRatio> in Interop files uses
+   excessive decimal places and (sometimes) the wrong decimal point character.
+*/
+BOOST_AUTO_TEST_CASE (cpl_sar)
+{
+       shared_ptr<dcp::ReelMonoPictureAsset> pa (new dcp::ReelMonoPictureAsset ());
+
+       {
+               pa->set_screen_aspect_ratio (dcp::Fraction (1998, 1080));
+               xmlpp::Document doc;
+               xmlpp::Element* el = doc.create_root_node ("Test");
+               pa->write_to_cpl (el, dcp::INTEROP);
+               
+               cxml::Node node (el);
+               BOOST_CHECK_EQUAL (node.node_child("MainPicture")->string_child ("ScreenAspectRatio"), "1.85");
+       }
+
+       {
+               pa->set_screen_aspect_ratio (dcp::Fraction (2048, 858));
+               xmlpp::Document doc;
+               xmlpp::Element* el = doc.create_root_node ("Test");
+               pa->write_to_cpl (el, dcp::INTEROP);
+               
+               cxml::Node node (el);
+               BOOST_CHECK_EQUAL (node.node_child("MainPicture")->string_child ("ScreenAspectRatio"), "2.39");
+       }
+}
index d0c8b973e91264d70c0db47e29f3565050d539a3..5c07a0c8fb7abd0a179341e2df4c2fc3325d5868 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <boost/test/unit_test.hpp>
 #include "dcp.h"
 #include "metadata.h"
 #include "cpl.h"
-#include "mono_picture_asset.h"
-#include "sound_asset.h"
+#include "mono_picture_mxf.h"
+#include "picture_mxf_writer.h"
+#include "sound_mxf_writer.h"
+#include "sound_mxf.h"
+#include "subtitle_content.h"
 #include "reel.h"
 #include "test.h"
+#include "file.h"
+#include "reel_mono_picture_asset.h"
+#include "reel_sound_asset.h"
 #include "KM_util.h"
+#include <sndfile.h>
+#include <boost/test/unit_test.hpp>
 
 using boost::shared_ptr;
 
@@ -35,43 +42,67 @@ BOOST_AUTO_TEST_CASE (dcp_test)
        Kumu::libdcp_test = true;
 
        /* Some known metadata */
-       libdcp::XMLMetadata xml_meta;
+       dcp::XMLMetadata xml_meta;
        xml_meta.issuer = "OpenDCP 0.0.25";
        xml_meta.creator = "OpenDCP 0.0.25";
        xml_meta.issue_date = "2012-07-17T04:45:18+00:00";
-       libdcp::MXFMetadata mxf_meta;
+       dcp::MXFMetadata mxf_meta;
        mxf_meta.company_name = "OpenDCP";
        mxf_meta.product_name = "OpenDCP";
        mxf_meta.product_version = "0.0.25";
 
-       /* We're making build/test/DCP/foo */
+       /* We're making build/test/foo */
        boost::filesystem::remove_all ("build/test/DCP/foo");
        boost::filesystem::create_directories ("build/test/DCP/foo");
-       libdcp::DCP d ("build/test/DCP/foo");
-       shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/DCP/foo", "A Test DCP", libdcp::FEATURE, 24, 24));
+       dcp::DCP d ("build/test/DCP/foo");
+       shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
+       cpl->set_content_version_id ("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00");
+       cpl->set_content_version_label_text ("81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00");
+       cpl->set_metadata (xml_meta);
 
-       shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/DCP/foo", "video.mxf"));
-       mp->set_progress (&d.Progress);
-       mp->set_edit_rate (24);
-       mp->set_intrinsic_duration (24);
-       mp->set_duration (24);
-       mp->set_size (libdcp::Size (32, 32));
+       shared_ptr<dcp::MonoPictureMXF> mp (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
        mp->set_metadata (mxf_meta);
-       mp->create (j2c);
+       shared_ptr<dcp::PictureMXFWriter> picture_writer = mp->start_write ("build/test/DCP/foo/video.mxf", dcp::SMPTE, false);
+       dcp::File j2c ("test/data/32x32_red_square.j2c");
+       for (int i = 0; i < 24; ++i) {
+               picture_writer->write (j2c.data (), j2c.size ());
+       }
+       picture_writer->finalize ();
 
-       shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset ("build/test/DCP/foo", "audio.mxf"));
-       ms->set_progress (&d.Progress);
-       ms->set_edit_rate (24);
-       ms->set_intrinsic_duration (24);
-       ms->set_duration (24);
-       ms->set_channels (2);
+       shared_ptr<dcp::SoundMXF> ms (new dcp::SoundMXF (dcp::Fraction (24, 1), 48000, 1));
        ms->set_metadata (mxf_meta);
-       ms->create (wav);
+       shared_ptr<dcp::SoundMXFWriter> sound_writer = ms->start_write ("build/test/DCP/foo/audio.mxf", dcp::SMPTE);
+
+       SF_INFO info;
+       info.format = 0;
+       SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
+       BOOST_CHECK (sndfile);
+       float buffer[4096*6];
+       float* channels[1];
+       channels[0] = buffer;
+       while (1) {
+               sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
+               sound_writer->write (channels, N);
+               if (N < 4096) {
+                       break;
+               }
+       }
+       
+       sound_writer->finalize ();
        
-       cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
-       d.add_cpl (cpl);
+       cpl->add (shared_ptr<dcp::Reel> (
+                         new dcp::Reel (
+                                 shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
+                                 shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
+                                 shared_ptr<dcp::ReelSubtitleAsset> ()
+                                 )
+                         ));
+                 
+       d.add (cpl);
+       d.add (mp);
+       d.add (ms);
 
-       d.write_xml (false, xml_meta);
+       d.write_xml (dcp::SMPTE, xml_meta);
 
        /* build/test/DCP/foo is checked against test/ref/DCP/foo by run-tests.sh */
 }
index 6892711eefb2a3623b1450f4ccafba121fd2ce75..2a5bca1b63c2a71d977bb9e0f5ae641819e2a1b4 100644 (file)
 #include <boost/test/unit_test.hpp>
 #include "dcp_time.h"
 
-/** Check that libdcp::Time works */
+/** Check that dcp::Time works */
 BOOST_AUTO_TEST_CASE (dcp_time)
 {
-       libdcp::Time t (977143, 24);
+       dcp::Time t (977143, 24);
 
        BOOST_CHECK_EQUAL (t.t, 73);
        BOOST_CHECK_EQUAL (t.s, 34);
@@ -32,10 +32,10 @@ BOOST_AUTO_TEST_CASE (dcp_time)
        BOOST_CHECK_EQUAL (t.to_string(), "11:18:34:73");
        BOOST_CHECK_EQUAL (t.to_ticks(), 10178573);
 
-       libdcp::Time a (3, 2, 3, 4);
-       libdcp::Time b (2, 3, 4, 5);
+       dcp::Time a (3, 2, 3, 4);
+       dcp::Time b (2, 3, 4, 5);
 
-       libdcp::Time r = a - b;
+       dcp::Time r = a - b;
        BOOST_CHECK_EQUAL (r.h, 0);
        BOOST_CHECK_EQUAL (r.m, 58);
        BOOST_CHECK_EQUAL (r.s, 58);
@@ -43,8 +43,8 @@ BOOST_AUTO_TEST_CASE (dcp_time)
        BOOST_CHECK_EQUAL (r.to_string(), "0:58:58:249");
        BOOST_CHECK_EQUAL (r.to_ticks(), 884749);
 
-       a = libdcp::Time (1, 58, 56, 240);
-       b = libdcp::Time (1, 7, 12, 120);
+       a = dcp::Time (1, 58, 56, 240);
+       b = dcp::Time (1, 7, 12, 120);
        r = a + b;
        BOOST_CHECK_EQUAL (r.h, 3);
        BOOST_CHECK_EQUAL (r.m, 6);
@@ -53,10 +53,10 @@ BOOST_AUTO_TEST_CASE (dcp_time)
        BOOST_CHECK_EQUAL (r.to_string(), "3:6:9:110");
        BOOST_CHECK_EQUAL (r.to_ticks(), 2792360);
 
-       a = libdcp::Time (24, 12, 6, 3);
-       b = libdcp::Time (16, 8, 4, 2);
+       a = dcp::Time (24, 12, 6, 3);
+       b = dcp::Time (16, 8, 4, 2);
        BOOST_CHECK_CLOSE (a / b, 1.5, 1e-5);
 
-       BOOST_CHECK_EQUAL (libdcp::Time (4128391203LL).to_ticks(), 4128391203LL);
-       BOOST_CHECK_EQUAL (libdcp::Time (60000).to_ticks(), 60000);
+       BOOST_CHECK_EQUAL (dcp::Time (4128391203LL).to_ticks(), 4128391203LL);
+       BOOST_CHECK_EQUAL (dcp::Time (60000).to_ticks(), 60000);
 }
index d0e067ee138610f3cccb053036861a532b45b803..f80bb900b18907cecc07b7bf8e348b7dbbc3525e 100644 (file)
 */
 
 #include <boost/test/unit_test.hpp>
-#include "kdm.h"
 #include "dcp.h"
 #include "mono_picture_frame.h"
 #include "cpl.h"
+#include "decrypted_kdm.h"
+#include "encrypted_kdm.h"
 #include "argb_frame.h"
-#include "mono_picture_asset.h"
+#include "mono_picture_mxf.h"
+#include "reel_picture_asset.h"
 #include "reel.h"
 #include "test.h"
 
 using boost::dynamic_pointer_cast;
 using boost::shared_ptr;
 
-static shared_ptr<const libdcp::ARGBFrame>
-get_frame (libdcp::DCP const & dcp)
+static shared_ptr<const dcp::ARGBFrame>
+get_frame (dcp::DCP const & dcp)
 {
-       shared_ptr<const libdcp::Reel> reel = dcp.cpls().front()->reels().front ();
-       shared_ptr<const libdcp::PictureAsset> picture = reel->main_picture ();
+       shared_ptr<const dcp::Reel> reel = dcp.cpls().front()->reels().front ();
+       shared_ptr<dcp::PictureMXF> picture = reel->main_picture()->mxf ();
        BOOST_CHECK (picture);
 
-       shared_ptr<const libdcp::MonoPictureAsset> mono_picture = dynamic_pointer_cast<const libdcp::MonoPictureAsset> (picture);
-       shared_ptr<const libdcp::MonoPictureFrame> j2k_frame = mono_picture->get_frame (0);
+       shared_ptr<const dcp::MonoPictureMXF> mono_picture = dynamic_pointer_cast<const dcp::MonoPictureMXF> (picture);
+       shared_ptr<const dcp::MonoPictureFrame> j2k_frame = mono_picture->get_frame (0);
        return j2k_frame->argb_frame ();
 }
 
@@ -47,25 +49,25 @@ BOOST_AUTO_TEST_CASE (decryption_test)
 {
        boost::filesystem::path plaintext_path = private_test;
        plaintext_path /= "TONEPLATES-SMPTE-PLAINTEXT_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV";
-       libdcp::DCP plaintext (plaintext_path.string ());
+       dcp::DCP plaintext (plaintext_path.string ());
        plaintext.read ();
        BOOST_CHECK_EQUAL (plaintext.encrypted (), false);
 
        boost::filesystem::path encrypted_path = private_test;
        encrypted_path /= "TONEPLATES-SMPTE-ENCRYPTED_TST_F_XX-XX_ITL-TD_51-XX_2K_WOE_20111001_WOE_OV";
-       libdcp::DCP encrypted (encrypted_path.string ());
+       dcp::DCP encrypted (encrypted_path.string ());
        encrypted.read ();
        BOOST_CHECK_EQUAL (encrypted.encrypted (), true);
 
-       libdcp::KDM kdm (
-               "test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml",
+       dcp::DecryptedKDM kdm (
+               dcp::EncryptedKDM ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
                "test/data/private.key"
                );
+       
+       encrypted.add (kdm);
 
-       encrypted.add_kdm (kdm);
-
-       shared_ptr<const libdcp::ARGBFrame> plaintext_frame = get_frame (plaintext);
-       shared_ptr<const libdcp::ARGBFrame> encrypted_frame = get_frame (encrypted);
+       shared_ptr<const dcp::ARGBFrame> plaintext_frame = get_frame (plaintext);
+       shared_ptr<const dcp::ARGBFrame> encrypted_frame = get_frame (encrypted);
 
        /* Check that plaintext and encrypted are the same */
        BOOST_CHECK_EQUAL (plaintext_frame->stride(), encrypted_frame->stride());
@@ -77,8 +79,8 @@ BOOST_AUTO_TEST_CASE (decryption_test)
 /** Load in a KDM that didn't work at first */
 BOOST_AUTO_TEST_CASE (failing_kdm_test)
 {
-       libdcp::KDM kdm (
-               "test/data/target.pem.crt.de5d4eba-e683-41ca-bdda-aa4ad96af3f4.kdm.xml",
+       dcp::DecryptedKDM kdm (
+               dcp::EncryptedKDM ("test/data/target.pem.crt.de5d4eba-e683-41ca-bdda-aa4ad96af3f4.kdm.xml"),
                "test/data/private.key"
                );
 }
index f08d48271e2a7c959a583c50b6039b45d3693726..d5cd46d407e9b8684f5d4507992423298682f450 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <boost/test/unit_test.hpp>
-#include "kdm.h"
 #include "KM_util.h"
 #include "metadata.h"
 #include "certificates.h"
 #include "dcp.h"
 #include "signer.h"
 #include "cpl.h"
-#include "mono_picture_asset.h"
-#include "sound_asset.h"
+#include "mono_picture_mxf.h"
+#include "picture_mxf_writer.h"
+#include "sound_mxf_writer.h"
+#include "sound_mxf.h"
 #include "reel.h"
 #include "test.h"
+#include "file.h"
 #include "signer_chain.h"
+#include "subtitle_content.h"
+#include "reel_mono_picture_asset.h"
+#include "reel_sound_asset.h"
+#include "encrypted_kdm.h"
+#include "decrypted_kdm.h"
+#include <sndfile.h>
+#include <boost/test/unit_test.hpp>
+#include <boost/shared_ptr.hpp>
 
 using boost::shared_ptr;
 
 /* Load a certificate chain from build/test/data/ *.pem and then build
    an encrypted DCP and a KDM using it.
 */
-BOOST_AUTO_TEST_CASE (encryption)
+BOOST_AUTO_TEST_CASE (encryption_test)
 {
        boost::filesystem::remove_all ("build/test/signer");
        boost::filesystem::create_directory ("build/test/signer");
-       libdcp::make_signer_chain ("build/test/signer", "openssl");
+       dcp::make_signer_chain ("build/test/signer", "openssl");
        
        Kumu::libdcp_test = true;
 
-       libdcp::MXFMetadata mxf_metadata;
+       dcp::MXFMetadata mxf_metadata;
        mxf_metadata.company_name = "OpenDCP";
        mxf_metadata.product_name = "OpenDCP";
        mxf_metadata.product_version = "0.0.25";
 
-       libdcp::XMLMetadata xml_metadata;
+       dcp::XMLMetadata xml_metadata;
        xml_metadata.issuer = "OpenDCP 0.0.25";
        xml_metadata.creator = "OpenDCP 0.0.25";
        xml_metadata.issue_date = "2012-07-17T04:45:18+00:00";
        
        boost::filesystem::remove_all ("build/test/DCP/bar");
        boost::filesystem::create_directories ("build/test/DCP/bar");
-       libdcp::DCP d ("build/test/DCP/bar");
+       dcp::DCP d ("build/test/DCP/bar");
 
        /* Use test/ref/crypt so this test is repeatable */
-       libdcp::CertificateChain chain;
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
+       dcp::CertificateChain chain;
+       chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/ca.self-signed.pem"))));
+       chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/intermediate.signed.pem"))));
+       chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("test/ref/crypt/leaf.signed.pem"))));
 
-       shared_ptr<libdcp::Signer> signer (
-               new libdcp::Signer (
+       shared_ptr<dcp::Signer> signer (
+               new dcp::Signer (
                        chain,
                        "test/ref/crypt/leaf.key"
                        )
                );
 
-       shared_ptr<libdcp::CPL> cpl (new libdcp::CPL ("build/test/DCP/bar", "A Test DCP", libdcp::FEATURE, 24, 24));
+       shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
 
-       libdcp::Key key;
+       dcp::Key key;
        
-       shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/DCP/bar", "video.mxf"));
-       mp->set_progress (&d.Progress);
-       mp->set_edit_rate (24);
-       mp->set_intrinsic_duration (24);
-       mp->set_duration (24);
-       mp->set_size (libdcp::Size (32, 32));
+       shared_ptr<dcp::MonoPictureMXF> mp (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
        mp->set_metadata (mxf_metadata);
        mp->set_key (key);
-       mp->create (j2c);
-
-       shared_ptr<libdcp::SoundAsset> ms (new libdcp::SoundAsset ("build/test/DCP/bar", "audio.mxf"));
-       ms->set_progress (&d.Progress);
-       ms->set_edit_rate (24);
-       ms->set_intrinsic_duration (24);
-       mp->set_duration (24);
-       ms->set_channels (2);
-       ms->set_metadata (mxf_metadata);
-       ms->set_key (key);
-       ms->create (wav);
-       
-       cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (mp, ms, shared_ptr<libdcp::SubtitleAsset> ())));
-       d.add_cpl (cpl);
 
-       d.write_xml (false, xml_metadata, signer);
+       shared_ptr<dcp::PictureMXFWriter> writer = mp->start_write ("build/test/DCP/bar/video.mxf", dcp::SMPTE, false);
+       dcp::File j2c ("test/data/32x32_red_square.j2c");
+       for (int i = 0; i < 24; ++i) {
+               writer->write (j2c.data (), j2c.size ());
+       }
+       writer->finalize ();
 
-       libdcp::KDM kdm (
+       shared_ptr<dcp::SoundMXF> ms (new dcp::SoundMXF (dcp::Fraction (24, 1), 48000, 1));
+       ms->set_key (key);
+       shared_ptr<dcp::SoundMXFWriter> sound_writer = ms->start_write ("build/test/DCP/bar/audio.mxf", dcp::SMPTE);
+       
+       SF_INFO info;
+       info.format = 0;
+       SNDFILE* sndfile = sf_open ("test/data/1s_24-bit_48k_silence.wav", SFM_READ, &info);
+       BOOST_CHECK (sndfile);
+       float buffer[4096*6];
+       float* channels[1];
+       channels[0] = buffer;
+       while (1) {
+               sf_count_t N = sf_readf_float (sndfile, buffer, 4096);
+               sound_writer->write (channels, N);
+               if (N < 4096) {
+                       break;
+               }
+       }
+       
+       sound_writer->finalize ();      
+
+       cpl->add (shared_ptr<dcp::Reel> (new dcp::Reel (
+                                                shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mp, 0)),
+                                                shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (ms, 0)),
+                                                shared_ptr<dcp::ReelSubtitleAsset> ()
+                                                )));
+       cpl->set_content_version_id ("urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00");
+       cpl->set_content_version_label_text ("81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00");
+       cpl->set_metadata (xml_metadata);
+       d.add (cpl);
+       d.write_xml (dcp::SMPTE, xml_metadata, signer);
+
+       dcp::DecryptedKDM kdm (
                cpl,
-               signer,
-               signer->certificates().leaf(),
-               boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
-               boost::posix_time::time_from_string ("2013-01-08 00:00:00"),
+               dcp::LocalTime ("2013-01-01T00:00:00+00:00"),
+               dcp::LocalTime ("2017-01-08T00:00:00+00:00"),
                "libdcp",
+               "test",
                "2012-07-17T04:45:18+00:00"
                );
 
-       kdm.as_xml ("build/test/bar.kdm.xml");
+       kdm.encrypt(signer, signer->certificates().leaf()).as_xml ("build/test/bar.kdm.xml");
        
        int r = system (
                "xmllint --path schema --nonet --noout --schema schema/SMPTE-430-1-2006-Amd-1-2009-KDM.xsd build/test/bar.kdm.xml "
diff --git a/test/error_test.cc b/test/error_test.cc
deleted file mode 100644 (file)
index eeff4f4..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "dcp.h"
-#include "mono_picture_asset.h"
-#include "sound_asset.h"
-#include "util.h"
-#include "exceptions.h"
-
-using std::vector;
-using std::string;
-
-/* Check that an exception is thrown when trying to create MXFs from non-existant sources */
-BOOST_AUTO_TEST_CASE (error_test)
-{
-       /* Create an empty DCP */
-       libdcp::DCP d ("build/test/fred");
-
-       /* Random filename that does not exist */
-       vector<boost::filesystem::path> p;
-       p.push_back ("frobozz");
-
-       /* Trying to create video/audio MXFs using a non-existant file should throw an exception */
-       libdcp::MonoPictureAsset pa ("build/test/fred", "video.mxf");
-       BOOST_CHECK_THROW (pa.create (p), libdcp::FileError);
-       
-       libdcp::SoundAsset sa ("build/test/fred", "audio.mxf");
-       sa.set_channels (1);
-       BOOST_CHECK_THROW (sa.create (p), libdcp::FileError);
-}
index db73d7fb5bfd10306d4ec6013ec673f495e4b985..02ff83aa59ebba64df2e555f5a7daea8f9371006 100644 (file)
 
 #include <fstream>
 #include <boost/test/unit_test.hpp>
-#include "picture_asset_writer.h"
+#include "picture_mxf.h"
+#include "picture_mxf_writer.h"
 
 using namespace std;
 
 /* Test writing and reading of frame_info_test with fstream and stdio */
 BOOST_AUTO_TEST_CASE (frame_info_test)
 {
-       libdcp::FrameInfo a (8589934592LL, 17179869184LL, "thisisahash");
+       dcp::FrameInfo a (8589934592LL, 17179869184LL, "thisisahash");
 
        ofstream o1 ("build/test/frame_info1");
        a.write (o1);
@@ -48,11 +49,11 @@ BOOST_AUTO_TEST_CASE (frame_info_test)
        BOOST_CHECK_EQUAL (s1, s2);
 
        ifstream l1 ("build/test/frame_info1");
-       libdcp::FrameInfo b1 (l1);
+       dcp::FrameInfo b1 (l1);
 
        FILE* l2 = fopen ("build/test/frame_info2", "r");
        BOOST_CHECK (l2);
-       libdcp::FrameInfo b2 (l2);
+       dcp::FrameInfo b2 (l2);
 
        BOOST_CHECK_EQUAL (b1.offset, b2.offset);
        BOOST_CHECK_EQUAL (b1.size, b2.size);
diff --git a/test/kdm_key_test.cc b/test/kdm_key_test.cc
deleted file mode 100644 (file)
index 05b8531..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "kdm.h"
-
-BOOST_AUTO_TEST_CASE (kdm_key_test)
-{
-       uint8_t foo[138];
-       memset (foo, 0, 138);
-       libdcp::KDMKey kkey (foo, 138);
-
-       uint8_t* raw = new uint8_t[16];
-       uint8_t* p = raw;
-       kkey.put_uuid (&p, "5d51e8a1-b2a5-4da6-9b66-4615c3609440");
-       BOOST_CHECK_EQUAL (raw[0], 0x5d);
-       BOOST_CHECK_EQUAL (raw[1], 0x51);
-       BOOST_CHECK_EQUAL (raw[2], 0xe8);
-       BOOST_CHECK_EQUAL (raw[3], 0xa1);
-       BOOST_CHECK_EQUAL (raw[4], 0xb2);
-       BOOST_CHECK_EQUAL (raw[5], 0xa5);
-       BOOST_CHECK_EQUAL (raw[6], 0x4d);
-       BOOST_CHECK_EQUAL (raw[7], 0xa6);
-       BOOST_CHECK_EQUAL (raw[8], 0x9b);
-       BOOST_CHECK_EQUAL (raw[9], 0x66);
-       BOOST_CHECK_EQUAL (raw[10], 0x46);
-       BOOST_CHECK_EQUAL (raw[11], 0x15);
-       BOOST_CHECK_EQUAL (raw[12], 0xc3);
-       BOOST_CHECK_EQUAL (raw[13], 0x60);
-       BOOST_CHECK_EQUAL (raw[14], 0x94);
-       BOOST_CHECK_EQUAL (raw[15], 0x40);
-       delete[] raw;
-}
index 0f0db031f81383a1d1125db4783255a73f7120d8..7de62f5a2aac9290b68ae96ed0ea394c99dd4f15 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 */
 
 #include <boost/test/unit_test.hpp>
-#include "kdm.h"
-#include "xml/kdm_smpte.h"
+#include <libxml++/libxml++.h>
+#include "encrypted_kdm.h"
+#include "decrypted_kdm.h"
 
 using std::list;
+using std::stringstream;
 using boost::shared_ptr;
 
 BOOST_AUTO_TEST_CASE (kdm_test)
 {
-       libdcp::KDM kdm (
-               "test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml",
+       dcp::DecryptedKDM kdm (
+               dcp::EncryptedKDM ("test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"),
                "test/data/private.key"
                );
 
-       list<libdcp::KDMKey> keys = kdm.keys ();
+       list<dcp::DecryptedKDMKey> keys = kdm.keys ();
        
        BOOST_CHECK_EQUAL (keys.size(), 2);
 
        BOOST_CHECK_EQUAL (keys.front().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
-       BOOST_CHECK_EQUAL (keys.front().key_id(), "4ac4f922-8239-4831-b23b-31426d0542c4");
-       BOOST_CHECK_EQUAL (keys.front().not_valid_before(), "2013-07-06T20:04:58+00:00");
-       BOOST_CHECK_EQUAL (keys.front().not_valid_after(), "2023-07-02T20:04:56+00:00");
+       BOOST_CHECK_EQUAL (keys.front().id(), "4ac4f922-8239-4831-b23b-31426d0542c4");
        BOOST_CHECK_EQUAL (keys.front().key().hex(), "8a2729c3e5b65c45d78305462104c3fb");
 
        BOOST_CHECK_EQUAL (keys.back().cpl_id(), "eece17de-77e8-4a55-9347-b6bab5724b9f");
-       BOOST_CHECK_EQUAL (keys.back().key_id(), "73baf5de-e195-4542-ab28-8a465f7d4079");
-       BOOST_CHECK_EQUAL (keys.back().not_valid_before(), "2013-07-06T20:04:58+00:00");
-       BOOST_CHECK_EQUAL (keys.back().not_valid_after(), "2023-07-02T20:04:56+00:00");
+       BOOST_CHECK_EQUAL (keys.back().id(), "73baf5de-e195-4542-ab28-8a465f7d4079");
        BOOST_CHECK_EQUAL (keys.back().key().hex(), "5327fb7ec2e807bd57059615bf8a169d");
 }
 
 /* Check that we can read in a KDM and then write it back out again the same */
 BOOST_AUTO_TEST_CASE (kdm_passthrough_test)
 {
-       libdcp::xml::DCinemaSecurityMessage kdm (
+       dcp::EncryptedKDM kdm (
                "test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml"
                );
 
-       shared_ptr<xmlpp::Document> doc = kdm.as_xml ();
-       doc->write_to_file_formatted ("build/kdm.xml", "UTF-8");
+       shared_ptr<xmlpp::DomParser> parser (new xmlpp::DomParser ());
+       stringstream s;
+       s << kdm.as_xml ();
+       parser->parse_stream (s);
+       parser->get_document()->write_to_file_formatted ("build/kdm.xml", "UTF-8");
        int const r = system (
                "xmldiff -c test/data/kdm_TONEPLATES-SMPTE-ENC_.smpte-430-2.ROOT.NOT_FOR_PRODUCTION_20130706_20230702_CAR_OV_t1_8971c838.xml build/kdm.xml"
                );
diff --git a/test/local_time_test.cc b/test/local_time_test.cc
new file mode 100644 (file)
index 0000000..2c61fd1
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/test/unit_test.hpp>
+#include "local_time.h"
+#include "exceptions.h"
+
+/** Check that dcp::LocalTime works */
+BOOST_AUTO_TEST_CASE (local_time_test)
+{
+       /* Badly-formatted times */
+       BOOST_CHECK_THROW (dcp::LocalTime (""), dcp::TimeFormatError);
+       BOOST_CHECK_THROW (dcp::LocalTime ("XXX"), dcp::TimeFormatError);
+       BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18:06:59+04:0"), dcp::TimeFormatError);
+       BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18:06:59X04:00"), dcp::TimeFormatError);
+       BOOST_CHECK_THROW (dcp::LocalTime ("2013-01-05T18-06:59+04:00"), dcp::TimeFormatError);
+       BOOST_CHECK_THROW (dcp::LocalTime ("2013!01-05T18:06:59+04:00"), dcp::TimeFormatError);
+
+       /* Correctly-formatted */
+       
+       {
+               dcp::LocalTime t ("2013-01-05T18:06:59+04:00");
+               BOOST_CHECK_EQUAL (t._year, 2013);
+               BOOST_CHECK_EQUAL (t._month, 1);
+               BOOST_CHECK_EQUAL (t._day, 5);
+               BOOST_CHECK_EQUAL (t._hour, 18);
+               BOOST_CHECK_EQUAL (t._minute, 6);
+               BOOST_CHECK_EQUAL (t._second, 59);
+               BOOST_CHECK_EQUAL (t._tz_hour, 4);
+               BOOST_CHECK_EQUAL (t._tz_minute, 0);
+               BOOST_CHECK_EQUAL (t.as_string(), "2013-01-05T18:06:59+04:00");
+       }
+
+       {
+               dcp::LocalTime t ("2011-11-20T01:06:59-09:30");
+               BOOST_CHECK_EQUAL (t._year, 2011);
+               BOOST_CHECK_EQUAL (t._month, 11);
+               BOOST_CHECK_EQUAL (t._day, 20);
+               BOOST_CHECK_EQUAL (t._hour, 1);
+               BOOST_CHECK_EQUAL (t._minute, 6);
+               BOOST_CHECK_EQUAL (t._second, 59);
+               BOOST_CHECK_EQUAL (t._tz_hour, -9);
+               BOOST_CHECK_EQUAL (t._tz_minute, 30);
+               BOOST_CHECK_EQUAL (t.as_string(), "2011-11-20T01:06:59-09:30");
+       }
+
+       /* Construction from boost::posix_time::ptime */
+       dcp::LocalTime b (boost::posix_time::time_from_string ("2002-01-20 19:03:56"));
+       BOOST_CHECK_EQUAL (b._year, 2002);
+       BOOST_CHECK_EQUAL (b._month, 1);
+       BOOST_CHECK_EQUAL (b._day, 20);
+       BOOST_CHECK_EQUAL (b._hour, 19);
+       BOOST_CHECK_EQUAL (b._minute, 3);
+       BOOST_CHECK_EQUAL (b._second, 56);
+}
+
diff --git a/test/lut_test.cc b/test/lut_test.cc
deleted file mode 100644 (file)
index c90dff7..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "opendcp_lut.h"
-#include "opendcp_lut.cc"
-#include "srgb_linearised_gamma_lut.h"
-#include "rec709_linearised_gamma_lut.h"
-#include "gamma_lut.h"
-
-/* Check that some of our LUTs match the ones from OpenDCP that
-   DVD-o-matic uses / once used.
-*/
-BOOST_AUTO_TEST_CASE (lut_test)
-{
-       libdcp::SRGBLinearisedGammaLUT lut_in_srgb (12, 2.4);
-       for (int i = 0; i < 4096; ++i) {
-               /* Hmm; 1% isn't exactly great... */
-               BOOST_CHECK_CLOSE (opendcp::lut_in[0][i], lut_in_srgb.lut()[i], 1);
-       }
-
-       libdcp::Rec709LinearisedGammaLUT lut_in_rec709 (12, 1 / 0.45);
-       for (int i = 0; i < 4096; ++i) {
-               /* Hmm; 1% isn't exactly great... */
-               BOOST_CHECK_CLOSE (opendcp::lut_in[1][i], lut_in_rec709.lut()[i], 1);
-       }
-
-       libdcp::GammaLUT lut_out (16, 1 / 2.6);
-       for (int i = 0; i < 65536; ++i) {
-               BOOST_CHECK_CLOSE (opendcp::lut_out[0][i], lut_out.lut()[i] * 4096, 1);
-       }
-}
index 0a9ac1198fe31706f6e9da9be4f0aa5b31d64ac8..a17b0a01dbcd138125c1aed77615bba99c2edd94 100644 (file)
@@ -27,14 +27,12 @@ using boost::shared_ptr;
 /* Read DCP that is in git and make sure that basic stuff is read in correctly */
 BOOST_AUTO_TEST_CASE (read_dcp)
 {
-       libdcp::DCP d ("test/ref/DCP/foo");
+       dcp::DCP d ("test/ref/DCP/foo");
        d.read ();
 
-       list<shared_ptr<libdcp::CPL> > cpls = d.cpls ();
+       list<shared_ptr<dcp::CPL> > cpls = d.cpls ();
        BOOST_CHECK_EQUAL (cpls.size(), 1);
 
-       BOOST_CHECK_EQUAL (cpls.front()->name(), "A Test DCP");
-       BOOST_CHECK_EQUAL (cpls.front()->content_kind(), libdcp::FEATURE);
-       BOOST_CHECK_EQUAL (cpls.front()->frames_per_second(), 24);
-       BOOST_CHECK_EQUAL (cpls.front()->length(), 24);
+       BOOST_CHECK_EQUAL (cpls.front()->annotation_text(), "A Test DCP");
+       BOOST_CHECK_EQUAL (cpls.front()->content_kind(), dcp::FEATURE);
 }
index 42697c37b5806ed2fa304fe164fd305ad2258ba2..1d51a711555fda23e65c4724a266e4de43e2d556 100644 (file)
@@ -19,8 +19,8 @@
 
 #include <boost/test/unit_test.hpp>
 #include <boost/filesystem.hpp>
-#include "mono_picture_asset_writer.h"
-#include "mono_picture_asset.h"
+#include "mono_picture_mxf_writer.h"
+#include "mono_picture_mxf.h"
 #include "KM_util.h"
 
 using std::string;
@@ -48,14 +48,12 @@ BOOST_AUTO_TEST_CASE (recovery)
        
        boost::filesystem::remove_all ("build/test/baz");
        boost::filesystem::create_directories ("build/test/baz");
-       shared_ptr<libdcp::MonoPictureAsset> mp (new libdcp::MonoPictureAsset ("build/test/baz", "video1.mxf"));
-       mp->set_edit_rate (24);
-       mp->set_size (libdcp::Size (32, 32));
-       shared_ptr<libdcp::PictureAssetWriter> writer = mp->start_write (false);
+       shared_ptr<dcp::MonoPictureMXF> mp (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
+       shared_ptr<dcp::PictureMXFWriter> writer = mp->start_write ("build/test/baz/video1.mxf", dcp::SMPTE, false);
 
        int written_size = 0;
        for (int i = 0; i < 24; ++i) {
-               libdcp::FrameInfo info = writer->write (data, size);
+               dcp::FrameInfo info = writer->write (data, size);
                written_size = info.size;
        }
 
@@ -78,10 +76,8 @@ BOOST_AUTO_TEST_CASE (recovery)
        Kumu::ResetTestRNG ();
 #endif 
 
-       mp.reset (new libdcp::MonoPictureAsset ("build/test/baz", "video2.mxf"));
-       mp->set_edit_rate (24);
-       mp->set_size (libdcp::Size (32, 32));
-       writer = mp->start_write (true);
+       mp.reset (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
+       writer = mp->start_write ("build/test/baz/video2.mxf", dcp::SMPTE, true);
 
        writer->write (data, size);
 
diff --git a/test/ref/DCP/bar/3a8a01d1-0e1c-4ff0-977d-63d32995a1fd_pkl.xml b/test/ref/DCP/bar/3a8a01d1-0e1c-4ff0-977d-63d32995a1fd_pkl.xml
new file mode 100644 (file)
index 0000000..f44c7fb
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<PackingList xmlns="http://www.smpte-ra.org/schemas/429-8/2007/PKL" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><Id>urn:uuid:3a8a01d1-0e1c-4ff0-977d-63d32995a1fd</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><AssetList><Asset><Id>urn:uuid:793170e7-e913-404b-8942-1c7f2e5cf012</Id><AnnotationText>793170e7-e913-404b-8942-1c7f2e5cf012</AnnotationText><Hash>+bH3rx2pluCjrgBADa5hCouMrEk=</Hash><Size>8412</Size><Type>text/xml</Type></Asset></AssetList><Signer><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509SubjectName>dnQualifier=d95fGDzERNdxfYPgphvAR8A18L4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509SubjectName></dsig:X509Data></Signer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><dsig:Reference URI=""><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>6RNJVv3Hmr+JgjPK64JHrINxFQA=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>1Rm332asXgopaVtn2/fLXBIEaqM9FRfS23YoN8u++6MlMHYmL/cyka3mSNh1YUhL
+zrdU0ZQrkhL9wNUjbkpVF3Xy9YHJ1fsLTe31GgS2NNZKbmvrAz7mW09lcZSWMTqN
+h9qdwU5mkH8py2Yi4XRsy+vqcngHwsWXraCbjTyUM0IpYAMaDOhaYqKaYSvApPyF
+2HDrCuSSN6RmoA0h4vZYM30IsNJ+xTUIlVq7hPcJYdaTzFr62Hemls5fOG5/7LOi
+FYarEcUm45QDWKSGPFbgoQRYIwc51At3ezyTnYegbY/6ZZkknyQhA2Mn+0ox2blX
+YASmNmTmaMTcAJ76eYHHKA==</dsig:SignatureValue><dsig:KeyInfo><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEezCCA2OgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBijEUMBIGA1UEChMLZXhh
+bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMTUwMwYDVQQDFCwuc21wdGUt
+NDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1JfUFJPRFVDVElPTjElMCMGA1UELhMc
+Ym10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3PTAeFw0xMzA3MDgwOTM5MDBaFw0y
+MzA3MDQwOTM5MDBaMIGEMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UECxML
+ZXhhbXBsZS5vcmcxLzAtBgNVBAMUJkNTLnNtcHRlLTQzMC0yLkxFQUYuTk9UX0ZP
+Ul9QUk9EVUNUSU9OMSUwIwYDVQQuExxkOTVmR0R6RVJOZHhmWVBncGh2QVI4QTE4
+TDQ9MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4Ai70YT7amWSvXWO
+2LL9HrkNEQkR0HNO4kZS+2v3CnbrlSnSkLgA4rgsmSPNhHHgskr3ljzxH/MugKv3
+tLlSHywcqPxj06SUCvd7aWfUKIGR33XxZwFB9Hc6hCKMcOYxFJISfFxcknSgcmQj
+RPnLTbaCc/i/sOHQiLnZXmTVIe43VrE+zzYJO7BBnU7Xnj1MGTzz7ccooL3dp8iS
+t4E7E3mGGVMyTndIEE3fx8y1tPFigNKdPKBo1fXD7pFkL7idH+DO5Phwf/OSjS4Z
+egBCrb96fRpWvzhh6z7USOe8iKY5xDir5c4UKY15JsVw2ESxkPF9wFpFHCyXt2YS
+kDYbHQIDAQABo4HvMIHsMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgWgMB0GA1Ud
+DgQWBBR33l8YPMRE13F9g+CmG8BHwDXwvjCBrwYDVR0jBIGnMIGkgBRua3BOGrey
+uDEgB5EyNfoEWGAsPKGBiKSBhTCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDAS
+BgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5O
+T1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZt
+ZlFub2FTYz2CAQYwDQYJKoZIhvcNAQELBQADggEBAKTd8vC9j06NL727VdnYZEwa
+D4MUTnv1UW5d/FdQR87WpGV+17CKblWGJtLTfFmV9LwcGlys6ZzrLRYpBf+81vYq
+VUhl7rrU7askIiacSlfYx4riv9KH5JLmyLdo7sjX4dhfj5IH/ilan7le1shjEl0P
+UrEgYX1dt5OOnMpzaIRHU+GWVlkY3M5VDdDfMPstuPJ+MeAP1fH0Ylajhc4O5nmu
+hWfXc9qa5bxOLzNDBOsXS8hbnTpUS1qpqzea5NSogdxGIiyk/OluU1ZJJPQhf0iK
+K6U6e4+TpHKVvqUwQcPUw9TcBGIDkwJTtLF48ZhFI9Gv016SmSwUobgcDA9e97o=</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>6</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEfzCCA2egAwIBAgIBBjANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
+bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
+NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
+T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU5WhcNMjMwNzA1MDkz
+ODU5WjCBijEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
+b3JnMTUwMwYDVQQDFCwuc21wdGUtNDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1Jf
+UFJPRFVDVElPTjElMCMGA1UELhMcYm10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3
+PTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrCpaiKl7M/NYtKNQAo
+H+zUcnBTNQ6xhRIDxvPAgvcGH4tf0kpXmZ8ppTU/ZZKaFZB+/pIU+9JfiCuuquvw
+4EQnLABr61m6aJlE1v+0ygzZsCyHyiRnp6OyOTrJnyL6ygniX/ggp+7zkuh4upAl
+QUaCDqNbUfs6Wh9BvmaBllvI3PxfaqDlQVgY53Ar991BA0kcFOdd/4ZLqZwhEiiE
+Y1s3ChYLTxlqPM/UxOeRLUphmGmTSKBKc9ppTN+2/04sQJ1Ux+RZ5PmJ4+eAEcW8
+CjKM5EqZAO3EA3u88Do3uZjLXMxSGeJzs3KQX/lVZklFsO5d1eAQm9ZizfCbPPoK
+f2kCAwEAAaOB9TCB8jASBgNVHRMBAf8ECDAGAQH/AgECMAsGA1UdDwQEAwIBBjAd
+BgNVHQ4EFgQUbmtwThq3srgxIAeRMjX6BFhgLDwwga8GA1UdIwSBpzCBpIAUA7Qg
+bb/sj/jyHsjw8TKBB7AIliihgYikgYUwgYIxFDASBgNVBAoTC2V4YW1wbGUub3Jn
+MRQwEgYDVQQLEwtleGFtcGxlLm9yZzEtMCsGA1UEAxQkLnNtcHRlLTQzMC0yLlJP
+T1QuTk9UX0ZPUl9QUk9EVUNUSU9OMSUwIwYDVQQuExxuZE5EOUEvY09EbzJyVGRy
+YkxWbWZRbm9hU2M9ggEFMA0GCSqGSIb3DQEBCwUAA4IBAQBnlzVaMqhuenDQV7Zg
+aMa6IisupI3k9J6Z24fiEOGhCfAp0fiulx2oCSrXlYnJFGe6ndeWWer9UN1h+lAc
+ozKYwxsJx8jSkgwGYX5v8Cn2Sp2/gV5umCcmpZfIExEOZRmjgKzqyr658EyvmrYJ
+nqrig/N8wUWeS5EzMSiE1sVfyIZmUpKhuqmGQUXnftVMCKSrqAh+Au7ndlR77/dm
+ZBJzzX79nn4L9XqghdoFCPRt4rx1CMU26MmiuEzNcYJ3uSJc1SSz5tT69JQvp8Fj
+InhKTMv0wWysEsfE6+aARPtrqAUJBRAG83oP6L0gdJYWJagGFVoZLfjPf+v9JgYZ
+Wo+D</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>5</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEdzCCA1+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
+bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
+NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
+T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU4WhcNMjMwNzA2MDkz
+ODU4WjCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
+b3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJ
+T04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZtZlFub2FTYz0wggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTZ9Xu3AxKzsCf8haW7QtEZEauMUfo
+SkbzUmNZUYOwzuOvfhqSzO1BpRVI8/k3ZzkXR7XsWp8SbPsE17s5WPLwo4uDSro9
+XtRDOFFaZ023Ed9o2Zvoc6bwz8CpnKpiBB1XHMw9Bmi3q886f7Kk0SCsbX9OJeUC
+5/FUfwDSZUs8qjRkXYd2zjxgFGvYbokKSW1ZwkoLIRoJJhScjSnwTd/aAlcenfcw
+DV6ccjOJph4Dsru1NpCm50vi5/PRr00vBXMxdC2YgNyWONM5k8InBiU0uCmWmU5Y
+SRSc9A3cVtrKzP1WHE2978TNFqKpjEa8tgOYualSfI2pMtA23EVG14RJAgMBAAGj
+gfUwgfIwEgYDVR0TAQH/BAgwBgEB/wIBAzALBgNVHQ8EBAMCAQYwHQYDVR0OBBYE
+FAO0IG2/7I/48h7I8PEygQewCJYoMIGvBgNVHSMEgacwgaSAFAO0IG2/7I/48h7I
+8PEygQewCJYooYGIpIGFMIGCMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UE
+CxMLZXhhbXBsZS5vcmcxLTArBgNVBAMUJC5zbXB0ZS00MzAtMi5ST09ULk5PVF9G
+T1JfUFJPRFVDVElPTjElMCMGA1UELhMcbmRORDlBL2NPRG8yclRkcmJMVm1mUW5v
+YVNjPYIBBTANBgkqhkiG9w0BAQsFAAOCAQEAp2V3npo0m7Vmdhpr6mk5UybM16Hx
+M99zoKGUpm6k9PYRk8ysEhD7M8hDuwiiFoRJj1+X8V4HLwNU3JTTQzSl88pMvLg8
+Tyl4ICELg9MN/Eyfnu6iyGDfPzcsuv8xZN53211LobqW+HGK8r+slf5GPDILka8r
+GB7sFRjvq3SEGpqupKI9sJFmvHyQplJ1ixrIkSnTEuBFBrDtQj+cjrqlvRw/dReo
+3uELGe5GyvBcP0hDgrvZQmTdCLQfo4998MMhLDyxvD9/D4jpRcCrLzs2uB3Tw3kT
+1KkAjDA/33J1XCZEkb1ArS2L/I12T0DoSQqb17NE3xccY+6iRFXRMFWDIQ==</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo></dsig:Signature></PackingList>
diff --git a/test/ref/DCP/bar/793170e7-e913-404b-8942-1c7f2e5cf012_cpl.xml b/test/ref/DCP/bar/793170e7-e913-404b-8942-1c7f2e5cf012_cpl.xml
new file mode 100644 (file)
index 0000000..52cbc95
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<CompositionPlaylist xmlns="http://www.smpte-ra.org/schemas/429-7/2006/CPL" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><Id>urn:uuid:793170e7-e913-404b-8942-1c7f2e5cf012</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><ContentTitleText>A Test DCP</ContentTitleText><ContentKind>feature</ContentKind><ContentVersion><Id>urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00</Id><LabelText>81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00</LabelText></ContentVersion><RatingList/><ReelList><Reel><Id>urn:uuid:f68fedf1-3277-45ce-80f9-465ffd2f8acc</Id><AssetList><MainPicture><Id>urn:uuid:8f8d5c1f-cd57-4bcc-9827-ff398e5832cf</Id><AnnotationText>video.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration><FrameRate>24 1</FrameRate><ScreenAspectRatio>32 32</ScreenAspectRatio></MainPicture><MainSound><Id>urn:uuid:ab16cd50-686f-4015-9498-4a142ecb0696</Id><AnnotationText>audio.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration></MainSound></AssetList></Reel></ReelList><Signer><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509SubjectName>dnQualifier=d95fGDzERNdxfYPgphvAR8A18L4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509SubjectName></dsig:X509Data></Signer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><dsig:Reference URI=""><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>DhLIlGJkPl1u30B9RO6a+tQTl2o=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>SNnN1x9ipOmPkSUT9p7drFTt8H2h8X+q1+A1W7B0oR1nXH5ypHyGUHaduCAiJ40h
+Y0v7Oo68+zEYhVK6yLvHfcEpEJOHrhxegw95KqBxQ9gsO84CoIDui9dB/+JPNQPP
+ZNNgbjK9waLGsRIlDhYubY17xNXT/o5m3pTHg2t+c7ZJqTYnr8cNDLWmLdPJOfpj
+8mVaCtZa0sJ7GFSgkiONS/SfUH1av3yfY/657K1bc+KNmODgerMs9mO+iY9V6pQv
+bIRPpkBD6m+VvJZWAvAJ4YqKDNse6acZQRehzA5kpl5XWkFfmibsP+GIGtRO9jCq
+JcFSlKXzxqy991O15ihi0Q==</dsig:SignatureValue><dsig:KeyInfo><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEezCCA2OgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBijEUMBIGA1UEChMLZXhh
+bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMTUwMwYDVQQDFCwuc21wdGUt
+NDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1JfUFJPRFVDVElPTjElMCMGA1UELhMc
+Ym10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3PTAeFw0xMzA3MDgwOTM5MDBaFw0y
+MzA3MDQwOTM5MDBaMIGEMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UECxML
+ZXhhbXBsZS5vcmcxLzAtBgNVBAMUJkNTLnNtcHRlLTQzMC0yLkxFQUYuTk9UX0ZP
+Ul9QUk9EVUNUSU9OMSUwIwYDVQQuExxkOTVmR0R6RVJOZHhmWVBncGh2QVI4QTE4
+TDQ9MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4Ai70YT7amWSvXWO
+2LL9HrkNEQkR0HNO4kZS+2v3CnbrlSnSkLgA4rgsmSPNhHHgskr3ljzxH/MugKv3
+tLlSHywcqPxj06SUCvd7aWfUKIGR33XxZwFB9Hc6hCKMcOYxFJISfFxcknSgcmQj
+RPnLTbaCc/i/sOHQiLnZXmTVIe43VrE+zzYJO7BBnU7Xnj1MGTzz7ccooL3dp8iS
+t4E7E3mGGVMyTndIEE3fx8y1tPFigNKdPKBo1fXD7pFkL7idH+DO5Phwf/OSjS4Z
+egBCrb96fRpWvzhh6z7USOe8iKY5xDir5c4UKY15JsVw2ESxkPF9wFpFHCyXt2YS
+kDYbHQIDAQABo4HvMIHsMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgWgMB0GA1Ud
+DgQWBBR33l8YPMRE13F9g+CmG8BHwDXwvjCBrwYDVR0jBIGnMIGkgBRua3BOGrey
+uDEgB5EyNfoEWGAsPKGBiKSBhTCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDAS
+BgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5O
+T1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZt
+ZlFub2FTYz2CAQYwDQYJKoZIhvcNAQELBQADggEBAKTd8vC9j06NL727VdnYZEwa
+D4MUTnv1UW5d/FdQR87WpGV+17CKblWGJtLTfFmV9LwcGlys6ZzrLRYpBf+81vYq
+VUhl7rrU7askIiacSlfYx4riv9KH5JLmyLdo7sjX4dhfj5IH/ilan7le1shjEl0P
+UrEgYX1dt5OOnMpzaIRHU+GWVlkY3M5VDdDfMPstuPJ+MeAP1fH0Ylajhc4O5nmu
+hWfXc9qa5bxOLzNDBOsXS8hbnTpUS1qpqzea5NSogdxGIiyk/OluU1ZJJPQhf0iK
+K6U6e4+TpHKVvqUwQcPUw9TcBGIDkwJTtLF48ZhFI9Gv016SmSwUobgcDA9e97o=</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>6</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEfzCCA2egAwIBAgIBBjANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
+bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
+NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
+T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU5WhcNMjMwNzA1MDkz
+ODU5WjCBijEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
+b3JnMTUwMwYDVQQDFCwuc21wdGUtNDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1Jf
+UFJPRFVDVElPTjElMCMGA1UELhMcYm10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3
+PTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrCpaiKl7M/NYtKNQAo
+H+zUcnBTNQ6xhRIDxvPAgvcGH4tf0kpXmZ8ppTU/ZZKaFZB+/pIU+9JfiCuuquvw
+4EQnLABr61m6aJlE1v+0ygzZsCyHyiRnp6OyOTrJnyL6ygniX/ggp+7zkuh4upAl
+QUaCDqNbUfs6Wh9BvmaBllvI3PxfaqDlQVgY53Ar991BA0kcFOdd/4ZLqZwhEiiE
+Y1s3ChYLTxlqPM/UxOeRLUphmGmTSKBKc9ppTN+2/04sQJ1Ux+RZ5PmJ4+eAEcW8
+CjKM5EqZAO3EA3u88Do3uZjLXMxSGeJzs3KQX/lVZklFsO5d1eAQm9ZizfCbPPoK
+f2kCAwEAAaOB9TCB8jASBgNVHRMBAf8ECDAGAQH/AgECMAsGA1UdDwQEAwIBBjAd
+BgNVHQ4EFgQUbmtwThq3srgxIAeRMjX6BFhgLDwwga8GA1UdIwSBpzCBpIAUA7Qg
+bb/sj/jyHsjw8TKBB7AIliihgYikgYUwgYIxFDASBgNVBAoTC2V4YW1wbGUub3Jn
+MRQwEgYDVQQLEwtleGFtcGxlLm9yZzEtMCsGA1UEAxQkLnNtcHRlLTQzMC0yLlJP
+T1QuTk9UX0ZPUl9QUk9EVUNUSU9OMSUwIwYDVQQuExxuZE5EOUEvY09EbzJyVGRy
+YkxWbWZRbm9hU2M9ggEFMA0GCSqGSIb3DQEBCwUAA4IBAQBnlzVaMqhuenDQV7Zg
+aMa6IisupI3k9J6Z24fiEOGhCfAp0fiulx2oCSrXlYnJFGe6ndeWWer9UN1h+lAc
+ozKYwxsJx8jSkgwGYX5v8Cn2Sp2/gV5umCcmpZfIExEOZRmjgKzqyr658EyvmrYJ
+nqrig/N8wUWeS5EzMSiE1sVfyIZmUpKhuqmGQUXnftVMCKSrqAh+Au7ndlR77/dm
+ZBJzzX79nn4L9XqghdoFCPRt4rx1CMU26MmiuEzNcYJ3uSJc1SSz5tT69JQvp8Fj
+InhKTMv0wWysEsfE6+aARPtrqAUJBRAG83oP6L0gdJYWJagGFVoZLfjPf+v9JgYZ
+Wo+D</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>5</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEdzCCA1+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
+bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
+NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
+T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU4WhcNMjMwNzA2MDkz
+ODU4WjCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
+b3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJ
+T04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZtZlFub2FTYz0wggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTZ9Xu3AxKzsCf8haW7QtEZEauMUfo
+SkbzUmNZUYOwzuOvfhqSzO1BpRVI8/k3ZzkXR7XsWp8SbPsE17s5WPLwo4uDSro9
+XtRDOFFaZ023Ed9o2Zvoc6bwz8CpnKpiBB1XHMw9Bmi3q886f7Kk0SCsbX9OJeUC
+5/FUfwDSZUs8qjRkXYd2zjxgFGvYbokKSW1ZwkoLIRoJJhScjSnwTd/aAlcenfcw
+DV6ccjOJph4Dsru1NpCm50vi5/PRr00vBXMxdC2YgNyWONM5k8InBiU0uCmWmU5Y
+SRSc9A3cVtrKzP1WHE2978TNFqKpjEa8tgOYualSfI2pMtA23EVG14RJAgMBAAGj
+gfUwgfIwEgYDVR0TAQH/BAgwBgEB/wIBAzALBgNVHQ8EBAMCAQYwHQYDVR0OBBYE
+FAO0IG2/7I/48h7I8PEygQewCJYoMIGvBgNVHSMEgacwgaSAFAO0IG2/7I/48h7I
+8PEygQewCJYooYGIpIGFMIGCMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UE
+CxMLZXhhbXBsZS5vcmcxLTArBgNVBAMUJC5zbXB0ZS00MzAtMi5ST09ULk5PVF9G
+T1JfUFJPRFVDVElPTjElMCMGA1UELhMcbmRORDlBL2NPRG8yclRkcmJMVm1mUW5v
+YVNjPYIBBTANBgkqhkiG9w0BAQsFAAOCAQEAp2V3npo0m7Vmdhpr6mk5UybM16Hx
+M99zoKGUpm6k9PYRk8ysEhD7M8hDuwiiFoRJj1+X8V4HLwNU3JTTQzSl88pMvLg8
+Tyl4ICELg9MN/Eyfnu6iyGDfPzcsuv8xZN53211LobqW+HGK8r+slf5GPDILka8r
+GB7sFRjvq3SEGpqupKI9sJFmvHyQplJ1ixrIkSnTEuBFBrDtQj+cjrqlvRw/dReo
+3uELGe5GyvBcP0hDgrvZQmTdCLQfo4998MMhLDyxvD9/D4jpRcCrLzs2uB3Tw3kT
+1KkAjDA/33J1XCZEkb1ArS2L/I12T0DoSQqb17NE3xccY+6iRFXRMFWDIQ==</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo></dsig:Signature></CompositionPlaylist>
diff --git a/test/ref/DCP/bar/7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c_cpl.xml b/test/ref/DCP/bar/7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c_cpl.xml
deleted file mode 100644 (file)
index 2e3c5b2..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<CompositionPlaylist xmlns="http://www.smpte-ra.org/schemas/429-7/2006/CPL" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><Id>urn:uuid:7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><ContentTitleText>A Test DCP</ContentTitleText><ContentKind>feature</ContentKind><ContentVersion><Id>urn:uri:7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c_2012-07-17T04:45:18+00:00</Id><LabelText>7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c_2012-07-17T04:45:18+00:00</LabelText></ContentVersion><RatingList/><ReelList><Reel><Id>urn:uuid:947716dd-c237-4f31-a6d8-f74990b79c73</Id><AssetList><MainPicture><Id>urn:uuid:93182bd2-b1e8-41a3-b5c8-6e6564273bff</Id><AnnotationText>video.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration><KeyId>urn:uuid:6af1e0c1-c441-47f8-a502-3efd46b1fa4f</KeyId><FrameRate>24 1</FrameRate><ScreenAspectRatio>32 32</ScreenAspectRatio></MainPicture><MainSound><Id>urn:uuid:822bd341-c751-45b1-94d2-410e4ffcff1b</Id><AnnotationText>audio.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>0</Duration><KeyId>urn:uuid:f28b567b-3b9a-417a-aee4-6fc1a2c6a3af</KeyId></MainSound></AssetList></Reel></ReelList><Signer><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509SubjectName>dnQualifier=d95fGDzERNdxfYPgphvAR8A18L4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509SubjectName></dsig:X509Data></Signer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><dsig:Reference URI=""><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>nxx0sUeTkcxIK9y7+JATMU5MzGg=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>NTsZK+pcfddOjpQC9rkBn0eRDG1D5tVkqvwYd9d577AMj17GxXizxsBa+DadQ3mB
-GiWeoiJ8/VVW9PQ8N28uOLRkv0er8Jzl6UXdASrki/FbMLLRIGT1IMmxurahUBQN
-hXrk7yphM/cGpIIRwQnAyAuG0xO7L3uDUuMhRftDplwGPU52Qa3opEEX1FtxC9TT
-7NrOGIs+Ks3WtUFOe5ZbfLt1McmnYzg6FJdnAEOfv3/AWQCBd1vakONNgoWKmCiK
-/kmeDIMBK4slHfGrauBcn7KVPpLS8lSFgXI1erifibRe4iyANql10qJ+NiYVgdxm
-udPzL8evhgSC8StQOpAy1g==</dsig:SignatureValue><dsig:KeyInfo><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEezCCA2OgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBijEUMBIGA1UEChMLZXhh
-bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMTUwMwYDVQQDFCwuc21wdGUt
-NDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1JfUFJPRFVDVElPTjElMCMGA1UELhMc
-Ym10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3PTAeFw0xMzA3MDgwOTM5MDBaFw0y
-MzA3MDQwOTM5MDBaMIGEMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UECxML
-ZXhhbXBsZS5vcmcxLzAtBgNVBAMUJkNTLnNtcHRlLTQzMC0yLkxFQUYuTk9UX0ZP
-Ul9QUk9EVUNUSU9OMSUwIwYDVQQuExxkOTVmR0R6RVJOZHhmWVBncGh2QVI4QTE4
-TDQ9MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4Ai70YT7amWSvXWO
-2LL9HrkNEQkR0HNO4kZS+2v3CnbrlSnSkLgA4rgsmSPNhHHgskr3ljzxH/MugKv3
-tLlSHywcqPxj06SUCvd7aWfUKIGR33XxZwFB9Hc6hCKMcOYxFJISfFxcknSgcmQj
-RPnLTbaCc/i/sOHQiLnZXmTVIe43VrE+zzYJO7BBnU7Xnj1MGTzz7ccooL3dp8iS
-t4E7E3mGGVMyTndIEE3fx8y1tPFigNKdPKBo1fXD7pFkL7idH+DO5Phwf/OSjS4Z
-egBCrb96fRpWvzhh6z7USOe8iKY5xDir5c4UKY15JsVw2ESxkPF9wFpFHCyXt2YS
-kDYbHQIDAQABo4HvMIHsMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgWgMB0GA1Ud
-DgQWBBR33l8YPMRE13F9g+CmG8BHwDXwvjCBrwYDVR0jBIGnMIGkgBRua3BOGrey
-uDEgB5EyNfoEWGAsPKGBiKSBhTCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDAS
-BgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5O
-T1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZt
-ZlFub2FTYz2CAQYwDQYJKoZIhvcNAQELBQADggEBAKTd8vC9j06NL727VdnYZEwa
-D4MUTnv1UW5d/FdQR87WpGV+17CKblWGJtLTfFmV9LwcGlys6ZzrLRYpBf+81vYq
-VUhl7rrU7askIiacSlfYx4riv9KH5JLmyLdo7sjX4dhfj5IH/ilan7le1shjEl0P
-UrEgYX1dt5OOnMpzaIRHU+GWVlkY3M5VDdDfMPstuPJ+MeAP1fH0Ylajhc4O5nmu
-hWfXc9qa5bxOLzNDBOsXS8hbnTpUS1qpqzea5NSogdxGIiyk/OluU1ZJJPQhf0iK
-K6U6e4+TpHKVvqUwQcPUw9TcBGIDkwJTtLF48ZhFI9Gv016SmSwUobgcDA9e97o=</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>6</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEfzCCA2egAwIBAgIBBjANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
-bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
-NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
-T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU5WhcNMjMwNzA1MDkz
-ODU5WjCBijEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
-b3JnMTUwMwYDVQQDFCwuc21wdGUtNDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1Jf
-UFJPRFVDVElPTjElMCMGA1UELhMcYm10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3
-PTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrCpaiKl7M/NYtKNQAo
-H+zUcnBTNQ6xhRIDxvPAgvcGH4tf0kpXmZ8ppTU/ZZKaFZB+/pIU+9JfiCuuquvw
-4EQnLABr61m6aJlE1v+0ygzZsCyHyiRnp6OyOTrJnyL6ygniX/ggp+7zkuh4upAl
-QUaCDqNbUfs6Wh9BvmaBllvI3PxfaqDlQVgY53Ar991BA0kcFOdd/4ZLqZwhEiiE
-Y1s3ChYLTxlqPM/UxOeRLUphmGmTSKBKc9ppTN+2/04sQJ1Ux+RZ5PmJ4+eAEcW8
-CjKM5EqZAO3EA3u88Do3uZjLXMxSGeJzs3KQX/lVZklFsO5d1eAQm9ZizfCbPPoK
-f2kCAwEAAaOB9TCB8jASBgNVHRMBAf8ECDAGAQH/AgECMAsGA1UdDwQEAwIBBjAd
-BgNVHQ4EFgQUbmtwThq3srgxIAeRMjX6BFhgLDwwga8GA1UdIwSBpzCBpIAUA7Qg
-bb/sj/jyHsjw8TKBB7AIliihgYikgYUwgYIxFDASBgNVBAoTC2V4YW1wbGUub3Jn
-MRQwEgYDVQQLEwtleGFtcGxlLm9yZzEtMCsGA1UEAxQkLnNtcHRlLTQzMC0yLlJP
-T1QuTk9UX0ZPUl9QUk9EVUNUSU9OMSUwIwYDVQQuExxuZE5EOUEvY09EbzJyVGRy
-YkxWbWZRbm9hU2M9ggEFMA0GCSqGSIb3DQEBCwUAA4IBAQBnlzVaMqhuenDQV7Zg
-aMa6IisupI3k9J6Z24fiEOGhCfAp0fiulx2oCSrXlYnJFGe6ndeWWer9UN1h+lAc
-ozKYwxsJx8jSkgwGYX5v8Cn2Sp2/gV5umCcmpZfIExEOZRmjgKzqyr658EyvmrYJ
-nqrig/N8wUWeS5EzMSiE1sVfyIZmUpKhuqmGQUXnftVMCKSrqAh+Au7ndlR77/dm
-ZBJzzX79nn4L9XqghdoFCPRt4rx1CMU26MmiuEzNcYJ3uSJc1SSz5tT69JQvp8Fj
-InhKTMv0wWysEsfE6+aARPtrqAUJBRAG83oP6L0gdJYWJagGFVoZLfjPf+v9JgYZ
-Wo+D</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>5</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEdzCCA1+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
-bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
-NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
-T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU4WhcNMjMwNzA2MDkz
-ODU4WjCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
-b3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJ
-T04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZtZlFub2FTYz0wggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTZ9Xu3AxKzsCf8haW7QtEZEauMUfo
-SkbzUmNZUYOwzuOvfhqSzO1BpRVI8/k3ZzkXR7XsWp8SbPsE17s5WPLwo4uDSro9
-XtRDOFFaZ023Ed9o2Zvoc6bwz8CpnKpiBB1XHMw9Bmi3q886f7Kk0SCsbX9OJeUC
-5/FUfwDSZUs8qjRkXYd2zjxgFGvYbokKSW1ZwkoLIRoJJhScjSnwTd/aAlcenfcw
-DV6ccjOJph4Dsru1NpCm50vi5/PRr00vBXMxdC2YgNyWONM5k8InBiU0uCmWmU5Y
-SRSc9A3cVtrKzP1WHE2978TNFqKpjEa8tgOYualSfI2pMtA23EVG14RJAgMBAAGj
-gfUwgfIwEgYDVR0TAQH/BAgwBgEB/wIBAzALBgNVHQ8EBAMCAQYwHQYDVR0OBBYE
-FAO0IG2/7I/48h7I8PEygQewCJYoMIGvBgNVHSMEgacwgaSAFAO0IG2/7I/48h7I
-8PEygQewCJYooYGIpIGFMIGCMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UE
-CxMLZXhhbXBsZS5vcmcxLTArBgNVBAMUJC5zbXB0ZS00MzAtMi5ST09ULk5PVF9G
-T1JfUFJPRFVDVElPTjElMCMGA1UELhMcbmRORDlBL2NPRG8yclRkcmJMVm1mUW5v
-YVNjPYIBBTANBgkqhkiG9w0BAQsFAAOCAQEAp2V3npo0m7Vmdhpr6mk5UybM16Hx
-M99zoKGUpm6k9PYRk8ysEhD7M8hDuwiiFoRJj1+X8V4HLwNU3JTTQzSl88pMvLg8
-Tyl4ICELg9MN/Eyfnu6iyGDfPzcsuv8xZN53211LobqW+HGK8r+slf5GPDILka8r
-GB7sFRjvq3SEGpqupKI9sJFmvHyQplJ1ixrIkSnTEuBFBrDtQj+cjrqlvRw/dReo
-3uELGe5GyvBcP0hDgrvZQmTdCLQfo4998MMhLDyxvD9/D4jpRcCrLzs2uB3Tw3kT
-1KkAjDA/33J1XCZEkb1ArS2L/I12T0DoSQqb17NE3xccY+6iRFXRMFWDIQ==</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo></dsig:Signature></CompositionPlaylist>
index 6b0ae98182c8455647b1eeba6334977f9ed998c7..d785f2d605b79771cdb5e633e7368aa7cddf1815 100644 (file)
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM"><Id>urn:uuid:aa157ca6-e3e3-4b6c-a7b6-50f52fcafb7f</Id><AnnotationText>Created by OpenDCP 0.0.25</AnnotationText><Creator>OpenDCP 0.0.25</Creator><VolumeCount>1</VolumeCount><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><AssetList><Asset><Id>urn:uuid:a12d2c1f-0daa-4a30-a4cd-c4266ac172a7</Id><PackingList>true</PackingList><ChunkList><Chunk><Path>a12d2c1f-0daa-4a30-a4cd-c4266ac172a7_pkl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>8019</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c</Id><ChunkList><Chunk><Path>7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c_cpl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>8531</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:822bd341-c751-45b1-94d2-410e4ffcff1b</Id><ChunkList><Chunk><Path>audio.mxf</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>308398</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:93182bd2-b1e8-41a3-b5c8-6e6564273bff</Id><ChunkList><Chunk><Path>video.mxf</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>28840</Length></Chunk></ChunkList></Asset></AssetList></AssetMap>
+<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM"><Id>urn:uuid:7f8b3329-371b-4342-b849-a2ee840e8f45</Id><AnnotationText>Created by OpenDCP 0.0.25</AnnotationText><Creator>OpenDCP 0.0.25</Creator><VolumeCount>1</VolumeCount><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><AssetList><Asset><Id>urn:uuid:3a8a01d1-0e1c-4ff0-977d-63d32995a1fd</Id><PackingList>true</PackingList><ChunkList><Chunk><Path>3a8a01d1-0e1c-4ff0-977d-63d32995a1fd_pkl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>7691</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:793170e7-e913-404b-8942-1c7f2e5cf012</Id><ChunkList><Chunk><Path>793170e7-e913-404b-8942-1c7f2e5cf012_cpl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>8412</Length></Chunk></ChunkList></Asset></AssetList></AssetMap>
diff --git a/test/ref/DCP/bar/a12d2c1f-0daa-4a30-a4cd-c4266ac172a7_pkl.xml b/test/ref/DCP/bar/a12d2c1f-0daa-4a30-a4cd-c4266ac172a7_pkl.xml
deleted file mode 100644 (file)
index 60bc204..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<PackingList xmlns="http://www.smpte-ra.org/schemas/429-8/2007/PKL" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"><Id>urn:uuid:a12d2c1f-0daa-4a30-a4cd-c4266ac172a7</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><AssetList><Asset><Id>urn:uuid:822bd341-c751-45b1-94d2-410e4ffcff1b</Id><AnnotationText>audio.mxf</AnnotationText><Hash>00dwsDwm9AGtL2ylcUZzm/UH/P8=</Hash><Size>308398</Size><Type>application/mxf</Type></Asset><Asset><Id>urn:uuid:93182bd2-b1e8-41a3-b5c8-6e6564273bff</Id><AnnotationText>video.mxf</AnnotationText><Hash>twmVEuhyyqVkEQo6tQbCN/V4MuY=</Hash><Size>28840</Size><Type>application/mxf</Type></Asset><Asset><Id>urn:uuid:7b6616d2-9afe-4d54-a2bb-4f3f71ac6e0c</Id><Hash>mTFgIKBQeWMqD6y+8CanXIJj7pQ=</Hash><Size>8531</Size><Type>text/xml</Type></Asset></AssetList><Signer><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509SubjectName>dnQualifier=d95fGDzERNdxfYPgphvAR8A18L4=,CN=CS.smpte-430-2.LEAF.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509SubjectName></dsig:X509Data></Signer><dsig:Signature><dsig:SignedInfo><dsig:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><dsig:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><dsig:Reference URI=""><dsig:Transforms><dsig:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></dsig:Transforms><dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><dsig:DigestValue>/xGgOim2E6rPVUvuWH97evvZ3E0=</dsig:DigestValue></dsig:Reference></dsig:SignedInfo><dsig:SignatureValue>X18JIXvv5fWjLTJPJYPg/KliGBtM1mZvEjaS0m+SUaM26WpTMT52nowtT/6PcGDX
-Qd0Klm6j2YZpXnXbVapyhY9hXpm8h2dTDRR8S3r2fC6lVc6LAEPz6WRhW7F0ltZ7
-LFYnOraZOm0hegih1lM2bPin9rbsrtSguSEAYMP7z0SNAHViJ/eiY4b/va8n1eVe
-bKqmdjN7CrD5OnxobDRSgBLeO9BqtrudPJlk4rCJmXIOxPrzywdRGd18VpoIqdNZ
-hOmkgKZZrCk/QDZGKZLd1pnZ6AxxUl3Qwap+rxuTINNW8rDmKVEO8tLe/swHN/TA
-zxrk+JtqOI/nDK5QRci2nQ==</dsig:SignatureValue><dsig:KeyInfo><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=bmtwThq3srgxIAeRMjX6BFhgLDw=,CN=.smpte-430-2.INTERMEDIATE.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>7</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEezCCA2OgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBijEUMBIGA1UEChMLZXhh
-bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMTUwMwYDVQQDFCwuc21wdGUt
-NDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1JfUFJPRFVDVElPTjElMCMGA1UELhMc
-Ym10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3PTAeFw0xMzA3MDgwOTM5MDBaFw0y
-MzA3MDQwOTM5MDBaMIGEMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UECxML
-ZXhhbXBsZS5vcmcxLzAtBgNVBAMUJkNTLnNtcHRlLTQzMC0yLkxFQUYuTk9UX0ZP
-Ul9QUk9EVUNUSU9OMSUwIwYDVQQuExxkOTVmR0R6RVJOZHhmWVBncGh2QVI4QTE4
-TDQ9MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4Ai70YT7amWSvXWO
-2LL9HrkNEQkR0HNO4kZS+2v3CnbrlSnSkLgA4rgsmSPNhHHgskr3ljzxH/MugKv3
-tLlSHywcqPxj06SUCvd7aWfUKIGR33XxZwFB9Hc6hCKMcOYxFJISfFxcknSgcmQj
-RPnLTbaCc/i/sOHQiLnZXmTVIe43VrE+zzYJO7BBnU7Xnj1MGTzz7ccooL3dp8iS
-t4E7E3mGGVMyTndIEE3fx8y1tPFigNKdPKBo1fXD7pFkL7idH+DO5Phwf/OSjS4Z
-egBCrb96fRpWvzhh6z7USOe8iKY5xDir5c4UKY15JsVw2ESxkPF9wFpFHCyXt2YS
-kDYbHQIDAQABo4HvMIHsMAwGA1UdEwEB/wQCMAAwCwYDVR0PBAQDAgWgMB0GA1Ud
-DgQWBBR33l8YPMRE13F9g+CmG8BHwDXwvjCBrwYDVR0jBIGnMIGkgBRua3BOGrey
-uDEgB5EyNfoEWGAsPKGBiKSBhTCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDAS
-BgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5O
-T1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZt
-ZlFub2FTYz2CAQYwDQYJKoZIhvcNAQELBQADggEBAKTd8vC9j06NL727VdnYZEwa
-D4MUTnv1UW5d/FdQR87WpGV+17CKblWGJtLTfFmV9LwcGlys6ZzrLRYpBf+81vYq
-VUhl7rrU7askIiacSlfYx4riv9KH5JLmyLdo7sjX4dhfj5IH/ilan7le1shjEl0P
-UrEgYX1dt5OOnMpzaIRHU+GWVlkY3M5VDdDfMPstuPJ+MeAP1fH0Ylajhc4O5nmu
-hWfXc9qa5bxOLzNDBOsXS8hbnTpUS1qpqzea5NSogdxGIiyk/OluU1ZJJPQhf0iK
-K6U6e4+TpHKVvqUwQcPUw9TcBGIDkwJTtLF48ZhFI9Gv016SmSwUobgcDA9e97o=</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>6</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEfzCCA2egAwIBAgIBBjANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
-bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
-NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
-T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU5WhcNMjMwNzA1MDkz
-ODU5WjCBijEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
-b3JnMTUwMwYDVQQDFCwuc21wdGUtNDMwLTIuSU5URVJNRURJQVRFLk5PVF9GT1Jf
-UFJPRFVDVElPTjElMCMGA1UELhMcYm10d1RocTNzcmd4SUFlUk1qWDZCRmhnTER3
-PTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrCpaiKl7M/NYtKNQAo
-H+zUcnBTNQ6xhRIDxvPAgvcGH4tf0kpXmZ8ppTU/ZZKaFZB+/pIU+9JfiCuuquvw
-4EQnLABr61m6aJlE1v+0ygzZsCyHyiRnp6OyOTrJnyL6ygniX/ggp+7zkuh4upAl
-QUaCDqNbUfs6Wh9BvmaBllvI3PxfaqDlQVgY53Ar991BA0kcFOdd/4ZLqZwhEiiE
-Y1s3ChYLTxlqPM/UxOeRLUphmGmTSKBKc9ppTN+2/04sQJ1Ux+RZ5PmJ4+eAEcW8
-CjKM5EqZAO3EA3u88Do3uZjLXMxSGeJzs3KQX/lVZklFsO5d1eAQm9ZizfCbPPoK
-f2kCAwEAAaOB9TCB8jASBgNVHRMBAf8ECDAGAQH/AgECMAsGA1UdDwQEAwIBBjAd
-BgNVHQ4EFgQUbmtwThq3srgxIAeRMjX6BFhgLDwwga8GA1UdIwSBpzCBpIAUA7Qg
-bb/sj/jyHsjw8TKBB7AIliihgYikgYUwgYIxFDASBgNVBAoTC2V4YW1wbGUub3Jn
-MRQwEgYDVQQLEwtleGFtcGxlLm9yZzEtMCsGA1UEAxQkLnNtcHRlLTQzMC0yLlJP
-T1QuTk9UX0ZPUl9QUk9EVUNUSU9OMSUwIwYDVQQuExxuZE5EOUEvY09EbzJyVGRy
-YkxWbWZRbm9hU2M9ggEFMA0GCSqGSIb3DQEBCwUAA4IBAQBnlzVaMqhuenDQV7Zg
-aMa6IisupI3k9J6Z24fiEOGhCfAp0fiulx2oCSrXlYnJFGe6ndeWWer9UN1h+lAc
-ozKYwxsJx8jSkgwGYX5v8Cn2Sp2/gV5umCcmpZfIExEOZRmjgKzqyr658EyvmrYJ
-nqrig/N8wUWeS5EzMSiE1sVfyIZmUpKhuqmGQUXnftVMCKSrqAh+Au7ndlR77/dm
-ZBJzzX79nn4L9XqghdoFCPRt4rx1CMU26MmiuEzNcYJ3uSJc1SSz5tT69JQvp8Fj
-InhKTMv0wWysEsfE6+aARPtrqAUJBRAG83oP6L0gdJYWJagGFVoZLfjPf+v9JgYZ
-Wo+D</dsig:X509Certificate></dsig:X509Data><dsig:X509Data><dsig:X509IssuerSerial><dsig:X509IssuerName>dnQualifier=ndND9A/cODo2rTdrbLVmfQnoaSc=,CN=.smpte-430-2.ROOT.NOT_FOR_PRODUCTION,OU=example.org,O=example.org</dsig:X509IssuerName><dsig:X509SerialNumber>5</dsig:X509SerialNumber></dsig:X509IssuerSerial><dsig:X509Certificate>MIIEdzCCA1+gAwIBAgIBBTANBgkqhkiG9w0BAQsFADCBgjEUMBIGA1UEChMLZXhh
-bXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUub3JnMS0wKwYDVQQDFCQuc21wdGUt
-NDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJT04xJTAjBgNVBC4THG5kTkQ5QS9j
-T0RvMnJUZHJiTFZtZlFub2FTYz0wHhcNMTMwNzA4MDkzODU4WhcNMjMwNzA2MDkz
-ODU4WjCBgjEUMBIGA1UEChMLZXhhbXBsZS5vcmcxFDASBgNVBAsTC2V4YW1wbGUu
-b3JnMS0wKwYDVQQDFCQuc21wdGUtNDMwLTIuUk9PVC5OT1RfRk9SX1BST0RVQ1RJ
-T04xJTAjBgNVBC4THG5kTkQ5QS9jT0RvMnJUZHJiTFZtZlFub2FTYz0wggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDTZ9Xu3AxKzsCf8haW7QtEZEauMUfo
-SkbzUmNZUYOwzuOvfhqSzO1BpRVI8/k3ZzkXR7XsWp8SbPsE17s5WPLwo4uDSro9
-XtRDOFFaZ023Ed9o2Zvoc6bwz8CpnKpiBB1XHMw9Bmi3q886f7Kk0SCsbX9OJeUC
-5/FUfwDSZUs8qjRkXYd2zjxgFGvYbokKSW1ZwkoLIRoJJhScjSnwTd/aAlcenfcw
-DV6ccjOJph4Dsru1NpCm50vi5/PRr00vBXMxdC2YgNyWONM5k8InBiU0uCmWmU5Y
-SRSc9A3cVtrKzP1WHE2978TNFqKpjEa8tgOYualSfI2pMtA23EVG14RJAgMBAAGj
-gfUwgfIwEgYDVR0TAQH/BAgwBgEB/wIBAzALBgNVHQ8EBAMCAQYwHQYDVR0OBBYE
-FAO0IG2/7I/48h7I8PEygQewCJYoMIGvBgNVHSMEgacwgaSAFAO0IG2/7I/48h7I
-8PEygQewCJYooYGIpIGFMIGCMRQwEgYDVQQKEwtleGFtcGxlLm9yZzEUMBIGA1UE
-CxMLZXhhbXBsZS5vcmcxLTArBgNVBAMUJC5zbXB0ZS00MzAtMi5ST09ULk5PVF9G
-T1JfUFJPRFVDVElPTjElMCMGA1UELhMcbmRORDlBL2NPRG8yclRkcmJMVm1mUW5v
-YVNjPYIBBTANBgkqhkiG9w0BAQsFAAOCAQEAp2V3npo0m7Vmdhpr6mk5UybM16Hx
-M99zoKGUpm6k9PYRk8ysEhD7M8hDuwiiFoRJj1+X8V4HLwNU3JTTQzSl88pMvLg8
-Tyl4ICELg9MN/Eyfnu6iyGDfPzcsuv8xZN53211LobqW+HGK8r+slf5GPDILka8r
-GB7sFRjvq3SEGpqupKI9sJFmvHyQplJ1ixrIkSnTEuBFBrDtQj+cjrqlvRw/dReo
-3uELGe5GyvBcP0hDgrvZQmTdCLQfo4998MMhLDyxvD9/D4jpRcCrLzs2uB3Tw3kT
-1KkAjDA/33J1XCZEkb1ArS2L/I12T0DoSQqb17NE3xccY+6iRFXRMFWDIQ==</dsig:X509Certificate></dsig:X509Data></dsig:KeyInfo></dsig:Signature></PackingList>
index 9fbf73b8c35bf2f9f496ee569154f414d774212a..46dbe0461f005af3fa744bc4452a36516befd350 100644 (file)
Binary files a/test/ref/DCP/bar/audio.mxf and b/test/ref/DCP/bar/audio.mxf differ
index 2aae069f56f7f7abf1c9a61a276b26fa9eb12a9c..814f852144923055f688eb0580bef2668c6adae0 100644 (file)
Binary files a/test/ref/DCP/bar/video.mxf and b/test/ref/DCP/bar/video.mxf differ
diff --git a/test/ref/DCP/foo/18be072e-5a0f-44e1-b2eb-c8a52ae12789_pkl.xml b/test/ref/DCP/foo/18be072e-5a0f-44e1-b2eb-c8a52ae12789_pkl.xml
new file mode 100644 (file)
index 0000000..bb6018f
--- /dev/null
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<PackingList xmlns="http://www.smpte-ra.org/schemas/429-8/2007/PKL"><Id>urn:uuid:18be072e-5a0f-44e1-b2eb-c8a52ae12789</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><AssetList><Asset><Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b</Id><AnnotationText>81fb54df-e1bf-4647-8788-ea7ba154375b</AnnotationText><Hash>YM2/2MuAtSN65l2nAtelkvbSXHw=</Hash><Size>1292</Size><Type>text/xml</Type></Asset><Asset><Id>urn:uuid:46c3eb45-15e5-47d6-8684-d8641e4dc516</Id><AnnotationText>46c3eb45-15e5-47d6-8684-d8641e4dc516</AnnotationText><Hash>no3HZX+eHqH8ieD5TMjU6ttBXT0=</Hash><Size>26080</Size><Type>application/mxf</Type></Asset><Asset><Id>urn:uuid:9482e87d-292d-4e0e-a98d-c61822b60fe9</Id><AnnotationText>9482e87d-292d-4e0e-a98d-c61822b60fe9</AnnotationText><Hash>iabMZ6tQHjrgQ+cXbCH1Y4d7Fjk=</Hash><Size>161326</Size><Type>application/mxf</Type></Asset></AssetList></PackingList>
index d2d2d5ffd741bf296e60db80a7f988ffd32849a3..01a775a4e225485dc4af2062702f323c40ab53d8 100644 (file)
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<CompositionPlaylist xmlns="http://www.smpte-ra.org/schemas/429-7/2006/CPL"><Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><ContentTitleText>A Test DCP</ContentTitleText><ContentKind>feature</ContentKind><ContentVersion><Id>urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00</Id><LabelText>81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00</LabelText></ContentVersion><RatingList/><ReelList><Reel><Id>urn:uuid:379fa64c-ad71-46cf-bef7-b45624006610</Id><AssetList><MainPicture><Id>urn:uuid:d36f4bb3-c4fa-4a95-9915-6fec3110cd71</Id><AnnotationText>video.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration><FrameRate>24 1</FrameRate><ScreenAspectRatio>32 32</ScreenAspectRatio></MainPicture><MainSound><Id>urn:uuid:c38bdd62-ce03-4988-8603-195f134207c7</Id><AnnotationText>audio.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration></MainSound></AssetList></Reel></ReelList></CompositionPlaylist>
+<CompositionPlaylist xmlns="http://www.smpte-ra.org/schemas/429-7/2006/CPL"><Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><ContentTitleText>A Test DCP</ContentTitleText><ContentKind>feature</ContentKind><ContentVersion><Id>urn:uri:81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00</Id><LabelText>81fb54df-e1bf-4647-8788-ea7ba154375b_2012-07-17T04:45:18+00:00</LabelText></ContentVersion><RatingList/><ReelList><Reel><Id>urn:uuid:8e293965-f8ad-48c6-971d-261b01f65cdb</Id><AssetList><MainPicture><Id>urn:uuid:46c3eb45-15e5-47d6-8684-d8641e4dc516</Id><AnnotationText>video.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration><FrameRate>24 1</FrameRate><ScreenAspectRatio>32 32</ScreenAspectRatio></MainPicture><MainSound><Id>urn:uuid:9482e87d-292d-4e0e-a98d-c61822b60fe9</Id><AnnotationText>audio.mxf</AnnotationText><EditRate>24 1</EditRate><IntrinsicDuration>24</IntrinsicDuration><EntryPoint>0</EntryPoint><Duration>24</Duration></MainSound></AssetList></Reel></ReelList></CompositionPlaylist>
index d7de236a91d25e2ecd97ba48b58adb4c4942ce89..90b0d80fdb9fd0586ae53dfefbeec8368cb3c01d 100644 (file)
@@ -1,2 +1,2 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM"><Id>urn:uuid:b135d5cf-d180-43d8-b0b3-7373737b73bf</Id><AnnotationText>Created by OpenDCP 0.0.25</AnnotationText><Creator>OpenDCP 0.0.25</Creator><VolumeCount>1</VolumeCount><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><AssetList><Asset><Id>urn:uuid:df0e4141-13c3-4a7a-bef8-b5a04fcbc4bb</Id><PackingList>true</PackingList><ChunkList><Chunk><Path>df0e4141-13c3-4a7a-bef8-b5a04fcbc4bb_pkl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>899</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b</Id><ChunkList><Chunk><Path>81fb54df-e1bf-4647-8788-ea7ba154375b_cpl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>1292</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:c38bdd62-ce03-4988-8603-195f134207c7</Id><ChunkList><Chunk><Path>audio.mxf</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>305326</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:d36f4bb3-c4fa-4a95-9915-6fec3110cd71</Id><ChunkList><Chunk><Path>video.mxf</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>26080</Length></Chunk></ChunkList></Asset></AssetList></AssetMap>
+<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM"><Id>urn:uuid:ae8a9818-872a-4f86-8493-11dfdea03e09</Id><AnnotationText>Created by OpenDCP 0.0.25</AnnotationText><Creator>OpenDCP 0.0.25</Creator><VolumeCount>1</VolumeCount><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><AssetList><Asset><Id>urn:uuid:18be072e-5a0f-44e1-b2eb-c8a52ae12789</Id><PackingList>true</PackingList><ChunkList><Chunk><Path>18be072e-5a0f-44e1-b2eb-c8a52ae12789_pkl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>1022</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b</Id><ChunkList><Chunk><Path>81fb54df-e1bf-4647-8788-ea7ba154375b_cpl.xml</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>1292</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:46c3eb45-15e5-47d6-8684-d8641e4dc516</Id><ChunkList><Chunk><Path>video.mxf</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>26080</Length></Chunk></ChunkList></Asset><Asset><Id>urn:uuid:9482e87d-292d-4e0e-a98d-c61822b60fe9</Id><ChunkList><Chunk><Path>audio.mxf</Path><VolumeIndex>1</VolumeIndex><Offset>0</Offset><Length>161326</Length></Chunk></ChunkList></Asset></AssetList></AssetMap>
index 9bc735afc03e7d041ef4982312865b584dbd0d96..0d6ab6b196deb3208c70ac82f566524f4a2c0013 100644 (file)
Binary files a/test/ref/DCP/foo/audio.mxf and b/test/ref/DCP/foo/audio.mxf differ
diff --git a/test/ref/DCP/foo/df0e4141-13c3-4a7a-bef8-b5a04fcbc4bb_pkl.xml b/test/ref/DCP/foo/df0e4141-13c3-4a7a-bef8-b5a04fcbc4bb_pkl.xml
deleted file mode 100644 (file)
index f17c417..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<PackingList xmlns="http://www.smpte-ra.org/schemas/429-8/2007/PKL"><Id>urn:uuid:df0e4141-13c3-4a7a-bef8-b5a04fcbc4bb</Id><AnnotationText>A Test DCP</AnnotationText><IssueDate>2012-07-17T04:45:18+00:00</IssueDate><Issuer>OpenDCP 0.0.25</Issuer><Creator>OpenDCP 0.0.25</Creator><AssetList><Asset><Id>urn:uuid:c38bdd62-ce03-4988-8603-195f134207c7</Id><AnnotationText>audio.mxf</AnnotationText><Hash>+qImGHkt/XouNaJ1V/+7BtcB4VU=</Hash><Size>305326</Size><Type>application/mxf</Type></Asset><Asset><Id>urn:uuid:d36f4bb3-c4fa-4a95-9915-6fec3110cd71</Id><AnnotationText>video.mxf</AnnotationText><Hash>E2vhyxdJQhEzSQZdp31w84ZZpfk=</Hash><Size>26080</Size><Type>application/mxf</Type></Asset><Asset><Id>urn:uuid:81fb54df-e1bf-4647-8788-ea7ba154375b</Id><Hash>fd/4cs9kXW7PyDSmJdXb4NkC2qU=</Hash><Size>1292</Size><Type>text/xml</Type></Asset></AssetList></PackingList>
index 645fb85affaaca1d273f5fff2b5f7abf10a850c5..dc0afeabcffbdb830b9ccc725e5d1634c9522eb6 100644 (file)
Binary files a/test/ref/DCP/foo/video.mxf and b/test/ref/DCP/foo/video.mxf differ
index 3a982d1b6d8472651cb34d9834e5c407c76c1435..553c6291b8ea3c21f163bd7021eb32cc77bc60c6 100644 (file)
@@ -1,15 +1,36 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
 #include <iostream>
 #include "dcp.h"
 #include "cpl.h"
 #include "reel.h"
-#include "subtitle_asset.h"
+#include "subtitle_content.h"
+#include "reel_subtitle_asset.h"
 #include "exceptions.h"
 
 using std::cout;
 using std::cerr;
 using std::list;
+using std::string;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 int
 main (int argc, char* argv[])
@@ -21,7 +42,7 @@ main (int argc, char* argv[])
                }
                
                DCP* dcp = new DCP (argv[1]);
-               dcp->read (false);
+               dcp->read (true);
                
                list<shared_ptr<CPL> > cpls = dcp->cpls ();
                for (list<boost::shared_ptr<CPL> >::iterator i = cpls.begin(); i != cpls.end(); ++i) {
@@ -30,7 +51,7 @@ main (int argc, char* argv[])
                        for (list<shared_ptr<Reel> >::iterator j = reels.begin(); j != reels.end(); ++j) {
                                
                                if ((*j)->main_subtitle()) {
-                                       (*j)->main_subtitle()->write_xml ();
+                                       (*j)->main_subtitle()->subtitle_content()->write_xml ();
                                }
                        }
                }
index a6ee20977bb7599e9a31eae1579dc19516789b5a..ef1f1f412018e911bc8d4cffe9ded78ecb82a65c 100644 (file)
 
 */
 
-#include <iostream>
-#include <boost/test/unit_test.hpp>
 #include "certificates.h"
-#include "kdm.h"
+#include "decrypted_kdm.h"
+#include "encrypted_kdm.h"
 #include "signer.h"
-#include "mono_picture_asset.h"
-#include "sound_asset.h"
+#include "mono_picture_mxf.h"
+#include "sound_mxf.h"
 #include "reel.h"
 #include "test.h"
 #include "cpl.h"
 #include "mono_picture_frame.h"
 #include "argb_frame.h"
 #include "signer_chain.h"
+#include "mono_picture_mxf_writer.h"
+#include "reel_picture_asset.h"
+#include "reel_mono_picture_asset.h"
+#include "file.h"
+#include <boost/test/unit_test.hpp>
+#include <iostream>
 
 using std::list;
 using boost::shared_ptr;
@@ -39,15 +44,15 @@ BOOST_AUTO_TEST_CASE (round_trip_test)
 {
        boost::filesystem::remove_all ("build/test/signer");
        boost::filesystem::create_directory ("build/test/signer");
-       libdcp::make_signer_chain ("build/test/signer", "openssl");
+       dcp::make_signer_chain ("build/test/signer", "openssl");
        
-       libdcp::CertificateChain chain;
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/ca.self-signed.pem"))));
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/intermediate.signed.pem"))));
-       chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (boost::filesystem::path ("build/test/signer/leaf.signed.pem"))));
+       dcp::CertificateChain chain;
+       chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/ca.self-signed.pem"))));
+       chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/intermediate.signed.pem"))));
+       chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (boost::filesystem::path ("build/test/signer/leaf.signed.pem"))));
 
-       shared_ptr<libdcp::Signer> signer (
-               new libdcp::Signer (
+       shared_ptr<dcp::Signer> signer (
+               new dcp::Signer (
                        chain,
                        "test/data/signer.key"
                        )
@@ -56,43 +61,46 @@ BOOST_AUTO_TEST_CASE (round_trip_test)
        boost::filesystem::path work_dir = "build/test/round_trip_test";
        boost::filesystem::create_directory (work_dir);
 
-       shared_ptr<libdcp::MonoPictureAsset> asset_A (new libdcp::MonoPictureAsset (work_dir, "video.mxf"));
-       asset_A->set_edit_rate (24);
-       asset_A->set_intrinsic_duration (24);
-       asset_A->set_size (libdcp::Size (32, 32));
-       asset_A->create (j2c);
+       shared_ptr<dcp::MonoPictureMXF> mxf_A (new dcp::MonoPictureMXF (dcp::Fraction (24, 1)));
+       shared_ptr<dcp::PictureMXFWriter> writer = mxf_A->start_write (work_dir / "video.mxf", dcp::SMPTE, false);
+       dcp::File j2c ("test/data/32x32_red_square.j2c");
+       for (int i = 0; i < 24; ++i) {
+               writer->write (j2c.data (), j2c.size ());
+       }
+       writer->finalize ();
 
-       libdcp::Key key;
+       dcp::Key key;
 
-       asset_A->set_key (key);
+       mxf_A->set_key (key);
 
-       shared_ptr<libdcp::CPL> cpl (new libdcp::CPL (work_dir, "A Test DCP", libdcp::FEATURE, 24, 24));
-       cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (asset_A, shared_ptr<libdcp::SoundAsset> (), shared_ptr<libdcp::SubtitleAsset> ())));
+       shared_ptr<dcp::CPL> cpl (new dcp::CPL ("A Test DCP", dcp::FEATURE));
+       shared_ptr<dcp::Reel> reel (new dcp::Reel ());
+       reel->add (shared_ptr<dcp::ReelMonoPictureAsset> (new dcp::ReelMonoPictureAsset (mxf_A, 0)));
+       cpl->add (reel);
 
        /* A KDM using our certificate chain's leaf key pair */
-       libdcp::KDM kdm_A (
+       dcp::DecryptedKDM kdm_A (
                cpl,
-               signer,
-               signer->certificates().leaf(),
-               boost::posix_time::time_from_string ("2013-01-01 00:00:00"),
-               boost::posix_time::time_from_string ("2013-01-08 00:00:00"),
+               dcp::LocalTime ("2013-01-01T00:00:00+00:00"),
+               dcp::LocalTime ("2013-01-08T00:00:00+00:00"),
                "libdcp",
+               "test",
                "2012-07-17T04:45:18+00:00"
                );
 
        boost::filesystem::path const kdm_file = work_dir / "kdm.xml";
 
-       kdm_A.as_xml (kdm_file);
+       kdm_A.encrypt(signer, signer->certificates().leaf()).as_xml (kdm_file);
 
        /* Reload the KDM, using our private key to decrypt it */
-       libdcp::KDM kdm_B (kdm_file, "build/test/signer/leaf.key");
+       dcp::DecryptedKDM kdm_B (dcp::EncryptedKDM (kdm_file), "build/test/signer/leaf.key");
 
        /* Check that the decrypted KDMKeys are the same as the ones we started with */
        BOOST_CHECK_EQUAL (kdm_A.keys().size(), kdm_B.keys().size());
-       list<libdcp::KDMKey> keys_A = kdm_A.keys ();
-       list<libdcp::KDMKey> keys_B = kdm_B.keys ();
-       list<libdcp::KDMKey>::const_iterator i = keys_A.begin();
-       list<libdcp::KDMKey>::const_iterator j = keys_B.begin();
+       list<dcp::DecryptedKDMKey> keys_A = kdm_A.keys ();
+       list<dcp::DecryptedKDMKey> keys_B = kdm_B.keys ();
+       list<dcp::DecryptedKDMKey>::const_iterator i = keys_A.begin();
+       list<dcp::DecryptedKDMKey>::const_iterator j = keys_B.begin();
        while (i != keys_A.end ()) {
                BOOST_CHECK (*i == *j);
                ++i;
@@ -100,14 +108,15 @@ BOOST_AUTO_TEST_CASE (round_trip_test)
        }
 
        /* Reload the picture MXF */
-       shared_ptr<libdcp::MonoPictureAsset> asset_B (
-               new libdcp::MonoPictureAsset (work_dir, "video.mxf")
+       shared_ptr<dcp::MonoPictureMXF> mxf_B (
+               new dcp::MonoPictureMXF (work_dir / "video.mxf")
                );
 
-       asset_B->set_key (kdm_B.keys().front().key());
+       BOOST_CHECK (!kdm_B.keys().empty ());
+       mxf_B->set_key (kdm_B.keys().front().key());
 
-       shared_ptr<libdcp::ARGBFrame> frame_A = asset_A->get_frame(0)->argb_frame ();
-       shared_ptr<libdcp::ARGBFrame> frame_B = asset_B->get_frame(0)->argb_frame ();
+       shared_ptr<dcp::ARGBFrame> frame_A = mxf_A->get_frame(0)->argb_frame ();
+       shared_ptr<dcp::ARGBFrame> frame_B = mxf_B->get_frame(0)->argb_frame ();
        BOOST_CHECK_EQUAL (frame_A->size().width, frame_B->size().width);
        BOOST_CHECK_EQUAL (frame_A->size().height, frame_B->size().height);
        BOOST_CHECK_EQUAL (memcmp (frame_A->data(), frame_B->data(), frame_A->size().width * frame_A->size().height), 0);
index f0a7e96f1039d7c6ba7817203edccc008ef961d9..f3f4baf979be5ac33c8ceb8feaee75a26cf064b8 100644 (file)
@@ -1,5 +1,5 @@
 #include <iostream>
-#include "subtitle_asset.h"
+#include "subtitle_content.h"
 
 using namespace std;
 
@@ -10,8 +10,7 @@ int main (int argc, char* argv[])
                exit (EXIT_FAILURE);
        }
        
-       libdcp::SubtitleAsset s ("foo", "bar", "baz");
-       s.read_xml (argv[1]);
+       dcp::SubtitleContent s (argv[1], false);
        cout << s.xml_as_string ();
        return 0;
 }
index a8fa78b7f5d050b253688d115861332bd95ef523..2f7331949995dc281d55f94929b9cb87086085e7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+#include "subtitle_content.h"
+#include "subtitle_string.h"
 #include <boost/test/unit_test.hpp>
-#include "subtitle_asset.h"
 
 using std::list;
 using boost::shared_ptr;
 
-/* Load a subtitle asset from XML and check that it is read correctly */
+/* Load some subtitle content from XML and check that it is read correctly */
 BOOST_AUTO_TEST_CASE (subtitles1)
 {
-       libdcp::SubtitleAsset subs ("test/data", "subs1.xml");
+       dcp::SubtitleContent subs ("test/data/subs1.xml", false);
 
        BOOST_CHECK_EQUAL (subs.language(), "French");
 
-       list<shared_ptr<libdcp::Subtitle> > s = subs.subtitles_at (libdcp::Time (0, 0, 6, 1));
+       list<shared_ptr<dcp::SubtitleString> > s = subs.subtitles_at (dcp::Time (0, 0, 6, 1));
        BOOST_CHECK_EQUAL (s.size(), 1);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   39,
-                                  libdcp::Time (0, 0, 5, 198),
-                                  libdcp::Time (0, 0, 7, 115),
+                                  dcp::Time (0, 0, 5, 198),
+                                  dcp::Time (0, 0, 7, 115),
                                   15,
-                                  libdcp::BOTTOM,
+                                  dcp::BOTTOM,
                                   "My jacket was Idi Amin's",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 1),
-                                  libdcp::Time (0, 0, 0, 1)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 1),
+                                  dcp::Time (0, 0, 0, 1)
                                   ));
                                                         
-       s = subs.subtitles_at (libdcp::Time (0, 0, 7, 190));
+       s = subs.subtitles_at (dcp::Time (0, 0, 7, 190));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   39,
-                                  libdcp::Time (0, 0, 7, 177),
-                                  libdcp::Time (0, 0, 11, 31),
+                                  dcp::Time (0, 0, 7, 177),
+                                  dcp::Time (0, 0, 11, 31),
                                   21,
-                                  libdcp::BOTTOM,
+                                  dcp::BOTTOM,
                                   "My corset was H.M. The Queen's",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 1),
-                                  libdcp::Time (0, 0, 0, 1)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 1),
+                                  dcp::Time (0, 0, 0, 1)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   39,
-                                  libdcp::Time (0, 0, 7, 177),
-                                  libdcp::Time (0, 0, 11, 31),
+                                  dcp::Time (0, 0, 7, 177),
+                                  dcp::Time (0, 0, 11, 31),
                                   15,
-                                  libdcp::BOTTOM,
+                                  dcp::BOTTOM,
                                   "My large wonderbra",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 1),
-                                  libdcp::Time (0, 0, 0, 1)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 1),
+                                  dcp::Time (0, 0, 0, 1)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 0, 11, 95));
+       s = subs.subtitles_at (dcp::Time (0, 0, 11, 95));
        BOOST_CHECK_EQUAL (s.size(), 1);
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   39,
-                                  libdcp::Time (0, 0, 11, 94),
-                                  libdcp::Time (0, 0, 13, 63),
+                                  dcp::Time (0, 0, 11, 94),
+                                  dcp::Time (0, 0, 13, 63),
                                   15,
-                                  libdcp::BOTTOM,
+                                  dcp::BOTTOM,
                                   "Once belonged to the Shah",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 1),
-                                  libdcp::Time (0, 0, 0, 1)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 1),
+                                  dcp::Time (0, 0, 0, 1)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 0, 14, 42));
+       s = subs.subtitles_at (dcp::Time (0, 0, 14, 42));
        BOOST_CHECK_EQUAL (s.size(), 1);
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   39,
-                                  libdcp::Time (0, 0, 13, 104),
-                                  libdcp::Time (0, 0, 15, 177),
+                                  dcp::Time (0, 0, 13, 104),
+                                  dcp::Time (0, 0, 15, 177),
                                   15,
-                                  libdcp::BOTTOM,
+                                  dcp::BOTTOM,
                                   "And these are Roy Hattersley's jeans",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 1),
-                                  libdcp::Time (0, 0, 0, 1)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 1),
+                                  dcp::Time (0, 0, 0, 1)
                                   ));
 }
 
 /** And similarly for another one */
 BOOST_AUTO_TEST_CASE (subtitles2)
 {
-       libdcp::SubtitleAsset subs ("test/data", "subs2.xml");
+       dcp::SubtitleContent subs ("test/data/subs2.xml", false);
 
-       list<shared_ptr<libdcp::Subtitle> > s = subs.subtitles_at (libdcp::Time (0, 0, 42, 100));
+       list<shared_ptr<dcp::SubtitleString> > s = subs.subtitles_at (dcp::Time (0, 0, 42, 100));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 0, 41, 62),
-                                  libdcp::Time (0, 0, 43, 52),
+                                  dcp::Time (0, 0, 41, 62),
+                                  dcp::Time (0, 0, 43, 52),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "At afternoon tea with John Peel",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 0, 41, 62),
-                                  libdcp::Time (0, 0, 43, 52),
+                                  dcp::Time (0, 0, 41, 62),
+                                  dcp::Time (0, 0, 43, 52),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "I enquired if his accent was real",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 0, 50, 50));
+       s = subs.subtitles_at (dcp::Time (0, 0, 50, 50));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 0, 50, 42),
-                                  libdcp::Time (0, 0, 52, 21),
+                                  dcp::Time (0, 0, 50, 42),
+                                  dcp::Time (0, 0, 52, 21),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "He said \"out of the house",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 0, 50, 42),
-                                  libdcp::Time (0, 0, 52, 21),
+                                  dcp::Time (0, 0, 50, 42),
+                                  dcp::Time (0, 0, 52, 21),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "I'm incredibly scouse",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 1, 2, 300));
+       s = subs.subtitles_at (dcp::Time (0, 1, 2, 300));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 2, 208),
-                                  libdcp::Time (0, 1, 4, 10),
+                                  dcp::Time (0, 1, 2, 208),
+                                  dcp::Time (0, 1, 4, 10),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "At home it depends how I feel.\"",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 2, 208),
-                                  libdcp::Time (0, 1, 4, 10),
+                                  dcp::Time (0, 1, 2, 208),
+                                  dcp::Time (0, 1, 4, 10),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "I spent a long weekend in Brighton",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 1, 15, 50));
+       s = subs.subtitles_at (dcp::Time (0, 1, 15, 50));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 15, 42),
-                                  libdcp::Time (0, 1, 16, 42),
+                                  dcp::Time (0, 1, 15, 42),
+                                  dcp::Time (0, 1, 16, 42),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "With the legendary Miss Enid Blyton",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 15, 42),
-                                  libdcp::Time (0, 1, 16, 42),
+                                  dcp::Time (0, 1, 15, 42),
+                                  dcp::Time (0, 1, 16, 42),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "She said \"you be Noddy",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 1, 27, 200));
+       s = subs.subtitles_at (dcp::Time (0, 1, 27, 200));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 27, 115),
-                                  libdcp::Time (0, 1, 28, 208),
+                                  dcp::Time (0, 1, 27, 115),
+                                  dcp::Time (0, 1, 28, 208),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "That curious creature the Sphinx",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 27, 115),
-                                  libdcp::Time (0, 1, 28, 208),
+                                  dcp::Time (0, 1, 27, 115),
+                                  dcp::Time (0, 1, 28, 208),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "Is smarter than anyone thinks",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 1, 42, 300));
+       s = subs.subtitles_at (dcp::Time (0, 1, 42, 300));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 42, 229),
-                                  libdcp::Time (0, 1, 45, 62),
+                                  dcp::Time (0, 1, 42, 229),
+                                  dcp::Time (0, 1, 45, 62),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "It sits there and smirks",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 42, 229),
-                                  libdcp::Time (0, 1, 45, 62),
+                                  dcp::Time (0, 1, 42, 229),
+                                  dcp::Time (0, 1, 45, 62),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "And you don't think it works",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 1, 45, 200));
+       s = subs.subtitles_at (dcp::Time (0, 1, 45, 200));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 45, 146),
-                                  libdcp::Time (0, 1, 47, 94),
+                                  dcp::Time (0, 1, 45, 146),
+                                  dcp::Time (0, 1, 47, 94),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "Then when you're not looking, it winks.",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 45, 146),
-                                  libdcp::Time (0, 1, 47, 94),
+                                  dcp::Time (0, 1, 45, 146),
+                                  dcp::Time (0, 1, 47, 94),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "When it snows you will find Sister Sledge",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 1, 47, 249));
+       s = subs.subtitles_at (dcp::Time (0, 1, 47, 249));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 47, 146),
-                                  libdcp::Time (0, 1, 48, 167),
+                                  dcp::Time (0, 1, 47, 146),
+                                  dcp::Time (0, 1, 48, 167),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "Out mooning, at night, on the ledge",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   false,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 1, 47, 146),
-                                  libdcp::Time (0, 1, 48, 167),
+                                  dcp::Time (0, 1, 47, 146),
+                                  dcp::Time (0, 1, 48, 167),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "One storey down",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
-       s = subs.subtitles_at (libdcp::Time (0, 2, 6, 210));
+       s = subs.subtitles_at (dcp::Time (0, 2, 6, 210));
        BOOST_CHECK_EQUAL (s.size(), 2);
-       BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.front().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 2, 5, 208),
-                                  libdcp::Time (0, 2, 7, 31),
+                                  dcp::Time (0, 2, 5, 208),
+                                  dcp::Time (0, 2, 7, 31),
                                   89,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "HELLO",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
-       BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle (
+       BOOST_CHECK_EQUAL (*(s.back().get()), dcp::SubtitleString (
                                   "Arial",
                                   true,
-                                  libdcp::Color (255, 255, 255),
+                                  dcp::Color (255, 255, 255),
                                   42,
-                                  libdcp::Time (0, 2, 5, 208),
-                                  libdcp::Time (0, 2, 7, 31),
+                                  dcp::Time (0, 2, 5, 208),
+                                  dcp::Time (0, 2, 7, 31),
                                   95,
-                                  libdcp::TOP,
+                                  dcp::TOP,
                                   "WORLD",
-                                  libdcp::BORDER,
-                                  libdcp::Color (0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0),
-                                  libdcp::Time (0, 0, 0, 0)
+                                  dcp::BORDER,
+                                  dcp::Color (0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0),
+                                  dcp::Time (0, 0, 0, 0)
                                   ));
 
        
index f51b019894db8a505c8c2ae0640f0b199078261a..995a5b23db488e67935d12c9113589cf4bf5d54c 100644 (file)
@@ -21,6 +21,7 @@
 #define BOOST_TEST_MODULE libdcp_test
 #include <boost/test/unit_test.hpp>
 #include "util.h"
+#include "test.h"
 
 using std::string;
 
@@ -30,7 +31,7 @@ struct TestConfig
 {
        TestConfig()
        {
-               libdcp::init ();
+               dcp::init ();
                if (boost::unit_test::framework::master_test_suite().argc >= 2) {
                        private_test = boost::unit_test::framework::master_test_suite().argv[1];
                }
@@ -46,7 +47,7 @@ j2c (int)
 }
 
 boost::filesystem::path
-wav (libdcp::Channel)
+wav (dcp::Channel)
 {
        return "test/data/1s_24-bit_48k_silence.wav";
 }
index 47a615d8df46bc7823ef1b3d2872647cacc0934b..f139fa283313ff14935fe355c07d3301d03313ad 100644 (file)
@@ -17,6 +17,4 @@
 
 */
 
-extern boost::filesystem::path j2c (int);
-extern boost::filesystem::path wav (libdcp::Channel);
 extern std::string private_test;
diff --git a/test/utc_offset_to_string_test.cc b/test/utc_offset_to_string_test.cc
deleted file mode 100644 (file)
index af9c653..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program 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.
-
-    This program 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 this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "metadata.h"
-#include "util.h"
-
-/** Test libdcp::utc_offset_to_string */
-BOOST_AUTO_TEST_CASE (utc_offset_to_string_test)
-{
-       BOOST_CHECK_EQUAL (libdcp::utc_offset_to_string (30), "+00:30");
-       BOOST_CHECK_EQUAL (libdcp::utc_offset_to_string (60), "+01:00");
-       BOOST_CHECK_EQUAL (libdcp::utc_offset_to_string (61), "+01:01");
-       BOOST_CHECK_EQUAL (libdcp::utc_offset_to_string (7 * 60), "+07:00");
-       BOOST_CHECK_EQUAL (libdcp::utc_offset_to_string (-11 * 60), "-11:00");
-}
index f7114e90752d59a1257f58e45ade2825e2e47b7f..15f22f57db5806099825661115d4b19c8af2ea92 100644 (file)
@@ -47,7 +47,7 @@ BOOST_AUTO_TEST_CASE (base64_decode_test)
        }
 
        unsigned char decoded[N];
-       int const r = libdcp::base64_decode (s, decoded, N);
+       int const r = dcp::base64_decode (s, decoded, N);
        BOOST_CHECK_EQUAL (r, N);
 
        for (int i = 0; i < N; ++i) {
@@ -57,16 +57,59 @@ BOOST_AUTO_TEST_CASE (base64_decode_test)
 
 BOOST_AUTO_TEST_CASE (content_kind_test)
 {
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("feature"), libdcp::FEATURE);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("Feature"), libdcp::FEATURE);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("FeaturE"), libdcp::FEATURE);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("Short"), libdcp::SHORT);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("trailer"), libdcp::TRAILER);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("test"), libdcp::TEST);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("transitional"), libdcp::TRANSITIONAL);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("rating"), libdcp::RATING);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("teaser"), libdcp::TEASER);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("policy"), libdcp::POLICY);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("psa"), libdcp::PUBLIC_SERVICE_ANNOUNCEMENT);
-       BOOST_CHECK_EQUAL (libdcp::content_kind_from_string ("advertisement"), libdcp::ADVERTISEMENT);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("feature"), dcp::FEATURE);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("Feature"), dcp::FEATURE);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("FeaturE"), dcp::FEATURE);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("Short"), dcp::SHORT);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("trailer"), dcp::TRAILER);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("test"), dcp::TEST);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("transitional"), dcp::TRANSITIONAL);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("rating"), dcp::RATING);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("teaser"), dcp::TEASER);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("policy"), dcp::POLICY);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("psa"), dcp::PUBLIC_SERVICE_ANNOUNCEMENT);
+       BOOST_CHECK_EQUAL (dcp::content_kind_from_string ("advertisement"), dcp::ADVERTISEMENT);
+}
+
+BOOST_AUTO_TEST_CASE (relative_to_root_test)
+{
+       {
+               boost::filesystem::path root = "a";
+               root /= "b";
+               
+               boost::filesystem::path file = "a";
+               file /= "b";
+               file /= "c";
+               
+               boost::optional<boost::filesystem::path> rel = dcp::relative_to_root (root, file);
+               BOOST_CHECK (rel);
+               BOOST_CHECK_EQUAL (rel.get(), boost::filesystem::path ("c"));
+       }
+
+       {
+               boost::filesystem::path root = "a";
+               root /= "b";
+               root /= "c";
+               
+               boost::filesystem::path file = "a";
+               file /= "b";
+               
+               boost::optional<boost::filesystem::path> rel = dcp::relative_to_root (root, file);
+               BOOST_CHECK (!rel);
+       }
+
+       {
+               boost::filesystem::path root = "a";
+               
+               boost::filesystem::path file = "a";
+               file /= "b";
+               file /= "c";
+               
+               boost::optional<boost::filesystem::path> rel = dcp::relative_to_root (root, file);
+               BOOST_CHECK (rel);
+
+               boost::filesystem::path check = "b";
+               check /= "c";
+               BOOST_CHECK_EQUAL (rel.get(), check);
+       }
 }
index 7bb682522f47b1bd5261250e1236039d6199fc58..7e7122b178481d18fade0725bca64eb244409725 100644 (file)
@@ -4,58 +4,55 @@ def configure(conf):
     else:
         boost_lib_suffix = ''
 
-    conf.check_cxx(fragment = """
-                              #define BOOST_TEST_MODULE Config test\n
-                             #include <boost/test/unit_test.hpp>\n
-                              int main() {}
-                              """,
-                              msg = 'Checking for boost unit testing library',
-                              lib = 'boost_unit_test_framework%s' % boost_lib_suffix,
-                              uselib_store = 'BOOST_TEST')
+    conf.check_cxx(fragment="""
+                            #define BOOST_TEST_MODULE Config test\n
+                           #include <boost/test/unit_test.hpp>\n
+                            int main() {}
+                            """,
+                              msg='Checking for boost unit testing library',
+                              lib='boost_unit_test_framework%s' % boost_lib_suffix,
+                              uselib_store='BOOST_TEST')
 
     conf.env.prepend_value('LINKFLAGS', '-Lsrc')
 
 def build(bld):
-    obj = bld(features = 'cxx cxxprogram')
+    obj = bld(features='cxx cxxprogram')
     obj.name   = 'tests'
-    obj.uselib = 'BOOST_TEST OPENJPEG CXML XMLSEC1'
-    obj.use    = 'libdcp'
+    obj.uselib = 'BOOST_TEST OPENJPEG CXML XMLSEC1 SNDFILE'
+    obj.use    = 'libdcp%s' % bld.env.API_VERSION
     obj.source = """
                  certificates_test.cc
                  color_test.cc
-                 cpl_sar.cc
+                 cpl_sar_test.cc
                  dcp_test.cc
                  dcp_time_test.cc
                  decryption_test.cc
                  encryption_test.cc
-                 error_test.cc
                  frame_info_test.cc
-                 kdm_key_test.cc
+                 local_time_test.cc
                  kdm_test.cc
-                 lut_test.cc
                  read_dcp_test.cc
                  recovery_test.cc
                  round_trip_test.cc
                  subtitle_tests.cc
                  test.cc 
-                 utc_offset_to_string_test.cc
                  util_test.cc
                  """
     obj.target = 'tests'
     obj.install_path = ''
 
-    obj = bld(features = 'cxx cxxprogram')
+    obj = bld(features='cxx cxxprogram')
     obj.name   = 'subs_in_out'
     obj.uselib = 'BOOST_TEST OPENJPEG CXML'
-    obj.use    = 'libdcp'
+    obj.use    = 'libdcp%s' % bld.env.API_VERSION
     obj.source = 'subs_in_out.cc'
     obj.target = 'subs_in_out'
     obj.install_path = ''
 
-    obj = bld(features = 'cxx cxxprogram')
+    obj = bld(features='cxx cxxprogram')
     obj.name   = 'rewrite_subs'
     obj.uselib = 'BOOST_TEST OPENJPEG CXML'
-    obj.use    = 'libdcp'
+    obj.use    = 'libdcp%s' % bld.env.API_VERSION
     obj.source = 'rewrite_subs.cc'
     obj.target = 'rewrite_subs'
     obj.install_path = ''
diff --git a/tools/common.cc b/tools/common.cc
new file mode 100644 (file)
index 0000000..1c0c1df
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "common.h"
+#include "dcp.h"
+
+using std::list;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+void
+dcp::filter_errors (dcp::DCP::ReadErrors& errors, bool ignore_missing_assets)
+{
+       for (DCP::ReadErrors::iterator i = errors.begin(); i != errors.end(); ) {
+
+               DCP::ReadErrors::iterator tmp = i;
+               ++tmp;
+               
+               if (ignore_missing_assets && dynamic_pointer_cast<MissingAssetError> (*i)) {
+                       errors.erase (i);
+               }
+
+               i = tmp;
+       }
+}
+
diff --git a/tools/common.h b/tools/common.h
new file mode 100644 (file)
index 0000000..a9c657c
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "exceptions.h"
+
+namespace dcp {
+
+extern void filter_errors (std::list<boost::shared_ptr<DCPReadError> >& errors, bool ignore_missing_assets);
+
+}
index 020c7ce1751a6513080a87debd7e398d4a120e96..75664810ee6059b88ac24a9bb8976951a596faee 100644 (file)
 #include <getopt.h>
 #include "dcp.h"
 #include "exceptions.h"
+#include "common.h"
 
 using namespace std;
 using namespace boost;
-using namespace libdcp;
+using namespace dcp;
 
 static bool verbose = false;
 
@@ -33,12 +34,14 @@ static void
 help (string n)
 {
        cerr << "Syntax: " << n << " [OPTION] <DCP> <DCP>\n"
-            << "  -V, --version        show libdcp version\n"
-            << "  -h, --help           show this help\n"
-            << "  -v, --verbose        be verbose\n"
-            << "  -n, --names          allow differing MXF names\n"
-            << "  -m, --mean-pixel     maximum allowed mean pixel error (default 5)\n"
-            << "  -s, --std-dev-pixel  maximum allowed standard deviation of pixel error (default 5)\n"
+            << "  -V, --version                show libdcp version\n"
+            << "  -h, --help                   show this help\n"
+            << "  -v, --verbose                be verbose\n"
+            << "  -n, --names                  allow differing MXF names\n"
+            << "  -m, --mean-pixel             maximum allowed mean pixel error (default 5)\n"
+            << "  -s, --std-dev-pixel          maximum allowed standard deviation of pixel error (default 5)\n"
+            << "  -k, --keep-going             carry on in the event of errors, if possible\n"
+            << "      --ignore-missing-assets  ignore missing asset files\n"
             << "\n"
             << "The <DCP>s are the DCP directories to compare.\n"
             << "Comparison is of metadata and content, ignoring timestamps\n"
@@ -53,12 +56,34 @@ note (NoteType t, string n)
        }
 }
 
+DCP *
+load_dcp (boost::filesystem::path path, bool keep_going, bool ignore_missing_assets)
+{
+       DCP* dcp = 0;
+       try {
+               dcp = new DCP (path);
+               DCP::ReadErrors errors;
+               dcp->read (keep_going, &errors);
+               filter_errors (errors, ignore_missing_assets);
+               for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
+                       cerr << (*i)->what() << "\n";
+               }
+       } catch (FileError& e) {
+               cerr << "Could not read DCP " << path.string() << "; " << e.what() << " " << e.filename() << "\n";
+               exit (EXIT_FAILURE);
+       }
+
+       return dcp;
+}
+
 int
 main (int argc, char* argv[])
 {
        EqualityOptions options;
        options.max_mean_pixel_error = 5;
        options.max_std_dev_pixel_error = 5;
+       bool keep_going = false;
+       bool ignore_missing_assets = false;
        
        int option_index = 0;
        while (1) {
@@ -69,10 +94,12 @@ main (int argc, char* argv[])
                        { "names", no_argument, 0, 'n'},
                        { "mean-pixel", required_argument, 0, 'm'},
                        { "std-dev-pixel", required_argument, 0, 's'},
+                       { "keep-going", no_argument, 0, 'k'},
+                       { "ignore-missing-assets", no_argument, 0, 'A'},
                        { 0, 0, 0, 0 }
                };
 
-               int c = getopt_long (argc, argv, "Vhvnm:s:", long_options, &option_index);
+               int c = getopt_long (argc, argv, "Vhvnm:s:kA", long_options, &option_index);
 
                if (c == -1) {
                        break;
@@ -97,6 +124,12 @@ main (int argc, char* argv[])
                case 's':
                        options.max_std_dev_pixel_error = atof (optarg);
                        break;
+               case 'k':
+                       keep_going = true;
+                       break;
+               case 'A':
+                       ignore_missing_assets = true;
+                       break;
                }
        }
 
@@ -115,23 +148,8 @@ main (int argc, char* argv[])
                exit (EXIT_FAILURE);
        }
 
-       DCP* a = 0;
-       try {
-               a = new DCP (argv[optind]);
-               a->read ();
-       } catch (FileError& e) {
-               cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << " " << e.filename() << "\n";
-               exit (EXIT_FAILURE);
-       }
-
-       DCP* b = 0;
-       try {
-               b = new DCP (argv[optind + 1]);
-               b->read ();
-       } catch (FileError& e) {
-               cerr << "Could not read DCP " << argv[optind + 1] << "; " << e.what() << " " << e.filename() << "\n";
-               exit (EXIT_FAILURE);
-       }
+       DCP* a = load_dcp (argv[optind], keep_going, ignore_missing_assets);
+       DCP* b = load_dcp (argv[optind + 1], keep_going, ignore_missing_assets);
 
        /* I think this is just below the LSB at 16-bits (ie the 8th most significant bit at 24-bit) */
        options.max_audio_sample_error = 255;
index 59e676f69dd7f3354f60ca7cc26255630aad78cd..0c41b0c3a00fddbf7eb472b2818b05006792ca7b 100644 (file)
@@ -1,3 +1,22 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program 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.
+
+    This program 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 this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
 #include <iostream>
 #include <cstdlib>
 #include <boost/filesystem.hpp>
 #include "dcp.h"
 #include "exceptions.h"
 #include "reel.h"
-#include "sound_asset.h"
-#include "picture_asset.h"
-#include "subtitle_asset.h"
+#include "sound_mxf.h"
+#include "picture_mxf.h"
+#include "subtitle_content.h"
+#include "reel_picture_asset.h"
+#include "reel_sound_asset.h"
+#include "reel_subtitle_asset.h"
+#include "subtitle_string.h"
 #include "cpl.h"
+#include "common.h"
 
 using std::string;
 using std::cerr;
 using std::cout;
 using std::list;
 using boost::shared_ptr;
-using namespace libdcp;
+using namespace dcp;
 
 static void
 help (string n)
 {
        cerr << "Syntax: " << n << " [options] <DCP>\n"
-            << "  -s, --subtitles  list all subtitles\n";
+            << "  -s, --subtitles              list all subtitles\n"
+            << "  -k, --keep-going             carry on in the event of errors, if possible\n"
+            << "      --ignore-missing-assets  ignore missing asset files\n";
+}
+
+static void
+main_picture (shared_ptr<Reel> reel)
+{
+       if (reel->main_picture() && reel->main_picture()->mxf()) {
+               cout << "      Picture:  "
+                    << reel->main_picture()->mxf()->size().width
+                    << "x"
+                    << reel->main_picture()->mxf()->size().height << "\n";
+       }
+}
+
+static void
+main_sound (shared_ptr<Reel> reel)
+{
+       if (reel->main_sound() && reel->main_sound()->mxf()) {
+               cout << "      Sound:    "
+                    << reel->main_sound()->mxf()->channels()
+                    << " channels at "
+                    << reel->main_sound()->mxf()->sampling_rate() << "Hz\n";
+       }
+}
+
+static void
+main_subtitle (shared_ptr<Reel> reel, bool list_subtitles)
+{
+       if (!reel->main_subtitle()) {
+               return;
+       }
+       
+       list<shared_ptr<SubtitleString> > subs = reel->main_subtitle()->subtitle_content()->subtitles ();
+       cout << "      Subtitle: " << subs.size() << " subtitles in " << reel->main_subtitle()->subtitle_content()->language() << "\n";
+       if (list_subtitles) {
+               for (list<shared_ptr<SubtitleString> >::const_iterator k = subs.begin(); k != subs.end(); ++k) {
+                       cout << "        " << (*k)->text() << "\n";
+                       cout << "          "
+                            << "font:" << (*k)->font() << "; "
+                            << "italic:" << (*k)->italic() << "; "
+                            << "color:" << (*k)->color() << "; "
+                            << "in:" << (*k)->in() << "; "
+                            << "out:" << (*k)->out() << "; "
+                            << "v_position:" << (*k)->v_position() << "; "
+                            << "v_align:" << (*k)->v_align() << "; "
+                            << "effect:" << (*k)->effect() << "; "
+                            << "effect_color:" << (*k)->effect_color() << "; "
+                            << "fade_up_time:" << (*k)->fade_up_time() << "; "
+                            << "fade_down_time:" << (*k)->fade_down_time() << "; "
+                            << "size: " << (*k)->size() << "\n";
+               }
+       }
 }
 
 int
 main (int argc, char* argv[])
 {
        bool subtitles = false;
+       bool keep_going = false;
+       bool ignore_missing_assets = false;
        
        int option_index = 0;
        while (1) {
                static struct option long_options[] = {
-                       { "version", no_argument, 0, 'v'},
-                       { "help", no_argument, 0, 'h'},
-                       { "subtitles", no_argument, 0, 's'},
+                       { "version", no_argument, 0, 'v' },
+                       { "help", no_argument, 0, 'h' },
+                       { "subtitles", no_argument, 0, 's' },
+                       { "keep-going", no_argument, 0, 'k' },
+                       { "ignore-missing-assets", no_argument, 0, 'A' },
                        { 0, 0, 0, 0 }
                };
 
-               int c = getopt_long (argc, argv, "vhs", long_options, &option_index);
+               int c = getopt_long (argc, argv, "vhskA", long_options, &option_index);
 
                if (c == -1) {
                        break;
@@ -54,6 +135,12 @@ main (int argc, char* argv[])
                case 's':
                        subtitles = true;
                        break;
+               case 'k':
+                       keep_going = true;
+                       break;
+               case 'A':
+                       ignore_missing_assets = true;
+                       break;
                }
        }
 
@@ -68,57 +155,72 @@ main (int argc, char* argv[])
        }
 
        DCP* dcp = 0;
+       DCP::ReadErrors errors;
        try {
                dcp = new DCP (argv[optind]);
-               dcp->read (false);
+               dcp->read (keep_going, &errors);
        } catch (FileError& e) {
-               cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << " " << e.filename() << "\n";
+               cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
+               exit (EXIT_FAILURE);
+       } catch (DCPReadError& e) {
+               cerr << "Could not read DCP " << argv[optind] << "; " << e.what() << "\n";
                exit (EXIT_FAILURE);
        }
-
+       
        cout << "DCP: " << boost::filesystem::path(argv[optind]).filename().string() << "\n";
 
+       dcp::filter_errors (errors, ignore_missing_assets);
+       for (DCP::ReadErrors::const_iterator i = errors.begin(); i != errors.end(); ++i) {
+               cerr << "Error: " << (*i)->what() << "\n";
+       }
+
        list<shared_ptr<CPL> > cpls = dcp->cpls ();
 
        for (list<shared_ptr<CPL> >::iterator i = cpls.begin(); i != cpls.end(); ++i) {
-               cout << "  CPL: " << (*i)->name() << "\n"
-                    << "    Length: " << (*i)->length() << "\n"
-                    << "    Frames per second: " << (*i)->frames_per_second() << "\n";
+               cout << "  CPL: " << (*i)->annotation_text() << "\n";
                
                list<shared_ptr<Reel> > reels = (*i)->reels ();
 
                int R = 1;
                for (list<shared_ptr<Reel> >::const_iterator j = reels.begin(); j != reels.end(); ++j) {
                        cout << "    Reel " << R << "\n";
-                       
-                       if ((*j)->main_picture()) {
-                               cout << "      Picture:  " << (*j)->main_picture()->size().width << "x" << (*j)->main_picture()->size().height << "\n";
+
+                       try {
+                               main_picture (*j);
+                       } catch (UnresolvedRefError& e) {
+                               if (keep_going) {
+                                       if (!ignore_missing_assets) {
+                                               cerr << e.what() << " (for main picture)\n";
+                                       }
+                               } else {
+                                       throw;
+                               }
                        }
-                       if ((*j)->main_sound()) {
-                               cout << "      Sound:    " << (*j)->main_sound()->channels() << " channels at " << (*j)->main_sound()->sampling_rate() << "Hz\n";
+
+                       try {
+                               main_sound (*j);
+                       } catch (UnresolvedRefError& e) {
+                               if (keep_going) {
+                                       if (!ignore_missing_assets) {
+                                               cerr << e.what() << " (for main sound)\n";
+                                       }
+                               } else {
+                                       throw;
+                               }
                        }
-                       if ((*j)->main_subtitle()) {
-                               list<shared_ptr<Subtitle> > subs = (*j)->main_subtitle()->subtitles ();
-                               cout << "      Subtitle: " << subs.size() << " subtitles in " << (*j)->main_subtitle()->language() << "\n";
-                               if (subtitles) {
-                                       for (list<shared_ptr<Subtitle> >::const_iterator k = subs.begin(); k != subs.end(); ++k) {
-                                               cout << "        " << (*k)->text() << "\n";
-                                               cout << "          "
-                                                    << "font:" << (*k)->font() << "; "
-                                                    << "italic:" << (*k)->italic() << "; "
-                                                    << "color:" << (*k)->color() << "; "
-                                                    << "in:" << (*k)->in() << "; "
-                                                    << "out:" << (*k)->out() << "; "
-                                                    << "v_position:" << (*k)->v_position() << "; "
-                                                    << "v_align:" << (*k)->v_align() << "; "
-                                                    << "effect:" << (*k)->effect() << "; "
-                                                    << "effect_color:" << (*k)->effect_color() << "; "
-                                                    << "fade_up_time:" << (*k)->fade_up_time() << "; "
-                                                    << "fade_down_time:" << (*k)->fade_down_time() << "; "
-                                                    << "size: " << (*k)->size() << "\n";
+
+                       try {
+                               main_subtitle (*j, subtitles);
+                       } catch (UnresolvedRefError& e) {
+                               if (keep_going) {
+                                       if (!ignore_missing_assets) {
+                                               cerr << e.what() << " (for main subtitle)\n";
                                        }
+                               } else {
+                                       throw;
                                }
                        }
+
                        ++R;
                }
        }
index e4a32534efbeb07ce30065d1bf4bd8dc2605c9b6..10389eaccc1bf9e491c987a895346af0c93b2cab 100644 (file)
@@ -1,13 +1,13 @@
 def build(bld):
     obj = bld(features = 'cxx cxxprogram')
-    obj.use = ['libdcp']
+    obj.use = ['libdcp%s' % bld.env.API_VERSION]
     obj.uselib = 'OPENJPEG CXML'
-    obj.source = 'dcpdiff.cc'
+    obj.source = 'dcpdiff.cc common.cc'
     obj.target = 'dcpdiff'
 
     obj = bld(features = 'cxx cxxprogram')
-    obj.use = ['libdcp']
+    obj.use = ['libdcp%s' % bld.env.API_VERSION]
     obj.uselib = 'OPENJPEG CXML'
-    obj.source = 'dcpinfo.cc'
+    obj.source = 'dcpinfo.cc common.cc'
     obj.target = 'dcpinfo'
 
diff --git a/wscript b/wscript
index 3f7da8776d992691f3f6e94918427ceaf6d4fc6d..a647c62cbcfebde33b9b5bf73381817b6dcfccc1 100644 (file)
--- a/wscript
+++ b/wscript
@@ -2,14 +2,15 @@ import subprocess
 import os
 
 APPNAME = 'libdcp'
-VERSION = '0.93pre'
+VERSION = '1.00.0devel'
+API_VERSION = '-1.0'
 
 def options(opt):
     opt.load('compiler_cxx')
-    opt.add_option('--target-windows', action='store_true', default = False, help = 'set up to do a cross-compile to Windows')
-    opt.add_option('--osx', action='store_true', default = False, help = 'set up to build on OS X')
-    opt.add_option('--enable-debug', action='store_true', default = False, help = 'build with debugging information and without optimisation')
-    opt.add_option('--static', action='store_true', default = False, help = 'build libdcp and in-tree dependencies statically, and link statically to openjpeg and cxml')
+    opt.add_option('--target-windows', action='store_true', default=False, help='set up to do a cross-compile to Windows')
+    opt.add_option('--target-osx', action='store_true', default=False, help='set up to build on OS X')
+    opt.add_option('--enable-debug', action='store_true', default=False, help='build with debugging information and without optimisation')
+    opt.add_option('--static', action='store_true', default=False, help='build libdcp and in-tree dependencies statically, and link statically to openjpeg and cxml')
 
 def configure(conf):
     conf.load('compiler_cxx')
@@ -17,34 +18,37 @@ def configure(conf):
     conf.env.append_value('CXXFLAGS', ['-DLIBDCP_VERSION="%s"' % VERSION])
 
     conf.env.TARGET_WINDOWS = conf.options.target_windows
-    conf.env.STATIC = conf.options.static
-    conf.env.OSX = conf.options.osx
+    conf.env.TARGET_OSX = conf.options.target_osx
     conf.env.ENABLE_DEBUG = conf.options.enable_debug
+    conf.env.STATIC = conf.options.static
+    conf.env.API_VERSION = API_VERSION
 
     if conf.options.target_windows:
         conf.env.append_value('CXXFLAGS', '-DLIBDCP_WINDOWS')
     else:
         conf.env.append_value('CXXFLAGS', '-DLIBDCP_POSIX')
 
-    if not conf.options.osx:
+    if not conf.options.target_osx:
         conf.env.append_value('CXXFLAGS', ['-Wno-unused-result', '-Wno-unused-parameter'])
 
-    conf.check_cfg(package = 'openssl', args = '--cflags --libs', uselib_store = 'OPENSSL', mandatory = True)
-    conf.check_cfg(package = 'libxml++-2.6', args = '--cflags --libs', uselib_store = 'LIBXML++', mandatory = True)
-    conf.check_cfg(package = 'xmlsec1', args = '--cflags --libs', uselib_store = 'XMLSEC1', mandatory = True)
+    conf.check_cfg(package='openssl', args='--cflags --libs', uselib_store='OPENSSL', mandatory=True)
+    conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='LIBXML++', mandatory=True)
+    conf.check_cfg(package='xmlsec1', args='--cflags --libs', uselib_store='XMLSEC1', mandatory=True)
     # Remove erroneous escaping of quotes from xmlsec1 defines
     conf.env.DEFINES_XMLSEC1 = [f.replace('\\', '') for f in conf.env.DEFINES_XMLSEC1]
+    conf.check_cfg(package='', path='Magick++-config', args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=False)
+    conf.check_cfg(package='sndfile', args='--cflags --libs', uselib_store='SNDFILE', mandatory=False)
 
     if conf.options.static:
-        conf.check_cc(fragment = """
-                       #include <stdio.h>\n
-                       #include <openjpeg.h>\n
-                       int main () {\n
-                       void* p = (void *) opj_image_create;\n
-                       return 0;\n
-                       }
-                       """,
-                       msg = 'Checking for library openjpeg', stlib = 'openjpeg', uselib_store = 'OPENJPEG', mandatory = True)
+        conf.check_cc(fragment="""
+                     #include <stdio.h>\n
+                     #include <openjpeg.h>\n
+                     int main () {\n
+                     void* p = (void *) opj_image_create;\n
+                     return 0;\n
+                     }
+                     """,
+                       msg='Checking for library openjpeg', stlib='openjpeg', uselib_store='OPENJPEG', mandatory=True)
         
         conf.env.HAVE_CXML = 1
         conf.env.STLIB_CXML = ['cxml']
@@ -64,42 +68,42 @@ def configure(conf):
         # Windows builds are any more reliable
         conf.env.append_value('CXXFLAGS', '-O2')
 
-    conf.check_cxx(fragment = """
-                              #include <boost/version.hpp>\n
-                              #if BOOST_VERSION < 104500\n
-                              #error boost too old\n
-                              #endif\n
-                              int main(void) { return 0; }\n
-                              """,
-                   mandatory = True,
-                   msg = 'Checking for boost library >= 1.45',
-                   okmsg = 'yes',
-                   errmsg = 'too old\nPlease install boost version 1.45 or higher.')
-
-    conf.check_cxx(fragment = """
-                             #include <boost/filesystem.hpp>\n
-                             int main() { boost::filesystem::copy_file ("a", "b"); }\n
-                             """,
-                   msg = 'Checking for boost filesystem library',
-                   libpath = '/usr/local/lib',
-                   lib = ['boost_filesystem%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
-                   uselib_store = 'BOOST_FILESYSTEM')
-
-    conf.check_cxx(fragment = """
-                             #include <boost/signals2.hpp>\n
-                             int main() { boost::signals2::signal<void (int)> x; }\n
-                             """,
-                   msg = 'Checking for boost signals2 library',
-                   uselib_store = 'BOOST_SIGNALS2')
-
-    conf.check_cxx(fragment = """
-                             #include <boost/date_time.hpp>\n
-                             int main() { boost::gregorian::day_clock::local_day(); }\n
-                             """,
-                   msg = 'Checking for boost datetime library',
-                   libpath = '/usr/local/lib',
-                   lib = ['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
-                   uselib_store = 'BOOST_DATETIME')
+    conf.check_cxx(fragment="""
+                            #include <boost/version.hpp>\n
+                            #if BOOST_VERSION < 104500\n
+                            #error boost too old\n
+                            #endif\n
+                            int main(void) { return 0; }\n
+                            """,
+                   mandatory=True,
+                   msg='Checking for boost library >= 1.45',
+                   okmsg='yes',
+                   errmsg='too old\nPlease install boost version 1.45 or higher.')
+
+    conf.check_cxx(fragment="""
+                           #include <boost/filesystem.hpp>\n
+                           int main() { boost::filesystem::copy_file ("a", "b"); }\n
+                           """,
+                   msg='Checking for boost filesystem library',
+                   libpath='/usr/local/lib',
+                   lib=['boost_filesystem%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
+                   uselib_store='BOOST_FILESYSTEM')
+
+    conf.check_cxx(fragment="""
+                           #include <boost/signals2.hpp>\n
+                           int main() { boost::signals2::signal<void (int)> x; }\n
+                           """,
+                   msg='Checking for boost signals2 library',
+                   uselib_store='BOOST_SIGNALS2')
+
+    conf.check_cxx(fragment="""
+                           #include <boost/date_time.hpp>\n
+                           int main() { boost::gregorian::day_clock::local_day(); }\n
+                           """,
+                   msg='Checking for boost datetime library',
+                   libpath='/usr/local/lib',
+                   lib=['boost_date_time%s' % boost_lib_suffix, 'boost_system%s' % boost_lib_suffix],
+                   uselib_store='BOOST_DATETIME')
 
     conf.recurse('test')
     conf.recurse('asdcplib')
@@ -112,11 +116,11 @@ def build(bld):
     else:
         boost_lib_suffix = ''
 
-    bld(source = 'libdcp.pc.in',
-        version = VERSION,
-        includedir = '%s/include' % bld.env.PREFIX,
-        libs = "-L${libdir} -ldcp -lasdcp-libdcp -lkumu-libdcp -lcxml -lboost_system%s" % boost_lib_suffix,
-        install_path = '${LIBDIR}/pkgconfig')
+    bld(source='libdcp%s.pc.in' % bld.env.API_VERSION,
+        version=VERSION,
+        includedir='%s/include/libdcp%s' % (bld.env.PREFIX, bld.env.API_VERSION),
+        libs="-L${libdir} -ldcp%s -lasdcp-libdcp%s -lkumu-libdcp%s -lcxml -lboost_system%s" % (API_VERSION, API_VERSION, API_VERSION, boost_lib_suffix),
+        install_path='${LIBDIR}/pkgconfig')
 
     bld.recurse('src')
     bld.recurse('tools')
@@ -140,8 +144,8 @@ def create_version_cc(bld, version):
 
     try:
         text =  '#include "version.h"\n'
-        text += 'char const * libdcp::git_commit = \"%s\";\n' % commit
-        text += 'char const * libdcp::version = \"%s\";\n' % version
+        text += 'char const * dcp::git_commit = \"%s\";\n' % commit
+        text += 'char const * dcp::version = \"%s\";\n' % version
         if bld.env.ENABLE_DEBUG:
             debug_string = 'true'
         else: