* Make server finding more reliable when
there are more than a few servers.
+2014-10-05 Carl Hetherington <cth@carlh.net>
+
+ * Use a more sensible default position and size for
+ .srt subs.
+
+2014-10-03 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.14 released.
+
+2014-10-01 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.13 released.
+
+2014-09-30 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.12 released.
+
+2014-09-30 Carl Hetherington <cth@carlh.net>
+
+ * Add basic video fade in/out.
+
+2014-09-22 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.11 released.
+
+2014-09-18 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.10 released.
+
2014-10-08 Carl Hetherington <cth@carlh.net>
* Version 1.74.2 released.
2014-09-12 Carl Hetherington <cth@carlh.net>
+ * Version 2.0.9 released.
+
+2014-09-12 Carl Hetherington <cth@carlh.net>
+
+ * Add "re-examine" option to content context menu (#339).
+
+2014-09-11 Carl Hetherington <cth@carlh.net>
+
+ * Restore encoding optimisations for still-image sources.
+
+ * Add option to re-make signing chain with specified organisation,
+ common names etc. (#354)
+
* Allow separate X and Y scale for subtitles (#337).
2014-09-10 Carl Hetherington <cth@carlh.net>
* Fix hidden advanced preferences button in some locales.
-2014-09-08 Carl Hetherington <cth@carlh.net>
+ * Version 2.0.8 released.
+
+2014-09-10 Carl Hetherington <cth@carlh.net>
+
+ * Fix loading of 1.x films.
+
+ * Fix crash on audio analysis in some cases.
- * Version 1.73.4 released.
+2014-09-09 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.7 released.
+
+2014-09-09 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.6 released.
+
+2014-09-09 Carl Hetherington <cth@carlh.net>
+
+ * Fix missing OS X dependencies.
+
+ * Use a different directory for DCP-o-matic 2
+ configuration (not the same as 1.x).
2014-09-08 Carl Hetherington <cth@carlh.net>
- * Fix failure to load Targa files.
+ * Version 2.0.5 released.
-2014-09-07 Carl Hetherington <cth@carlh.net>
+ * Fix hidden advanced preferences button in some locales.
+
+2014-09-08 Carl Hetherington <cth@carlh.net>
- * Version 1.73.3 released.
+ * Fix failure to load Targa files.
2014-09-07 Carl Hetherington <cth@carlh.net>
* Fix a few bad fuzzy translations from the preferences dialog.
-2014-09-03 Carl Hetherington <cth@carlh.net>
-
- * Version 1.73.2 released.
-
2014-09-03 Carl Hetherington <cth@carlh.net>
* Fix server certificate downloads on OS X (#376).
2014-08-29 Carl Hetherington <cth@carlh.net>
+ * Version 2.0.4 released.
+
+2014-08-24 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.3 released.
+
+2014-08-24 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.2 released.
+
+2014-08-06 Carl Hetherington <cth@carlh.net>
+
+ * Version 2.0.1 released.
+
+2014-07-15 Carl Hetherington <cth@carlh.net>
+
+ * A variety of changes were made on the 2.0 branch
+ but not documented in the ChangeLog. Most sigificantly:
+
+ - DCP import
+ - Creation of DCPs with proper XML subtitles
+ - Import of .srt and .xml subtitles
+ - Audio processing framework (with some basic processors).
+
+2014-03-07 Carl Hetherington <cth@carlh.net>
+
+ * Add subtitle view.
* Some improvements to the manual.
2014-08-26 Carl Hetherington <cth@carlh.net>
* Attempt to fix random crashes on OS X (especially during encodes)
thought to be caused by multiple threads using (different) stringstreams
at the same time; see src/lib/safe_stringstream.
+>>>>>>> origin/master
2014-08-09 Carl Hetherington <cth@carlh.net>
2014-07-10 Carl Hetherington <cth@carlh.net>
* Version 1.72.2 released.
+>>>>>>> origin/master
2014-07-10 Carl Hetherington <cth@carlh.net>
-# Doxyfile 1.8.2
+# Doxyfile 1.8.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
-# All text after a hash (#) is considered a comment and will be ignored.
+# 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 single hash (#) is considered a comment and will be ignored.
# The format is:
-# TAG = value [value, ...]
-# For lists items can also be appended using:
-# TAG += value [value, ...]
-# Values that contain spaces should be placed between quotes (" ").
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the config file
-# that follow. The default is UTF-8 which is also the encoding used for all
-# text before the first occurrence of this tag. Doxygen uses libiconv (or the
-# iconv built into libc) for the transcoding. See
-# http://www.gnu.org/software/libiconv for the list of possible encodings.
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
-# The PROJECT_NAME tag is a single word (or sequence of words) that should
-# identify the project. Note that if you do not use Doxywizard you need
-# to put quotes around the project name if it contains spaces.
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
PROJECT_NAME = DCP-o-matic
-# The PROJECT_NUMBER tag can be used to enter a project or revision number.
-# This could be handy for archiving the generated documentation or
-# if some version control system is used.
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
PROJECT_NUMBER =
# Using the PROJECT_BRIEF tag one can provide an optional one line description
-# for a project that appears at the top of each page and should give viewer
-# a quick idea about the purpose of the project. Keep the description short.
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF =
-# With the PROJECT_LOGO tag one can specify an logo or icon that is
-# included in the documentation. The maximum height of the logo should not
-# exceed 55 pixels and the maximum width should not exceed 200 pixels.
-# Doxygen will copy the logo to the output directory.
+# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
+# the documentation. The maximum height of the logo should not exceed 55 pixels
+# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
+# to the output directory.
PROJECT_LOGO =
-# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
-# base path where the generated documentation will be put.
-# If a relative path is entered, it will be relative to the location
-# where doxygen was started. If left blank the current directory will be used.
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
OUTPUT_DIRECTORY = build/doc
-# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
-# 4096 sub-directories (in 2 levels) under the output directory of each output
-# format and will distribute the generated files over these directories.
-# Enabling this option can be useful when feeding doxygen a huge amount of
-# source files, where putting all generated files in the same directory would
-# otherwise cause performance problems for the file system.
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
CREATE_SUBDIRS = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
-# The default language is English, other supported languages are:
-# 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.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
OUTPUT_LANGUAGE = English
-# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
-# include brief member descriptions after the members that are listed in
-# the file and class documentation (similar to JavaDoc).
-# Set to NO to disable this.
+# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
BRIEF_MEMBER_DESC = YES
-# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
-# the brief description of a member or function before the detailed description.
-# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
+# The default value is: YES.
REPEAT_BRIEF = YES
-# This tag implements a quasi-intelligent brief description abbreviator
-# that is used to form the text in various listings. Each string
-# in this list, if found as the leading text of the brief description, will be
-# stripped from the text and the result after processing the whole list, is
-# used as the annotated text. Otherwise, the brief description is used as-is.
-# If left blank, the following values are used ("$name" is automatically
-# replaced with the name of the entity): "The $name class" "The $name widget"
-# "The $name file" "is" "provides" "specifies" "contains"
-# "represents" "a" "an" "the"
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
-# Doxygen will generate a detailed section even if there is only a brief
+# doxygen will generate a detailed section even if there is only a brief
# description.
+# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
+# The default value is: NO.
INLINE_INHERITED_MEMB = NO
-# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
-# path before files name in the file list and in the header files. If set
-# to NO the shortest path that makes the file name unique will be used.
+# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
FULL_PATH_NAMES = YES
-# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
-# can be used to strip a user-defined part of the path. Stripping is
-# 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. Note that you specify absolute paths here, but also
-# relative paths, which will be relative from the directory where doxygen is
-# started.
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is 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.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
-# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
-# the path mentioned in the documentation of a class, which tells
-# the reader which header file to include in order to use a class.
-# If left blank only the name of the header file containing the class
-# definition is used. Otherwise one should specify the include paths that
-# are normally passed to the compiler using the -I flag.
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
STRIP_FROM_INC_PATH =
-# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
-# (but less readable) file names. This can be useful if your file system
-# doesn't support long names like on DOS, Mac, or CD-ROM.
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
SHORT_NAMES = NO
-# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
-# will interpret the first line (until the first dot) of a JavaDoc-style
-# comment as the brief description. If set to NO, the JavaDoc
-# comments will behave just like regular Qt-style comments
-# (thus requiring an explicit @brief command for a brief description.)
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
-# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
-# interpret the first line (until the first dot) of a Qt-style
-# comment as the brief description. If set to NO, the comments
-# will behave just like regular Qt-style comments (thus requiring
-# an explicit \brief command for a brief description.)
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
QT_AUTOBRIEF = NO
-# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
-# treat a multi-line C++ special comment block (i.e. a block of //! or ///
-# comments) as a brief description. This used to be the default behaviour.
-# The new default is to treat a multi-line C++ comment block as a detailed
-# description. Set this tag to YES if you prefer the old behaviour instead.
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
-# re-implements.
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
INHERIT_DOCS = YES
-# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
-# a new page for each member. If set to NO, the documentation of a member will
-# be part of the file/class/namespace that contains it.
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a
+# new page for each member. If set to NO, the documentation of a member will be
+# part of the file/class/namespace that contains it.
+# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
-# The TAB_SIZE tag can be used to set the number of spaces in a tab.
-# Doxygen uses this value to replace tabs by spaces in code fragments.
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 8
-# This tag can be used to specify a number of aliases that acts
-# as commands in the documentation. An alias has the form "name=value".
-# For example adding "sideeffect=\par Side Effects:\n" will allow you to
-# put the command \sideeffect (or @sideeffect) in the documentation, which
-# will result in a user-defined paragraph with heading "Side Effects:".
-# You can put \n's in the value part of an alias to insert newlines.
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding
-# "class=itcl::class" will allow you to use the command class in the
-# itcl::class meaning.
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
-# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
-# sources only. Doxygen will then generate output that is more tailored for C.
-# For instance, some of the names that are used will be different. The list
-# of all members will be omitted, etc.
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
-# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
-# sources only. Doxygen will then generate output that is more tailored for
-# Java. For instance, namespaces will be presented as packages, qualified
-# scopes will look different, etc.
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
-# sources only. Doxygen will then generate output that is more tailored for
-# Fortran.
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
-# sources. Doxygen will then generate output that is tailored for
-# VHDL.
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: 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.
+# 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,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. 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 For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
EXTENSION_MAPPING =
-# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
-# comments according to the Markdown format, which allows for more readable
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
# documentation. See http://daringfireball.net/projects/markdown/ for details.
-# The output of markdown processing is further processed by doxygen, so you
-# can mix doxygen, HTML, and XML commands with Markdown formatting.
-# Disable only in case of backward compatibilities issues.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
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.
+# 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.
+# The default value is: YES.
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
-# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
-# func(std::string) {}). This also makes the inheritance and collaboration
+# 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
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
+# The default value is: NO.
CPP_CLI_SUPPORT = NO
-# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
-# Doxygen will parse them like normal C++ but will assume all classes use public
-# instead of private inheritance when no explicit protection keyword is present.
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: 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 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.
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to 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.
+# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
+# The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO
-# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
-# the same type (for instance a group of public functions) to be put as a
-# subgroup of that type (e.g. under the Public Functions section). Set it to
-# NO to prevent subgrouping. Alternatively, this can be done per class using
-# the \nosubgrouping command.
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
SUBGROUPING = YES
-# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
-# unions are shown inside the group in which they are included (e.g. using
-# @ingroup) instead of on a separate page (for HTML and Man pages) or
-# section (for LaTeX and RTF).
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
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).
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and 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, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
-# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
-# is documented as struct, union, or enum with the name of the typedef. So
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
-# namespace, or class. And the struct will be named TypeS. This can typically
-# be useful for C code in case the coding convention dictates that all compound
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: 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 appears 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. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
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
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
EXTRACT_ALL = NO
-# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
-# will be included in the documentation.
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal
# scope will be included in the documentation.
+# The default value is: NO.
EXTRACT_PACKAGE = NO
-# If the EXTRACT_STATIC tag is set to YES all static members of a file
-# will be included in the documentation.
+# If the EXTRACT_STATIC tag is set to YES all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
EXTRACT_STATIC = NO
-# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
-# defined locally in source files will be included in the documentation.
-# If set to NO only classes defined in header files are included.
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
EXTRACT_LOCAL_CLASSES = YES
-# This flag is only useful for Objective-C code. When set to YES local
-# methods, which are defined in the implementation section but not in
-# the interface are included in the documentation.
-# If set to NO (the default) only methods in the interface are included.
+# This flag is only useful for Objective-C code. When set to YES local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO only methods in the interface are
+# included.
+# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
-# 'anonymous_namespace{file}', where file will be replaced with the base
-# name of the file that contains the anonymous namespace. By default
-# anonymous namespaces are hidden.
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
-# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
-# undocumented members of documented classes, files or namespaces.
-# If set to NO (the default) these members will be included in the
-# various overviews, but no documentation section is generated.
-# This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
-# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
-# undocumented classes that are normally visible in the class hierarchy.
-# If set to NO (the default) these classes will be included in the various
-# overviews. This option has no effect if EXTRACT_ALL is enabled.
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO these classes will be included in the various overviews. This option has
+# no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
-# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
-# friend (class|struct|union) declarations.
-# If set to NO (the default) these declarations will be included in the
-# documentation.
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO these declarations will be
+# included in the documentation.
+# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
-# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
-# documentation blocks found inside the body of a function.
-# If set to NO (the default) these blocks will be appended to the
-# function's detailed documentation block.
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
-# The INTERNAL_DOCS tag determines if documentation
-# that is typed after a \internal command is included. If the tag is set
-# to NO (the default) then the documentation will be excluded.
-# Set it to YES to include the internal documentation.
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
INTERNAL_DOCS = NO
-# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
-# file names in lower-case letters. If set to YES upper-case letters are also
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
CASE_SENSE_NAMES = YES
-# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
-# will show members with their full class and namespace scopes in the
-# documentation. If set to YES the scope will be hidden.
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES the
+# scope will be hidden.
+# The default value is: NO.
HIDE_SCOPE_NAMES = NO
-# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
-# will put a list of the files that are included by a file in the documentation
-# of that file.
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
SHOW_INCLUDE_FILES = YES
-# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
-# will list include files with double quotes in the documentation
-# rather than with sharp brackets.
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
-# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
-# is inserted in the documentation for inline members.
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
INLINE_INFO = YES
-# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
-# will sort the (detailed) documentation of file and class members
-# alphabetically by member name. If set to NO the members will appear in
-# declaration order.
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order.
+# The default value is: YES.
SORT_MEMBER_DOCS = YES
-# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
-# brief documentation of file, namespace and class members alphabetically
-# by member name. If set to NO (the default) the members will appear in
-# declaration order.
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
SORT_BRIEF_DOCS = NO
-# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
-# will sort the (brief and detailed) documentation of class members so that
-# constructors and destructors are listed first. If set to NO (the default)
-# the constructors will appear in the respective orders defined by
-# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
-# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
-# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
-# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
-# hierarchy of group names into alphabetical order. If set to NO (the default)
-# the group names will appear in their defined order.
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
SORT_GROUP_NAMES = NO
-# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
-# sorted by fully-qualified names, including namespaces. If set to
-# NO (the default), the class list will be sorted only by class name,
-# not including the namespace part.
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
-# Note: This option applies only to the class list, not to the
-# alphabetical list.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
SORT_BY_SCOPE_NAME = NO
-# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
-# do proper type resolution of all parameters of a function it will reject a
-# match between the prototype and the implementation of a member function even
-# if there is only one candidate or it is obvious which candidate to choose
-# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
-# will still accept a match between prototype and implementation in such cases.
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
STRICT_PROTO_MATCHING = NO
-# The GENERATE_TODOLIST tag can be used to enable (YES) or
-# disable (NO) the todo list. This list is created by putting \todo
-# commands in the documentation.
+# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the
+# todo list. This list is created by putting \todo commands in the
+# documentation.
+# The default value is: YES.
GENERATE_TODOLIST = YES
-# The GENERATE_TESTLIST tag can be used to enable (YES) or
-# disable (NO) the test list. This list is created by putting \test
-# commands in the documentation.
+# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the
+# test list. This list is created by putting \test commands in the
+# documentation.
+# The default value is: YES.
GENERATE_TESTLIST = YES
-# The GENERATE_BUGLIST tag can be used to enable (YES) or
-# disable (NO) the bug list. This list is created by putting \bug
-# commands in the documentation.
+# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
-# \deprecated commands in the documentation.
+# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
-# documentation sections, marked by \if sectionname ... \endif.
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
ENABLED_SECTIONS =
-# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
-# the initial value of a variable or macro consists of for it to appear in
-# the documentation. If the initializer consists of more lines than specified
-# here it will be hidden. Use a value of 0 to hide initializers completely.
-# The appearance of the initializer of individual variables and macros in the
-# documentation can be controlled using \showinitializer or \hideinitializer
-# command in the documentation regardless of this setting.
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 30
-# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
-# at the bottom of the documentation of classes and structs. If set to YES the
-# list will mention the files that were used to generate the documentation.
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES the list
+# will mention the files that were used to generate the documentation.
+# The default value is: YES.
SHOW_USED_FILES = YES
-# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
-# This will remove the Files entry from the Quick Index and from the
-# Folder Tree View (if specified). The default is YES.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
SHOW_FILES = YES
-# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
-# Namespaces page.
-# This will remove the Namespaces entry from the Quick Index
-# and from the Folder Tree View (if specified). The default is YES.
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
-# popen()) the command <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
-# is used as the file version. See the manual for examples.
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
-# that represents doxygen's defaults, run doxygen with the -l option.
-# You can optionally specify a file name after the option, if omitted
-# DoxygenLayout.xml will be used as the name of the layout file.
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
LAYOUT_FILE =
-# The CITE_BIB_FILES tag can be used to specify one or more bib files
-# containing the references data. This must be a list of .bib files. The
-# .bib extension is automatically appended if omitted. Using this command
-# 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.
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This 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. Do not use file names with spaces, bibtex cannot handle them. See
+# also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
+# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
-# by doxygen. Possible values are YES and NO. If left blank NO is used.
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
QUIET = YES
# The WARNINGS tag can be used to turn on/off the warning messages that are
-# generated by doxygen. Possible values are YES and NO. If left blank
-# NO is used.
+# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
WARNINGS = YES
-# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
-# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
-# automatically be disabled.
+# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
-# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
-# potential errors in the documentation, such as not documenting some
-# parameters in a documented function, or documenting parameters that
-# don't exist or using markup commands wrongly.
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
WARN_IF_DOC_ERROR = YES
-# The WARN_NO_PARAMDOC option can be enabled to get warnings for
-# functions that are documented, but have no documentation for their parameters
-# or return value. If set to NO (the default) doxygen will only warn about
-# wrong or incomplete parameter documentation, but not about the absence of
-# documentation.
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO doxygen will only warn about wrong or incomplete parameter
+# documentation, but not about the absence of documentation.
+# The default value is: NO.
WARN_NO_PARAMDOC = NO
-# The WARN_FORMAT tag determines the format of the warning messages that
-# doxygen can produce. The string should contain the $file, $line, and $text
-# tags, which will be replaced by the file and line number from which the
-# warning originated and the warning text. Optionally the format may contain
-# $version, which will be replaced by the version of the file (if it could
-# be obtained via FILE_VERSION_FILTER)
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
-# The WARN_LOGFILE tag can be used to specify a file to which warning
-# and error messages should be written. If left blank the output is written
-# to stderr.
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
WARN_LOGFILE =
#---------------------------------------------------------------------------
-# configuration options related to the input files
+# Configuration options related to the input files
#---------------------------------------------------------------------------
-# The INPUT tag can be used to specify the files and/or directories that contain
-# documented source files. You may enter file names like "myfile.cpp" or
-# directories like "/usr/src/myproject". Separate the files or directories
-# with spaces.
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces.
+# Note: If this tag is empty the current directory is searched.
-INPUT = src/lib src/wx src/tools \
+INPUT = src/lib \
+ src/wx \
+ src/tools \
+ test \
doc/mainpage.txt
# This tag can be used to specify the character encoding of the source files
-# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
-# also the default input encoding. Doxygen uses libiconv (or the iconv built
-# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
-# the list of possible encodings.
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
-# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank the following patterns are tested:
-# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
-# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
-# *.f90 *.f *.for *.vhd *.vhdl
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank the
+# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
+# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
+# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
+# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
+# *.qsf, *.as and *.js.
FILE_PATTERNS =
-# The RECURSIVE tag can be used to turn specify whether or not subdirectories
-# should be searched for input files as well. Possible values are YES and NO.
-# If left blank NO is used.
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
# Note that relative paths are relative to the directory from which doxygen is
# run.
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
+# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
-# certain files from those directories. Note that the wildcards are matched
-# against the file with absolute path, so to exclude all test directories
-# for example use the pattern */test/*
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
-# The EXAMPLE_PATH tag can be used to specify one or more files or
-# directories that contain example code fragments that are included (see
-# the \include command).
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
-# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
-# and *.h) to filter out the source-files in the directories. If left
-# blank all files are included.
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
EXAMPLE_PATTERNS =
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
-# searched for input files to be used with the \include or \dontinclude
-# commands irrespective of the value of the RECURSIVE tag.
-# Possible values are YES and NO. If left blank NO is used.
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
EXAMPLE_RECURSIVE = NO
-# The IMAGE_PATH tag can be used to specify one or more files or
-# directories that contain image that are included in the documentation (see
-# the \image command).
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
-# by executing (via popen()) the command <filter> <input-file>, where <filter>
-# 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.
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> 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.
+#
+# 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 =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
-# basis.
-# Doxygen will compare the file name with each pattern and apply the
-# filter if there is a match.
-# The filters are a list of the form:
-# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
-# info on how filters are used. If FILTER_PATTERNS is empty or if
-# non of the patterns match the file name, INPUT_FILTER is applied.
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
-# INPUT_FILTER) will be used to filter the input files when producing source
-# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# INPUT_FILTER ) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
-# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
-# and it is also possible to disable source filtering for a specific pattern
-# using *.ext= (so without naming a filter). This option only has effect when
-# FILTER_SOURCE_FILES is enabled.
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
+# If the USE_MDFILE_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 to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE =
+
#---------------------------------------------------------------------------
-# configuration options related to source browsing
+# Configuration options related to source browsing
#---------------------------------------------------------------------------
-# If the SOURCE_BROWSER tag is set to YES then a list of source files will
-# be generated. Documented entities will be cross-referenced with these sources.
-# Note: To get rid of all source code in the generated output, make sure also
-# VERBATIM_HEADERS is set to NO.
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
SOURCE_BROWSER = NO
-# Setting the INLINE_SOURCES tag to YES will include the body
-# of functions and classes directly in the documentation.
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
INLINE_SOURCES = NO
-# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
-# doxygen to hide any special comment blocks from generated source code
-# fragments. Normal C, C++ and Fortran comments will always remain visible.
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
STRIP_CODE_COMMENTS = YES
-# If the REFERENCED_BY_RELATION tag is set to YES
-# then for each documented function all documented
-# functions referencing it will be listed.
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
REFERENCED_BY_RELATION = NO
-# If the REFERENCES_RELATION tag is set to YES
-# then for each documented function all documented entities
-# called/used by that function will be listed.
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
REFERENCES_RELATION = NO
-# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
-# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
-# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
-# link to the source code.
-# Otherwise they will link to the documentation.
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
-# If the USE_HTAGS tag is set to YES then the references to source code
-# will point to the HTML generated by the htags(1) tool instead of doxygen
-# built-in source browser. The htags tool is part of GNU's global source
-# tagging system (see http://www.gnu.org/software/global/global.html). You
-# will need version 4.8.6 or higher.
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
-# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
-# will generate a verbatim copy of the header file for each class for
-# which an include is specified. Set to NO to disable this.
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
+# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
-# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
-# of all compounds will be generated. Enable this if the project
-# contains a lot of classes, structs, unions or interfaces.
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
ALPHABETICAL_INDEX = YES
-# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
-# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
-# in which this list will be split (can be a number in the range [1..20])
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
-# In case all classes in a project start with a common prefix, all
-# classes will be put under the same header in the alphabetical index.
-# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
-# should be ignored while generating the index headers.
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
-# configuration options related to the HTML output
+# Configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
-# generate HTML output.
+# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output
+# The default value is: YES.
GENERATE_HTML = YES
-# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `html' will be used as the default path.
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html
-# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
-# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
-# doxygen will generate files with .html extension.
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
-# The HTML_HEADER tag can be used to specify a personal HTML header for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard header. Note that when using a custom header you are responsible
-# for the proper inclusion of any scripts and style sheets that doxygen
-# needs, which is dependent on the configuration options used.
-# It is advised to generate a default header using "doxygen -w html
-# header.html footer.html stylesheet.css YourConfigFile" and then modify
-# that header. Note that the header is subject to change so you typically
-# have to redo this when upgrading to a newer version of doxygen or when
-# changing the value of configuration settings such as GENERATE_TREEVIEW!
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
-# The HTML_FOOTER tag can be used to specify a personal HTML footer for
-# each generated HTML page. If it is left blank doxygen will generate a
-# standard footer.
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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 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.
+# 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 left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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.
+# 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. For an example
+# see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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
-# 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.
+# $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.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
-# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
-# Doxygen will adjust the colors in the style sheet and background images
-# according to this color. Hue is specified as an angle on a colorwheel,
-# see http://en.wikipedia.org/wiki/Hue for more information.
-# For instance the value 0 represents red, 60 is yellow, 120 is green,
-# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
-# The allowed range is 0 to 359.
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the stylesheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
-# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
-# the colors in the HTML output. For a value of 0 the output will use
-# grayscales only. A value of 255 will produce the most vivid colors.
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
-# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
-# the luminance component of the colors in the HTML output. Values below
-# 100 gradually make the output lighter, whereas values above 100 make
-# the output darker. The value divided by 100 is the actual gamma applied,
-# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
-# and 100 does not change the gamma.
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
-# page will contain the date and time when the page was generated. Setting
-# this to NO can help when comparing the output of multiple runs.
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
-# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
-# entries shown in the various tree structured indices initially; the user
-# can expand and collapse entries dynamically later on. Doxygen will expand
-# the tree to such a level that at most the specified number of entries are
-# visible (unless a fully collapsed tree already exceeds this amount).
-# So setting the number of entries 1 will produce a full collapsed tree by
-# default. 0 is a special value representing an infinite number of entries
-# and will result in a full expanded tree by default.
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
-# If the GENERATE_DOCSET tag is set to YES, additional index files
-# will be generated that can be used as input for Apple's Xcode 3
-# integrated development environment, introduced with OSX 10.5 (Leopard).
-# To create a documentation set, doxygen will generate a Makefile in the
-# HTML output directory. Running make will produce the docset in that
-# directory and running "make install" will install the docset in
-# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
-# it at startup.
-# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
-# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
-# feed. A documentation feed provides an umbrella under which multiple
-# documentation sets from a single provider (such as a company or product suite)
-# can be grouped.
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
-# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
-# should uniquely identify the documentation set bundle. This should be a
-# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
-# will append .docset to the name.
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
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.
+# The DOCSET_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.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
-# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
-# If the GENERATE_HTMLHELP tag is set to YES, additional index files
-# will be generated that can be used as input for tools like the
-# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
-# of the generated HTML documentation.
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
-# be used to specify the file name of the resulting .chm file. You
-# can add a path in front of the file if the result should not be
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
-# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
-# be used to specify the location (absolute path including file name) of
-# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
-# the HTML help compiler on the generated index.hhp.
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler ( hhc.exe). If non-empty
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
-# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
-# controls if a separate .chi index file is generated (YES) or that
-# it should be included in the master .chm file (NO).
+# The GENERATE_CHI flag controls if a separate .chi index file is generated (
+# YES) or that it should be included in the master .chm file ( NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
-# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
-# is used to encode HtmlHelp index (hhk), content (hhc) and project file
-# content.
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
-# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
-# controls whether a binary table of contents is generated (YES) or a
-# normal table of contents (NO) in the .chm file.
+# The BINARY_TOC flag controls whether a binary table of contents is generated (
+# YES) or a normal table of contents ( NO) in the .chm file.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
-# The TOC_EXPAND flag can be set to YES to add extra items for group members
-# to the contents of the HTML help documentation and to the tree view.
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
-# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
-# that can be used as input for Qt's qhelpgenerator to generate a
-# Qt Compressed Help (.qch) of the generated HTML documentation.
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
-# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
-# be used to specify the file name of the resulting .qch file.
-# The path specified is relative to the HTML output folder.
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
-# The QHP_NAMESPACE tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#namespace
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.doxygen.Project
-# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
-# Qt Help Project output. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = doc
-# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
-# add. For more information please see
-# http://doc.trolltech.com/qthelpproject.html#custom-filters
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
-# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
-# custom filter to add. For more information please see
-# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
-# Qt Help Project / Custom Filters</a>.
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
-# project's
-# filter section matches.
-# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
-# Qt Help Project / Filter Attributes</a>.
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
-# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
-# be used to specify the location of Qt's qhelpgenerator.
-# If non-empty doxygen will try to run qhelpgenerator on the generated
-# .qhp file.
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
-# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
-# will be generated, which together with the HTML files, form an Eclipse help
-# plugin. To install this plugin and make it available under the help contents
-# menu in Eclipse, the contents of the directory containing the HTML and XML
-# files needs to be copied into the plugins directory of eclipse. The name of
-# the directory within the plugins directory should be the same as
-# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
-# the help appears.
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
-# A unique identifier for the eclipse help plugin. When installing the plugin
-# the directory name containing the HTML and XML files should also have
-# this name.
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
-# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
-# at top of each HTML page. The value NO (the default) enables the index and
-# the value YES disables it. Since the tabs have the same information as the
-# navigation tree you can set this option to NO if you already set
-# GENERATE_TREEVIEW to YES.
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
-# structure should be generated to display hierarchical information.
-# If the tag value is set to YES, a side panel will be generated
-# containing a tree-like index structure (just like the one that
-# is generated for HTML Help). For this to work a browser that supports
-# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
-# Windows users are probably better off using the HTML help feature.
-# Since the tree basically has the same information as the tab index you
-# could consider to set DISABLE_INDEX to NO when enabling this option.
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = NO
-# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
-# (range [0,1..20]) that doxygen will group on one line in the generated HTML
-# documentation. Note that a value of 0 will completely suppress the enum
-# values from appearing in the overview section.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
-# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
-# used to set the initial width (in pixels) of the frame in which the tree
-# is shown.
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 250
-# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
-# links to external symbols imported via tag files in a separate window.
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
-# Use this tag to change the font size of Latex formulas included
-# as images in the HTML documentation. The default is 10. Note that
-# when you change the font size after a successful doxygen run you need
-# to manually remove any form_*.png images from the HTML output directory
-# to force them to be regenerated.
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANPARENT tag to determine whether or not the images
-# generated for formulas are transparent PNGs. Transparent PNGs are
-# not supported properly for IE 6.0, but are supported on all modern browsers.
-# Note that when changing this option you need to delete any form_*.png files
-# in the HTML output before the changes have effect.
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
-# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
-# (see http://www.mathjax.org) which uses client side Javascript for the
-# rendering instead of using prerendered bitmaps. Use this if you do not
-# have LaTeX installed or if you want to formulas look prettier in the HTML
-# output. When enabled you may also need to install MathJax separately and
-# configure the path to it using the MATHJAX_RELPATH option.
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using prerendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
-# 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
-# directory is located at the same level as the HTML output directory, then
-# MATHJAX_RELPATH should be ../mathjax. The default value points to
-# the MathJax Content Delivery Network so you can quickly see the result without
-# installing MathJax.
-# However, it is strongly recommended to install a local
-# copy of MathJax from http://www.mathjax.org before deployment.
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+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 directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = http://www.mathjax.org/mathjax
-# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
-# names that should be enabled during MathJax rendering.
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
-# 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
-# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
-# (GENERATE_DOCSET) there is already a search function so this one should
-# typically be disabled. For large projects the javascript based search engine
-# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+# 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. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+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 HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
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 searching 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 section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
SERVER_BASED_SEARCH = NO
+# When EXTERNAL_SEARCH tag 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: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer ( doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+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.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+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.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+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 = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
+# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
-# generate Latex output.
+# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output.
+# The default value is: YES.
GENERATE_LATEX = YES
-# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `latex' will be used as the default path.
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_OUTPUT = latex
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
-# invoked. If left blank `latex' will be used as the default command name.
-# Note that when enabling USE_PDFLATEX this option is only used for
-# generating bitmaps for formulas in the HTML output, but not in the
-# Makefile that is written to the output directory.
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_CMD_NAME = latex
-# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
-# generate index for LaTeX. If left blank `makeindex' will be used as the
-# default command name.
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
MAKEINDEX_CMD_NAME = makeindex
-# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
-# LaTeX documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
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.
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4
-# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
-# packages that should be included in the LaTeX output.
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. To get the times font for
+# instance you can specify
+# EXTRA_PACKAGES=times
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
-# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
-# the generated latex document. The header should contain everything until
-# the first chapter. If it is left blank doxygen will generate a
-# standard header. Notice: only use this tag if you know what you are doing!
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
+# replace them by respectively the title of the page, the current date and time,
+# only the current date, the version number of doxygen, the project name (see
+# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
-# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
-# the generated latex document. The footer should contain everything after
-# the last chapter. If it is left blank doxygen will generate a
-# standard footer. Notice: only use this tag if you know what you are doing!
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
-# 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
-# This makes the output suitable for online browsing using a pdf viewer.
+# 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 output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+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 or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = YES
-# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
-# plain latex in the generated Makefile. Set this option to YES to get a
+# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES to get a
# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = YES
-# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
-# command to the generated LaTeX files. This will instruct LaTeX to keep
-# running if errors occur, instead of asking the user for help.
-# This option is also used when generating formulas in HTML.
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BATCHMODE = NO
-# If LATEX_HIDE_INDICES is set to YES then doxygen will not
-# include the index chapters (such as File Index, Compound Index, etc.)
-# in the output.
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HIDE_INDICES = NO
-# If LATEX_SOURCE_CODE is set to YES then doxygen will include
-# source code with syntax highlighting in the LaTeX output.
-# Note that which sources are shown also depends on other settings
-# such as SOURCE_BROWSER.
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_SOURCE_CODE = NO
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
-# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
-# http://en.wikipedia.org/wiki/BibTeX for more info.
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_BIB_STYLE = plain
#---------------------------------------------------------------------------
-# configuration options related to the RTF output
+# Configuration options related to the RTF output
#---------------------------------------------------------------------------
-# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
-# The RTF output is optimized for Word 97 and may not look very pretty with
-# other RTF readers or editors.
+# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
GENERATE_RTF = NO
-# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
-# If a relative path is entered the value of OUTPUT_DIRECTORY will be
-# put in front of it. If left blank `rtf' will be used as the default path.
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_OUTPUT = rtf
-# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
-# RTF documents. This may be useful for small projects and may help to
-# save some trees in general.
+# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
COMPACT_RTF = NO
-# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
-# will contain hyperlink fields. The RTF file will
-# contain links (just like the HTML output) instead of page references.
-# This makes the output suitable for online browsing using WORD or other
-# programs which support those fields.
-# Note: wordpad (write) and others do not support links.
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_HYPERLINKS = NO
-# Load style sheet definitions from file. Syntax is similar to doxygen's
-# config file, i.e. a series of assignments. You only have to provide
-# replacements, missing definitions are set to their default value.
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
-# Syntax is similar to doxygen's config file.
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
-# configuration options related to the man page output
+# Configuration options related to the man page output
#---------------------------------------------------------------------------
-# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
-# generate man pages
+# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
GENERATE_MAN = NO
-# The MAN_OUTPUT tag is used to specify where the man 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 `man' will be used as the default path.
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_OUTPUT = man
-# The MAN_EXTENSION tag determines the extension that is added to
-# the generated man pages (default is the subroutine's section .3)
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_EXTENSION = .3
-# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
-# then it will generate one additional man file for each entity
-# documented in the real man page(s). These additional files
-# only source the real man page, but without them the man command
-# would be unable to find the correct page. The default is NO.
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_LINKS = NO
#---------------------------------------------------------------------------
-# configuration options related to the XML output
+# Configuration options related to the XML output
#---------------------------------------------------------------------------
-# If the GENERATE_XML tag is set to YES Doxygen will
-# generate an XML file that captures the structure of
-# the code including all documentation.
+# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
GENERATE_XML = NO
-# The XML_OUTPUT tag is used to specify where the XML 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 `xml' will be used as the default path.
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify an XML schema,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_SCHEMA =
-# The XML_DTD tag can be used to specify an XML DTD,
-# which can be used by a validating XML parser to check the
-# syntax of the XML files.
+# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
+# validating XML parser to check the syntax of the XML files.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_DTD =
-# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
-# dump the program listings (including syntax highlighting
-# and cross-referencing information) to the XML output. Note that
-# enabling this will significantly increase the size of the XML output.
+# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
+# 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.
+# The default value is: NO.
+
+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.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
-# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
-# generate an AutoGen Definitions (see autogen.sf.net) file
-# that captures the structure of the code including all
-# documentation. Note that this feature is still experimental
-# and incomplete at the moment.
+# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen
+# Definitions (see http://autogen.sf.net) file that captures the structure of
+# the code including all documentation. Note that this feature is still
+# experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
+# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
-# If the GENERATE_PERLMOD tag is set to YES Doxygen will
-# generate a Perl module file that captures the structure of
-# the code including all documentation. Note that this
-# feature is still experimental and incomplete at the
-# moment.
+# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
GENERATE_PERLMOD = NO
-# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
-# the necessary Makefile rules, Perl scripts and LaTeX code to be able
-# to generate PDF and DVI output from the Perl module output.
+# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_LATEX = NO
-# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
-# nicely formatted so it can be parsed by a human reader.
-# This is useful
-# if you want to understand what is going on.
-# On the other hand, if this
-# tag is set to NO the size of the Perl module output will be much smaller
-# and Perl will parse it just the same.
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_PRETTY = YES
-# The names of the make variables in the generated doxyrules.make file
-# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
-# This is useful so different doxyrules.make files included by the same
-# Makefile don't overwrite each other's variables.
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
-# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
-# evaluate all C-preprocessor directives found in the sources and include
-# files.
+# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
ENABLE_PREPROCESSING = YES
-# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
-# names in the source code. If set to NO (the default) only conditional
-# compilation will be performed. Macro expansion can be done in a controlled
-# way by setting EXPAND_ONLY_PREDEF to YES.
+# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names
+# in the source code. If set to NO only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
-# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
-# then the macro expansion is limited to the macros specified with the
-# PREDEFINED and EXPAND_AS_DEFINED tags.
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_ONLY_PREDEF = NO
-# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
-# pointed to by INCLUDE_PATH will be searched when a #include is found.
+# If the SEARCH_INCLUDES tag is set to YES the includes files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
-# contain include files that are not input files but should be processed by
-# the preprocessor.
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
-# directories. If left blank, the patterns specified with FILE_PATTERNS will
-# be used.
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
-# The PREDEFINED tag can be used to specify one or more macro names that
-# are defined before the preprocessor is started (similar to the -D option of
-# gcc). The argument of the tag is a list of macros of the form: name
-# or name=definition (no spaces). If the definition and the = are
-# omitted =1 is assumed. To prevent a macro definition from being
-# undefined via #undef or recursively expanded use the := operator
-# instead of the = operator.
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
-# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
-# this tag can be used to specify a list of macro names that should be expanded.
-# The macro definition that is found in the sources will be used.
-# Use the PREDEFINED tag if you want to use a different macro definition that
-# overrules the definition found in the source code.
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
-# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
-# doxygen's preprocessor will remove all references to function-like macros
-# that are alone on a line, have an all uppercase name, and do not end with a
-# semicolon, because these will confuse the parser if not removed.
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all refrences to function-like macros that are alone on a line, have an
+# all uppercase name, and do not end with a semicolon. Such function macros are
+# typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# Configuration options related to external references
#---------------------------------------------------------------------------
-# The TAGFILES option can be used to specify one or more tagfiles. For each
-# tag file the location of the external documentation should be added. The
-# format of a tag file without this location is as follows:
-#
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
# TAGFILES = file1 file2 ...
# Adding location for the tag files is done as follows:
-#
# TAGFILES = file1=loc1 "file2 = loc2" ...
-# where "loc1" and "loc2" can be relative or absolute paths
-# or URLs. Note that each tag file must have a unique name (where the name does
-# NOT include the path). If a tag file is not located in the directory in which
-# doxygen is run, you must also specify the path to the tagfile here.
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have an unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
TAGFILES =
-# When a file name is specified after GENERATE_TAGFILE, doxygen will create
-# a tag file that is based on the input files it reads.
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
-# If the ALLEXTERNALS tag is set to YES all external classes will be listed
-# in the class index. If set to NO only the inherited external classes
-# will be listed.
+# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
+# class index. If set to NO only the inherited external classes will be listed.
+# The default value is: NO.
ALLEXTERNALS = NO
-# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
-# in the modules index. If set to NO, only the current project's groups will
-# be listed.
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in
+# the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
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.
+# The default value is: YES.
+
+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').
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
PERL_PATH = /usr/bin/perl
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
-# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
-# or super classes. Setting the tag to NO turns the diagrams off. Note that
-# this option also works with HAVE_DOT disabled, but it is recommended to
-# install and use dot, since it yields more powerful graphs.
+# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
CLASS_DIAGRAMS = YES
# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see
-# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
-# or is not a class.
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
-# available from the path. This tool is part of Graphviz, a graph visualization
-# toolkit from AT&T and Lucent Bell Labs. The other options in this section
-# have no effect if this option is set to NO (the default)
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
HAVE_DOT = NO
-# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
-# allowed to run in parallel. When set to 0 (the default) doxygen will
-# base this on the number of processors available in the system. You can set it
-# explicitly to a value larger than 0 to get control over the balance
-# between CPU load and processing speed.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NUM_THREADS = 0
-# By default doxygen will use the Helvetica font for all dot files that
-# doxygen generates. When you want a differently looking font you can specify
-# the font name using DOT_FONTNAME. You need to make sure dot is able to find
-# the font, which can be done by putting it in a standard location or by setting
-# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
-# directory containing the font.
+# When you want a differently looking font n the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTNAME = Helvetica
-# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
-# The default size is 10pt.
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTSIZE = 10
-# By default doxygen will tell dot to use the Helvetica font.
-# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
-# set the path where dot can find it.
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
-# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect inheritance relations. Setting this tag to YES will force the
-# CLASS_DIAGRAMS tag to NO.
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
CLASS_GRAPH = YES
-# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for each documented class showing the direct and
-# indirect implementation dependencies (inheritance, containment, and
-# class references variables) of the class with other documented classes.
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
-# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
-# will generate a graph for groups, showing the direct groups dependencies
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
UML_LOOK = NO
-# If the UML_LOOK tag is enabled, the fields and methods are shown inside
-# 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
-# exceeded by 50% before the limit is enforced.
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside 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 manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
UML_LIMIT_NUM_FIELDS = 10
-# If set to YES, the inheritance and collaboration graphs will show the
-# relations between templates and their instances.
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
TEMPLATE_RELATIONS = NO
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
-# tags are set to YES then doxygen will generate a graph for each documented
-# file showing the direct and indirect include dependencies of the file with
-# other documented files.
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDE_GRAPH = YES
-# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
-# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
-# documented header file showing the documented files that directly or
-# indirectly include this file.
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
INCLUDED_BY_GRAPH = YES
-# If the CALL_GRAPH and HAVE_DOT options are set to YES then
-# doxygen will generate a call dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable call graphs
-# for selected functions only using the \callgraph command.
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALL_GRAPH = NO
-# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
-# doxygen will generate a caller dependency graph for every global function
-# or class method. Note that enabling this option will significantly increase
-# the time of a run. So in most cases it will be better to enable caller
-# graphs for selected functions only using the \callergraph command.
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
CALLER_GRAPH = NO
-# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
-# will generate a graphical hierarchy of all classes instead of a textual one.
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GRAPHICAL_HIERARCHY = YES
-# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES
-# then doxygen will show the dependencies a directory has on other directories
-# in a graphical way. The dependency relations are determined by the #include
-# relations between the files in the directories.
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
-# generated by dot. Possible values are svg, png, jpg, or gif.
-# If left blank png will be used. If you choose svg you need to set
-# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible in IE 9+ (other browsers do not have this requirement).
+# generated by dot.
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif and svg.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_IMAGE_FORMAT = png
# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
# enable generation of interactive SVG images that allow zooming and panning.
-# Note that this requires a modern browser other than Internet Explorer.
-# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
-# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
-# visible. Older versions of IE do not have SVG support.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
INTERACTIVE_SVG = NO
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
-# contain dot files that are included in the documentation (see the
-# \dotfile command).
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
-# contain msc files that are included in the documentation (see the
-# \mscfile command).
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
MSCFILE_DIRS =
-# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
-# nodes that will be shown in the graph. If the number of nodes in a graph
-# becomes larger than this value, doxygen will truncate the graph, which is
-# visualized by representing a node as a red box. Note that doxygen if the
-# number of direct children of the root node in a graph is already larger than
-# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
-# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_GRAPH_MAX_NODES = 50
-# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
-# graphs generated by dot. A depth value of 3 means that only nodes reachable
-# from the root by following a path via at most 3 edges will be shown. Nodes
-# that lay further from the root node will be omitted. Note that setting this
-# option to 1 or 2 may greatly reduce the computation time needed for large
-# code bases. Also note that the size of a graph can be further restricted by
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
-# background. This is disabled by default, because dot on Windows does not
-# seem to support this out of the box. Warning: Depending on the platform used,
-# enabling this option may lead to badly anti-aliased labels on the edges of
-# a graph (i.e. they become hard to read).
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
-# makes dot run faster, but since only newer versions of dot (>1.8.10)
-# support this, this feature is disabled by default.
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_MULTI_TARGETS = YES
-# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
-# generate a legend page explaining the meaning of the various boxes and
-# arrows in the dot generated graphs.
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
GENERATE_LEGEND = YES
-# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
-# remove the intermediate dot files that are used to generate
-# the various graphs.
+# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
DOT_CLEANUP = YES
def dependencies(target):
return (('ffmpeg-cdist', '2dffa11'),
- ('libdcp', 'd5accd6'))
+ ('libdcp', '1.0'),
+ ('libsub', None))
def build(target, options):
cmd = './waf configure --prefix=%s' % target.directory
"%s/SOURCES/dcpomatic-%s.tar.bz2" % (topdir, version)
)
- target.command('rpmbuild --define \'_topdir %s\' -bb build/platform/linux/dcpomatic.spec' % topdir)
+ target.command('rpmbuild --define \'_topdir %s\' -bb build/platform/linux/dcpomatic2.spec' % topdir)
rpms = []
if cpu == "amd64":
-dcpomatic (1.76.2-1) UNRELEASED; urgency=low
+dcpomatic (2.0.14-1) UNRELEASED; urgency=low
* New upstream release.
* New upstream release.
--- /dev/null
+\documentclass{article}
+\begin{document}
+
+\section{Status quo}
+
+As at 0.78 there is an unfortunate mish-mash of code to handle the
+input `content' the goes into a DCP.
+
+The Film has a `content' file name. This is guessed to be either a
+movie (for FFmpeg) or a still-image (for ImageMagick) based on its
+extension. We also have `external audio', which is a set of WAV files
+for libsndfile, and a flag to enable that.
+
+The `content' file is badly named and limiting. We can't have
+multiple content files, and it's not really the `content' as such (it
+used to be, but increasingly it's only a part of the content, on equal
+footing with `external' audio).
+
+The choice of sources for sound is expressed clumsily by the
+AudioStream class hierarchy.
+
+
+\section{Targets}
+
+We want to be able to implement the following:
+
+\begin{itemize}
+\item Immediately:
+\begin{itemize}
+\item Multiple still images, each with their own duration, made into a `slide-show'
+\item Lack of bugs in adding WAV-file audio to still images.
+\item External subtitle files (either XML or SRT) to be converted to XML subtitles in the DCP.
+\end{itemize}
+
+\item In the future:
+\begin{itemize}
+\item Playlist-style multiple video / audio (perhaps).
+\end{itemize}
+\end{itemize}
+
+
+\section{Content hierarchy}
+
+One idea is to have a hierarchy of Content classes (\texttt{Content},
+\texttt{\{Video/Audio\}Content}, \texttt{FFmpegContent}, \texttt{ImageMagickContent},
+\texttt{SndfileContent}).
+
+Then the Film has a list of these, and decides what to put into the
+DCP based on some rules. These rules would probably be fixed (for
+now), with the possibility to expand later into some kind of playlist.
+
+
+\section{Immediate questions}
+
+\subsection{What Film attributes are video-content specific, and which are general?}
+
+Questionable attributes:
+
+\begin{itemize}
+\item Trust content header
+\item Crop
+\item Filters
+
+Post-processing (held as part of the filters description) is done in
+the encoder, by which time all knowledge of the source is lost.
+
+\item Scaler
+\item Trim start/end
+
+Messily tied in with the encoding side. We want to implement this
+using start/end point specifications in the DCP reel, otherwise
+modifying the trim points requires a complete re-encode.
+
+\item Audio gain
+\item Audio delay
+\item With subtitles
+\item Subtitle offset/scale
+\item Colour LUT
+\end{itemize}
+
+Attributes that I think must remain in Film:
+\begin{itemize}
+\item DCP content type
+\item Format
+\item A/B
+\item J2K bandwidth
+\end{itemize}
+
+Part of the consideration here is that per-content attributes need to
+be represented in the GUI differently to how things are represented
+now.
+
+Bear in mind also that, as it stands, the only options for video are:
+
+\begin{enumerate}
+\item An FFmpeg video
+\item A set of stills
+\end{enumerate}
+
+and so the need for multiple scalers, crop and filters is
+questionable. Also, there is one set of audio (either from WAVs or
+from the FFMpeg file), so per-content audio gain/delay is also
+questionable. Trust content header is only applicable for FFmpeg
+content, really. Similarly trim, with-subtitles, subtitle details,
+colour LUT; basically none of it is really important right now.
+
+Hence it may be sensible to keep everything in Film and move it later
+along YAGNI lines.
+
+
+\subsection{Who answers questions like: ``what is the length of video?''?}
+
+If we have FFmpeg video, the question is easy to answer. For a set of
+stills, it is less easy. Who knows that we are sticking them all
+together end-to-end, with different durations for each?
+
+If we have one-content-object equalling one file, the content objects
+will presumably know how long their file should be displayed for.
+There would appear to be two options following this:
+
+\begin{enumerate}
+\item There is one \texttt{ImageMagickDecoder} which is fed all the
+ files, and outputs them in order. The magic knowledge is then
+ within this class, really.
+\item There are multiple \texttt{ImageMagickDecoder} classes, one per
+ \texttt{..Content}, and some controlling (`playlist') class to manage
+ them. The `playlist' is then itself a
+ \texttt{\{Video/Audio\}Source}, and has the magic knowledge.
+\end{enumerate}
+
+
+\section{Playlist approach}
+
+Let's try the playlist approach. We define a hierarchy of content classes:
+
+\begin{verbatim}
+
+class Content
+{
+public:
+ boost::filesystem::path file () const;
+};
+
+class VideoContent : virtual public Content
+{
+public:
+ VideoContentFrame video_length () const;
+ float video_frame_rate () const;
+ libdcp::Size size () const;
+
+};
+
+class AudioContent : virtual public Content
+{
+
+};
+
+class FFmpegContent : public VideoContent, public AudioContent
+{
+public:
+ .. stream stuff ..
+};
+
+class ImageMagickContent : public VideoContent
+{
+
+};
+
+class SndfileContent : public AudioContent
+{
+public:
+ .. channel allocation for this file ..
+};
+\end{verbatim}
+
+Then Film has a \texttt{Playlist} which has a
+\texttt{vector<shared\_ptr<Content> >}. It can answer questions
+about audio/video length, frame rate, audio channels and so on.
+
+\texttt{Playlist} can also be a source of video and audio, so clients can do:
+
+\begin{verbatim}
+shared_ptr<Playlist> p = film->playlist ();
+p->Video.connect (foo);
+p->Audio.connect (foo);
+while (!p->pass ()) {
+ /* carry on */
+}
+\end{verbatim}
+
+Playlist could be created on-demand for all the difference it would
+make. And perhaps it should, since it will hold Decoders which are
+probably run-once.
+
+\end{document}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1052.3622"
+ height="744.09448"
+ id="svg3115"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="audio_path.svg">
+ <defs
+ id="defs3117">
+ <marker
+ inkscape:stockid="Arrow2Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow2Mend"
+ style="overflow:visible;">
+ <path
+ id="path3860"
+ style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+ d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+ transform="scale(0.6) rotate(180) translate(0,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.88221578"
+ inkscape:cx="342.66212"
+ inkscape:cy="409.15497"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:object-paths="false"
+ inkscape:snap-global="true"
+ inkscape:window-width="1366"
+ inkscape:window-height="714"
+ inkscape:window-x="1280"
+ inkscape:window-y="283"
+ inkscape:window-maximized="1"
+ inkscape:snap-bbox="false"
+ inkscape:snap-nodes="true"
+ inkscape:object-nodes="true" />
+ <metadata
+ id="metadata3120">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-308.2677)">
+ <rect
+ style="color:#000000;fill:#cdde87;fill-opacity:1;fill-rule:nonzero;stroke:#ff5555;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 4;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3395"
+ width="861"
+ height="34"
+ x="22"
+ y="326.09448"
+ transform="translate(0,308.2677)" />
+ <rect
+ style="color:#000000;fill:#ffeeaa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3393"
+ width="861.04535"
+ height="36.999996"
+ x="22"
+ y="597.36218" />
+ <rect
+ style="color:#000000;fill:#ff9955;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:1, 4;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3391"
+ width="860.48584"
+ height="37.999996"
+ x="22"
+ y="251.09448"
+ transform="translate(0,308.2677)" />
+ <rect
+ style="color:#000000;fill:#ffaaaa;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3389"
+ width="860.78772"
+ height="29.7075"
+ x="22"
+ y="529.65466" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="186"
+ y="548.36212"
+ id="text3123"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="186"
+ y="548.36212"
+ id="tspan3127">AVPacket</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="342"
+ y="548.36212"
+ id="text3137"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3139"
+ x="342"
+ y="548.36212">AVFrame</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="462"
+ y="548.36212"
+ id="text3143"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3145"
+ x="462"
+ y="548.36212">AudioBuffers</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="31"
+ y="548.36212"
+ id="text3165"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3167"
+ x="31"
+ y="548.36212">Data type</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="118"
+ y="656.36218"
+ id="text3151"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3153"
+ x="118"
+ y="656.36218">FFmpegDecoder</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="510.276"
+ y="656.36218"
+ id="text3155"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3157"
+ x="510.276"
+ y="656.36218">AudioDecoder</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="30.747999"
+ y="656.36218"
+ id="text3169"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3171"
+ x="30.747999"
+ y="656.36218">Class</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="679"
+ y="656.36218"
+ id="text3238"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3240"
+ x="679"
+ y="656.36218">Player</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="219.51123"
+ y="584.11017"
+ id="text3129"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3131"
+ x="219.51123"
+ y="584.11017">avcodec_decode_audio4</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="118"
+ y="584.11017"
+ id="text3133"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3135"
+ x="118"
+ y="584.11017">av_read_frame</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="371.99997"
+ y="584.11017"
+ id="text3147"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3149"
+ x="371.99997"
+ y="584.11017">deinterleave_audio</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="510"
+ y="584.11017"
+ id="text3159"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3161"
+ x="510"
+ y="584.11017">audio</tspan><tspan
+ sodipodi:role="line"
+ x="510"
+ y="599.11017"
+ id="tspan3163" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="30.976"
+ y="584.11017"
+ id="text3181"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3183"
+ x="30.976"
+ y="584.11017">Method</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="678.96399"
+ y="584.11017"
+ id="text3242"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3244"
+ x="678.96399"
+ y="584.11017">get_audio</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:14px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="30.747999"
+ y="620.27814"
+ id="text3185"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3187"
+ x="30.747999"
+ y="620.27814">Operation</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="191"
+ y="620.27814"
+ id="text3222"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3224"
+ x="191"
+ y="620.27814">Decode</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="370"
+ y="620.27814"
+ id="text3226"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3228"
+ x="370"
+ y="620.27814">Deinterleave</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="510.17999"
+ y="620.27814"
+ id="text3230"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3232"
+ x="510.17999"
+ y="620.27814">Resample</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="573"
+ y="620.27814"
+ id="text3234"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3236"
+ x="573"
+ y="620.27814">Run Processor</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="678.85602"
+ y="620.27814"
+ id="text3246"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3248"
+ x="678.85602"
+ y="620.27814">Gain</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="731.56293"
+ y="620.27814"
+ id="text3250"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3252"
+ x="731.56293"
+ y="620.27814">Channel remap</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ x="841"
+ y="620.27814"
+ id="text3254"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3256"
+ x="841"
+ y="620.27814">Mix</tspan></text>
+ <rect
+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect3356"
+ width="861"
+ height="138.66901"
+ x="22"
+ y="529.69318" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 22,251.09448 860.78771,0"
+ id="path3358"
+ inkscape:connector-curvature="0"
+ transform="translate(0,308.2677)"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 22,289.09448 860.48582,0"
+ id="path3360"
+ inkscape:connector-curvature="0"
+ transform="translate(0,308.2677)"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 22,326.09448 860.69386,0"
+ id="path3362"
+ inkscape:connector-curvature="0"
+ transform="translate(0,308.2677)"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 100,221.37674 0,138.63456"
+ id="path3364"
+ inkscape:connector-curvature="0"
+ transform="translate(0,308.2677)"
+ sodipodi:nodetypes="cc" />
+ <g
+ id="g4273"
+ transform="translate(165.08717,-48.74091)">
+ <text
+ transform="translate(0,308.2677)"
+ sodipodi:linespacing="125%"
+ id="text3036"
+ y="437.11526"
+ x="165.91304"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Roman;-inkscape-font-specification:Latin Modern Roman"
+ xml:space="preserve"><tspan
+ y="437.11526"
+ x="165.91304"
+ id="tspan3038"
+ sodipodi:role="line">Data path </tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path3059"
+ d="m 223.62193,742.39257 183.54631,0"
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)" />
+ </g>
+ </g>
+</svg>
+++ /dev/null
-\documentclass{article}
-\begin{document}
-
-\section{Status quo}
-
-As at 0.78 there is an unfortunate mish-mash of code to handle the
-input `content' the goes into a DCP.
-
-The Film has a `content' file name. This is guessed to be either a
-movie (for FFmpeg) or a still-image (for ImageMagick) based on its
-extension. We also have `external audio', which is a set of WAV files
-for libsndfile, and a flag to enable that.
-
-The `content' file is badly named and limiting. We can't have
-multiple content files, and it's not really the `content' as such (it
-used to be, but increasingly it's only a part of the content, on equal
-footing with `external' audio).
-
-The choice of sources for sound is expressed clumsily by the
-AudioStream class hierarchy.
-
-
-\section{Targets}
-
-We want to be able to implement the following:
-
-\begin{itemize}
-\item Immediately:
-\begin{itemize}
-\item Multiple still images, each with their own duration, made into a `slide-show'
-\item Lack of bugs in adding WAV-file audio to still images.
-\item External subtitle files (either XML or SRT) to be converted to XML subtitles in the DCP.
-\end{itemize}
-
-\item In the future:
-\begin{itemize}
-\item Playlist-style multiple video / audio (perhaps).
-\end{itemize}
-\end{itemize}
-
-
-\section{Content hierarchy}
-
-One idea is to have a hierarchy of Content classes (\texttt{Content},
-\texttt{\{Video/Audio\}Content}, \texttt{FFmpegContent}, \texttt{ImageMagickContent},
-\texttt{SndfileContent}).
-
-Then the Film has a list of these, and decides what to put into the
-DCP based on some rules. These rules would probably be fixed (for
-now), with the possibility to expand later into some kind of playlist.
-
-
-\section{Immediate questions}
-
-\subsection{What Film attributes are video-content specific, and which are general?}
-
-Questionable attributes:
-
-\begin{itemize}
-\item Trust content header
-\item Crop
-\item Filters
-
-Post-processing (held as part of the filters description) is done in
-the encoder, by which time all knowledge of the source is lost.
-
-\item Scaler
-\item Trim start/end
-
-Messily tied in with the encoding side. We want to implement this
-using start/end point specifications in the DCP reel, otherwise
-modifying the trim points requires a complete re-encode.
-
-\item Audio gain
-\item Audio delay
-\item With subtitles
-\item Subtitle offset/scale
-\item Colour LUT
-\end{itemize}
-
-Attributes that I think must remain in Film:
-\begin{itemize}
-\item DCP content type
-\item Format
-\item A/B
-\item J2K bandwidth
-\end{itemize}
-
-Part of the consideration here is that per-content attributes need to
-be represented in the GUI differently to how things are represented
-now.
-
-Bear in mind also that, as it stands, the only options for video are:
-
-\begin{enumerate}
-\item An FFmpeg video
-\item A set of stills
-\end{enumerate}
-
-and so the need for multiple scalers, crop and filters is
-questionable. Also, there is one set of audio (either from WAVs or
-from the FFMpeg file), so per-content audio gain/delay is also
-questionable. Trust content header is only applicable for FFmpeg
-content, really. Similarly trim, with-subtitles, subtitle details,
-colour LUT; basically none of it is really important right now.
-
-Hence it may be sensible to keep everything in Film and move it later
-along YAGNI lines.
-
-
-\subsection{Who answers questions like: ``what is the length of video?''?}
-
-If we have FFmpeg video, the question is easy to answer. For a set of
-stills, it is less easy. Who knows that we are sticking them all
-together end-to-end, with different durations for each?
-
-If we have one-content-object equalling one file, the content objects
-will presumably know how long their file should be displayed for.
-There would appear to be two options following this:
-
-\begin{enumerate}
-\item There is one \texttt{ImageMagickDecoder} which is fed all the
- files, and outputs them in order. The magic knowledge is then
- within this class, really.
-\item There are multiple \texttt{ImageMagickDecoder} classes, one per
- \texttt{..Content}, and some controlling (`playlist') class to manage
- them. The `playlist' is then itself a
- \texttt{\{Video/Audio\}Source}, and has the magic knowledge.
-\end{enumerate}
-
-
-\section{Playlist approach}
-
-Let's try the playlist approach. We define a hierarchy of content classes:
-
-\begin{verbatim}
-
-class Content
-{
-public:
- boost::filesystem::path file () const;
-};
-
-class VideoContent : virtual public Content
-{
-public:
- VideoContentFrame video_length () const;
- float video_frame_rate () const;
- libdcp::Size size () const;
-
-};
-
-class AudioContent : virtual public Content
-{
-
-};
-
-class FFmpegContent : public VideoContent, public AudioContent
-{
-public:
- .. stream stuff ..
-};
-
-class ImageMagickContent : public VideoContent
-{
-
-};
-
-class SndfileContent : public AudioContent
-{
-public:
- .. channel allocation for this file ..
-};
-\end{verbatim}
-
-Then Film has a \texttt{Playlist} which has a
-\texttt{vector<shared\_ptr<Content> >}. It can answer questions
-about audio/video length, frame rate, audio channels and so on.
-
-\texttt{Playlist} can also be a source of video and audio, so clients can do:
-
-\begin{verbatim}
-shared_ptr<Playlist> p = film->playlist ();
-p->Video.connect (foo);
-p->Audio.connect (foo);
-while (!p->pass ()) {
- /* carry on */
-}
-\end{verbatim}
-
-Playlist could be created on-demand for all the difference it would
-make. And perhaps it should, since it will hold Decoders which are
-probably run-once.
-
-\end{document}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="player_get_audio.svg">
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.2517416"
+ inkscape:cx="368.22037"
+ inkscape:cy="938.8543"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-width="1366"
+ inkscape:window-height="714"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mstart"
+ style="overflow:visible">
+ <path
+ id="path3983"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mend"
+ style="overflow:visible;">
+ <path
+ id="path3986"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart-1"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3983-6"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-4"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3986-5"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1MstartQ"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1MstartQ"
+ style="overflow:visible">
+ <path
+ id="path4874"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="stroke:#008000;stroke-width:1.0pt;fill:#008000;fill-rule:evenodd"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mendh"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mendh"
+ style="overflow:visible;">
+ <path
+ id="path4877"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="stroke:#008000;stroke-width:1.0pt;fill:#008000;fill-rule:evenodd"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mstart-2"
+ style="overflow:visible">
+ <path
+ id="path3983-60"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-9"
+ style="overflow:visible">
+ <path
+ id="path3986-52"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1MstartM"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1MstartM"
+ style="overflow:visible">
+ <path
+ id="path5026"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="stroke:#ff0000;stroke-width:1.0pt;fill:#ff0000;fill-rule:evenodd"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1MendT"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1MendT"
+ style="overflow:visible;">
+ <path
+ id="path5029"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="stroke:#ff0000;stroke-width:1.0pt;fill:#ff0000;fill-rule:evenodd"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1MstartM"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1MstartM-0"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5026-3"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt"
+ transform="matrix(0.4,0,0,0.4,4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1MendT"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1MendT-0"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5029-9"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill:#ff0000;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ </defs>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 55,34.711899 55,198.7119"
+ id="path2985"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 47,190.36218 641,0"
+ id="path2987"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="25.429111"
+ y="210.57095"
+ id="text2989"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan2991"
+ x="25.429111"
+ y="210.57095">DCP time 0</tspan></text>
+ <rect
+ style="color:#000000;fill:#000000;fill-opacity:0.15425535;stroke:none;stroke-width:0.99999988px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2995"
+ width="267.44409"
+ height="153.85982"
+ x="205.08385"
+ y="-190.34831"
+ transform="scale(1,-1)" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 205.14391,36.525554 0,164.454206"
+ id="path3783"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 472.50163,36.353991 0,164.625769"
+ id="path3785"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="191.90874"
+ y="212.87964"
+ id="text3787"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3789"
+ x="191.90874"
+ y="212.87964">time</tspan></text>
+ <path
+ style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-mid:none;marker-end:url(#Arrow1Mend)"
+ d="m 207.02561,30.692594 263.76528,0"
+ id="path3791"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="319.72653"
+ y="23.80699"
+ id="text4787"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4789"
+ x="319.72653"
+ y="23.80699">length</tspan></text>
+ <path
+ style="fill:none;stroke:#008000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 206.07602,19.432227 0,210.960753"
+ id="path4817"
+ inkscape:connector-curvature="0" />
+ <path
+ style="stroke-linejoin:miter;marker-end:url(#Arrow1Mendh);stroke-opacity:1;marker-start:url(#Arrow1MstartQ);stroke:#008000;stroke-linecap:butt;stroke-width:1px;marker-mid:none;fill:none"
+ d="m 207.02561,13.263911 263.76528,0"
+ id="path3791-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="297.97812"
+ y="6.3783064"
+ id="text4787-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4789-8"
+ x="297.97812"
+ y="6.3783064">length_frames</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4929"
+ y="238.74654"
+ x="205.10674"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ xml:space="preserve"><tspan
+ y="238.74654"
+ x="205.10674"
+ id="tspan4931"
+ sodipodi:role="line">out</tspan><tspan
+ id="tspan4933"
+ y="253.74654"
+ x="205.10674"
+ sodipodi:role="line" /></text>
+ <rect
+ y="67.863129"
+ x="123.76559"
+ height="71.25898"
+ width="448.18149"
+ id="rect4973"
+ style="color:#000000;fill:#ff0000;fill-opacity:0.30319148;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4975"
+ d="m 125.66194,148.65781 77.93059,0"
+ style="color:#000000;fill:none;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:url(#Arrow1MstartM);marker-end:url(#Arrow1MendT);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="85.083427"
+ y="163.35722"
+ id="text5098"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5100"
+ x="85.083427"
+ y="163.35722">dcp_to_content_audio(time)</tspan><tspan
+ sodipodi:role="line"
+ x="85.083427"
+ y="178.35722"
+ id="tspan5102" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="126.75607"
+ y="78.363503"
+ id="text5123"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5125"
+ x="126.75607"
+ y="78.363503">Content</tspan></text>
+ <rect
+ style="color:#000000;fill:#ff0000;fill-opacity:0.48404256;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5148"
+ width="204.37869"
+ height="70.190666"
+ x="239.47403"
+ y="68.041344" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="244.01578"
+ y="78.363503"
+ id="text5567"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5569"
+ x="244.01578"
+ y="78.363503">in</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4975-9"
+ d="m 125.66194,111.10032 112.28273,0"
+ style="color:#000000;fill:none;stroke:#ff0000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-start:url(#Arrow1MstartM);marker-end:url(#Arrow1MendT);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:125%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Latin Modern Mono;-inkscape-font-specification:Latin Modern Mono"
+ x="135.21161"
+ y="106.87954"
+ id="text5620"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5622"
+ x="135.21161"
+ y="106.87954">in->frame</tspan></text>
+ </g>
+</svg>
\documentclass{article}
+\usepackage{amsmath}
\begin{document}
Here is what resampling we need to do. Content video is at $C_V$ fps, audio at $C_A$.
\textbf{Resample $C_A$ to the DCI rate.}
\section{Hard case 1}
+\label{sec:hard1}
$C_V$ is not a DCI rate, $C_A$ is, e.g.\ if $C_V = 25$, $C_A =
48\times{}10^3$. We will run the video at a nearby DCI rate $F_V$,
\medskip
\textbf{Resample $C_A$ to $C_V C_A / F_V$}
+\section{Hard case 2}
+
+Neither $C_V$ nor $C_A$ is not a DCI rate, e.g.\ if $C_V = 25$, $C_A =
+44.1\times{}10^3$. We will run the video at a nearby DCI rate $F_V$,
+meaning that it will run faster or slower than it should. We first
+resample the audio to a DCI rate $F_A$, then perform as with
+Section~\ref{sec:hard1} above.
+
+\medskip
+\textbf{Resample $C_A$ to $C_V F_A / F_V$}
+
+
+\section{The general case}
+
+Given a DCP running at $F_V$ and $F_A$ and a piece of content at $C_V$
+and $C_A$, resample the audio to $R_A$ where
+\begin{align*}
+R_A &= \frac{C_V F_A}{F_V}
+\end{align*}
\end{document}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1052.3622"
+ height="744.09448"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="timing.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="Arrow1Mend"
+ style="overflow:visible;">
+ <path
+ id="path3830"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-9"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3830-0"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-2"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3830-7"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-7"
+ style="overflow:visible">
+ <path
+ inkscape:connector-curvature="0"
+ id="path3830-77"
+ d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
+ transform="matrix(-0.4,0,0,-0.4,-4,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.7392904"
+ inkscape:cx="260.70009"
+ inkscape:cy="491.51669"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1680"
+ inkscape:window-height="1023"
+ inkscape:window-x="1366"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-308.2677)">
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:TeX Gyre Schola;-inkscape-font-specification:TeX Gyre Schola"
+ x="17"
+ y="22.094482"
+ id="text2985"
+ sodipodi:linespacing="125%"
+ transform="translate(0,308.2677)"><tspan
+ sodipodi:role="line"
+ id="tspan2987"
+ x="17"
+ y="22.094482">FFmpeg sources are by far the most complicated, so we consider those only.</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="37.094482"
+ id="tspan2989"></tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="52.094482"
+ id="tspan4675">Hardest video case:</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="67.094482"
+ id="tspan4584" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="82.094482"
+ id="tspan4586" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="97.094482"
+ id="tspan4588" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="112.09448"
+ id="tspan4590" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="127.09448"
+ id="tspan4592" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="142.09448"
+ id="tspan4594" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="157.09448"
+ id="tspan4596" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="172.09448"
+ id="tspan4598" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="187.09448"
+ id="tspan4600" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="202.09448"
+ id="tspan4602" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="217.09448"
+ id="tspan4604" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="232.09448"
+ id="tspan4606" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="247.09448"
+ id="tspan4608" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="262.09448"
+ id="tspan4610" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="277.09448"
+ id="tspan4612">Content frames arrive with ContentTime; this is converted to DCP time and checked for validity against</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="292.09448"
+ id="tspan4622">_video_position. _video_position is incremented by one DCP frame period each time a frame is emitted.</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="307.09448"
+ id="tspan4657">Emission is timestamped with _video_position.</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="322.09448"
+ id="tspan4661" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="337.09448"
+ id="tspan4663">In general, the Decoded::dcp_time is used as a check against the actual position in the DCP (based</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="352.09448"
+ id="tspan4630">on what has been emitted).</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="367.09448"
+ id="tspan4671" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="382.09448"
+ id="tspan4673">Hardest audio case:</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="397.09448"
+ id="tspan4785" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="412.09448"
+ id="tspan4787" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="427.09448"
+ id="tspan4789" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="442.09448"
+ id="tspan4791" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="457.09448"
+ id="tspan4793" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="472.09448"
+ id="tspan4795" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="487.09448"
+ id="tspan4797" /><tspan
+ sodipodi:role="line"
+ x="17"
+ y="502.09448"
+ id="tspan4799">Timestamps are not sample-accurate, here B should be after A but its timestamp says different. Things</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="517.09448"
+ id="tspan4801">are further complicated by resampling, which renders timestamps invalid. The upshot is that audio</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="532.09448"
+ id="tspan4803">timestamps are basically useless. However, since audio is (apparently) always continuous in content files</tspan><tspan
+ sodipodi:role="line"
+ x="17"
+ y="547.09448"
+ id="tspan4805">we can make our own timestamps.</tspan></text>
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9"
+ width="30.424219"
+ height="37.906269"
+ x="311.70773"
+ y="461.45099" />
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9-5"
+ width="30.424219"
+ height="37.906269"
+ x="405.98038"
+ y="461.45099" />
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9-5-5"
+ width="30.424219"
+ height="37.906269"
+ x="217.43507"
+ y="461.45099" />
+ <g
+ id="g4368"
+ transform="translate(185.74311,19.474125)">
+ <rect
+ y="363.08694"
+ x="61.408226"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4319"
+ y="373.75745"
+ x="64.765747"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="64.765747"
+ id="tspan4321"
+ sodipodi:role="line">A</tspan></text>
+ </g>
+ <g
+ id="g4363"
+ transform="translate(185.7431,19.474125)">
+ <rect
+ y="363.08694"
+ x="104.95609"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-4"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4323"
+ y="373.75745"
+ x="108.47467"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="108.47467"
+ id="tspan4325"
+ sodipodi:role="line">B</tspan></text>
+ </g>
+ <g
+ id="g4358"
+ transform="translate(185.74312,19.474125)">
+ <rect
+ y="363.08694"
+ x="148.50394"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-4-1"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4327"
+ y="373.75745"
+ x="151.86452"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="151.86452"
+ id="tspan4329"
+ sodipodi:role="line">C</tspan></text>
+ </g>
+ <g
+ id="g4353"
+ transform="translate(183.96221,19.474125)">
+ <rect
+ y="363.08694"
+ x="237.38057"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-4-1-7"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4331"
+ y="373.75745"
+ x="240.5585"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="240.5585"
+ id="tspan4333"
+ sodipodi:role="line">D</tspan></text>
+ </g>
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9-5-5-3"
+ width="30.424219"
+ height="37.906269"
+ x="248.85928"
+ y="461.45099" />
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9-5-5-2"
+ width="30.424219"
+ height="37.906269"
+ x="280.28351"
+ y="461.45099" />
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9-5-5-1"
+ width="30.424219"
+ height="37.906269"
+ x="343.13196"
+ y="461.45099" />
+ <rect
+ style="color:#000000;fill:#de8787;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect2991-9-5-5-6"
+ width="30.424219"
+ height="37.906269"
+ x="374.55618"
+ y="461.45099" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="-190.66861"
+ y="507.70966"
+ id="text4412"
+ sodipodi:linespacing="125%"
+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)"><tspan
+ sodipodi:role="line"
+ id="tspan4414"
+ x="-190.66861"
+ y="507.70966">BLACK</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="251.85396"
+ y="472.95575"
+ id="text4430"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4432"
+ x="251.85396"
+ y="472.95575">A</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="376.83502"
+ y="472.95575"
+ id="text4438"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4440"
+ x="376.83502"
+ y="472.95575">D</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="283.88882"
+ y="472.95575"
+ id="text4442"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4444"
+ x="283.88882"
+ y="472.95575">B</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="314.1189"
+ y="472.95575"
+ id="text4446"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4448"
+ x="314.1189"
+ y="472.95575">C</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="346.7153"
+ y="472.95575"
+ id="text4446-9"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4448-9"
+ x="346.7153"
+ y="472.95575">C</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="-56.312008"
+ y="640.36365"
+ id="text4412-5"
+ sodipodi:linespacing="125%"
+ transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)"><tspan
+ sodipodi:role="line"
+ id="tspan4414-1"
+ x="-56.312008"
+ y="640.36365">BLACK</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="144.21622"
+ y="541.08624"
+ id="text4492"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4494"
+ x="144.21622"
+ y="541.08624">(outside content, so black)</tspan></text>
+ <path
+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 220.72149,530.25751 c 1.80478,-3.15836 7.07827,-26.41646 7.07827,-26.41646"
+ id="path4496"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="334.42267"
+ y="559.13403"
+ id="text4521"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4523"
+ x="334.42267"
+ y="559.13403">(repeat)</tspan></text>
+ <path
+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 358.45245,547.88911 c -1.80478,-3.15836 -8.88305,-42.65947 -8.88305,-42.65947"
+ id="path4496-5"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="color:#000000;fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow1Mend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 227.48942,529.80632 c 57.58855,-44.73312 159.20595,24.81179 194.4651,-25.26693"
+ id="path4549"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000080;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="68.27523"
+ y="399.59995"
+ id="text4574"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4576"
+ x="68.27523"
+ y="399.59995">CONTENT</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000080;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="68.119232"
+ y="476.80835"
+ id="text4578"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4580"
+ x="68.119232"
+ y="476.80835">DCP</tspan><tspan
+ sodipodi:role="line"
+ x="68.119232"
+ y="491.80835"
+ id="tspan4582">(at higher frame rate)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ x="255.87259"
+ y="280.20578"
+ id="text4616"
+ sodipodi:linespacing="125%"
+ transform="translate(0,308.2677)"><tspan
+ sodipodi:role="line"
+ id="tspan4618"
+ x="255.87259"
+ y="280.20578" /></text>
+ <g
+ id="g4368-8"
+ transform="translate(77.477495,337.87916)">
+ <rect
+ y="363.08694"
+ x="61.408226"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-5"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4319-7"
+ y="373.75745"
+ x="64.765747"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="64.765747"
+ id="tspan4321-8"
+ sodipodi:role="line">A</tspan></text>
+ </g>
+ <g
+ id="g4368-8-8"
+ transform="translate(108.10564,376.16434)">
+ <rect
+ y="363.08694"
+ x="61.408226"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-5-0"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4319-7-8"
+ y="373.75745"
+ x="64.765747"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="64.765747"
+ id="tspan4321-8-1"
+ sodipodi:role="line">B</tspan></text>
+ </g>
+ <g
+ id="g4368-8-8-9"
+ transform="translate(166.7546,336.60299)">
+ <rect
+ y="363.08694"
+ x="61.408226"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-5-0-9"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4319-7-8-0"
+ y="373.75745"
+ x="64.765747"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="64.765747"
+ id="tspan4321-8-1-5"
+ sodipodi:role="line">C</tspan></text>
+ </g>
+ <g
+ id="g4368-8-8-9-2"
+ transform="translate(210.30246,336.60299)">
+ <rect
+ y="363.08694"
+ x="61.408226"
+ height="37.906269"
+ width="42.547859"
+ id="rect2991-5-0-9-0"
+ style="color:#000000;fill:#ffb380;stroke:#000000;stroke-width:0.99999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4319-7-8-0-0"
+ y="373.75745"
+ x="64.765747"
+ style="font-size:12px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Inconsolata;-inkscape-font-specification:Inconsolata Medium"
+ xml:space="preserve"><tspan
+ y="373.75745"
+ x="64.765747"
+ id="tspan4321-8-1-5-7"
+ sodipodi:role="line">D</tspan></text>
+ </g>
+ </g>
+</svg>
--- /dev/null
+\documentclass{article}
+\begin{document}
+
+There is a lot of dancing about to handle potential gaps and sync
+problems in the FFmpeg decoder. It might be nicer if
+\texttt{FFmpegDecoder} could just spit out video and audio with
+timestamps and let the player sort it out, since the player must
+already handle insertion of black and silence.
+
+The first question would be what time unit the decoder should use to
+stamp its output. Initially we have the PTS, in some time base, and
+we can convert that to seconds at the content's frame rate; this is
+basically a \texttt{Time}. So we could emit video and audio content
+with \texttt{Time} stamps.
+
+Then the player receives video frames, and can fill in gaps.
+
+The FFmpeg decoder would still have to account for non-zero initial
+PTS, as it is expected that such `dead time' is trimmed from the
+source implicitly.
+
+The snag with this is that hitherto \texttt{Time} has meant DCP time,
+not time at a content's rates (before the content is potentially sped
+up). As it stands, seek takes a \texttt{Time} in the DCP and the
+content class converts it to content frames. This is then (rather
+grottily) converted back to time again via the content frame rate.
+All a bit grim. Everything should probably work in time rather than
+frame rates.
+
+\end{document}
/** @mainpage DCP-o-matic
*
* DCP-o-matic is a tool to create digital cinema packages (DCPs) from
- * video files, or from sets of TIFF image files. It is written in C++
+ * video files, or from sets of image files. It is written in C++
* and distributed under the GPL.
*
* Video files are decoded using FFmpeg (http://ffmpeg.org), so any video
* and libsndfile (http://www.mega-nerd.com/libsndfile/) for WAV file manipulation. It
* also makes heavy use of the boost libraries (http://www.boost.org/). ImageMagick
* (http://www.imagemagick.org/) is used for still-image encoding and decoding, and the GUI is
- * built using wxWidgets (http://wxwidgets.org/). It also uses libmhash (http://mhash.sourceforge.net/)
- * for debugging purposes.
+ * built using wxWidgets (http://wxwidgets.org/).
*
* Thanks are due to the authors and communities of all DCP-o-matic's dependencies.
- *
- * DCP-o-matic is distributed in the hope that there are still cinemas with projectionists
- * who might want to use it. As Mark Kermode says, "if it doesn't have a projectionist
- * it's not a cinema - it's a sweetshop with a video-screen."
*
- * Email correspondance is welcome to cth@carlh.net
+ * Email correspondance is welcome to carl@dcpomatic.com
*
- * More details can be found at http://carlh.net/software/dcpomatic
+ * More details can be found at http://dcpomatic.com/
*/
still-select-content-file.png examine-thumbs.png examine-content.png timing-tab.png \
calculate-audio-gain.png add-file.png dcp-tab.png colour-conversion.png \
prefs-kdm-email.png prefs-colour-conversions.png prefs-metadata.png prefs-general.png prefs-tms.png \
- prefs-advanced.png prefs-defaults.png prefs-servers.png \
+ prefs-advanced.png prefs-defaults.png prefs-servers.png prefs-keys.png \
making-dcp.png filters.png video-tab.png audio-tab.png subtitles-tab.png timing-tab.png \
audio-plot.png audio-map-eg1.png audio-map-eg2.png audio-map-eg3.png kdm.png
<para>
We suppose that we are trying to distribute a DCP to
-Alice's cinema, without a troublemaker called Mallory being able to
+Alice's cinema without a troublemaker called Mallory being able to
watch it himself.
</para>
<!-- ============================================================== -->
+<!-- PREFERENCES -->
+<!-- ============================================================== -->
+
<chapter xml:id="ch-preferences" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
<title>Preferences</title>
<para>
The preferences dialogue is opened by choosing
<guilabel>Preferences...</guilabel> from the <guilabel>Edit</guilabel>
-menu. The dialogue is split into seven tabs.
+menu. The dialogue is split into eight tabs.
</para>
<!-- ============================================================== -->
<para>
The <guilabel>Check for testing updates as well as stable
ones</guilabel> option will also check for test updates as well as
-those that are formally ‘released’ This is useful if you
+those that are formally ‘released’. This is useful if you
like to live on the bleeding edge!
</para>
</section>
</section>
+<!-- ============================================================== -->
+<section>
+<title>Keys</title>
+
+<para>
+The Keys tab (shown in <xref linkend="fig-prefs-keys"/>) holds options
+related to the keys and certificates used in some parts of DCP
+creation.
+</para>
+
+<figure id="fig-prefs-keys">
+ <title>Keys preferences</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/prefs-keys&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+At the top of the tab is the chain of certificates that will be used
+to sign DCPs and KDMs. DCP-o-matic creates a random chain when you
+first run it, so if you are happy to use a randomly-generated chain
+you can ignore the preferences. Otherwise, you can add or remove
+certificates from the chain using the <guilabel>Add...</guilabel> and
+<guilabel>Remove</guilabel> buttons.
+</para>
+
+<para>
+If you want DCP-o-matic to re-create the certificate chain (using new,
+random certificates) click <guilabel>Re-make
+certificates...</guilabel> and specify your organisation and common
+names in the dialogue box that opens.
+</para>
+
+<para>
+Underneath the certificate chain is the private key that corresponds
+to the leaf certificate in the chain. You can specify your own
+private key by clicking <guilabel>Load...</guilabel>. You must do
+this if you change the leaf certificate, so that the leaf private key
+corresponds to the public key held in the leaf certificate.
+</para>
+
+<para>
+The bottom of the tab specifies the certificate and private key that
+is used to decrypt DCPs if they are imported as sources to
+DCP-o-matic. If you want to import an encrypted DCP you will need to
+give the decryption certificate to the distributor of the DCP so that
+they can generate a DKDM for you. As with the certificate chain,
+DCP-o-matic will create a certificate and private key for you. You
+can also choose to load your own certificate and key.
+</para>
+
+</section>
+
<!-- ============================================================== -->
<section xml:id="sec-prefs-tms">
<title>TMS</title>
</para>
<para>
-Video rate conversion is harder. DCP-o-matic's basic strategy to deal
+Video rate conversion is harder. DCP-o-matic's strategy to deal
with a non-supported content rate is to run it at the wrong speed, and
to adjust the audio to keep it in sync.
</para>
-<para>Let us consider the example of a 25fps source for which you want
+<para>Consider the example of a 25fps source for which you want
to create a 24fps DCP. DCP-o-matic will put the frames from the
source directly into the DCP without modification, but will tell the
projector to play them back at 24fps. This means that the DCP's video
The <guilabel>Frame Rate</guilabel> control in the
<guilabel>DCP</guilabel> tab sets the video frame rate that the DCP
will use. Clicking <guilabel>Use best</guilabel> sets the rate to
-what DVD-o-matic thinks is the best for your content. With this
+what DCP-o-matic thinks is the best for your content. With this
button, DCP-o-matic assumes that the whole range of frame rates (24,
25, 30 and 48fps) are allowable.
</para>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- id="svg4217"
- viewBox="0 0 744.09449 1052.3622"
- version="1.0"
- inkscape:version="0.48.4 r9939"
- width="100%"
- height="100%"
- sodipodi:docname="kdm_email.svg"
- inkscape:export-filename="/home/carl/src/dcpomatic/icons/kdm_email.png"
- inkscape:export-xdpi="6.5100002"
- inkscape:export-ydpi="6.5100002">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1366"
- inkscape:window-height="714"
- id="namedview6320"
- showgrid="false"
- inkscape:zoom="0.60313323"
- inkscape:cx="-87.032436"
- inkscape:cy="195.645"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1"
- inkscape:current-layer="svg4217" />
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:ns1="http://sozi.baierouge.fr"
+ id="svg5816"
+ viewBox="0 0 48 48"
+ sodipodi:version="0.32"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:version="0.46"
+ sodipodi:docname="internet-mail.svg"
+ sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/apps"
+ >
<defs
- id="defs4219">
+ id="defs3"
+ >
+ <radialGradient
+ id="radialGradient6719"
+ xlink:href="#linearGradient5060"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(-2.7744 0 0 1.9697 112.76 -872.89)"
+ r="117.14"
+ inkscape:collect="always"
+ />
<linearGradient
- id="linearGradient3594"
- y2="742.5"
- gradientUnits="userSpaceOnUse"
- x2="-886.76001"
- gradientTransform="matrix(-0.84033,-0.84033,-0.84033,0.84033,214.12,-1075.4)"
- y1="742.5"
- x1="-772.01001">
- <stop
- id="stop4687"
- stop-color="#fff"
- offset="0" />
- <stop
- id="stop4689"
- stop-color="#fff"
- stop-opacity="0"
- offset="1" />
- </linearGradient>
+ id="linearGradient5060"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop5062"
+ style="stop-color:black"
+ offset="0"
+ />
+ <stop
+ id="stop5064"
+ style="stop-color:black;stop-opacity:0"
+ offset="1"
+ />
+ </linearGradient
+ >
+ <radialGradient
+ id="radialGradient6717"
+ xlink:href="#linearGradient5060"
+ gradientUnits="userSpaceOnUse"
+ cy="486.65"
+ cx="605.71"
+ gradientTransform="matrix(2.7744 0 0 1.9697 -1891.6 -872.89)"
+ r="117.14"
+ inkscape:collect="always"
+ />
<linearGradient
- id="linearGradient3601"
- y2="613.94"
- gradientUnits="userSpaceOnUse"
- x2="385.04001"
- gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-126.18,372.21)"
- y1="63.870998"
- x1="386.39001">
- <stop
- id="stop3797"
- stop-color="#ffe800"
- offset="0" />
- <stop
- id="stop3799"
- stop-color="#dfb300"
- offset="1" />
- </linearGradient>
+ id="linearGradient6715"
+ y2="609.51"
+ gradientUnits="userSpaceOnUse"
+ x2="302.86"
+ gradientTransform="matrix(2.7744 0 0 1.9697 -1892.2 -872.89)"
+ y1="366.65"
+ x1="302.86"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop5050"
+ style="stop-color:black;stop-opacity:0"
+ offset="0"
+ />
+ <stop
+ id="stop5056"
+ style="stop-color:black"
+ offset=".5"
+ />
+ <stop
+ id="stop5052"
+ style="stop-color:black;stop-opacity:0"
+ offset="1"
+ />
+ </linearGradient
+ >
<linearGradient
- id="linearGradient3609"
- y2="161.84"
- gradientUnits="userSpaceOnUse"
- x2="212.92999"
- y1="358.29999"
- x1="409.38">
- <stop
- id="stop4034"
- stop-color="#dfb300"
- offset="0" />
- <stop
- id="stop3374"
- stop-color="#dfb300"
- offset=".5" />
- <stop
- id="stop3376"
- stop-color="#dfb300"
- offset="1" />
- </linearGradient>
+ id="linearGradient2152"
+ >
+ <stop
+ id="stop2154"
+ style="stop-color:#9aa29a"
+ offset="0"
+ />
+ <stop
+ id="stop2156"
+ style="stop-color:#b5beb5"
+ offset="1"
+ />
+ </linearGradient
+ >
<linearGradient
- id="linearGradient3632"
- y2="448.35001"
- gradientUnits="userSpaceOnUse"
- x2="382.89999"
- gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-126.18,372.21)"
- y1="448.35001"
- x1="403.63">
- <stop
- id="stop3636"
- stop-color="#ffe800"
- stop-opacity=".39216"
- offset="0" />
- <stop
- id="stop3638"
- stop-color="#dfb300"
- stop-opacity=".39216"
- offset="1" />
- </linearGradient>
- </defs>
+ id="linearGradient27463"
+ y2="32.203"
+ gradientUnits="userSpaceOnUse"
+ y1="37.785"
+ gradientTransform="matrix(2.3949 0 0 .78106 2.8795 0.343)"
+ x2="9.7619"
+ x1="8.7804"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop2276"
+ style="stop-color:#000000;stop-opacity:.12871"
+ offset="0"
+ />
+ <stop
+ id="stop2278"
+ style="stop-color:#000000;stop-opacity:0"
+ offset="1"
+ />
+ </linearGradient
+ >
+ <linearGradient
+ id="linearGradient27468"
+ y2="24.133"
+ gradientUnits="userSpaceOnUse"
+ y1="13.686"
+ gradientTransform="matrix(1.3709 0 0 1.4438 2.4311 -.14079)"
+ x2="21.112"
+ x1="11.233"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop9751"
+ style="stop-color:#ffffff"
+ offset="0"
+ />
+ <stop
+ id="stop9753"
+ style="stop-color:#ededed"
+ offset="1"
+ />
+ </linearGradient
+ >
+ <linearGradient
+ id="linearGradient27471"
+ y2="52.091"
+ xlink:href="#linearGradient2152"
+ gradientUnits="userSpaceOnUse"
+ y1="37.197"
+ gradientTransform="matrix(2.4548 0 0 0.762 2.8795 0.343)"
+ x2="9.8855"
+ x1="8.9156"
+ inkscape:collect="always"
+ />
+ <linearGradient
+ id="linearGradient27477"
+ y2="29.569"
+ gradientUnits="userSpaceOnUse"
+ y1="15.148"
+ gradientTransform="matrix(1.8193 0 0 1.0282 2.8795 0.343)"
+ x2="15.311"
+ x1="10.184"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop2168"
+ style="stop-color:#ffffff"
+ offset="0"
+ />
+ <stop
+ id="stop2170"
+ style="stop-color:#dcdcdc"
+ offset="1"
+ />
+ </linearGradient
+ >
+ <linearGradient
+ id="linearGradient27483"
+ y2="17.877"
+ gradientUnits="userSpaceOnUse"
+ y1="7.2311"
+ gradientTransform="matrix(1.5706 0 0 1.191 2.8795 0.343)"
+ x2="13.467"
+ x1="5.8266"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop18915"
+ style="stop-color:#ededed"
+ offset="0"
+ />
+ <stop
+ id="stop18917"
+ style="stop-color:#c8c8c8"
+ offset="1"
+ />
+ </linearGradient
+ >
+ <linearGradient
+ id="linearGradient27486"
+ y2="26.023"
+ gradientUnits="userSpaceOnUse"
+ y1="4.7462"
+ gradientTransform="matrix(1.3435 0 0 1.4179 2.8795 .31460)"
+ x2="18.475"
+ x1="11.573"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop15109"
+ style="stop-color:#ffffff"
+ offset="0"
+ />
+ <stop
+ id="stop15111"
+ style="stop-color:#e2e2e2"
+ offset="1"
+ />
+ </linearGradient
+ >
+ <linearGradient
+ id="linearGradient27488"
+ y2="15.257"
+ gradientUnits="userSpaceOnUse"
+ y1="15.257"
+ gradientTransform="matrix(1.3435 0 0 1.4179 2.8795 .31460)"
+ x2="30.6"
+ x1="2.0619"
+ inkscape:collect="always"
+ >
+ <stop
+ id="stop2138"
+ style="stop-color:#989690"
+ offset="0"
+ />
+ <stop
+ id="stop2140"
+ style="stop-color:#656460"
+ offset="1"
+ />
+ </linearGradient
+ >
+ </defs
+ >
+ <sodipodi:namedview
+ id="base"
+ bordercolor="#666666"
+ inkscape:window-x="331"
+ inkscape:window-y="105"
+ pagecolor="#ffffff"
+ inkscape:grid-bbox="true"
+ inkscape:zoom="1"
+ inkscape:pageshadow="2"
+ showgrid="false"
+ borderopacity="1.0"
+ inkscape:current-layer="layer1"
+ inkscape:cx="28.384904"
+ inkscape:cy="18.816166"
+ inkscape:window-width="872"
+ inkscape:pageopacity="0.0"
+ inkscape:window-height="743"
+ inkscape:document-units="px"
+ />
<g
- id="layer1"
- transform="translate(-77.797413,384.00351)">
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ >
+ <g
+ id="g6707"
+ transform="matrix(0.0227 0 0 .022979 44.989 37.784)"
+ >
+ <rect
+ id="rect6709"
+ style="opacity:.40206;color:black;fill:url(#linearGradient6715)"
+ height="478.36"
+ width="1339.6"
+ y="-150.7"
+ x="-1559.3"
+ />
+ <path
+ id="path6711"
+ sodipodi:nodetypes="cccc"
+ style="opacity:.40206;color:black;fill:url(#radialGradient6717)"
+ d="m-219.62-150.68v478.33c142.88 0.9 345.4-107.17 345.4-239.2 0-132.02-159.44-239.13-345.4-239.13z"
+ />
+ <path
+ id="path6713"
+ sodipodi:nodetypes="cccc"
+ style="opacity:.40206;color:black;fill:url(#radialGradient6719)"
+ d="m-1559.3-150.68v478.33c-142.8 0.9-345.4-107.17-345.4-239.2 0-132.02 159.5-239.13 345.4-239.13z"
+ />
+ </g
+ >
+ <path
+ id="path12723"
+ sodipodi:nodetypes="ccczzzz"
+ style="stroke-linejoin:round;fill-rule:evenodd;stroke:url(#linearGradient27488);stroke-width:.85660;fill:url(#linearGradient27486)"
+ d="m6.3334 16.972v24.51h36.973l-0.062-24.392c-0.003-1.378-11.848-14.678-14.033-14.678l-8.552 0.0001c-2.297 0-14.326 13.262-14.326 14.56z"
+ />
<path
- id="path6625"
- d="m 227.2,177.73 c -46.65,46.65 -46.67,122.4 -0.03,169.04 30.92,30.92 74.6,41.33 114.12,31.27 l 22.3,22.29 39.67,4.86 4.91,39.73 39.68,4.86 4.89,39.7 39.72,4.91 4.86,39.68 70.53,-5.91 5.66,-0.46 1.07,-12.21 5.62,-63.77 L 558.13,429.65 536.1,407.62 514.05,385.57 492.02,363.55 469.97,341.5 447.92,319.45 425.87,297.4 c 12.55,-40.94 2.66,-87.25 -29.71,-119.62 -46.64,-46.64 -122.32,-46.69 -168.96,-0.05 z m 24.21,24.32 c 21.41,-21.41 52.07,-25.54 68.44,-9.17 16.37,16.37 12.26,47.05 -9.14,68.46 -21.41,21.41 -52.07,25.49 -68.44,9.12 -16.37,-16.37 -12.27,-47.01 9.14,-68.41 z"
- style="color:#000000;fill:url(#linearGradient3601)"
- inkscape:connector-curvature="0" />
+ id="path18153"
+ sodipodi:nodetypes="czzzccz"
+ style="fill-rule:evenodd;fill:url(#linearGradient27483)"
+ d="m6.9231 16.787c-0.3981-0.43 11.887-13.694 13.744-13.694l8.376 0.0005c1.747 0 14.037 13.128 13.427 13.886l-10.861 13.495-12.314-0.318-12.372-13.37z"
+ />
<path
- id="path6871"
- d="m 388.43,339.03 c -1.68,1.68 -2.88,3.59 -3.69,5.59 -0.74,1.82 -1.28,3.93 -1.28,6.32 0,2.4 0.52,4.53 1.26,6.35 0.77,1.9 2.01,3.91 3.69,5.59 L 551.53,526 l 3.27,3.27 4.62,-0.38 5.68,-0.46 8.39,-0.71 0.75,-8.4 1.06,-12.19 0.4,-4.64 -3.29,-3.3 -160.16,-160.16 c -1.68,-1.68 -3.68,-2.91 -5.59,-3.69 -1.81,-0.73 -3.92,-1.28 -6.32,-1.28 -2.4,0 -4.51,0.55 -6.32,1.28 -1.91,0.78 -3.91,2.01 -5.59,3.69 z"
- style="color:#000000;fill:url(#linearGradient3632)"
- inkscape:connector-curvature="0" />
+ id="path2164"
+ sodipodi:nodetypes="ccccc"
+ style="fill-rule:evenodd;fill:#000000;fill-opacity:.14620"
+ d="m19.078 30.018l-7.333-8.746 24.818-6.936 3.029 6.216-7.416 9.44"
+ />
<path
- id="path2365"
- d="m 239.44,192.85 c -2.29,2.41 -4.43,4.92 -6.43,7.49 2.5,-3.23 5.25,-6.31 8.22,-9.28 -0.6,0.6 -1.21,1.18 -1.79,1.79 z m 3.62,-3.58 c 4.29,-4.07 8.84,-7.67 13.61,-10.83 -4.76,3.15 -9.33,6.77 -13.61,10.83 z m -11.62,13.17 c -0.93,1.27 -1.81,2.53 -2.67,3.82 0.85,-1.29 1.74,-2.56 2.67,-3.82 z m -2.81,4.05 c -0.89,1.35 -1.76,2.74 -2.58,4.13 0.82,-1.4 1.68,-2.77 2.58,-4.13 z m -2.58,4.13 c -1.66,2.8 -3.16,5.65 -4.51,8.57 1.35,-2.91 2.86,-5.78 4.51,-8.57 z m -4.51,8.57 c -1.35,2.92 -2.55,5.9 -3.6,8.91 1.05,-3.01 2.25,-6 3.6,-8.91 z m -3.6,8.91 c -1.05,3 -1.97,6.05 -2.72,9.12 0.75,-3.08 1.67,-6.12 2.72,-9.12 z m -2.72,9.12 c -0.31,1.27 -0.58,2.53 -0.84,3.8 0.26,-1.27 0.53,-2.53 0.84,-3.8 z m 43.53,-60.1 c 1.38,-0.87 2.79,-1.69 4.2,-2.48 -1.41,0.79 -2.82,1.61 -4.2,2.48 z m 8.49,-4.73 c 1.44,-0.71 2.88,-1.37 4.35,-2.01 -1.46,0.63 -2.92,1.3 -4.35,2.01 z m 17.89,-6.76 c 1.53,-0.41 3.06,-0.77 4.6,-1.11 -1.54,0.34 -3.07,0.7 -4.6,1.11 z m 4.62,-1.13 c 1.54,-0.34 3.09,-0.62 4.64,-0.88 -1.55,0.26 -3.1,0.54 -4.64,0.88 z m -75.52,77.34 c -0.16,0.79 -0.29,1.58 -0.42,2.36 0.13,-0.77 0.27,-1.58 0.42,-2.36 z m -0.42,2.36 c -0.13,0.78 -0.27,1.55 -0.38,2.33 0.11,-0.79 0.24,-1.55 0.38,-2.33 z m -0.69,4.67 c -0.09,0.78 -0.17,1.57 -0.24,2.36 0.07,-0.78 0.15,-1.58 0.24,-2.36 z m -0.44,4.73 c -0.06,0.78 -0.1,1.56 -0.13,2.34 0.03,-0.79 0.07,-1.56 0.13,-2.34 z m 93.47,-91.26 c 1.18,-0.06 2.37,-0.1 3.56,-0.12 -1.2,0.02 -2.37,0.06 -3.56,0.12 z m 4.71,-0.12 c 0.78,0 1.57,0.01 2.36,0.03 -0.79,-0.02 -1.57,-0.03 -2.36,-0.03 z m -98.18,105.52 c 0.11,1.58 0.25,3.16 0.44,4.73 -0.19,-1.57 -0.33,-3.16 -0.44,-4.73 z M 322.66,162.93 c 1.56,0.19 3.12,0.4 4.68,0.66 -1.56,-0.26 -3.12,-0.47 -4.68,-0.66 z m -108.85,114.2 c 0.13,0.78 0.26,1.58 0.42,2.36 -0.15,-0.77 -0.29,-1.58 -0.42,-2.36 z m 0.42,2.36 c 0.14,0.77 0.31,1.54 0.48,2.3 -0.17,-0.77 -0.33,-1.52 -0.48,-2.3 z m 1.61,6.92 c 0.21,0.77 0.41,1.55 0.64,2.32 -0.23,-0.76 -0.44,-1.55 -0.64,-2.32 z m 1.35,4.57 c 0.24,0.76 0.49,1.51 0.75,2.26 -0.27,-0.75 -0.51,-1.5 -0.75,-2.26 z m 0.75,2.26 c 0.25,0.71 0.5,1.43 0.77,2.14 -0.26,-0.7 -0.52,-1.43 -0.77,-2.14 z m 1.7,4.48 c 0.3,0.75 0.61,1.48 0.93,2.21 -0.32,-0.73 -0.63,-1.47 -0.93,-2.21 z m 0.93,2.21 c 0.32,0.74 0.63,1.48 0.97,2.21 -0.34,-0.72 -0.65,-1.47 -0.97,-2.21 z m 0.97,2.21 c 0.34,0.73 0.7,1.45 1.06,2.17 -0.36,-0.72 -0.72,-1.44 -1.06,-2.17 z m 2.14,4.31 c 0.38,0.72 0.78,1.43 1.17,2.15 -0.39,-0.71 -0.79,-1.43 -1.17,-2.15 z M 359.25,174.91 c 1.12,0.63 2.23,1.28 3.34,1.97 -1.11,-0.69 -2.22,-1.34 -3.34,-1.97 z M 227.33,312.79 c 0.38,0.61 0.77,1.23 1.17,1.84 -0.4,-0.61 -0.79,-1.22 -1.17,-1.84 z m 1.57,2.46 c 0.42,0.62 0.85,1.24 1.28,1.85 -0.43,-0.62 -0.86,-1.23 -1.28,-1.85 z m 2.72,3.86 c 0.95,1.3 1.95,2.57 2.98,3.83 -1.02,-1.25 -2.03,-2.54 -2.98,-3.83 z M 371.07,182.75 c 1.3,1.01 2.61,2.04 3.87,3.12 -1.27,-1.09 -2.56,-2.1 -3.87,-3.12 z M 244.92,333.79 c 38.64,34.9 98.35,33.74 135.59,-3.49 37.23,-37.24 38.39,-96.96 3.49,-135.59 31.41,35.32 -20.5,44.27 -57.68,81.45 -37.17,37.17 -46.08,89.04 -81.4,57.63 z"
- style="color:#000000;fill:url(#linearGradient3609)"
- inkscape:connector-curvature="0" />
+ id="path2162"
+ sodipodi:nodetypes="ccccc"
+ style="fill-rule:evenodd;fill:#000000;fill-opacity:.14620"
+ d="m18.292 29.836l-7.483-8.81 24.648-6.893 3.174 6.271-7.241 9.407"
+ />
<path
- id="path6632"
- d="m 239.44,192.87 c -36.65,38.56 -36.03,99.58 1.8,137.42 38.44,38.43 46.67,-15.69 85.1,-54.13 38.43,-38.43 92.59,-46.69 54.15,-85.12 -38.43,-38.44 -100.81,-38.41 -139.25,0.03 -0.6,0.6 -1.22,1.19 -1.8,1.8 z m 11.99,9.17 c 21.4,-21.41 52.05,-25.51 68.42,-9.14 16.37,16.37 12.27,47.02 -9.14,68.43 -21.4,21.4 -52.05,25.5 -68.42,9.13 -16.37,-16.37 -12.27,-47.02 9.14,-68.42 z"
- style="color:#000000;fill:url(#linearGradient3594)"
- inkscape:connector-curvature="0" />
- </g>
+ id="path2160"
+ sodipodi:nodetypes="ccccc"
+ style="fill-rule:evenodd;fill:#000000;fill-opacity:.14620"
+ d="m18.775 29.957l-7.675-8.66 24.968-7.065 3.286 6.593-7.48 9.107"
+ />
+ <path
+ id="path15105"
+ sodipodi:nodetypes="ccccc"
+ style="fill-rule:evenodd;fill:url(#linearGradient27477)"
+ d="m18.594 30.441l-7.333-8.746 24.712-6.894 3.11 6.388-7.12 8.986"
+ />
+ <path
+ id="path14245"
+ sodipodi:nodetypes="ccccccc"
+ style="fill:url(#linearGradient27471);fill-rule:evenodd"
+ d="m20.488 29.064l-13.396 10.972 13.909-9.604h9.018l12.42 9.482-11.864-10.85h-10.087z"
+ />
+ <path
+ id="path14339"
+ sodipodi:nodetypes="cccc"
+ style="fill-rule:evenodd;color:#000000;fill:url(#linearGradient27471)"
+ d="m6.9635 16.885l11.516 14.316 1.068-0.854-12.584-13.462z"
+ />
+ <path
+ id="path15103"
+ sodipodi:nodetypes="ccczzzz"
+ style="stroke:url(#linearGradient27468);stroke-width:.85660;fill:none"
+ d="m7.3077 17.131l0.0312 23.211h34.945l-0.063-23.084c-0.002-0.75-11.216-13.799-13.384-13.799l-7.895 0.0002c-2.253 0-13.635 12.892-13.634 13.672z"
+ />
+ <path
+ id="path17393"
+ sodipodi:nodetypes="cccccc"
+ style="fill-rule:evenodd;fill:#ffffff"
+ d="m20.957 30.453l-11.941 8.271 2.219 0.006 9.998-6.869 8.822-1.423-9.098 0.015z"
+ />
+ <path
+ id="path2174"
+ sodipodi:nodetypes="ccccccc"
+ style="fill-rule:evenodd;fill:#ffffff"
+ d="m11.428 21.67l1.324 1.411 22.791-6.884 2.915 5.682 0.614-0.712-3.069-6.378-24.575 6.881z"
+ />
+ <path
+ id="path2272"
+ sodipodi:nodetypes="ccccccc"
+ style="fill-rule:evenodd;fill:url(#linearGradient27463)"
+ d="m13.308 23.636l6.026 6.454 1.197-1.026 10.087 0.043 0.812 0.727 3.975-4.744c-1.154-1.411-22.097-1.454-22.097-1.454z"
+ />
+ <path
+ id="path27492"
+ sodipodi:nodetypes="cccc"
+ style="fill-rule:evenodd;color:#000000;fill:#b1b1b1"
+ d="m41.813 17.848l-9.952 12.631-1.068-0.855 11.02-11.776z"
+ />
+ </g
+ >
<metadata
- id="metadata6318">
- <rdf:RDF>
- <cc:Work>
- <dc:format>image/svg+xml</dc:format>
+ >
+ <rdf:RDF
+ >
+ <cc:Work
+ >
+ <dc:format
+ >image/svg+xml</dc:format
+ >
<dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage"
+ />
<cc:license
- rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
- <dc:publisher>
+ rdf:resource="http://creativecommons.org/licenses/publicdomain/"
+ />
+ <dc:publisher
+ >
+ <cc:Agent
+ rdf:about="http://openclipart.org/"
+ >
+ <dc:title
+ >Openclipart</dc:title
+ >
+ </cc:Agent
+ >
+ </dc:publisher
+ >
+ <dc:title
+ >tango internet mail</dc:title
+ >
+ <dc:date
+ >2010-03-29T08:04:16</dc:date
+ >
+ <dc:description
+ >"E-mail" icon from <a href="http://tango.freedesktop.org/Tango_Desktop_Project"> Tango Project </a> \n<br><br> \nSince version 0.8.90 Tango Project icons are Public Domain: <a href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"> Tango Project FAQ </a></dc:description
+ >
+ <dc:source
+ >https://openclipart.org/detail/35215/tango-internet-mail-by-warszawianka</dc:source
+ >
+ <dc:creator
+ >
<cc:Agent
- rdf:about="http://openclipart.org/">
- <dc:title>Openclipart</dc:title>
- </cc:Agent>
- </dc:publisher>
- <dc:title>Key</dc:title>
- <dc:date>2007-02-27T15:15:43</dc:date>
- <dc:description>A key icon.</dc:description>
- <dc:source>http://openclipart.org/detail/3330/key-by-barretr</dc:source>
- <dc:creator>
- <cc:Agent>
- <dc:title>barretr</dc:title>
- </cc:Agent>
- </dc:creator>
- <dc:subject>
- <rdf:Bag>
- <rdf:li>clip art</rdf:li>
- <rdf:li>clipart</rdf:li>
- <rdf:li>icon</rdf:li>
- <rdf:li>image</rdf:li>
- <rdf:li>key</rdf:li>
- <rdf:li>media</rdf:li>
- <rdf:li>png</rdf:li>
- <rdf:li>public domain</rdf:li>
- <rdf:li>svg</rdf:li>
- </rdf:Bag>
- </dc:subject>
- </cc:Work>
+ >
+ <dc:title
+ >warszawianka</dc:title
+ >
+ </cc:Agent
+ >
+ </dc:creator
+ >
+ <dc:subject
+ >
+ <rdf:Bag
+ >
+ <rdf:li
+ >email</rdf:li
+ >
+ <rdf:li
+ >envelope</rdf:li
+ >
+ <rdf:li
+ >externalsource</rdf:li
+ >
+ <rdf:li
+ >icon</rdf:li
+ >
+ <rdf:li
+ >letter</rdf:li
+ >
+ <rdf:li
+ >tango</rdf:li
+ >
+ </rdf:Bag
+ >
+ </dc:subject
+ >
+ </cc:Work
+ >
<cc:License
- rdf:about="http://creativecommons.org/licenses/publicdomain/">
+ rdf:about="http://creativecommons.org/licenses/publicdomain/"
+ >
<cc:permits
- rdf:resource="http://creativecommons.org/ns#Reproduction" />
+ rdf:resource="http://creativecommons.org/ns#Reproduction"
+ />
<cc:permits
- rdf:resource="http://creativecommons.org/ns#Distribution" />
+ rdf:resource="http://creativecommons.org/ns#Distribution"
+ />
<cc:permits
- rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
- </cc:License>
- </rdf:RDF>
- </metadata>
- <rect
- style="color:#000000;fill:#ff0000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
- id="rect6322"
- width="442.68826"
- height="442.68826"
- x="84.558434"
- y="505.21939" />
-</svg>
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
+ />
+ </cc:License
+ >
+ </rdf:RDF
+ >
+ </metadata
+ >
+</svg
+>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ id="svg4217"
+ viewBox="0 0 744.09449 1052.3622"
+ version="1.0"
+ inkscape:version="0.48.4 r9939"
+ width="100%"
+ height="100%"
+ sodipodi:docname="kdm_email.svg"
+ inkscape:export-filename="/home/carl/src/dcpomatic/icons/kdm_email.png"
+ inkscape:export-xdpi="6.5100002"
+ inkscape:export-ydpi="6.5100002">
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1366"
+ inkscape:window-height="714"
+ id="namedview6320"
+ showgrid="false"
+ inkscape:zoom="0.60313323"
+ inkscape:cx="-87.032436"
+ inkscape:cy="195.645"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4217" />
+ <defs
+ id="defs4219">
+ <linearGradient
+ id="linearGradient3594"
+ y2="742.5"
+ gradientUnits="userSpaceOnUse"
+ x2="-886.76001"
+ gradientTransform="matrix(-0.84033,-0.84033,-0.84033,0.84033,214.12,-1075.4)"
+ y1="742.5"
+ x1="-772.01001">
+ <stop
+ id="stop4687"
+ stop-color="#fff"
+ offset="0" />
+ <stop
+ id="stop4689"
+ stop-color="#fff"
+ stop-opacity="0"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3601"
+ y2="613.94"
+ gradientUnits="userSpaceOnUse"
+ x2="385.04001"
+ gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-126.18,372.21)"
+ y1="63.870998"
+ x1="386.39001">
+ <stop
+ id="stop3797"
+ stop-color="#ffe800"
+ offset="0" />
+ <stop
+ id="stop3799"
+ stop-color="#dfb300"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3609"
+ y2="161.84"
+ gradientUnits="userSpaceOnUse"
+ x2="212.92999"
+ y1="358.29999"
+ x1="409.38">
+ <stop
+ id="stop4034"
+ stop-color="#dfb300"
+ offset="0" />
+ <stop
+ id="stop3374"
+ stop-color="#dfb300"
+ offset=".5" />
+ <stop
+ id="stop3376"
+ stop-color="#dfb300"
+ offset="1" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3632"
+ y2="448.35001"
+ gradientUnits="userSpaceOnUse"
+ x2="382.89999"
+ gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-126.18,372.21)"
+ y1="448.35001"
+ x1="403.63">
+ <stop
+ id="stop3636"
+ stop-color="#ffe800"
+ stop-opacity=".39216"
+ offset="0" />
+ <stop
+ id="stop3638"
+ stop-color="#dfb300"
+ stop-opacity=".39216"
+ offset="1" />
+ </linearGradient>
+ </defs>
+ <g
+ id="layer1"
+ transform="translate(-77.797413,384.00351)">
+ <path
+ id="path6625"
+ d="m 227.2,177.73 c -46.65,46.65 -46.67,122.4 -0.03,169.04 30.92,30.92 74.6,41.33 114.12,31.27 l 22.3,22.29 39.67,4.86 4.91,39.73 39.68,4.86 4.89,39.7 39.72,4.91 4.86,39.68 70.53,-5.91 5.66,-0.46 1.07,-12.21 5.62,-63.77 L 558.13,429.65 536.1,407.62 514.05,385.57 492.02,363.55 469.97,341.5 447.92,319.45 425.87,297.4 c 12.55,-40.94 2.66,-87.25 -29.71,-119.62 -46.64,-46.64 -122.32,-46.69 -168.96,-0.05 z m 24.21,24.32 c 21.41,-21.41 52.07,-25.54 68.44,-9.17 16.37,16.37 12.26,47.05 -9.14,68.46 -21.41,21.41 -52.07,25.49 -68.44,9.12 -16.37,-16.37 -12.27,-47.01 9.14,-68.41 z"
+ style="color:#000000;fill:url(#linearGradient3601)"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path6871"
+ d="m 388.43,339.03 c -1.68,1.68 -2.88,3.59 -3.69,5.59 -0.74,1.82 -1.28,3.93 -1.28,6.32 0,2.4 0.52,4.53 1.26,6.35 0.77,1.9 2.01,3.91 3.69,5.59 L 551.53,526 l 3.27,3.27 4.62,-0.38 5.68,-0.46 8.39,-0.71 0.75,-8.4 1.06,-12.19 0.4,-4.64 -3.29,-3.3 -160.16,-160.16 c -1.68,-1.68 -3.68,-2.91 -5.59,-3.69 -1.81,-0.73 -3.92,-1.28 -6.32,-1.28 -2.4,0 -4.51,0.55 -6.32,1.28 -1.91,0.78 -3.91,2.01 -5.59,3.69 z"
+ style="color:#000000;fill:url(#linearGradient3632)"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path2365"
+ d="m 239.44,192.85 c -2.29,2.41 -4.43,4.92 -6.43,7.49 2.5,-3.23 5.25,-6.31 8.22,-9.28 -0.6,0.6 -1.21,1.18 -1.79,1.79 z m 3.62,-3.58 c 4.29,-4.07 8.84,-7.67 13.61,-10.83 -4.76,3.15 -9.33,6.77 -13.61,10.83 z m -11.62,13.17 c -0.93,1.27 -1.81,2.53 -2.67,3.82 0.85,-1.29 1.74,-2.56 2.67,-3.82 z m -2.81,4.05 c -0.89,1.35 -1.76,2.74 -2.58,4.13 0.82,-1.4 1.68,-2.77 2.58,-4.13 z m -2.58,4.13 c -1.66,2.8 -3.16,5.65 -4.51,8.57 1.35,-2.91 2.86,-5.78 4.51,-8.57 z m -4.51,8.57 c -1.35,2.92 -2.55,5.9 -3.6,8.91 1.05,-3.01 2.25,-6 3.6,-8.91 z m -3.6,8.91 c -1.05,3 -1.97,6.05 -2.72,9.12 0.75,-3.08 1.67,-6.12 2.72,-9.12 z m -2.72,9.12 c -0.31,1.27 -0.58,2.53 -0.84,3.8 0.26,-1.27 0.53,-2.53 0.84,-3.8 z m 43.53,-60.1 c 1.38,-0.87 2.79,-1.69 4.2,-2.48 -1.41,0.79 -2.82,1.61 -4.2,2.48 z m 8.49,-4.73 c 1.44,-0.71 2.88,-1.37 4.35,-2.01 -1.46,0.63 -2.92,1.3 -4.35,2.01 z m 17.89,-6.76 c 1.53,-0.41 3.06,-0.77 4.6,-1.11 -1.54,0.34 -3.07,0.7 -4.6,1.11 z m 4.62,-1.13 c 1.54,-0.34 3.09,-0.62 4.64,-0.88 -1.55,0.26 -3.1,0.54 -4.64,0.88 z m -75.52,77.34 c -0.16,0.79 -0.29,1.58 -0.42,2.36 0.13,-0.77 0.27,-1.58 0.42,-2.36 z m -0.42,2.36 c -0.13,0.78 -0.27,1.55 -0.38,2.33 0.11,-0.79 0.24,-1.55 0.38,-2.33 z m -0.69,4.67 c -0.09,0.78 -0.17,1.57 -0.24,2.36 0.07,-0.78 0.15,-1.58 0.24,-2.36 z m -0.44,4.73 c -0.06,0.78 -0.1,1.56 -0.13,2.34 0.03,-0.79 0.07,-1.56 0.13,-2.34 z m 93.47,-91.26 c 1.18,-0.06 2.37,-0.1 3.56,-0.12 -1.2,0.02 -2.37,0.06 -3.56,0.12 z m 4.71,-0.12 c 0.78,0 1.57,0.01 2.36,0.03 -0.79,-0.02 -1.57,-0.03 -2.36,-0.03 z m -98.18,105.52 c 0.11,1.58 0.25,3.16 0.44,4.73 -0.19,-1.57 -0.33,-3.16 -0.44,-4.73 z M 322.66,162.93 c 1.56,0.19 3.12,0.4 4.68,0.66 -1.56,-0.26 -3.12,-0.47 -4.68,-0.66 z m -108.85,114.2 c 0.13,0.78 0.26,1.58 0.42,2.36 -0.15,-0.77 -0.29,-1.58 -0.42,-2.36 z m 0.42,2.36 c 0.14,0.77 0.31,1.54 0.48,2.3 -0.17,-0.77 -0.33,-1.52 -0.48,-2.3 z m 1.61,6.92 c 0.21,0.77 0.41,1.55 0.64,2.32 -0.23,-0.76 -0.44,-1.55 -0.64,-2.32 z m 1.35,4.57 c 0.24,0.76 0.49,1.51 0.75,2.26 -0.27,-0.75 -0.51,-1.5 -0.75,-2.26 z m 0.75,2.26 c 0.25,0.71 0.5,1.43 0.77,2.14 -0.26,-0.7 -0.52,-1.43 -0.77,-2.14 z m 1.7,4.48 c 0.3,0.75 0.61,1.48 0.93,2.21 -0.32,-0.73 -0.63,-1.47 -0.93,-2.21 z m 0.93,2.21 c 0.32,0.74 0.63,1.48 0.97,2.21 -0.34,-0.72 -0.65,-1.47 -0.97,-2.21 z m 0.97,2.21 c 0.34,0.73 0.7,1.45 1.06,2.17 -0.36,-0.72 -0.72,-1.44 -1.06,-2.17 z m 2.14,4.31 c 0.38,0.72 0.78,1.43 1.17,2.15 -0.39,-0.71 -0.79,-1.43 -1.17,-2.15 z M 359.25,174.91 c 1.12,0.63 2.23,1.28 3.34,1.97 -1.11,-0.69 -2.22,-1.34 -3.34,-1.97 z M 227.33,312.79 c 0.38,0.61 0.77,1.23 1.17,1.84 -0.4,-0.61 -0.79,-1.22 -1.17,-1.84 z m 1.57,2.46 c 0.42,0.62 0.85,1.24 1.28,1.85 -0.43,-0.62 -0.86,-1.23 -1.28,-1.85 z m 2.72,3.86 c 0.95,1.3 1.95,2.57 2.98,3.83 -1.02,-1.25 -2.03,-2.54 -2.98,-3.83 z M 371.07,182.75 c 1.3,1.01 2.61,2.04 3.87,3.12 -1.27,-1.09 -2.56,-2.1 -3.87,-3.12 z M 244.92,333.79 c 38.64,34.9 98.35,33.74 135.59,-3.49 37.23,-37.24 38.39,-96.96 3.49,-135.59 31.41,35.32 -20.5,44.27 -57.68,81.45 -37.17,37.17 -46.08,89.04 -81.4,57.63 z"
+ style="color:#000000;fill:url(#linearGradient3609)"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path6632"
+ d="m 239.44,192.87 c -36.65,38.56 -36.03,99.58 1.8,137.42 38.44,38.43 46.67,-15.69 85.1,-54.13 38.43,-38.43 92.59,-46.69 54.15,-85.12 -38.43,-38.44 -100.81,-38.41 -139.25,0.03 -0.6,0.6 -1.22,1.19 -1.8,1.8 z m 11.99,9.17 c 21.4,-21.41 52.05,-25.51 68.42,-9.14 16.37,16.37 12.27,47.02 -9.14,68.43 -21.4,21.4 -52.05,25.5 -68.42,9.13 -16.37,-16.37 -12.27,-47.02 9.14,-68.42 z"
+ style="color:#000000;fill:url(#linearGradient3594)"
+ inkscape:connector-curvature="0" />
+ </g>
+ <metadata
+ id="metadata6318">
+ <rdf:RDF>
+ <cc:Work>
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
+ <dc:publisher>
+ <cc:Agent
+ rdf:about="http://openclipart.org/">
+ <dc:title>Openclipart</dc:title>
+ </cc:Agent>
+ </dc:publisher>
+ <dc:title>Key</dc:title>
+ <dc:date>2007-02-27T15:15:43</dc:date>
+ <dc:description>A key icon.</dc:description>
+ <dc:source>http://openclipart.org/detail/3330/key-by-barretr</dc:source>
+ <dc:creator>
+ <cc:Agent>
+ <dc:title>barretr</dc:title>
+ </cc:Agent>
+ </dc:creator>
+ <dc:subject>
+ <rdf:Bag>
+ <rdf:li>clip art</rdf:li>
+ <rdf:li>clipart</rdf:li>
+ <rdf:li>icon</rdf:li>
+ <rdf:li>image</rdf:li>
+ <rdf:li>key</rdf:li>
+ <rdf:li>media</rdf:li>
+ <rdf:li>png</rdf:li>
+ <rdf:li>public domain</rdf:li>
+ <rdf:li>svg</rdf:li>
+ </rdf:Bag>
+ </dc:subject>
+ </cc:Work>
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/publicdomain/">
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution" />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+ </cc:License>
+ </rdf:RDF>
+ </metadata>
+ <rect
+ style="color:#000000;fill:#ff0000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect6322"
+ width="442.68826"
+ height="442.68826"
+ x="84.558434"
+ y="505.21939" />
+</svg>
Version=1.0
Type=Application
Terminal=false
-Exec=@INSTALL_PREFIX@/bin/dcpomatic
-Name=DCP-o-matic
+Exec=@INSTALL_PREFIX@/bin/dcpomatic2
+Name=DCP-o-matic 2
Icon=dcpomatic
Comment=DCP generator
Categories=AudioVideo;Video
Summary:A program that generates Digital Cinema Packages (DCPs) from video and audio files
-Name:dcpomatic
+Name:dcpomatic2
Version:@VERSION@
Release:1%{?dist}
License:GPL
digital projectors.
%files
-%{_bindir}/dcpomatic
-%{_bindir}/dcpomatic_batch
-%{_bindir}/dcpomatic_cli
-%{_bindir}/dcpomatic_create
-%{_bindir}/dcpomatic_kdm
-%{_bindir}/dcpomatic_server
-%{_bindir}/dcpomatic_server_cli
-%{_datadir}/applications/dcpomatic.desktop
-%{_datadir}/applications/dcpomatic_batch.desktop
-%{_datadir}/applications/dcpomatic_server.desktop
-%{_datadir}/dcpomatic/taskbar_icon.png
-%{_datadir}/icons/hicolor/128x128/apps/dcpomatic.png
-%{_datadir}/icons/hicolor/22x22/apps/dcpomatic.png
-%{_datadir}/icons/hicolor/32x32/apps/dcpomatic.png
-%{_datadir}/icons/hicolor/48x48/apps/dcpomatic.png
-%{_datadir}/icons/hicolor/64x64/apps/dcpomatic.png
-%{_datadir}/locale/de_DE/LC_MESSAGES/dcpomatic.mo
-%{_datadir}/locale/de_DE/LC_MESSAGES/libdcpomatic-wx.mo
-%{_datadir}/locale/de_DE/LC_MESSAGES/libdcpomatic.mo
-%{_datadir}/locale/es_ES/LC_MESSAGES/dcpomatic.mo
-%{_datadir}/locale/es_ES/LC_MESSAGES/libdcpomatic-wx.mo
-%{_datadir}/locale/es_ES/LC_MESSAGES/libdcpomatic.mo
-%{_datadir}/locale/fr_FR/LC_MESSAGES/dcpomatic.mo
-%{_datadir}/locale/fr_FR/LC_MESSAGES/libdcpomatic-wx.mo
-%{_datadir}/locale/fr_FR/LC_MESSAGES/libdcpomatic.mo
-%{_datadir}/locale/it_IT/LC_MESSAGES/dcpomatic.mo
-%{_datadir}/locale/it_IT/LC_MESSAGES/libdcpomatic-wx.mo
-%{_datadir}/locale/it_IT/LC_MESSAGES/libdcpomatic.mo
-%{_datadir}/locale/sv_SE/LC_MESSAGES/dcpomatic.mo
-%{_datadir}/locale/sv_SE/LC_MESSAGES/libdcpomatic-wx.mo
-%{_datadir}/locale/sv_SE/LC_MESSAGES/libdcpomatic.mo
-%{_datadir}/locale/nl_NL/LC_MESSAGES/dcpomatic.mo
-%{_datadir}/locale/nl_NL/LC_MESSAGES/libdcpomatic-wx.mo
-%{_datadir}/locale/nl_NL/LC_MESSAGES/libdcpomatic.mo
+%{_bindir}/dcpomatic2
+%{_bindir}/dcpomatic2_batch
+%{_bindir}/dcpomatic2_cli
+%{_bindir}/dcpomatic2_create
+%{_bindir}/dcpomatic2_kdm
+%{_bindir}/dcpomatic2_server
+%{_bindir}/dcpomatic2_server_cli
+%{_datadir}/applications/dcpomatic2.desktop
+%{_datadir}/applications/dcpomatic2_batch.desktop
+%{_datadir}/applications/dcpomatic2_server.desktop
+%{_datadir}/dcpomatic2/taskbar_icon.png
+%{_datadir}/icons/hicolor/128x128/apps/dcpomatic2.png
+%{_datadir}/icons/hicolor/22x22/apps/dcpomatic2.png
+%{_datadir}/icons/hicolor/32x32/apps/dcpomatic2.png
+%{_datadir}/icons/hicolor/48x48/apps/dcpomatic2.png
+%{_datadir}/icons/hicolor/64x64/apps/dcpomatic2.png
+%{_datadir}/locale/de_DE/LC_MESSAGES/dcpomatic2.mo
+%{_datadir}/locale/de_DE/LC_MESSAGES/libdcpomatic2-wx.mo
+%{_datadir}/locale/de_DE/LC_MESSAGES/libdcpomatic2.mo
+%{_datadir}/locale/es_ES/LC_MESSAGES/dcpomatic2.mo
+%{_datadir}/locale/es_ES/LC_MESSAGES/libdcpomatic2-wx.mo
+%{_datadir}/locale/es_ES/LC_MESSAGES/libdcpomatic2.mo
+%{_datadir}/locale/fr_FR/LC_MESSAGES/dcpomatic2.mo
+%{_datadir}/locale/fr_FR/LC_MESSAGES/libdcpomatic2-wx.mo
+%{_datadir}/locale/fr_FR/LC_MESSAGES/libdcpomatic2.mo
+%{_datadir}/locale/it_IT/LC_MESSAGES/dcpomatic2.mo
+%{_datadir}/locale/it_IT/LC_MESSAGES/libdcpomatic2-wx.mo
+%{_datadir}/locale/it_IT/LC_MESSAGES/libdcpomatic2.mo
+%{_datadir}/locale/sv_SE/LC_MESSAGES/dcpomatic2.mo
+%{_datadir}/locale/sv_SE/LC_MESSAGES/libdcpomatic2-wx.mo
+%{_datadir}/locale/sv_SE/LC_MESSAGES/libdcpomatic2.mo
+%{_datadir}/locale/nl_NL/LC_MESSAGES/dcpomatic2.mo
+%{_datadir}/locale/nl_NL/LC_MESSAGES/libdcpomatic2-wx.mo
+%{_datadir}/locale/nl_NL/LC_MESSAGES/libdcpomatic2.mo
%prep
rm -rf $RPM_BUILD_DIR/dcpomatic-@VERSION@
def build(bld):
obj = bld(features='subst')
obj.source = 'dcpomatic.desktop.in'
- obj.target = 'dcpomatic.desktop'
+ obj.target = 'dcpomatic2.desktop'
obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX
obj.VERSION = bld.env.VERSION
obj = bld(features='subst')
obj.source = 'dcpomatic_batch.desktop.in'
- obj.target = 'dcpomatic_batch.desktop'
+ obj.target = 'dcpomatic2_batch.desktop'
obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX
obj.VERSION = bld.env.VERSION
obj = bld(features='subst')
obj.source = 'dcpomatic_server.desktop.in'
- obj.target = 'dcpomatic_server.desktop'
+ obj.target = 'dcpomatic2_server.desktop'
obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX
obj.VERSION = bld.env.VERSION
obj = bld(features='subst')
obj.source = 'dcpomatic.spec.in'
- obj.target = 'dcpomatic.spec'
+ obj.target = 'dcpomatic2.spec'
obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX
obj.VERSION = bld.env.VERSION
if bld.env.TARGET_CENTOS_6:
elif bld.env.TARGET_CENTOS_7:
obj.CENTOS_VERSION = '7'
- bld.install_files('${PREFIX}/share/applications', ['dcpomatic.desktop', 'dcpomatic_batch.desktop', 'dcpomatic_server.desktop'])
+ bld.install_files('${PREFIX}/share/applications', ['dcpomatic2.desktop', 'dcpomatic2_batch.desktop', 'dcpomatic2_server.desktop'])
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
- <string>dcpomatic</string>
+ <string>dcpomatic2</string>
<key>CFBundleGetInfoString</key>
<string>DCP generator</string>
<key>CFBundleIconFile</key>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
- <string>DCP-o-matic</string>
+ <string>DCP-o-matic 2</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersions</key>
ENV=/Users/carl/Environments/osx/10.6
ROOT=$1
-appdir="DCP-o-matic.app"
+appdir="DCP-o-matic 2.app"
approot="$appdir/Contents"
libs="$approot/lib"
macos="$approot/MacOS"
relink="$relink|$2"
}
-universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic "$WORK/$macos"
-universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic_cli "$WORK/$macos"
-universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic_server_cli "$WORK/$macos"
-universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic_batch "$WORK/$macos"
-universal_copy $ROOT src/dcpomatic/build/src/lib/libdcpomatic.dylib "$WORK/$libs"
-universal_copy $ROOT src/dcpomatic/build/src/wx/libdcpomatic-wx.dylib "$WORK/$libs"
+universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2 "$WORK/$macos"
+universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_cli "$WORK/$macos"
+universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_server_cli "$WORK/$macos"
+universal_copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_batch "$WORK/$macos"
+universal_copy $ROOT src/dcpomatic/build/src/lib/libdcpomatic2.dylib "$WORK/$libs"
+universal_copy $ROOT src/dcpomatic/build/src/wx/libdcpomatic2-wx.dylib "$WORK/$libs"
universal_copy_lib $ROOT libcxml "$WORK/$libs"
-universal_copy_lib $ROOT libdcp "$WORK/$libs"
-universal_copy_lib $ROOT libasdcp-libdcp "$WORK/$libs"
-universal_copy_lib $ROOT libkumu-libdcp "$WORK/$libs"
+universal_copy_lib $ROOT libdcp-1.0 "$WORK/$libs"
+universal_copy_lib $ROOT libasdcp-libdcp-1.0 "$WORK/$libs"
+universal_copy_lib $ROOT libkumu-libdcp-1.0 "$WORK/$libs"
universal_copy_lib $ROOT libopenjpeg "$WORK/$libs"
universal_copy_lib $ROOT libavdevice "$WORK/$libs"
universal_copy_lib $ROOT libavformat "$WORK/$libs"
universal_copy_lib $ENV libiconv "$WORK/$libs"
universal_copy_lib $ENV libpango "$WORK/$libs"
universal_copy_lib $ENV libcairo "$WORK/$libs"
+universal_copy_lib $ENV libpixman "$WORK/$libs"
+universal_copy_lib $ENV libharfbuzz "$WORK/$libs"
relink=`echo $relink | sed -e "s/\+//g"`
-for obj in "$WORK/$macos/dcpomatic" "$WORK/$macos/dcpomatic_batch" "$WORK/$macos/dcpomatic_cli" "$WORK/$macos/dcpomatic_server_cli" "$WORK/$macos/ffprobe" "$WORK/$libs/"*.dylib; do
+for obj in "$WORK/$macos/dcpomatic2" "$WORK/$macos/dcpomatic2_batch" "$WORK/$macos/dcpomatic2_cli" "$WORK/$macos/dcpomatic2_server_cli" "$WORK/$macos/ffprobe" "$WORK/$libs/"*.dylib; do
deps=`otool -L "$obj" | awk '{print $1}' | egrep "($relink)" | egrep "($ENV|$ROOT|boost)"`
changes=""
for dep in $deps; do
+ echo "Relinking $dep into $obj"
base=`basename $dep`
# $dep will be a path within 64/; make a 32/ path too
dep32=`echo $dep | sed -e "s/\/64\//\/32\//g"`
cp icons/kdm_email.png "$WORK/$resources"
cp icons/servers.png "$WORK/$resources"
cp icons/tms.png "$WORK/$resources"
+cp icons/keys.png "$WORK/$resources"
# i18n: DCP-o-matic .mo files
for lang in de_DE es_ES fr_FR it_IT sv_SE nl_NL; do
set theViewOptions to the icon view options of container window
set arrangement of theViewOptions to not arranged
set icon size of theViewOptions to 64
- set position of item "DCP-o-matic.app" of container window to {90, 80}
+ set position of item "DCP-o-matic 2.app" of container window to {90, 80}
set position of item "Applications" of container window to {310, 80}
close
open
!define MUI_SPECIALBITMAP "%resources%/dcpomatic.bmp"
!include "Sections.nsh"
-InstallDir "$PROGRAMFILES\\DCP-o-matic"
+InstallDir "$PROGRAMFILES\\DCP-o-matic 2"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "../../../COPYING"
; disable registry redirection (enable access to 64-bit portion of registry)
SetRegView 64
; change install dir
- StrCpy $INSTDIR "$PROGRAMFILES64\DCP-o-matic"
+ StrCpy $INSTDIR "$PROGRAMFILES64\DCP-o-matic 2"
${EndIf}
""", file=f)
File "%static_deps%/bin/libcurl-4.dll"
File "%static_deps%/bin/ssleay32.dll"
File "%static_deps%/bin/libzip-2.dll"
+File "%static_deps%/bin/libcairomm-1.0-1.dll"
+File "%static_deps%/bin/libpangomm-1.4-1.dll"
+File "%static_deps%/bin/pango-querymodules.exe"
-File "%cdist_deps%/bin/asdcp-libdcp.dll"
-File "%cdist_deps%/bin/kumu-libdcp.dll"
+File "%cdist_deps%/bin/asdcp-libdcp-1.0.dll"
+File "%cdist_deps%/bin/kumu-libdcp-1.0.dll"
File "%cdist_deps%/bin/avcodec-56.dll"
File "%cdist_deps%/bin/avfilter-5.dll"
File "%cdist_deps%/bin/avformat-56.dll"
File "%cdist_deps%/bin/avutil-54.dll"
File "%cdist_deps%/bin/avdevice-56.dll"
File "%cdist_deps%/bin/postproc-53.dll"
-File "%cdist_deps%/bin/dcp.dll"
+File "%cdist_deps%/bin/dcp-1.0.dll"
File "%cdist_deps%/bin/libopenjpeg-1.dll"
File "%cdist_deps%/bin/swresample-1.dll"
File "%cdist_deps%/bin/swscale-3.dll"
File "%cdist_deps%/bin/cxml-0.dll"
File "%cdist_deps%/bin/ffprobe.exe"
-File "%binaries%/src/wx/dcpomatic-wx.dll"
-File "%binaries%/src/lib/dcpomatic.dll"
+File "%binaries%/src/wx/dcpomatic2-wx.dll"
+File "%binaries%/src/lib/dcpomatic2.dll"
+SetOutPath "$INSTDIR\\lib\\pango\\1.8.0\\modules"
+File "%static_deps%/lib/pango/1.8.0/modules/pango-arabic-lang.dll"
+File "%static_deps%/lib/pango/1.8.0/modules/pango-basic-win32.dll"
+File "%static_deps%/lib/pango/1.8.0/modules/pango-indic-lang.dll"
+
+SetOutPath "$INSTDIR\\bin"
# I don't know why, but sometimes it seems that
# delegates.xml must be in with the binaries, and
# sometimes in the $PROFILE. Meh.
File "%static_deps%/etc/ImageMagick-6/delegates.xml"
SetOutPath "$INSTDIR\\locale\\fr\\LC_MESSAGES"
-File "%binaries%/src/lib/mo/fr_FR/libdcpomatic.mo"
-File "%binaries%/src/wx/mo/fr_FR/libdcpomatic-wx.mo"
-File "%binaries%/src/tools/mo/fr_FR/dcpomatic.mo"
+File "%binaries%/src/lib/mo/fr_FR/libdcpomatic2.mo"
+File "%binaries%/src/wx/mo/fr_FR/libdcpomatic2-wx.mo"
+File "%binaries%/src/tools/mo/fr_FR/dcpomatic2.mo"
File "%static_deps%/share/locale/fr/LC_MESSAGES/wxstd.mo"
SetOutPath "$INSTDIR\\locale\\it\\LC_MESSAGES"
-File "%binaries%/src/lib/mo/it_IT/libdcpomatic.mo"
-File "%binaries%/src/wx/mo/it_IT/libdcpomatic-wx.mo"
-File "%binaries%/src/tools/mo/it_IT/dcpomatic.mo"
+File "%binaries%/src/lib/mo/it_IT/libdcpomatic2.mo"
+File "%binaries%/src/wx/mo/it_IT/libdcpomatic2-wx.mo"
+File "%binaries%/src/tools/mo/it_IT/dcpomatic2.mo"
File "%static_deps%/share/locale/it/LC_MESSAGES/wxstd.mo"
SetOutPath "$INSTDIR\\locale\\es\\LC_MESSAGES"
-File "%binaries%/src/lib/mo/es_ES/libdcpomatic.mo"
-File "%binaries%/src/wx/mo/es_ES/libdcpomatic-wx.mo"
-File "%binaries%/src/tools/mo/es_ES/dcpomatic.mo"
+File "%binaries%/src/lib/mo/es_ES/libdcpomatic2.mo"
+File "%binaries%/src/wx/mo/es_ES/libdcpomatic2-wx.mo"
+File "%binaries%/src/tools/mo/es_ES/dcpomatic2.mo"
File "%static_deps%/share/locale/es/LC_MESSAGES/wxstd.mo"
SetOutPath "$INSTDIR\\locale\\sv\\LC_MESSAGES"
-File "%binaries%/src/lib/mo/sv_SE/libdcpomatic.mo"
-File "%binaries%/src/wx/mo/sv_SE/libdcpomatic-wx.mo"
-File "%binaries%/src/tools/mo/sv_SE/dcpomatic.mo"
+File "%binaries%/src/lib/mo/sv_SE/libdcpomatic2.mo"
+File "%binaries%/src/wx/mo/sv_SE/libdcpomatic2-wx.mo"
+File "%binaries%/src/tools/mo/sv_SE/dcpomatic2.mo"
File "%static_deps%/share/locale/sv/LC_MESSAGES/wxstd.mo"
SetOutPath "$INSTDIR\\locale\\de\\LC_MESSAGES"
-File "%binaries%/src/lib/mo/de_DE/libdcpomatic.mo"
-File "%binaries%/src/wx/mo/de_DE/libdcpomatic-wx.mo"
-File "%binaries%/src/tools/mo/de_DE/dcpomatic.mo"
+File "%binaries%/src/lib/mo/de_DE/libdcpomatic2.mo"
+File "%binaries%/src/wx/mo/de_DE/libdcpomatic2-wx.mo"
+File "%binaries%/src/tools/mo/de_DE/dcpomatic2.mo"
File "%static_deps%/share/locale/de/LC_MESSAGES/wxstd.mo"
SetOutPath "$INSTDIR\\locale\\nl\\LC_MESSAGES"
-File "%binaries%/src/lib/mo/nl_NL/libdcpomatic.mo"
-File "%binaries%/src/wx/mo/nl_NL/libdcpomatic-wx.mo"
-File "%binaries%/src/tools/mo/nl_NL/dcpomatic.mo"
+File "%binaries%/src/lib/mo/nl_NL/libdcpomatic2.mo"
+File "%binaries%/src/wx/mo/nl_NL/libdcpomatic2-wx.mo"
+File "%binaries%/src/tools/mo/nl_NL/dcpomatic2.mo"
File "%static_deps%/share/locale/nl/LC_MESSAGES/wxstd.mo"
-WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic" "DisplayName" "DCP-o-matic (remove only)"
-WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic" "UninstallString" "$INSTDIR\\Uninstall.exe"
+WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic2" "DisplayName" "DCP-o-matic 2 (remove only)"
+WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic2" "UninstallString" "$INSTDIR\\Uninstall.exe"
WriteUninstaller "$INSTDIR\\Uninstall.exe"
+CreateDirectory "$INSTDIR\etc\pango"
+ReadEnvStr $0 COMSPEC
+SetOutPath "$INSTDIR"
+nsExec::ExecToLog '$0 /C bin\pango-querymodules.exe > etc\pango\pango.modules'
+
SectionEnd
Section "DCP-o-matic" SEC_MASTER
SetOutPath "$INSTDIR\\bin"
-CreateDirectory "$SMPROGRAMS\\DCP-o-matic"
-File "%binaries%/src/tools/dcpomatic.exe"
-File "%binaries%/src/tools/dcpomatic_batch.exe"
-File "%binaries%/src/tools/dcpomatic_cli.exe"
-CreateShortCut "$DESKTOP\\DCP-o-matic.lnk" "$INSTDIR\\bin\\dcpomatic.exe" ""
-CreateShortCut "$SMPROGRAMS\\DCP-o-matic\\DCP-o-matic.lnk" "$INSTDIR\\bin\\dcpomatic.exe" "" "$INSTDIR\\bin\\dcpomatic.exe" 0
-CreateShortCut "$DESKTOP\\DCP-o-matic batch converter.lnk" "$INSTDIR\\bin\\dcpomatic_batch.exe" ""
-CreateShortCut "$SMPROGRAMS\\DCP-o-matic\\DCP-o-matic batch converter.lnk" "$INSTDIR\\bin\\dcpomatic.exe" "" "$INSTDIR\\bin\\dcpomatic_batch.exe" 0
-CreateShortCut "$SMPROGRAMS\\DCP-o-matic\\Uninstall DCP-o-matic.lnk" "$INSTDIR\\Uninstall.exe" "" "$INSTDIR\\Uninstall.exe" 0
-WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic" "DisplayName" "DCP-o-matic (remove only)"
-WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic" "UninstallString" "$INSTDIR\\Uninstall.exe"
+CreateDirectory "$SMPROGRAMS\\DCP-o-matic 2"
+File "%binaries%/src/tools/dcpomatic2.exe"
+File "%binaries%/src/tools/dcpomatic2_batch.exe"
+File "%binaries%/src/tools/dcpomatic2_cli.exe"
+CreateShortCut "$DESKTOP\\DCP-o-matic 2.lnk" "$INSTDIR\\bin\\dcpomatic2.exe" ""
+CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2.lnk" "$INSTDIR\\bin\\dcpomatic2.exe" "" "$INSTDIR\\bin\\dcpomatic2.exe" 0
+CreateShortCut "$DESKTOP\\DCP-o-matic 2 batch converter.lnk" "$INSTDIR\\bin\\dcpomatic2_batch.exe" ""
+CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 batch converter.lnk" "$INSTDIR\\bin\\dcpomatic2.exe" "" "$INSTDIR\\bin\\dcpomatic2_batch.exe" 0
+CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\Uninstall DCP-o-matic 2.lnk" "$INSTDIR\\Uninstall.exe" "" "$INSTDIR\\Uninstall.exe" 0
+WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic2" "DisplayName" "DCP-o-matic 2 (remove only)"
+WriteRegStr HKLM "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic2" "UninstallString" "$INSTDIR\\Uninstall.exe"
WriteUninstaller "$INSTDIR\\Uninstall.exe"
SectionEnd
Section "Encode server" SEC_SERVER
SetOutPath "$INSTDIR\\bin"
-CreateDirectory "$SMPROGRAMS\\DCP-o-matic"
-File "%binaries%/src/tools/dcpomatic_server_cli.exe"
-File "%binaries%/src/tools/dcpomatic_server.exe"
-CreateShortCut "$DESKTOP\\DCP-o-matic encode server.lnk" "$INSTDIR\\bin\\dcpomatic_server.exe" ""
-CreateShortCut "$SMPROGRAMS\\DCP-o-matic\\DCP-o-matic encode server.lnk" "$INSTDIR\\bin\\dcpomatic_server.exe" "" "$INSTDIR\\bin\\dcpomatic_server.exe" 0
-CreateShortCut "$SMPROGRAMS\\DCP-o-matic\\Uninstall DCP-o-matic.lnk" "$INSTDIR\\Uninstall.exe" "" "$INSTDIR\\Uninstall.exe" 0
+CreateDirectory "$SMPROGRAMS\\DCP-o-matic 2"
+File "%binaries%/src/tools/dcpomatic2_server_cli.exe"
+File "%binaries%/src/tools/dcpomatic2_server.exe"
+CreateShortCut "$DESKTOP\\DCP-o-matic 2 encode server.lnk" "$INSTDIR\\bin\\dcpomatic2_server.exe" ""
+CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\DCP-o-matic 2 encode server.lnk" "$INSTDIR\\bin\\dcpomatic_server.exe" "" "$INSTDIR\\bin\\dcpomatic2_server.exe" 0
+CreateShortCut "$SMPROGRAMS\\DCP-o-matic 2\\Uninstall DCP-o-matic 2.lnk" "$INSTDIR\\Uninstall.exe" "" "$INSTDIR\\Uninstall.exe" 0
SectionEnd
LangString DESC_SEC_MASTER ${LANG_ENGLISH} "DCP-o-matic"
Section "Uninstall"
RMDir /r "$INSTDIR\\*.*"
RMDir "$INSTDIR"
-Delete "$DESKTOP\\DCP-o-matic.lnk"
-Delete "$DESKTOP\\DCP-o-matic batch converter.lnk"
-Delete "$DESKTOP\\DCP-o-matic encode server.lnk"
-Delete "$SMPROGRAMS\\DCP-o-matic\\*.*"
-RmDir "$SMPROGRAMS\\DCP-o-matic"
-DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\\DCP-o-matic"
-DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic"
+Delete "$DESKTOP\\DCP-o-matic 2.lnk"
+Delete "$DESKTOP\\DCP-o-matic 2 batch converter.lnk"
+Delete "$DESKTOP\\DCP-o-matic 2 encode server.lnk"
+Delete "$SMPROGRAMS\\DCP-o-matic 2\\*.*"
+RmDir "$SMPROGRAMS\\DCP-o-matic 2"
+DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\\DCP-o-matic2"
+DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\DCP-o-matic2"
SectionEnd
""", file=f)
export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH
if [ "$1" == "--debug" ]; then
shift
- gdb --args build/src/tools/dcpomatic $*
+ gdb --args build/src/tools/dcpomatic2 $*
elif [ "$1" == "--valgrind" ]; then
shift
- valgrind --tool="memcheck" build/src/tools/dcpomatic $*
+ valgrind --tool="memcheck" build/src/tools/dcpomatic2 $*
elif [ "$1" == "--callgrind" ]; then
shift
- valgrind --tool="callgrind" build/src/tools/dcpomatic $*
+ valgrind --tool="callgrind" build/src/tools/dcpomatic2 $*
elif [ "$1" == "--massif" ]; then
shift
- valgrind --tool="massif" build/src/tools/dcpomatic $*
+ valgrind --tool="massif" build/src/tools/dcpomatic2 $*
elif [ "$1" == "--i18n" ]; then
shift
- LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 LC_ALL=fr_FR.UTF8 build/src/tools/dcpomatic "$*"
+ LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 LC_ALL=fr_FR.UTF8 build/src/tools/dcpomatic2 "$*"
elif [ "$1" == "--perf" ]; then
shift
- perf record build/src/tools/dcpomatic $*
+ perf record build/src/tools/dcpomatic2 $*
else
- build/src/tools/dcpomatic $*
+ build/src/tools/dcpomatic2 $*
fi
fi
export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH:build/src
if [ "$1" == "--debug" ]; then
shift
- gdb --args build/src/tools/dcpomatic_cli "$@"
+ gdb --args build/src/tools/dcpomatic2_cli "$@"
elif [ "$1" == "--valgrind" ]; then
shift
- valgrind --tool="memcheck" --num-callers=24 --suppressions=suppressions build/src/tools/dcpomatic_cli "$@"
-# valgrind --tool="memcheck" --leak-check=full --show-reachable=yes --num-callers=24 --suppressions=suppressions build/src/tools/dcpomatic_cli "$@"
+ valgrind --tool="memcheck" --num-callers=24 --suppressions=suppressions build/src/tools/dcpomatic2_cli "$@"
+# valgrind --tool="memcheck" --leak-check=full --show-reachable=yes --num-callers=24 --suppressions=suppressions build/src/tools/dcpomatic2_cli "$@"
else
- build/src/tools/dcpomatic_cli "$@"
+ build/src/tools/dcpomatic2_cli "$@"
fi
*/
#include "audio_analysis.h"
+#include "audio_buffers.h"
#include "analyse_audio_job.h"
#include "compose.hpp"
#include "film.h"
shared_ptr<Playlist> playlist (new Playlist);
playlist->add (content);
shared_ptr<Player> player (new Player (_film, playlist));
- player->disable_video ();
- player->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1, _2));
-
- _samples_per_point = max (int64_t (1), _film->time_to_audio_frames (_film->length()) / _num_points);
+ int64_t const len = _film->length().frames (_film->audio_frame_rate());
+ _samples_per_point = max (int64_t (1), len / _num_points);
_current.resize (_film->audio_channels ());
_analysis.reset (new AudioAnalysis (_film->audio_channels ()));
_done = 0;
- OutputAudioFrame const len = _film->time_to_audio_frames (_film->length ());
- while (!player->pass ()) {
- set_progress (double (_done) / len);
+ DCPTime const block = DCPTime::from_seconds (1.0 / 8);
+ for (DCPTime t; t < _film->length(); t += block) {
+ analyse (player->get_audio (t, block, false));
+ set_progress (t.seconds() / _film->length().seconds());
}
_analysis->write (content->audio_analysis_path ());
}
void
-AnalyseAudioJob::audio (shared_ptr<const AudioBuffers> b, Time)
+AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b)
{
for (int i = 0; i < b->frames(); ++i) {
for (int j = 0; j < b->channels(); ++j) {
/*
- 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/lib/analyse_audio_job.h
+ * @brief AnalyseAudioJob class.
+ */
+
#include "job.h"
#include "audio_analysis.h"
#include "types.h"
+#include "dcpomatic_time.h"
class AudioBuffers;
class AudioContent;
+/** @class AnalyseAudioJob
+ * @brief A job to analyse the audio of a piece of AudioContent and make a note of its
+ * broad peak and RMS levels.
+ *
+ * After computing the peak and RMS levels over the length of the content, the job
+ * will write a file to Content::audio_analysis_path.
+ */
class AnalyseAudioJob : public Job
{
public:
void run ();
private:
- void audio (boost::shared_ptr<const AudioBuffers>, Time);
+ void analyse (boost::shared_ptr<const AudioBuffers>);
boost::weak_ptr<AudioContent> _content;
- OutputAudioFrame _done;
+ int64_t _done;
int64_t _samples_per_point;
std::vector<AudioPoint> _current;
/*
- 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/lib/audio_analysis.h
+ * @brief AudioAnalysis and AudioPoint classes.
+ */
+
#ifndef DCPOMATIC_AUDIO_ANALYSIS_H
#define DCPOMATIC_AUDIO_ANALYSIS_H
#include <list>
#include <boost/filesystem.hpp>
+/** @class AudioPoint
+ * @brief A single point of an audio analysis for one portion of one channel.
+ */
class AudioPoint
{
public:
float _data[COUNT];
};
+/** @class AudioAnalysis
+ * @brief An analysis of the audio data in a piece of AudioContent.
+ *
+ * This is a set of AudioPoints for each channel. The AudioPoints
+ * each represent some measurement of the audio over a portion of the
+ * content. For example each AudioPoint may give the RMS level of
+ * a 1-minute portion of the audio.
+ */
class AudioAnalysis : public boost::noncopyable
{
public:
void
AudioBuffers::allocate (int channels, int frames)
{
+ assert (frames >= 0);
+ assert (channels >= 0);
+
_channels = channels;
_frames = frames;
_allocated_frames = frames;
void
AudioBuffers::copy_from (AudioBuffers const * from, int frames_to_copy, int read_offset, int write_offset)
{
+ if (frames_to_copy == 0) {
+ /* Prevent the asserts from firing if there is nothing to do */
+ return;
+ }
+
assert (from->channels() == channels());
assert (from);
AudioBuffers::accumulate_frames (AudioBuffers const * from, int read_offset, int write_offset, int frames)
{
assert (_channels == from->channels ());
+ assert (read_offset >= 0);
+ assert (write_offset >= 0);
for (int i = 0; i < _channels; ++i) {
for (int j = 0; j < frames; ++j) {
}
}
}
+
+/** @param c Channel index.
+ * @return AudioBuffers object containing only channel `c' from this AudioBuffers.
+ */
+shared_ptr<AudioBuffers>
+AudioBuffers::channel (int c) const
+{
+ shared_ptr<AudioBuffers> o (new AudioBuffers (1, frames ()));
+ o->copy_channel_from (this, c, 0);
+ return o;
+}
+
+void
+AudioBuffers::copy_channel_from (AudioBuffers const * from, int from_channel, int to_channel)
+{
+ assert (from->frames() == frames());
+ memcpy (data(to_channel), from->data(from_channel), frames() * sizeof (float));
+}
+
+shared_ptr<AudioBuffers>
+AudioBuffers::clone () const
+{
+ shared_ptr<AudioBuffers> b (new AudioBuffers (channels (), frames ()));
+ b->copy_from (this, frames (), 0, 0);
+ return b;
+}
/*
- 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 DVDOMATIC_AUDIO_BUFFERS_H
-#define DVDOMATIC_AUDIO_BUFFERS_H
+/** @file src/lib/audio_buffers.h
+ * @brief AudioBuffers class.
+ */
+
+#ifndef DCPOMATIC_AUDIO_BUFFERS_H
+#define DCPOMATIC_AUDIO_BUFFERS_H
#include <boost/shared_ptr.hpp>
AudioBuffers & operator= (AudioBuffers const &);
+ boost::shared_ptr<AudioBuffers> clone () const;
+ boost::shared_ptr<AudioBuffers> channel (int) const;
+
void ensure_size (int);
float** data () const {
void apply_gain (float);
void copy_from (AudioBuffers const * from, int frames_to_copy, int read_offset, int write_offset);
+ void copy_channel_from (AudioBuffers const * from, int from_channel, int to_channel);
void move (int from, int to, int frames);
- void accumulate_channel (AudioBuffers const *, int, int, float gain = 1);
+ void accumulate_channel (AudioBuffers const * from, int from_channel, int to_channel, float gain = 1);
void accumulate_frames (AudioBuffers const *, int read_offset, int write_offset, int frames);
private:
/*
- 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 <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "audio_content.h"
#include "analyse_audio_job.h"
#include "job_manager.h"
#include "exceptions.h"
#include "config.h"
#include "frame_rate_change.h"
+#include "audio_processor.h"
#include "i18n.h"
using std::vector;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
int const AudioContentProperty::AUDIO_CHANNELS = 200;
int const AudioContentProperty::AUDIO_LENGTH = 201;
int const AudioContentProperty::AUDIO_GAIN = 203;
int const AudioContentProperty::AUDIO_DELAY = 204;
int const AudioContentProperty::AUDIO_MAPPING = 205;
+int const AudioContentProperty::AUDIO_PROCESSOR = 206;
-AudioContent::AudioContent (shared_ptr<const Film> f, Time s)
+AudioContent::AudioContent (shared_ptr<const Film> f)
+ : Content (f)
+ , _audio_gain (0)
+ , _audio_delay (Config::instance()->default_audio_delay ())
+ , _audio_processor (0)
+{
+
+}
+
+AudioContent::AudioContent (shared_ptr<const Film> f, DCPTime s)
: Content (f, s)
, _audio_gain (0)
, _audio_delay (Config::instance()->default_audio_delay ())
+ , _audio_processor (0)
{
}
: Content (f, p)
, _audio_gain (0)
, _audio_delay (Config::instance()->default_audio_delay ())
+ , _audio_processor (0)
{
}
-AudioContent::AudioContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+AudioContent::AudioContent (shared_ptr<const Film> f, cxml::ConstNodePtr node)
: Content (f, node)
+ , _audio_processor (0)
{
_audio_gain = node->number_child<float> ("AudioGain");
_audio_delay = node->number_child<int> ("AudioDelay");
+ if (node->optional_string_child ("AudioProcessor")) {
+ _audio_processor = AudioProcessor::from_id (node->string_child ("AudioProcessor"));
+ }
}
AudioContent::AudioContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
_audio_gain = ref->audio_gain ();
_audio_delay = ref->audio_delay ();
+ _audio_processor = ref->audio_processor ();
}
void
boost::mutex::scoped_lock lm (_mutex);
node->add_child("AudioGain")->add_child_text (raw_convert<string> (_audio_gain));
node->add_child("AudioDelay")->add_child_text (raw_convert<string> (_audio_delay));
+ if (_audio_processor) {
+ node->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
+ }
}
signal_changed (AudioContentProperty::AUDIO_DELAY);
}
+void
+AudioContent::set_audio_processor (AudioProcessor const * p)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _audio_processor = p;
+ }
+
+ /* The channel count might have changed, so reset the mapping */
+ AudioMapping m (processed_audio_channels ());
+ m.make_default ();
+ set_audio_mapping (m);
+
+ signal_changed (AudioContentProperty::AUDIO_PROCESSOR);
+}
+
boost::signals2::connection
AudioContent::analyse_audio (boost::function<void()> finished)
{
string
AudioContent::technical_summary () const
{
- return String::compose ("audio: channels %1, length %2, raw rate %3, out rate %4", audio_channels(), audio_length(), content_audio_frame_rate(), output_audio_frame_rate());
+ return String::compose (
+ "audio: channels %1, length %2, content rate %3, resampled rate %4",
+ audio_channels(),
+ audio_length().seconds(),
+ audio_frame_rate(),
+ resampled_audio_frame_rate()
+ );
+}
+
+void
+AudioContent::set_audio_mapping (AudioMapping)
+{
+ signal_changed (AudioContentProperty::AUDIO_MAPPING);
}
+/** @return the frame rate that this content should be resampled to in order
+ * that it is in sync with the active video content at its start time.
+ */
int
-AudioContent::output_audio_frame_rate () const
+AudioContent::resampled_audio_frame_rate () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
/* Resample to a DCI-approved sample rate */
- double t = dcp_audio_frame_rate (content_audio_frame_rate ());
+ double t = dcp_audio_frame_rate (audio_frame_rate ());
FrameRateChange frc = film->active_frame_rate_change (position ());
/* Compensate if the DCP is being run at a different frame rate
to the source; that is, if the video is run such that it will
look different in the DCP compared to the source (slower or faster).
- skip/repeat doesn't come into effect here.
*/
if (frc.change_speed) {
return rint (t);
}
+int
+AudioContent::processed_audio_channels () const
+{
+ if (!audio_processor ()) {
+ return audio_channels ();
+ }
+
+ return audio_processor()->out_channels (audio_channels ());
+}
+
/*
- 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/lib/audio_content.h
+ * @brief AudioContent and AudioContentProperty classes.
+ */
+
#ifndef DCPOMATIC_AUDIO_CONTENT_H
#define DCPOMATIC_AUDIO_CONTENT_H
class Node;
}
+class AudioProcessor;
+
+/** @class AudioContentProperty
+ * @brief Names for properties of AudioContent.
+ */
class AudioContentProperty
{
public:
static int const AUDIO_GAIN;
static int const AUDIO_DELAY;
static int const AUDIO_MAPPING;
+ static int const AUDIO_PROCESSOR;
};
+/** @class AudioContent
+ * @brief Parent class for content which may contain audio data.
+ */
class AudioContent : public virtual Content
{
public:
typedef int64_t Frame;
- AudioContent (boost::shared_ptr<const Film>, Time);
+ AudioContent (boost::shared_ptr<const Film>);
+ AudioContent (boost::shared_ptr<const Film>, DCPTime);
AudioContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- AudioContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ AudioContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr);
AudioContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
void as_xml (xmlpp::Node *) const;
std::string technical_summary () const;
+ /** @return number of audio channels in the content */
virtual int audio_channels () const = 0;
- virtual AudioContent::Frame audio_length () const = 0;
- virtual int content_audio_frame_rate () const = 0;
+ /** @return the length of the audio in the content */
+ virtual ContentTime audio_length () const = 0;
+ /** @return the frame rate of the content */
+ virtual int audio_frame_rate () const = 0;
virtual AudioMapping audio_mapping () const = 0;
- virtual void set_audio_mapping (AudioMapping) = 0;
+ virtual void set_audio_mapping (AudioMapping);
virtual boost::filesystem::path audio_analysis_path () const;
- int output_audio_frame_rate () const;
-
+ int resampled_audio_frame_rate () const;
+ int processed_audio_channels () const;
+
boost::signals2::connection analyse_audio (boost::function<void()>);
void set_audio_gain (double);
void set_audio_delay (int);
+ void set_audio_processor (AudioProcessor const *);
double audio_gain () const {
boost::mutex::scoped_lock lm (_mutex);
return _audio_delay;
}
+ AudioProcessor const * audio_processor () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_processor;
+ }
+
private:
/** Gain to apply to audio in dB */
double _audio_gain;
/** Delay to apply to audio (positive moves audio later) in milliseconds */
int _audio_delay;
+ AudioProcessor const * _audio_processor;
};
#endif
/*
- 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 <iostream>
#include "audio_decoder.h"
#include "audio_buffers.h"
-#include "exceptions.h"
-#include "log.h"
+#include "audio_processor.h"
#include "resampler.h"
+#include "util.h"
#include "i18n.h"
using std::list;
using std::pair;
using std::cout;
+using std::min;
+using std::max;
using boost::optional;
using boost::shared_ptr;
-AudioDecoder::AudioDecoder (shared_ptr<const Film> film, shared_ptr<const AudioContent> content)
- : Decoder (film)
- , _audio_content (content)
- , _audio_position (0)
+AudioDecoder::AudioDecoder (shared_ptr<const AudioContent> content)
+ : _audio_content (content)
{
+ if (content->resampled_audio_frame_rate() != content->audio_frame_rate() && content->audio_channels ()) {
+ _resampler.reset (new Resampler (content->audio_frame_rate(), content->resampled_audio_frame_rate(), content->audio_channels ()));
+ }
+ if (content->audio_processor ()) {
+ _processor = content->audio_processor()->clone (content->resampled_audio_frame_rate ());
+ }
+
+ reset_decoded_audio ();
}
void
-AudioDecoder::audio (shared_ptr<const AudioBuffers> data, AudioContent::Frame frame)
+AudioDecoder::reset_decoded_audio ()
{
- Audio (data, frame);
- _audio_position = frame + data->frames ();
+ _decoded_audio = ContentAudio (shared_ptr<AudioBuffers> (new AudioBuffers (_audio_content->processed_audio_channels(), 0)), 0);
}
-/** This is a bit odd, but necessary when we have (e.g.) FFmpegDecoders with no audio.
- * The player needs to know that there is no audio otherwise it will keep trying to
- * pass() the decoder to get it to emit audio.
+shared_ptr<ContentAudio>
+AudioDecoder::get_audio (AudioFrame frame, AudioFrame length, bool accurate)
+{
+ shared_ptr<ContentAudio> dec;
+
+ AudioFrame const end = frame + length - 1;
+
+ if (frame < _decoded_audio.frame || end > (_decoded_audio.frame + length * 4)) {
+ /* Either we have no decoded data, or what we do have is a long way from what we want: seek */
+ seek (ContentTime::from_frames (frame, _audio_content->audio_frame_rate()), accurate);
+ }
+
+ /* Offset of the data that we want from the start of _decoded_audio.audio
+ (to be set up shortly)
+ */
+ AudioFrame decoded_offset = 0;
+
+ /* Now enough pass() calls will either:
+ * (a) give us what we want, or
+ * (b) hit the end of the decoder.
+ *
+ * If we are being accurate, we want the right frames,
+ * otherwise any frames will do.
+ */
+ if (accurate) {
+ /* Keep stuffing data into _decoded_audio until we have enough data, or the subclass does not want to give us any more */
+ while ((_decoded_audio.frame > frame || (_decoded_audio.frame + _decoded_audio.audio->frames()) < end) && !pass ()) {}
+ decoded_offset = frame - _decoded_audio.frame;
+ } else {
+ while (_decoded_audio.audio->frames() < length && !pass ()) {}
+ /* Use decoded_offset of 0, as we don't really care what frames we return */
+ }
+
+ /* The amount of data available in _decoded_audio.audio starting from `frame'. This could be -ve
+ if pass() returned true before we got enough data.
+ */
+ AudioFrame const available = _decoded_audio.audio->frames() - decoded_offset;
+
+ /* We will return either that, or the requested amount, whichever is smaller */
+ AudioFrame const to_return = max ((AudioFrame) 0, min (available, length));
+
+ /* Copy our data to the output */
+ shared_ptr<AudioBuffers> out (new AudioBuffers (_decoded_audio.audio->channels(), to_return));
+ out->copy_from (_decoded_audio.audio.get(), to_return, decoded_offset, 0);
+
+ AudioFrame const remaining = max ((AudioFrame) 0, available - to_return);
+
+ /* Clean up decoded; first, move the data after what we just returned to the start of the buffer */
+ _decoded_audio.audio->move (decoded_offset + to_return, 0, remaining);
+ /* And set up the number of frames we have left */
+ _decoded_audio.audio->set_frames (remaining);
+ /* Also bump where those frames are in terms of the content */
+ _decoded_audio.frame += decoded_offset + to_return;
+
+ return shared_ptr<ContentAudio> (new ContentAudio (out, frame));
+}
+
+/** Called by subclasses when audio data is ready.
+ *
+ * Audio timestamping is made hard by many factors, but perhaps the most entertaining is resampling.
+ * We have to assume that we are feeding continuous data into the resampler, and so we get continuous
+ * data out. Hence we do the timestamping here, post-resampler, just by counting samples.
+ *
+ * The time is passed in here so that after a seek we can set up our _audio_position. The
+ * time is ignored once this has been done.
*/
-bool
-AudioDecoder::has_audio () const
+void
+AudioDecoder::audio (shared_ptr<const AudioBuffers> data, ContentTime time)
+{
+ if (_resampler) {
+ data = _resampler->run (data);
+ }
+
+ if (_processor) {
+ data = _processor->run (data);
+ }
+
+ AudioFrame const frame_rate = _audio_content->resampled_audio_frame_rate ();
+
+ if (_seek_reference) {
+ /* We've had an accurate seek and now we're seeing some data */
+ ContentTime const delta = time - _seek_reference.get ();
+ AudioFrame const delta_frames = delta.frames (frame_rate);
+ if (delta_frames > 0) {
+ /* This data comes after the seek time. Pad the data with some silence. */
+ shared_ptr<AudioBuffers> padded (new AudioBuffers (data->channels(), data->frames() + delta_frames));
+ padded->make_silent ();
+ padded->copy_from (data.get(), data->frames(), 0, delta_frames);
+ data = padded;
+ time -= delta;
+ } else if (delta_frames < 0) {
+ /* This data comes before the seek time. Throw some data away */
+ AudioFrame const to_discard = min (-delta_frames, static_cast<AudioFrame> (data->frames()));
+ AudioFrame const to_keep = data->frames() - to_discard;
+ if (to_keep == 0) {
+ /* We have to throw all this data away, so keep _seek_reference and
+ try again next time some data arrives.
+ */
+ return;
+ }
+ shared_ptr<AudioBuffers> trimmed (new AudioBuffers (data->channels(), to_keep));
+ trimmed->copy_from (data.get(), to_keep, to_discard, 0);
+ data = trimmed;
+ time += ContentTime::from_frames (to_discard, frame_rate);
+ }
+ _seek_reference = optional<ContentTime> ();
+ }
+
+ if (!_audio_position) {
+ _audio_position = time.frames (frame_rate);
+ }
+
+ assert (_audio_position.get() >= (_decoded_audio.frame + _decoded_audio.audio->frames()));
+
+ add (data);
+}
+
+void
+AudioDecoder::add (shared_ptr<const AudioBuffers> data)
+{
+ if (!_audio_position) {
+ /* This should only happen when there is a seek followed by a flush, but
+ we need to cope with it.
+ */
+ return;
+ }
+
+ /* Resize _decoded_audio to fit the new data */
+ int new_size = 0;
+ if (_decoded_audio.audio->frames() == 0) {
+ /* There's nothing in there, so just store the new data */
+ new_size = data->frames ();
+ _decoded_audio.frame = _audio_position.get ();
+ } else {
+ /* Otherwise we need to extend _decoded_audio to include the new stuff */
+ new_size = _audio_position.get() + data->frames() - _decoded_audio.frame;
+ }
+
+ _decoded_audio.audio->ensure_size (new_size);
+ _decoded_audio.audio->set_frames (new_size);
+
+ /* Copy new data in */
+ _decoded_audio.audio->copy_from (data.get(), data->frames(), 0, _audio_position.get() - _decoded_audio.frame);
+ _audio_position = _audio_position.get() + data->frames ();
+
+ /* Limit the amount of data we keep in case nobody is asking for it */
+ int const max_frames = _audio_content->resampled_audio_frame_rate () * 10;
+ if (_decoded_audio.audio->frames() > max_frames) {
+ int const to_remove = _decoded_audio.audio->frames() - max_frames;
+ _decoded_audio.frame += to_remove;
+ _decoded_audio.audio->move (to_remove, 0, max_frames);
+ _decoded_audio.audio->set_frames (max_frames);
+ }
+}
+
+void
+AudioDecoder::flush ()
+{
+ if (!_resampler) {
+ return;
+ }
+
+ shared_ptr<const AudioBuffers> b = _resampler->flush ();
+ if (b) {
+ add (b);
+ }
+}
+
+void
+AudioDecoder::seek (ContentTime t, bool accurate)
{
- return _audio_content->audio_channels () > 0;
+ _audio_position.reset ();
+ reset_decoded_audio ();
+ if (accurate) {
+ _seek_reference = t;
+ }
+ if (_processor) {
+ _processor->flush ();
+ }
}
/*
- 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 "decoder.h"
#include "content.h"
#include "audio_content.h"
+#include "content_audio.h"
class AudioBuffers;
+class Resampler;
/** @class AudioDecoder.
* @brief Parent class for audio decoders.
class AudioDecoder : public virtual Decoder
{
public:
- AudioDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const AudioContent>);
-
- bool has_audio () const;
-
- /** Emitted when some audio data is ready */
- boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame)> Audio;
+ AudioDecoder (boost::shared_ptr<const AudioContent>);
+
+ boost::shared_ptr<const AudioContent> audio_content () const {
+ return _audio_content;
+ }
+ /** Try to fetch some audio from a specific place in this content.
+ * @param frame Frame to start from (after resampling, if applicable)
+ * @param length Frames to get (after resampling, if applicable)
+ * @param accurate true to try hard to return frames from exactly `frame', false if we don't mind nearby frames.
+ * @return Time-stamped audio data which may or may not be from the location (and of the length) requested.
+ */
+ boost::shared_ptr<ContentAudio> get_audio (AudioFrame time, AudioFrame length, bool accurate);
+
protected:
- void audio (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
+ void seek (ContentTime time, bool accurate);
+ void audio (boost::shared_ptr<const AudioBuffers>, ContentTime);
+ void flush ();
+ void reset_decoded_audio ();
+ void add (boost::shared_ptr<const AudioBuffers>);
+
boost::shared_ptr<const AudioContent> _audio_content;
- AudioContent::Frame _audio_position;
+ boost::shared_ptr<Resampler> _resampler;
+ boost::shared_ptr<AudioProcessor> _processor;
+ boost::optional<AudioFrame> _audio_position;
+ /** Currently-available decoded audio data */
+ ContentAudio _decoded_audio;
+ /** The time of an accurate seek after which we have not yet received any actual
+ data at the seek time.
+ */
+ boost::optional<ContentTime> _seek_reference;
};
#endif
--- /dev/null
+/*
+ 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/lib/audio_examiner.h
+ * @brief AudioExaminer class.
+ */
+
+/** @class AudioExaminer
+ * @brief Parent for classes which examine AudioContent for their pertinent details.
+ */
+class AudioExaminer
+{
+public:
+ virtual ~AudioExaminer () {}
+
+ virtual int audio_channels () const = 0;
+ virtual ContentTime audio_length () const = 0;
+ virtual int audio_frame_rate () const = 0;
+};
--- /dev/null
+/*
+ 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 <cmath>
+#include "audio_filter.h"
+#include "audio_buffers.h"
+
+using std::vector;
+using std::min;
+using boost::shared_ptr;
+
+vector<float>
+AudioFilter::sinc_blackman (float cutoff, bool invert) const
+{
+ vector<float> ir (_M + 1);
+
+ /* Impulse response */
+
+ for (int i = 0; i <= _M; ++i) {
+ if (i == (_M / 2)) {
+ ir[i] = 2 * M_PI * cutoff;
+ } else {
+ /* sinc */
+ ir[i] = sin (2 * M_PI * cutoff * (i - _M / 2)) / (i - _M / 2);
+ /* Blackman window */
+ ir[i] *= (0.42 - 0.5 * cos (2 * M_PI * i / _M) + 0.08 * cos (4 * M_PI * i / _M));
+ }
+ }
+
+ /* Normalise */
+
+ float sum = 0;
+ for (int i = 0; i <= _M; ++i) {
+ sum += ir[i];
+ }
+
+ for (int i = 0; i <= _M; ++i) {
+ ir[i] /= sum;
+ }
+
+ /* Frequency inversion (swapping low-pass for high-pass, or whatever) */
+
+ if (invert) {
+ for (int i = 0; i <= _M; ++i) {
+ ir[i] = -ir[i];
+ }
+ ir[_M / 2] += 1;
+ }
+
+ return ir;
+}
+
+shared_ptr<AudioBuffers>
+AudioFilter::run (shared_ptr<AudioBuffers> in)
+{
+ shared_ptr<AudioBuffers> out (new AudioBuffers (in->channels(), in->frames()));
+
+ if (!_tail) {
+ _tail.reset (new AudioBuffers (in->channels(), _M + 1));
+ _tail->make_silent ();
+ }
+
+ for (int i = 0; i < in->channels(); ++i) {
+ for (int j = 0; j < in->frames(); ++j) {
+ float s = 0;
+ for (int k = 0; k <= _M; ++k) {
+ if ((j - k) < 0) {
+ s += _tail->data(i)[j - k + _M + 1] * _ir[k];
+ } else {
+ s += in->data(i)[j - k] * _ir[k];
+ }
+ }
+
+ out->data(i)[j] = s;
+ }
+ }
+
+ int const amount = min (in->frames(), _tail->frames());
+ if (amount < _tail->frames ()) {
+ _tail->move (amount, 0, _tail->frames() - amount);
+ }
+ _tail->copy_from (in.get(), amount, in->frames() - amount, _tail->frames () - amount);
+
+ return out;
+}
+
+void
+AudioFilter::flush ()
+{
+ _tail.reset ();
+}
+
+LowPassAudioFilter::LowPassAudioFilter (float transition_bandwidth, float cutoff)
+ : AudioFilter (transition_bandwidth)
+{
+ _ir = sinc_blackman (cutoff, false);
+}
+
+
+HighPassAudioFilter::HighPassAudioFilter (float transition_bandwidth, float cutoff)
+ : AudioFilter (transition_bandwidth)
+{
+ _ir = sinc_blackman (cutoff, true);
+}
+
+BandPassAudioFilter::BandPassAudioFilter (float transition_bandwidth, float lower, float higher)
+ : AudioFilter (transition_bandwidth)
+{
+ vector<float> lpf = sinc_blackman (lower, false);
+ vector<float> hpf = sinc_blackman (higher, true);
+
+ _ir.resize (_M + 1);
+ for (int i = 0; i <= _M; ++i) {
+ _ir[i] = lpf[i] + hpf[i];
+ }
+
+ /* We now have a band-stop, so invert for band-pass */
+ for (int i = 0; i <= _M; ++i) {
+ _ir[i] = -_ir[i];
+ }
+
+ _ir[_M / 2] += 1;
+}
--- /dev/null
+/*
+ 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 <vector>
+#include <boost/shared_ptr.hpp>
+
+class AudioBuffers;
+class audio_filter_impulse_kernel_test;
+struct audio_filter_impulse_input_test;
+
+class AudioFilter
+{
+public:
+ AudioFilter (float transition_bandwidth)
+ {
+ _M = 4 / transition_bandwidth;
+ if (_M % 2) {
+ ++_M;
+ }
+ }
+
+ boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<AudioBuffers> in);
+
+ void flush ();
+
+protected:
+ friend struct audio_filter_impulse_kernel_test;
+ friend struct audio_filter_impulse_input_test;
+
+ std::vector<float> sinc_blackman (float cutoff, bool invert) const;
+
+ std::vector<float> _ir;
+ int _M;
+ boost::shared_ptr<AudioBuffers> _tail;
+};
+
+class LowPassAudioFilter : public AudioFilter
+{
+public:
+ /** Construct a windowed-sinc low-pass filter using the Blackman window.
+ * @param transition_bandwidth Transition bandwidth as a fraction of the sampling rate.
+ * @param cutoff Cutoff frequency as a fraction of the sampling rate.
+ */
+ LowPassAudioFilter (float transition_bandwidth, float cutoff);
+};
+
+class HighPassAudioFilter : public AudioFilter
+{
+public:
+ /** Construct a windowed-sinc high-pass filter using the Blackman window.
+ * @param transition_bandwidth Transition bandwidth as a fraction of the sampling rate.
+ * @param cutoff Cutoff frequency as a fraction of the sampling rate.
+ */
+ HighPassAudioFilter (float transition_bandwidth, float cutoff);
+};
+
+class BandPassAudioFilter : public AudioFilter
+{
+public:
+ /** Construct a windowed-sinc band-pass filter using the Blackman window.
+ * @param transition_bandwidth Transition bandwidth as a fraction of the sampling rate.
+ * @param lower Lower cutoff frequency as a fraction of the sampling rate.
+ * @param higher Higher cutoff frequency as a fraction of the sampling rate.
+ */
+ BandPassAudioFilter (float transition_bandwidth, float lower, float higher);
+};
/*
- 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 <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "audio_mapping.h"
#include "util.h"
#include "md5_digester.h"
using std::min;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
AudioMapping::AudioMapping ()
: _content_channels (0)
}
-/** Create a default AudioMapping for a given channel count.
- * @param c Number of channels.
+/** Create an empty AudioMapping for a given channel count.
+ * @param channels Number of channels.
*/
-AudioMapping::AudioMapping (int c)
+AudioMapping::AudioMapping (int channels)
{
- setup (c);
+ setup (channels);
}
void
if (_content_channels == 1) {
/* Mono -> Centre */
- set (0, libdcp::CENTRE, 1);
+ set (0, dcp::CENTRE, 1);
} else {
/* 1:1 mapping */
for (int i = 0; i < min (_content_channels, MAX_DCP_AUDIO_CHANNELS); ++i) {
- set (i, static_cast<libdcp::Channel> (i), 1);
+ set (i, static_cast<dcp::Channel> (i), 1);
}
}
}
-AudioMapping::AudioMapping (shared_ptr<const cxml::Node> node, int state_version)
+AudioMapping::AudioMapping (cxml::ConstNodePtr node, int state_version)
{
setup (node->number_child<int> ("ContentChannels"));
/* Old-style: on/off mapping */
list<cxml::NodePtr> const c = node->node_children ("Map");
for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
- set ((*i)->number_child<int> ("ContentIndex"), static_cast<libdcp::Channel> ((*i)->number_child<int> ("DCP")), 1);
+ set ((*i)->number_child<int> ("ContentIndex"), static_cast<dcp::Channel> ((*i)->number_child<int> ("DCP")), 1);
}
} else {
list<cxml::NodePtr> const c = node->node_children ("Gain");
for (list<cxml::NodePtr>::const_iterator i = c.begin(); i != c.end(); ++i) {
set (
(*i)->number_attribute<int> ("Content"),
- static_cast<libdcp::Channel> ((*i)->number_attribute<int> ("DCP")),
+ static_cast<dcp::Channel> ((*i)->number_attribute<int> ("DCP")),
raw_convert<float> ((*i)->content ())
);
}
}
void
-AudioMapping::set (int c, libdcp::Channel d, float g)
+AudioMapping::set (int c, dcp::Channel d, float g)
{
_gain[c][d] = g;
}
float
-AudioMapping::get (int c, libdcp::Channel d) const
+AudioMapping::get (int c, dcp::Channel d) const
{
return _gain[c][d];
}
xmlpp::Element* t = node->add_child ("Gain");
t->set_attribute ("Content", raw_convert<string> (c));
t->set_attribute ("DCP", raw_convert<string> (d));
- t->add_child_text (raw_convert<string> (get (c, static_cast<libdcp::Channel> (d))));
+ t->add_child_text (raw_convert<string> (get (c, static_cast<dcp::Channel> (d))));
}
}
}
/*
- 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/lib/audio_mapping.h
+ * @brief AudioMapping class.
+ */
+
#ifndef DCPOMATIC_AUDIO_MAPPING_H
#define DCPOMATIC_AUDIO_MAPPING_H
#include <vector>
-#include <libdcp/types.h>
#include <boost/shared_ptr.hpp>
+#include <dcp/types.h>
+#include <libcxml/cxml.h>
namespace xmlpp {
class Node;
class Node;
}
-/** A many-to-many mapping from some content channels to DCP channels.
+/** @class AudioMapping.
+ * @brief A many-to-many mapping from some content channels to DCP channels.
+ *
* The number of content channels is set on construction and fixed,
* and then each of those content channels are mapped to each DCP channel
* by a linear gain.
{
public:
AudioMapping ();
- AudioMapping (int);
- AudioMapping (boost::shared_ptr<const cxml::Node>, int);
+ AudioMapping (int channels);
+ AudioMapping (cxml::ConstNodePtr, int);
/* Default copy constructor is fine */
void make_default ();
- void set (int, libdcp::Channel, float);
- float get (int, libdcp::Channel) const;
+ void set (int, dcp::Channel, float);
+ float get (int, dcp::Channel) const;
int content_channels () const {
return _content_channels;
+++ /dev/null
-/*
- 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 "audio_buffers.h"
-#include "util.h"
-
-template <class T, class F>
-class AudioMerger
-{
-public:
- AudioMerger (int channels, boost::function<F (T)> t_to_f, boost::function<T (F)> f_to_t)
- : _buffers (new AudioBuffers (channels, 0))
- , _last_pull (0)
- , _t_to_f (t_to_f)
- , _f_to_t (f_to_t)
- {}
-
- /** Pull audio up to a given time; after this call, no more data can be pushed
- * before the specified time.
- */
- TimedAudioBuffers<T>
- pull (T time)
- {
- TimedAudioBuffers<T> out;
-
- F const to_return = _t_to_f (time - _last_pull);
- out.audio.reset (new AudioBuffers (_buffers->channels(), to_return));
- /* And this is how many we will get from our buffer */
- F const to_return_from_buffers = min (to_return, _buffers->frames ());
-
- /* Copy the data that we have to the back end of the return buffer */
- out.audio->copy_from (_buffers.get(), to_return_from_buffers, 0, to_return - to_return_from_buffers);
- /* Silence any gap at the start */
- out.audio->make_silent (0, to_return - to_return_from_buffers);
-
- out.time = _last_pull;
- _last_pull = time;
-
- /* And remove the data we're returning from our buffers */
- if (_buffers->frames() > to_return_from_buffers) {
- _buffers->move (to_return_from_buffers, 0, _buffers->frames() - to_return_from_buffers);
- }
- _buffers->set_frames (_buffers->frames() - to_return_from_buffers);
-
- return out;
- }
-
- void
- push (boost::shared_ptr<const AudioBuffers> audio, T time)
- {
- assert (time >= _last_pull);
-
- F frame = _t_to_f (time);
- F after = max (_buffers->frames(), frame + audio->frames() - _t_to_f (_last_pull));
- _buffers->ensure_size (after);
- _buffers->accumulate_frames (audio.get(), 0, frame - _t_to_f (_last_pull), audio->frames ());
- _buffers->set_frames (after);
- }
-
- F min (F a, int b)
- {
- if (a < b) {
- return a;
- }
-
- return b;
- }
-
- F max (int a, F b)
- {
- if (a > b) {
- return a;
- }
-
- return b;
- }
-
- TimedAudioBuffers<T>
- flush ()
- {
- if (_buffers->frames() == 0) {
- return TimedAudioBuffers<T> ();
- }
-
- return TimedAudioBuffers<T> (_buffers, _last_pull);
- }
-
-private:
- boost::shared_ptr<AudioBuffers> _buffers;
- T _last_pull;
- boost::function<F (T)> _t_to_f;
- boost::function<T (F)> _f_to_t;
-};
--- /dev/null
+/*
+ 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 "audio_processor.h"
+#include "mid_side_decoder.h"
+#include "upmixer_a.h"
+
+using std::string;
+using std::list;
+
+list<AudioProcessor const *> AudioProcessor::_all;
+
+void
+AudioProcessor::setup_audio_processors ()
+{
+ _all.push_back (new MidSideDecoder ());
+ _all.push_back (new UpmixerA (48000));
+}
+
+AudioProcessor const *
+AudioProcessor::from_id (string id)
+{
+ for (list<AudioProcessor const *>::const_iterator i = _all.begin(); i != _all.end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+
+ return 0;
+}
+
+list<AudioProcessor const *>
+AudioProcessor::all ()
+{
+ return _all;
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_AUDIO_PROCESSOR_H
+#define DCPOMATIC_AUDIO_PROCESSOR_H
+
+#include <list>
+#include <string>
+#include <boost/shared_ptr.hpp>
+#include "channel_count.h"
+
+class AudioBuffers;
+
+class AudioProcessor
+{
+public:
+ virtual ~AudioProcessor () {}
+
+ virtual std::string name () const = 0;
+ virtual std::string id () const = 0;
+ virtual ChannelCount in_channels () const = 0;
+ virtual int out_channels (int) const = 0;
+ virtual boost::shared_ptr<AudioProcessor> clone (int sampling_rate) const = 0;
+ virtual boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>) = 0;
+ virtual void flush () {}
+
+ static std::list<AudioProcessor const *> all ();
+ static void setup_audio_processors ();
+ static AudioProcessor const * from_id (std::string);
+
+private:
+ static std::list<AudioProcessor const *> _all;
+};
+
+#endif
--- /dev/null
+/*
+ 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 DCPOMATIC_CHANNEL_COUNT_H
+#define DCPOMATIC_CHANNEL_COUNT_H
+
+class ChannelCount
+{
+public:
+ ChannelCount ()
+ : min (0)
+ , max (0)
+ {}
+
+ ChannelCount (int n)
+ : min (n)
+ , max (n)
+ {}
+
+ ChannelCount (int min_, int max_)
+ : min (min_)
+ , max (max_)
+ {}
+
+ bool includes (int c) {
+ return min <= c && c <= max;
+ }
+
+ int min;
+ int max;
+};
+
+#endif
using std::list;
using boost::shared_ptr;
-Cinema::Cinema (shared_ptr<const cxml::Node> node)
+Cinema::Cinema (cxml::ConstNodePtr node)
: name (node->string_child ("Name"))
, email (node->string_child ("Email"))
{
a constructor)
*/
void
-Cinema::read_screens (shared_ptr<const cxml::Node> node)
+Cinema::read_screens (cxml::ConstNodePtr node)
{
list<cxml::NodePtr> s = node->node_children ("Screen");
for (list<cxml::NodePtr>::iterator i = s.begin(); i != s.end(); ++i) {
_screens.remove (s);
}
-Screen::Screen (shared_ptr<const cxml::Node> node)
+Screen::Screen (cxml::ConstNodePtr node)
{
name = node->string_child ("Name");
- certificate = shared_ptr<libdcp::Certificate> (new libdcp::Certificate (node->string_child ("Certificate")));
+ if (node->optional_string_child ("Certificate")) {
+ certificate = dcp::Certificate (node->string_child ("Certificate"));
+ }
}
void
Screen::as_xml (xmlpp::Element* parent) const
{
parent->add_child("Name")->add_child_text (name);
- parent->add_child("Certificate")->add_child_text (certificate->certificate (true));
+ if (certificate) {
+ parent->add_child("Certificate")->add_child_text (certificate->certificate (true));
+ }
}
/*
- 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/lib/cinema.h
+ * @brief Screen and Cinema classes.
+ */
+
#include <boost/enable_shared_from_this.hpp>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
+#include <libcxml/cxml.h>
class Cinema;
-namespace cxml {
- class Node;
-}
-
+/** @class Screen
+ * @brief A representation of a Screen for KDM generation.
+ *
+ * This is the name of the screen and the certificate of its
+ * server.
+ */
class Screen
{
public:
- Screen (std::string const & n, boost::shared_ptr<libdcp::Certificate> cert)
+ Screen (std::string const & n, boost::optional<dcp::Certificate> cert)
: name (n)
, certificate (cert)
{}
- Screen (boost::shared_ptr<const cxml::Node>);
+ Screen (cxml::ConstNodePtr);
void as_xml (xmlpp::Element *) const;
boost::shared_ptr<Cinema> cinema;
std::string name;
- boost::shared_ptr<libdcp::Certificate> certificate;
+ boost::optional<dcp::Certificate> certificate;
};
+/** @class Cinema
+ * @brief A description of a Cinema for KDM generation.
+ *
+ * This is a cinema name, contact email address and a list of
+ * Screen objects.
+ */
class Cinema : public boost::enable_shared_from_this<Cinema>
{
public:
, email (e)
{}
- Cinema (boost::shared_ptr<const cxml::Node>);
+ Cinema (cxml::ConstNodePtr);
- void read_screens (boost::shared_ptr<const cxml::Node>);
+ void read_screens (cxml::ConstNodePtr);
void as_xml (xmlpp::Element *) const;
--- /dev/null
+/*
+ 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_processor.cc
+ * @brief CinemaSoundProcessor class.
+ */
+
+#include <iostream>
+#include <cassert>
+#include "cinema_sound_processor.h"
+#include "dolby_cp750.h"
+
+using namespace std;
+
+vector<CinemaSoundProcessor const *> CinemaSoundProcessor::_cinema_sound_processors;
+
+/** @param i Our id.
+ * @param n User-visible name.
+ */
+CinemaSoundProcessor::CinemaSoundProcessor (string i, string n)
+ : _id (i)
+ , _name (n)
+{
+
+}
+
+/** @return All available sound processors */
+vector<CinemaSoundProcessor const *>
+CinemaSoundProcessor::all ()
+{
+ return _cinema_sound_processors;
+}
+
+/** Set up the static _sound_processors vector; must be called before from_*
+ * methods are used.
+ */
+void
+CinemaSoundProcessor::setup_cinema_sound_processors ()
+{
+ _cinema_sound_processors.push_back (new DolbyCP750);
+}
+
+/** @param id One of our ids.
+ * @return Corresponding sound processor, or 0.
+ */
+CinemaSoundProcessor const *
+CinemaSoundProcessor::from_id (string id)
+{
+ vector<CinemaSoundProcessor const *>::iterator i = _cinema_sound_processors.begin ();
+ while (i != _cinema_sound_processors.end() && (*i)->id() != id) {
+ ++i;
+ }
+
+ if (i == _cinema_sound_processors.end ()) {
+ return 0;
+ }
+
+ return *i;
+}
+
+/** @param s A sound processor from our static list.
+ * @return Index of the sound processor with the list, or -1.
+ */
+int
+CinemaSoundProcessor::as_index (CinemaSoundProcessor const * s)
+{
+ vector<CinemaSoundProcessor*>::size_type i = 0;
+ while (i < _cinema_sound_processors.size() && _cinema_sound_processors[i] != s) {
+ ++i;
+ }
+
+ if (i == _cinema_sound_processors.size ()) {
+ return -1;
+ }
+
+ return i;
+}
+
+/** @param i An index returned from as_index().
+ * @return Corresponding sound processor.
+ */
+CinemaSoundProcessor const *
+CinemaSoundProcessor::from_index (int i)
+{
+ assert (i <= int(_cinema_sound_processors.size ()));
+ return _cinema_sound_processors[i];
+}
--- /dev/null
+/*
+ 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/cinema_sound_processor.h
+ * @brief CinemaSoundProcessor class
+ */
+
+#ifndef DCPOMATIC_CINEMA_SOUND_PROCESSOR_H
+#define DCPOMATIC_CINEMA_SOUND_PROCESSOR_H
+
+#include <string>
+#include <vector>
+#include <boost/utility.hpp>
+
+/** @class CinemaSoundProcessor
+ * @brief Class to describe a cimema's sound processor.
+ *
+ * In other words, the box in the rack that handles sound decoding and processing
+ * in a cinema.
+ */
+class CinemaSoundProcessor : public boost::noncopyable
+{
+public:
+ CinemaSoundProcessor (std::string i, std::string n);
+
+ virtual float db_for_fader_change (float from, float to) const = 0;
+
+ /** @return id for our use */
+ std::string id () const {
+ return _id;
+ }
+
+ /** @return user-visible name for this sound processor */
+ std::string name () const {
+ return _name;
+ }
+
+ static std::vector<CinemaSoundProcessor const *> all ();
+ static void setup_cinema_sound_processors ();
+ static CinemaSoundProcessor const * from_id (std::string id);
+ static CinemaSoundProcessor const * from_index (int);
+ static int as_index (CinemaSoundProcessor const *);
+
+private:
+ /** id for our use */
+ std::string _id;
+ /** user-visible name for this sound processor */
+ std::string _name;
+
+ /** sll available cinema sound processors */
+ static std::vector<CinemaSoundProcessor const *> _cinema_sound_processors;
+};
+
+#endif
*/
#include <libxml++/libxml++.h>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
#include <libcxml/cxml.h>
#include "config.h"
#include "colour_conversion.h"
using std::vector;
using boost::shared_ptr;
using boost::optional;
-using libdcp::raw_convert;
+using dcp::raw_convert;
ColourConversion::ColourConversion ()
: input_gamma (2.4)
{
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
- matrix (i, j) = libdcp::colour_matrix::srgb_to_xyz[i][j];
+ matrix (i, j) = dcp::colour_matrix::srgb_to_xyz[i][j];
}
}
}
#include <glib.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
+#include <dcp/signer.h>
+#include <dcp/certificate_chain.h>
#include <libcxml/cxml.h>
#include "config.h"
#include "server.h"
#include "filter.h"
#include "ratio.h"
#include "dcp_content_type.h"
-#include "sound_processor.h"
+#include "cinema_sound_processor.h"
#include "colour_conversion.h"
#include "cinema.h"
#include "util.h"
+#include "cross.h"
#include "i18n.h"
using boost::optional;
using boost::algorithm::is_any_of;
using boost::algorithm::split;
-using libdcp::raw_convert;
+using dcp::raw_convert;
Config* Config::_instance = 0;
, _server_port_base (6192)
, _use_any_servers (true)
, _tms_path (".")
- , _sound_processor (SoundProcessor::from_id (N_("dolby_cp750")))
+ , _cinema_sound_processor (CinemaSoundProcessor::from_id (N_("dolby_cp750")))
, _allow_any_dcp_frame_rate (false)
, _default_still_length (10)
, _default_scale (VideoContentScale (Ratio::from_id ("185")))
, _check_for_test_updates (false)
, _maximum_j2k_bandwidth (250000000)
, _log_types (Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR)
+#ifdef DCPOMATIC_WINDOWS
+ , _win32_console (false)
+#endif
{
_allowed_dcp_frame_rates.push_back (24);
_allowed_dcp_frame_rates.push_back (25);
_allowed_dcp_frame_rates.push_back (50);
_allowed_dcp_frame_rates.push_back (60);
- _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6));
- _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, libdcp::colour_matrix::srgb_to_xyz, 2.6));
- _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, libdcp::colour_matrix::rec709_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, dcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, dcp::colour_matrix::rec709_to_xyz, 2.6));
reset_kdm_email ();
}
void
Config::read ()
{
- if (!boost::filesystem::exists (file (false))) {
- read_old_metadata ();
+ if (!boost::filesystem::exists (file ())) {
+ /* Make a new set of signing certificates and key */
+ _signer.reset (new dcp::Signer (openssl_path ()));
+ /* And decryption keys */
+ make_decryption_keys ();
return;
}
cxml::Document f ("Config");
- f.read_file (file (false));
+ f.read_file (file ());
optional<string> c;
optional<int> version = f.optional_number_child<int> ("Version");
c = f.optional_string_child ("SoundProcessor");
if (c) {
- _sound_processor = SoundProcessor::from_id (c.get ());
+ _cinema_sound_processor = CinemaSoundProcessor::from_id (c.get ());
+ }
+ c = f.optional_string_child ("CinemaSoundProcessor");
+ if (c) {
+ _cinema_sound_processor = CinemaSoundProcessor::from_id (c.get ());
}
_language = f.optional_string_child ("Language");
/* Loading version 0 (before Rec. 709 was added as a preset).
Add it in.
*/
- _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, libdcp::colour_matrix::rec709_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, dcp::colour_matrix::rec709_to_xyz, 2.6));
}
list<cxml::NodePtr> cin = f.node_children ("Cinema");
_allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate");
_log_types = f.optional_number_child<int> ("LogTypes").get_value_or (Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR);
+#ifdef DCPOMATIC_WINDOWS
+ _win32_console = f.optional_bool_child ("Win32Console").get_value_or (false);
+#endif
list<cxml::NodePtr> his = f.node_children ("History");
for (list<cxml::NodePtr>::const_iterator i = his.begin(); i != his.end(); ++i) {
_history.push_back ((*i)->content ());
}
-}
-
-void
-Config::read_old_metadata ()
-{
- /* XXX: this won't work with non-Latin filenames */
- ifstream f (file(true).string().c_str ());
- string line;
- while (getline (f, line)) {
- if (line.empty ()) {
- continue;
+ cxml::NodePtr signer = f.optional_node_child ("Signer");
+ dcp::CertificateChain signer_chain;
+ if (signer) {
+ /* Read the signing certificates and private key in from the config file */
+ list<cxml::NodePtr> certificates = signer->node_children ("Certificate");
+ for (list<cxml::NodePtr>::const_iterator i = certificates.begin(); i != certificates.end(); ++i) {
+ signer_chain.add (dcp::Certificate ((*i)->content ()));
}
- if (line[0] == '#') {
- continue;
- }
+ _signer.reset (new dcp::Signer (signer_chain, signer->string_child ("PrivateKey")));
+ } else {
+ /* Make a new set of signing certificates and key */
+ _signer.reset (new dcp::Signer (openssl_path ()));
+ }
- size_t const s = line.find (' ');
- if (s == string::npos) {
- continue;
- }
-
- string const k = line.substr (0, s);
- string const v = line.substr (s + 1);
-
- if (k == N_("num_local_encoding_threads")) {
- _num_local_encoding_threads = atoi (v.c_str ());
- } else if (k == N_("default_directory")) {
- _default_directory = v;
- } else if (k == N_("server_port")) {
- _server_port_base = atoi (v.c_str ());
- } else if (k == N_("server")) {
- vector<string> b;
- split (b, v, is_any_of (" "));
- if (b.size() == 2) {
- _servers.push_back (b[0]);
- }
- } else if (k == N_("tms_ip")) {
- _tms_ip = v;
- } else if (k == N_("tms_path")) {
- _tms_path = v;
- } else if (k == N_("tms_user")) {
- _tms_user = v;
- } else if (k == N_("tms_password")) {
- _tms_password = v;
- } else if (k == N_("sound_processor")) {
- _sound_processor = SoundProcessor::from_id (v);
- } else if (k == "language") {
- _language = v;
- } else if (k == "default_container") {
- _default_container = Ratio::from_id (v);
- } else if (k == "default_dcp_content_type") {
- _default_dcp_content_type = DCPContentType::from_isdcf_name (v);
- } else if (k == "dcp_metadata_issuer") {
- _dcp_issuer = v;
- }
+ if (f.optional_string_child ("DecryptionCertificate")) {
+ _decryption_certificate = dcp::Certificate (f.string_child ("DecryptionCertificate"));
+ }
- _default_isdcf_metadata.read_old_metadata (k, v);
+ if (f.optional_string_child ("DecryptionPrivateKey")) {
+ _decryption_private_key = f.string_child ("DecryptionPrivateKey");
+ }
+
+ if (!f.optional_string_child ("DecryptionCertificate") || !f.optional_string_child ("DecryptionPrivateKey")) {
+ /* Generate our own decryption certificate and key if either is not present in config */
+ make_decryption_keys ();
}
}
-/** @return Filename to write configuration to */
-boost::filesystem::path
-Config::file (bool old) const
+void
+Config::make_decryption_keys ()
{
- boost::filesystem::path p;
- p /= g_get_user_config_dir ();
- boost::system::error_code ec;
- boost::filesystem::create_directory (p, ec);
- if (old) {
- p /= ".dvdomatic";
- } else {
- p /= "dcpomatic";
- boost::filesystem::create_directory (p, ec);
- p /= "config.xml";
- }
- return p;
+ boost::filesystem::path p = dcp::make_certificate_chain (openssl_path ());
+ _decryption_certificate = dcp::Certificate (dcp::file_to_string (p / "leaf.signed.pem"));
+ _decryption_private_key = dcp::file_to_string (p / "leaf.key");
+ boost::filesystem::remove_all (p);
}
+/** @return Filename to write configuration to */
boost::filesystem::path
-Config::signer_chain_directory () const
+Config::file () const
{
boost::filesystem::path p;
p /= g_get_user_config_dir ();
- p /= "dcpomatic";
- p /= "crypt";
- boost::filesystem::create_directories (p);
+ boost::system::error_code ec;
+ boost::filesystem::create_directory (p, ec);
+ p /= "dcpomatic2";
+ boost::filesystem::create_directory (p, ec);
+ p /= "config.xml";
return p;
}
root->add_child("TMSPath")->add_child_text (_tms_path);
root->add_child("TMSUser")->add_child_text (_tms_user);
root->add_child("TMSPassword")->add_child_text (_tms_password);
- if (_sound_processor) {
- root->add_child("SoundProcessor")->add_child_text (_sound_processor->id ());
+ if (_cinema_sound_processor) {
+ root->add_child("CinemaSoundProcessor")->add_child_text (_cinema_sound_processor->id ());
}
if (_language) {
root->add_child("Language")->add_child_text (_language.get());
root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
+#ifdef DCPOMATIC_WINDOWS
+ root->add_child("Win32Console")->add_child_text (_win32_console ? "1" : "0");
+#endif
+
+ xmlpp::Element* signer = root->add_child ("Signer");
+ dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
+ for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
+ signer->add_child("Certificate")->add_child_text (i->certificate (true));
+ }
+ signer->add_child("PrivateKey")->add_child_text (_signer->key ());
+
+ root->add_child("DecryptionCertificate")->add_child_text (_decryption_certificate.certificate (true));
+ root->add_child("DecryptionPrivateKey")->add_child_text (_decryption_private_key);
for (vector<boost::filesystem::path>::const_iterator i = _history.begin(); i != _history.end(); ++i) {
root->add_child("History")->add_child_text (i->string ());
}
- doc.write_to_file_formatted (file(false).string ());
+ doc.write_to_file_formatted (file().string ());
}
boost::filesystem::path
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/filesystem.hpp>
+#include <dcp/metadata.h>
+#include <dcp/certificates.h>
+#include <dcp/signer.h>
#include "isdcf_metadata.h"
#include "colour_conversion.h"
-#include "server.h"
#include "video_content.h"
class ServerDescription;
class Scaler;
class Filter;
-class SoundProcessor;
+class CinemaSoundProcessor;
class DCPContentType;
class Ratio;
class Cinema;
return _tms_password;
}
- /** @return The sound processor that we are using */
- SoundProcessor const * sound_processor () const {
- return _sound_processor;
+ /** @return The cinema sound processor that we are using */
+ CinemaSoundProcessor const * cinema_sound_processor () const {
+ return _cinema_sound_processor;
}
std::list<boost::shared_ptr<Cinema> > cinemas () const {
return _kdm_email;
}
+ boost::shared_ptr<const dcp::Signer> signer () const {
+ return _signer;
+ }
+
+ dcp::Certificate decryption_certificate () const {
+ return _decryption_certificate;
+ }
+
+ std::string decryption_private_key () const {
+ return _decryption_private_key;
+ }
+
bool check_for_updates () const {
return _check_for_updates;
}
return _log_types;
}
+#ifdef DCPOMATIC_WINDOWS
+ bool win32_console () const {
+ return _win32_console;
+ }
+#endif
+
std::vector<boost::filesystem::path> history () const {
return _history;
}
void reset_kdm_email ();
+ void set_signer (boost::shared_ptr<const dcp::Signer> s) {
+ _signer = s;
+ changed ();
+ }
+
+ void set_decryption_certificate (dcp::Certificate c) {
+ _decryption_certificate = c;
+ changed ();
+ }
+
+ void set_decryption_private_key (std::string k) {
+ _decryption_private_key = k;
+ changed ();
+ }
+
void set_check_for_updates (bool c) {
_check_for_updates = c;
changed ();
changed ();
}
+#ifdef DCPOMATIC_WINDOWS
+ void set_win32_console (bool c) {
+ _win32_console = c;
+ changed ();
+ }
+#endif
+
void clear_history () {
_history.clear ();
changed ();
void add_to_history (boost::filesystem::path p);
- boost::filesystem::path signer_chain_directory () const;
-
void changed ();
boost::signals2::signal<void ()> Changed;
private:
Config ();
- boost::filesystem::path file (bool) const;
+ boost::filesystem::path file () const;
void read ();
- void read_old_metadata ();
void write () const;
+ void make_decryption_keys ();
/** number of threads to use for J2K encoding on the local machine */
int _num_local_encoding_threads;
std::string _tms_user;
/** Password to log into the TMS with */
std::string _tms_password;
- /** Our sound processor */
- SoundProcessor const * _sound_processor;
+ /** Our cinema sound processor */
+ CinemaSoundProcessor const * _cinema_sound_processor;
std::list<int> _allowed_dcp_frame_rates;
/** Allow any video frame rate for the DCP; if true, overrides _allowed_dcp_frame_rates */
bool _allow_any_dcp_frame_rate;
std::string _kdm_cc;
std::string _kdm_bcc;
std::string _kdm_email;
+ boost::shared_ptr<const dcp::Signer> _signer;
+ dcp::Certificate _decryption_certificate;
+ std::string _decryption_private_key;
/** true to check for updates on startup */
bool _check_for_updates;
bool _check_for_test_updates;
/** maximum allowed J2K bandwidth in bits per second */
int _maximum_j2k_bandwidth;
int _log_types;
+#ifdef DCPOMATIC_WINDOWS
+ bool _win32_console;
+#endif
std::vector<boost::filesystem::path> _history;
/** Singleton instance, or 0 */
/*
- 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/lib/content.cc
+ * @brief Content class.
+ */
+
#include <boost/thread/mutex.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "content.h"
#include "util.h"
#include "content_factory.h"
using std::vector;
using std::max;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
int const ContentProperty::PATH = 400;
int const ContentProperty::POSITION = 401;
}
-Content::Content (shared_ptr<const Film> f, Time p)
+Content::Content (shared_ptr<const Film> f, DCPTime p)
: _film (f)
, _position (p)
, _trim_start (0)
_paths.push_back (p);
}
-Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
+Content::Content (shared_ptr<const Film> f, cxml::ConstNodePtr node)
: _film (f)
, _change_signals_frequent (false)
{
_paths.push_back ((*i)->content ());
}
_digest = node->string_child ("Digest");
- _position = node->number_child<Time> ("Position");
- _trim_start = node->number_child<Time> ("TrimStart");
- _trim_end = node->number_child<Time> ("TrimEnd");
+ _position = DCPTime (node->number_child<double> ("Position"));
+ _trim_start = DCPTime (node->number_child<double> ("TrimStart"));
+ _trim_end = DCPTime (node->number_child<double> ("TrimEnd"));
}
Content::Content (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
, _change_signals_frequent (false)
{
for (size_t i = 0; i < c.size(); ++i) {
- if (i > 0 && c[i]->trim_start ()) {
+ if (i > 0 && c[i]->trim_start() > DCPTime()) {
throw JoinError (_("Only the first piece of content to be joined can have a start trim."));
}
- if (i < (c.size() - 1) && c[i]->trim_end ()) {
+ if (i < (c.size() - 1) && c[i]->trim_end () > DCPTime()) {
throw JoinError (_("Only the last piece of content to be joined can have an end trim."));
}
node->add_child("Path")->add_child_text (i->string ());
}
node->add_child("Digest")->add_child_text (_digest);
- node->add_child("Position")->add_child_text (raw_convert<string> (_position));
- node->add_child("TrimStart")->add_child_text (raw_convert<string> (_trim_start));
- node->add_child("TrimEnd")->add_child_text (raw_convert<string> (_trim_end));
+ node->add_child("Position")->add_child_text (raw_convert<string> (_position.get ()));
+ node->add_child("TrimStart")->add_child_text (raw_convert<string> (_trim_start.get ()));
+ node->add_child("TrimEnd")->add_child_text (raw_convert<string> (_trim_end.get ()));
}
void
}
void
-Content::set_position (Time p)
+Content::set_position (DCPTime p)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-Content::set_trim_start (Time t)
+Content::set_trim_start (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
}
void
-Content::set_trim_end (Time t)
+Content::set_trim_end (DCPTime t)
{
{
boost::mutex::scoped_lock lm (_mutex);
string
Content::technical_summary () const
{
- return String::compose ("%1 %2 %3", path_summary(), digest(), position());
+ return String::compose ("%1 %2 %3", path_summary(), digest(), position().seconds());
}
-Time
+DCPTime
Content::length_after_trim () const
{
- return max (int64_t (0), full_length() - trim_start() - trim_end());
-}
-
-/** @param t A time relative to the start of this content (not the position).
- * @return true if this time is trimmed by our trim settings.
- */
-bool
-Content::trimmed (Time t) const
-{
- return (t < trim_start() || t > (full_length() - trim_end ()));
+ return max (DCPTime (), full_length() - trim_start() - trim_end());
}
/** @return string which includes everything about how this content affects
SafeStringStream s;
s << Content::digest()
- << "_" << position()
- << "_" << trim_start()
- << "_" << trim_end();
+ << "_" << position().get()
+ << "_" << trim_start().get()
+ << "_" << trim_end().get();
return s.str ();
}
/*
- 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/lib/content.h
+ * @brief Content class.
+ */
+
#ifndef DCPOMATIC_CONTENT_H
#define DCPOMATIC_CONTENT_H
#include <boost/thread/mutex.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <libxml++/libxml++.h>
+#include <libcxml/cxml.h>
#include "types.h"
+#include "dcpomatic_time.h"
namespace cxml {
class Node;
static int const TRIM_END;
};
+/** @class Content
+ * @brief A piece of content represented by one or more files on disk.
+ */
class Content : public boost::enable_shared_from_this<Content>, public boost::noncopyable
{
public:
Content (boost::shared_ptr<const Film>);
- Content (boost::shared_ptr<const Film>, Time);
+ Content (boost::shared_ptr<const Film>, DCPTime);
Content (boost::shared_ptr<const Film>, boost::filesystem::path);
- Content (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>);
+ Content (boost::shared_ptr<const Film>, cxml::ConstNodePtr);
Content (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
virtual ~Content () {}
+
+ /** Examine the content to establish digest, frame rates and any other
+ * useful metadata.
+ * @param job Job to use to report progress, or 0.
+ */
+ virtual void examine (boost::shared_ptr<Job> job);
- virtual void examine (boost::shared_ptr<Job>);
+ /** @return Quick one-line summary of the content, as will be presented in the
+ * film editor.
+ */
virtual std::string summary () const = 0;
+
/** @return Technical details of this content; these are written to logs to
* help with debugging.
*/
virtual std::string technical_summary () const;
+
virtual std::string information () const = 0;
virtual void as_xml (xmlpp::Node *) const;
- virtual Time full_length () const = 0;
+ virtual DCPTime full_length () const = 0;
virtual std::string identifier () const;
boost::shared_ptr<Content> clone () const;
return _digest;
}
- void set_position (Time);
+ void set_position (DCPTime);
- /** Time that this content starts; i.e. the time that the first
+ /** DCPTime that this content starts; i.e. the time that the first
* bit of the content (trimmed or not) will happen.
*/
- Time position () const {
+ DCPTime position () const {
boost::mutex::scoped_lock lm (_mutex);
return _position;
}
- void set_trim_start (Time);
+ void set_trim_start (DCPTime);
- Time trim_start () const {
+ DCPTime trim_start () const {
boost::mutex::scoped_lock lm (_mutex);
return _trim_start;
}
- void set_trim_end (Time);
+ void set_trim_end (DCPTime);
- Time trim_end () const {
+ DCPTime trim_end () const {
boost::mutex::scoped_lock lm (_mutex);
return _trim_end;
}
- Time end () const {
- return position() + length_after_trim() - 1;
+ DCPTime end () const {
+ return position() + length_after_trim();
}
- Time length_after_trim () const;
+ DCPTime length_after_trim () const;
void set_change_signals_frequent (bool f) {
_change_signals_frequent = f;
}
- bool trimmed (Time) const;
+ boost::shared_ptr<const Film> film () const {
+ return _film.lock ();
+ }
boost::signals2::signal<void (boost::weak_ptr<Content>, int, bool)> Changed;
boost::weak_ptr<const Film> _film;
/** _mutex which should be used to protect accesses, as examine
- jobs can update content state in threads other than the main one.
- */
+ * jobs can update content state in threads other than the main one.
+ */
mutable boost::mutex _mutex;
/** Paths of our data files */
private:
std::string _digest;
- Time _position;
- Time _trim_start;
- Time _trim_end;
+ DCPTime _position;
+ DCPTime _trim_start;
+ DCPTime _trim_end;
bool _change_signals_frequent;
};
--- /dev/null
+/*
+ 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/lib/content_audio.h
+ * @brief ContentAudio class.
+ */
+
+#include "audio_buffers.h"
+
+/** @class ContentAudio
+ * @brief A block of audio from a piece of content, with a timestamp as a frame within that content.
+ */
+class ContentAudio
+{
+public:
+ ContentAudio ()
+ : audio (new AudioBuffers (0, 0))
+ , frame (0)
+ {}
+
+ ContentAudio (boost::shared_ptr<AudioBuffers> a, AudioFrame f)
+ : audio (a)
+ , frame (f)
+ {}
+
+ boost::shared_ptr<AudioBuffers> audio;
+ AudioFrame frame;
+};
/*
- 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/lib/content_factory.cc
+ * @brief Methods to create content objects.
+ */
+
#include <libcxml/cxml.h>
#include "ffmpeg_content.h"
#include "image_content.h"
#include "sndfile_content.h"
+#include "subrip_content.h"
+#include "dcp_content.h"
+#include "dcp_subtitle_content.h"
#include "util.h"
using std::string;
using std::list;
using boost::shared_ptr;
+/** Create a Content object from an XML node.
+ * @param film Film that the content will be in.
+ * @param node XML description.
+ * @param version XML state version.
+ * @param notes A list to which is added descriptions of any non-critial warnings / messages.
+ * @return Content object, or 0 if no content was recognised in the XML.
+ */
shared_ptr<Content>
content_factory (shared_ptr<const Film> film, cxml::NodePtr node, int version, list<string>& notes)
{
content.reset (new ImageContent (film, node, version));
} else if (type == "Sndfile") {
content.reset (new SndfileContent (film, node, version));
+ } else if (type == "SubRip") {
+ content.reset (new SubRipContent (film, node, version));
+ } else if (type == "DCP") {
+ content.reset (new DCPContent (film, node, version));
+ } else if (type == "DCPSubtitle") {
+ content.reset (new DCPSubtitleContent (film, node, version));
}
return content;
}
+/** Create a Content object from a file, depending on its extension.
+ * @param film Film that the content will be in.
+ * @param path File's path.
+ * @return Content object.
+ */
shared_ptr<Content>
content_factory (shared_ptr<const Film> film, boost::filesystem::path path)
{
shared_ptr<Content> content;
+
+ string ext = path.extension().string ();
+ transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
if (valid_image_file (path)) {
content.reset (new ImageContent (film, path));
} else if (SndfileContent::valid_file (path)) {
content.reset (new SndfileContent (film, path));
+ } else if (ext == ".srt") {
+ content.reset (new SubRipContent (film, path));
+ } else if (ext == ".xml") {
+ content.reset (new DCPSubtitleContent (film, path));
} else {
content.reset (new FFmpegContent (film, path));
}
/*
- 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/lib/content_factory.h
+ * @brief Methods to create content objects.
+ */
+
class Film;
extern boost::shared_ptr<Content> content_factory (boost::shared_ptr<const Film>, cxml::NodePtr, int, std::list<std::string> &);
--- /dev/null
+/*
+ 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 "content_subtitle.h"
+
+ContentTimePeriod
+ContentTextSubtitle::period () const
+{
+ /* XXX: assuming we have some subs and they are all at the same time */
+ assert (!subs.empty ());
+ return ContentTimePeriod (
+ ContentTime::from_seconds (double (subs.front().in().to_ticks()) / 250),
+ ContentTime::from_seconds (double (subs.front().out().to_ticks()) / 250)
+ );
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_CONTENT_SUBTITLE_H
+#define DCPOMATIC_CONTENT_SUBTITLE_H
+
+#include <list>
+#include <dcp/subtitle_string.h>
+#include "dcpomatic_time.h"
+#include "rect.h"
+#include "image_subtitle.h"
+
+class Image;
+
+class ContentSubtitle
+{
+public:
+ virtual ContentTimePeriod period () const = 0;
+};
+
+class ContentImageSubtitle : public ContentSubtitle
+{
+public:
+ ContentImageSubtitle (ContentTimePeriod p, boost::shared_ptr<Image> im, dcpomatic::Rect<double> r)
+ : sub (im, r)
+ , _period (p)
+ {}
+
+ ContentTimePeriod period () const {
+ return _period;
+ }
+
+ /* Our subtitle, with its rectangle unmodified by any offsets or scales that the content specifies */
+ ImageSubtitle sub;
+
+private:
+ ContentTimePeriod _period;
+};
+
+class ContentTextSubtitle : public ContentSubtitle
+{
+public:
+ ContentTextSubtitle (std::list<dcp::SubtitleString> s)
+ : subs (s)
+ {}
+
+ ContentTimePeriod period () const;
+
+ std::list<dcp::SubtitleString> subs;
+};
+
+#endif
--- /dev/null
+/*
+ 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 DCPOMATIC_CONTENT_VIDEO_H
+#define DCPOMATIC_CONTENT_VIDEO_H
+
+class ImageProxy;
+
+/** @class ContentVideo
+ * @brief A frame of video straight out of some content.
+ */
+class ContentVideo
+{
+public:
+ ContentVideo ()
+ : eyes (EYES_BOTH)
+ {}
+
+ ContentVideo (boost::shared_ptr<const ImageProxy> i, Eyes e, Part p, VideoFrame f)
+ : image (i)
+ , eyes (e)
+ , part (p)
+ , frame (f)
+ {}
+
+ boost::shared_ptr<const ImageProxy> image;
+ Eyes eyes;
+ Part part;
+ VideoFrame frame;
+};
+
+#endif
/*
- 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
/*
- 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/lib/cross.h
+ * @brief Cross-platform compatibility code.
+ */
+
#ifndef DCPOMATIC_CROSS_H
#define DCPOMATIC_CROSS_H
extern FILE * fopen_boost (boost::filesystem::path, std::string);
extern int dcpomatic_fseek (FILE *, int64_t, int);
-/** A class which tries to keep the computer awake on various operating systems.
+/** @class Waker
+ * @brief A class which tries to keep the computer awake on various operating systems.
+ *
* Create a Waker to prevent sleep, and call ::nudge every so often (every minute or so).
* Destroy the Waker to allow sleep again.
*/
--- /dev/null
+/*
+ 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 <dcp/dcp.h>
+#include <dcp/exceptions.h>
+#include "dcp_content.h"
+#include "dcp_examiner.h"
+#include "job.h"
+#include "film.h"
+#include "config.h"
+#include "compose.hpp"
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using boost::optional;
+
+int const DCPContentProperty::CAN_BE_PLAYED = 600;
+
+DCPContent::DCPContent (shared_ptr<const Film> f, boost::filesystem::path p)
+ : Content (f)
+ , VideoContent (f)
+ , SingleStreamAudioContent (f)
+ , SubtitleContent (f)
+ , _has_subtitles (false)
+ , _encrypted (false)
+ , _directory (p)
+ , _kdm_valid (false)
+{
+ read_directory (p);
+}
+
+DCPContent::DCPContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
+ : Content (f, node)
+ , VideoContent (f, node, version)
+ , SingleStreamAudioContent (f, node, version)
+ , SubtitleContent (f, node, version)
+{
+ _name = node->string_child ("Name");
+ _has_subtitles = node->bool_child ("HasSubtitles");
+ _directory = node->string_child ("Directory");
+ _encrypted = node->bool_child ("Encrypted");
+ if (node->optional_node_child ("KDM")) {
+ _kdm = dcp::EncryptedKDM (node->string_child ("KDM"));
+ }
+ _kdm_valid = node->bool_child ("KDMValid");
+}
+
+void
+DCPContent::read_directory (boost::filesystem::path p)
+{
+ for (boost::filesystem::directory_iterator i(p); i != boost::filesystem::directory_iterator(); ++i) {
+ if (boost::filesystem::is_regular_file (i->path ())) {
+ _paths.push_back (i->path ());
+ } else if (boost::filesystem::is_directory (i->path ())) {
+ read_directory (i->path ());
+ }
+ }
+}
+
+void
+DCPContent::examine (shared_ptr<Job> job)
+{
+ bool const could_be_played = can_be_played ();
+
+ job->set_progress_unknown ();
+ Content::examine (job);
+
+ shared_ptr<DCPExaminer> examiner (new DCPExaminer (shared_from_this ()));
+ take_from_video_examiner (examiner);
+ take_from_audio_examiner (examiner);
+
+ boost::mutex::scoped_lock lm (_mutex);
+ _name = examiner->name ();
+ _has_subtitles = examiner->has_subtitles ();
+ _encrypted = examiner->encrypted ();
+ _kdm_valid = examiner->kdm_valid ();
+
+ if (could_be_played != can_be_played ()) {
+ signal_changed (DCPContentProperty::CAN_BE_PLAYED);
+ }
+}
+
+string
+DCPContent::summary () const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ return String::compose (_("%1 [DCP]"), _name);
+}
+
+string
+DCPContent::technical_summary () const
+{
+ return Content::technical_summary() + " - "
+ + VideoContent::technical_summary() + " - "
+ + AudioContent::technical_summary() + " - ";
+}
+
+void
+DCPContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("DCP");
+
+ Content::as_xml (node);
+ VideoContent::as_xml (node);
+ SingleStreamAudioContent::as_xml (node);
+ SubtitleContent::as_xml (node);
+
+ boost::mutex::scoped_lock lm (_mutex);
+ node->add_child("Name")->add_child_text (_name);
+ node->add_child("HasSubtitles")->add_child_text (_has_subtitles ? "1" : "0");
+ node->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
+ node->add_child("Directory")->add_child_text (_directory.string ());
+ if (_kdm) {
+ node->add_child("KDM")->add_child_text (_kdm->as_xml ());
+ }
+ node->add_child("KDMValid")->add_child_text (_kdm_valid ? "1" : "0");
+}
+
+DCPTime
+DCPContent::full_length () const
+{
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+ return DCPTime (video_length (), FrameRateChange (video_frame_rate (), film->video_frame_rate ()));
+}
+
+string
+DCPContent::identifier () const
+{
+ return SubtitleContent::identifier ();
+}
+
+void
+DCPContent::add_kdm (dcp::EncryptedKDM k)
+{
+ _kdm = k;
+}
+
+bool
+DCPContent::can_be_played () const
+{
+ return !_encrypted || _kdm_valid;
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_DCP_CONTENT_H
+#define DCPOMATIC_DCP_CONTENT_H
+
+/** @file src/lib/dcp_content.h
+ * @brief DCPContent class.
+ */
+
+#include <libcxml/cxml.h>
+#include <dcp/encrypted_kdm.h>
+#include <dcp/decrypted_kdm.h>
+#include "video_content.h"
+#include "single_stream_audio_content.h"
+#include "subtitle_content.h"
+
+class DCPContentProperty
+{
+public:
+ static int const CAN_BE_PLAYED;
+};
+
+/** @class DCPContent
+ * @brief An existing DCP used as input.
+ */
+class DCPContent : public VideoContent, public SingleStreamAudioContent, public SubtitleContent
+{
+public:
+ DCPContent (boost::shared_ptr<const Film> f, boost::filesystem::path p);
+ DCPContent (boost::shared_ptr<const Film> f, cxml::ConstNodePtr, int version);
+
+ boost::shared_ptr<DCPContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<DCPContent> (Content::shared_from_this ());
+ }
+
+ DCPTime full_length () const;
+
+ void examine (boost::shared_ptr<Job>);
+ std::string summary () const;
+ std::string technical_summary () const;
+ void as_xml (xmlpp::Node *) const;
+ std::string identifier () const;
+
+ /* SubtitleContent */
+ bool has_subtitles () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _has_subtitles;
+ }
+
+ boost::filesystem::path directory () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _directory;
+ }
+
+ bool encrypted () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _encrypted;
+ }
+
+ void add_kdm (dcp::EncryptedKDM);
+
+ boost::optional<dcp::EncryptedKDM> kdm () const {
+ return _kdm;
+ }
+
+ bool can_be_played () const;
+
+private:
+ void read_directory (boost::filesystem::path);
+
+ std::string _name;
+ bool _has_subtitles;
+ /** true if our DCP is encrypted */
+ bool _encrypted;
+ boost::filesystem::path _directory;
+ boost::optional<dcp::EncryptedKDM> _kdm;
+ /** true if _kdm successfully decrypts the first frame of our DCP */
+ bool _kdm_valid;
+};
+
+#endif
vector<DCPContentType const *> DCPContentType::_dcp_content_types;
-DCPContentType::DCPContentType (string p, libdcp::ContentKind k, string d)
+DCPContentType::DCPContentType (string p, dcp::ContentKind k, string d)
: _pretty_name (p)
, _libdcp_kind (k)
, _isdcf_name (d)
void
DCPContentType::setup_dcp_content_types ()
{
- _dcp_content_types.push_back (new DCPContentType (_("Feature"), libdcp::FEATURE, N_("FTR")));
- _dcp_content_types.push_back (new DCPContentType (_("Short"), libdcp::SHORT, N_("SHR")));
- _dcp_content_types.push_back (new DCPContentType (_("Trailer"), libdcp::TRAILER, N_("TLR")));
- _dcp_content_types.push_back (new DCPContentType (_("Test"), libdcp::TEST, N_("TST")));
- _dcp_content_types.push_back (new DCPContentType (_("Transitional"), libdcp::TRANSITIONAL, N_("XSN")));
- _dcp_content_types.push_back (new DCPContentType (_("Rating"), libdcp::RATING, N_("RTG")));
- _dcp_content_types.push_back (new DCPContentType (_("Teaser"), libdcp::TEASER, N_("TSR")));
- _dcp_content_types.push_back (new DCPContentType (_("Policy"), libdcp::POLICY, N_("POL")));
- _dcp_content_types.push_back (new DCPContentType (_("Public Service Announcement"), libdcp::PUBLIC_SERVICE_ANNOUNCEMENT, N_("PSA")));
- _dcp_content_types.push_back (new DCPContentType (_("Advertisement"), libdcp::ADVERTISEMENT, N_("ADV")));
+ _dcp_content_types.push_back (new DCPContentType (_("Feature"), dcp::FEATURE, N_("FTR")));
+ _dcp_content_types.push_back (new DCPContentType (_("Short"), dcp::SHORT, N_("SHR")));
+ _dcp_content_types.push_back (new DCPContentType (_("Trailer"), dcp::TRAILER, N_("TLR")));
+ _dcp_content_types.push_back (new DCPContentType (_("Test"), dcp::TEST, N_("TST")));
+ _dcp_content_types.push_back (new DCPContentType (_("Transitional"), dcp::TRANSITIONAL, N_("XSN")));
+ _dcp_content_types.push_back (new DCPContentType (_("Rating"), dcp::RATING, N_("RTG")));
+ _dcp_content_types.push_back (new DCPContentType (_("Teaser"), dcp::TEASER, N_("TSR")));
+ _dcp_content_types.push_back (new DCPContentType (_("Policy"), dcp::POLICY, N_("POL")));
+ _dcp_content_types.push_back (new DCPContentType (_("Public Service Announcement"), dcp::PUBLIC_SERVICE_ANNOUNCEMENT, N_("PSA")));
+ _dcp_content_types.push_back (new DCPContentType (_("Advertisement"), dcp::ADVERTISEMENT, N_("ADV")));
}
DCPContentType const *
#include <string>
#include <vector>
-#include <libdcp/dcp.h>
+#include <dcp/dcp.h>
/** @class DCPContentType
* @brief A description of the type of content for a DCP (e.g. feature, trailer etc.)
class DCPContentType : public boost::noncopyable
{
public:
- DCPContentType (std::string, libdcp::ContentKind, std::string);
+ DCPContentType (std::string, dcp::ContentKind, std::string);
/** @return user-visible `pretty' name */
std::string pretty_name () const {
return _pretty_name;
}
- libdcp::ContentKind libdcp_kind () const {
+ dcp::ContentKind libdcp_kind () const {
return _libdcp_kind;
}
private:
std::string _pretty_name;
- libdcp::ContentKind _libdcp_kind;
+ dcp::ContentKind _libdcp_kind;
std::string _isdcf_name;
/** All available DCP content types */
--- /dev/null
+/*
+ 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 <dcp/dcp.h>
+#include <dcp/cpl.h>
+#include <dcp/reel.h>
+#include <dcp/mono_picture_mxf.h>
+#include <dcp/stereo_picture_mxf.h>
+#include <dcp/reel_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/mono_picture_frame.h>
+#include <dcp/stereo_picture_frame.h>
+#include <dcp/sound_frame.h>
+#include "dcp_decoder.h"
+#include "dcp_content.h"
+#include "j2k_image_proxy.h"
+#include "image.h"
+#include "config.h"
+
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log)
+ : VideoDecoder (c)
+ , AudioDecoder (c)
+ , SubtitleDecoder (c)
+ , _log (log)
+ , _dcp_content (c)
+{
+ dcp::DCP dcp (c->directory ());
+ dcp.read ();
+ if (c->kdm ()) {
+ dcp.add (dcp::DecryptedKDM (c->kdm().get (), Config::instance()->decryption_private_key ()));
+ }
+ assert (dcp.cpls().size() == 1);
+ _reels = dcp.cpls().front()->reels ();
+ _reel = _reels.begin ();
+}
+
+bool
+DCPDecoder::pass ()
+{
+ if (_reel == _reels.end () || !_dcp_content->can_be_played ()) {
+ return true;
+ }
+
+ float const vfr = _dcp_content->video_frame_rate ();
+ int64_t const frame = _next.frames (vfr);
+
+ if ((*_reel)->main_picture ()) {
+ shared_ptr<dcp::PictureMXF> mxf = (*_reel)->main_picture()->mxf ();
+ shared_ptr<dcp::MonoPictureMXF> mono = dynamic_pointer_cast<dcp::MonoPictureMXF> (mxf);
+ shared_ptr<dcp::StereoPictureMXF> stereo = dynamic_pointer_cast<dcp::StereoPictureMXF> (mxf);
+ int64_t const entry_point = (*_reel)->main_picture()->entry_point ();
+ if (mono) {
+ video (shared_ptr<ImageProxy> (new J2KImageProxy (mono->get_frame (entry_point + frame), mxf->size(), _log)), frame);
+ } else {
+ video (
+ shared_ptr<ImageProxy> (new J2KImageProxy (stereo->get_frame (entry_point + frame), mxf->size(), dcp::EYE_LEFT, _log)),
+ frame
+ );
+
+ video (
+ shared_ptr<ImageProxy> (new J2KImageProxy (stereo->get_frame (entry_point + frame), mxf->size(), dcp::EYE_RIGHT, _log)),
+ frame
+ );
+ }
+ }
+
+ if ((*_reel)->main_sound ()) {
+ int64_t const entry_point = (*_reel)->main_sound()->entry_point ();
+ shared_ptr<const dcp::SoundFrame> sf = (*_reel)->main_sound()->mxf()->get_frame (entry_point + frame);
+ uint8_t const * from = sf->data ();
+
+ int const channels = _dcp_content->audio_channels ();
+ int const frames = sf->size() / (3 * channels);
+ shared_ptr<AudioBuffers> data (new AudioBuffers (channels, frames));
+ for (int i = 0; i < frames; ++i) {
+ for (int j = 0; j < channels; ++j) {
+ data->data()[j][i] = float (from[0] | (from[1] << 8) | (from[2] << 16)) / (1 << 23);
+ from += 3;
+ }
+ }
+
+ audio (data, _next);
+ }
+
+ /* XXX: subtitle */
+
+ _next += ContentTime::from_frames (1, vfr);
+
+ if ((*_reel)->main_picture ()) {
+ if (_next.frames (vfr) >= (*_reel)->main_picture()->duration()) {
+ ++_reel;
+ }
+ }
+
+ return false;
+}
+
+void
+DCPDecoder::seek (ContentTime t, bool accurate)
+{
+ VideoDecoder::seek (t, accurate);
+ AudioDecoder::seek (t, accurate);
+ SubtitleDecoder::seek (t, accurate);
+
+ _reel = _reels.begin ();
+ while (_reel != _reels.end() && t >= ContentTime::from_frames ((*_reel)->main_picture()->duration(), _dcp_content->video_frame_rate ())) {
+ t -= ContentTime::from_frames ((*_reel)->main_picture()->duration(), _dcp_content->video_frame_rate ());
+ ++_reel;
+ }
+
+ _next = t;
+}
+
+
+list<ContentTimePeriod>
+DCPDecoder::subtitles_during (ContentTimePeriod, bool starting) const
+{
+ return list<ContentTimePeriod> ();
+}
--- /dev/null
+/*
+ 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 "video_decoder.h"
+#include "audio_decoder.h"
+#include "subtitle_decoder.h"
+
+namespace dcp {
+ class Reel;
+}
+
+class DCPContent;
+class Log;
+
+class DCPDecoder : public VideoDecoder, public AudioDecoder, public SubtitleDecoder
+{
+public:
+ DCPDecoder (boost::shared_ptr<const DCPContent>, boost::shared_ptr<Log>);
+
+private:
+ void seek (ContentTime t, bool accurate);
+ bool pass ();
+ std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const;
+
+ ContentTime _next;
+ std::list<boost::shared_ptr<dcp::Reel> > _reels;
+ std::list<boost::shared_ptr<dcp::Reel> >::iterator _reel;
+ boost::shared_ptr<Log> _log;
+ boost::shared_ptr<const DCPContent> _dcp_content;
+};
--- /dev/null
+/*
+ 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 <dcp/dcp.h>
+#include <dcp/cpl.h>
+#include <dcp/reel.h>
+#include <dcp/reel_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/mono_picture_mxf.h>
+#include <dcp/mono_picture_frame.h>
+#include <dcp/stereo_picture_mxf.h>
+#include <dcp/stereo_picture_frame.h>
+#include <dcp/sound_mxf.h>
+#include "dcp_examiner.h"
+#include "dcp_content.h"
+#include "exceptions.h"
+#include "image.h"
+#include "config.h"
+
+#include "i18n.h"
+
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
+ : _video_length (0)
+ , _audio_length (0)
+ , _has_subtitles (false)
+ , _encrypted (false)
+ , _kdm_valid (false)
+{
+ dcp::DCP dcp (content->directory ());
+ dcp.read ();
+
+ if (content->kdm ()) {
+ dcp.add (dcp::DecryptedKDM (content->kdm().get(), Config::instance()->decryption_private_key ()));
+ }
+
+ if (dcp.cpls().size() == 0) {
+ throw DCPError ("No CPLs found in DCP");
+ } else if (dcp.cpls().size() > 1) {
+ throw DCPError ("Multiple CPLs found in DCP");
+ }
+
+ _name = dcp.cpls().front()->content_title_text ();
+
+ list<shared_ptr<dcp::Reel> > reels = dcp.cpls().front()->reels ();
+ for (list<shared_ptr<dcp::Reel> >::const_iterator i = reels.begin(); i != reels.end(); ++i) {
+
+ if ((*i)->main_picture ()) {
+ dcp::Fraction const frac = (*i)->main_picture()->frame_rate ();
+ float const fr = float(frac.numerator) / frac.denominator;
+ if (!_video_frame_rate) {
+ _video_frame_rate = fr;
+ } else if (_video_frame_rate.get() != fr) {
+ throw DCPError (_("Mismatched frame rates in DCP"));
+ }
+
+ shared_ptr<dcp::PictureMXF> mxf = (*i)->main_picture()->mxf ();
+ if (!_video_size) {
+ _video_size = mxf->size ();
+ } else if (_video_size.get() != mxf->size ()) {
+ throw DCPError (_("Mismatched video sizes in DCP"));
+ }
+
+ _video_length += ContentTime::from_frames ((*i)->main_picture()->duration(), _video_frame_rate.get ());
+ }
+
+ if ((*i)->main_sound ()) {
+ shared_ptr<dcp::SoundMXF> mxf = (*i)->main_sound()->mxf ();
+
+ if (!_audio_channels) {
+ _audio_channels = mxf->channels ();
+ } else if (_audio_channels.get() != mxf->channels ()) {
+ throw DCPError (_("Mismatched audio channel counts in DCP"));
+ }
+
+ if (!_audio_frame_rate) {
+ _audio_frame_rate = mxf->sampling_rate ();
+ } else if (_audio_frame_rate.get() != mxf->sampling_rate ()) {
+ throw DCPError (_("Mismatched audio frame rates in DCP"));
+ }
+
+ _audio_length += ContentTime::from_frames ((*i)->main_sound()->duration(), _video_frame_rate.get ());
+ }
+
+ if ((*i)->main_subtitle ()) {
+ _has_subtitles = true;
+ }
+ }
+
+ _encrypted = dcp.encrypted ();
+ _kdm_valid = true;
+
+ /* Check that we can read the first picture frame */
+ try {
+ if (!dcp.cpls().empty () && !dcp.cpls().front()->reels().empty ()) {
+ shared_ptr<dcp::PictureMXF> mxf = dcp.cpls().front()->reels().front()->main_picture()->mxf ();
+ shared_ptr<dcp::MonoPictureMXF> mono = dynamic_pointer_cast<dcp::MonoPictureMXF> (mxf);
+ shared_ptr<dcp::StereoPictureMXF> stereo = dynamic_pointer_cast<dcp::StereoPictureMXF> (mxf);
+
+ shared_ptr<Image> image (new Image (PIX_FMT_RGB24, _video_size.get(), false));
+
+ if (mono) {
+ mono->get_frame(0)->rgb_frame (image->data()[0]);
+ } else {
+ stereo->get_frame(0)->rgb_frame (dcp::EYE_LEFT, image->data()[0]);
+ }
+
+ }
+ } catch (dcp::DCPReadError& e) {
+ _kdm_valid = false;
+ if (_encrypted && content->kdm ()) {
+ /* XXX: maybe don't use an exception for this */
+ throw StringError (_("The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"));
+ }
+ }
+}
--- /dev/null
+/*
+ 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 "video_examiner.h"
+#include "audio_examiner.h"
+
+class DCPContent;
+
+class DCPExaminer : public VideoExaminer, public AudioExaminer
+{
+public:
+ DCPExaminer (boost::shared_ptr<const DCPContent>);
+
+ float video_frame_rate () const {
+ return _video_frame_rate.get_value_or (24);
+ }
+
+ dcp::Size video_size () const {
+ return _video_size.get_value_or (dcp::Size (1998, 1080));
+ }
+
+ ContentTime video_length () const {
+ return _video_length;
+ }
+
+ std::string name () const {
+ return _name;
+ }
+
+ bool has_subtitles () const {
+ return _has_subtitles;
+ }
+
+ bool encrypted () const {
+ return _encrypted;
+ }
+
+ int audio_channels () const {
+ return _audio_channels.get_value_or (0);
+ }
+
+ ContentTime audio_length () const {
+ return _audio_length;
+ }
+
+ int audio_frame_rate () const {
+ return _audio_frame_rate.get_value_or (48000);
+ }
+
+ bool kdm_valid () const {
+ return _kdm_valid;
+ }
+
+private:
+ boost::optional<float> _video_frame_rate;
+ boost::optional<dcp::Size> _video_size;
+ ContentTime _video_length;
+ boost::optional<int> _audio_channels;
+ boost::optional<int> _audio_frame_rate;
+ ContentTime _audio_length;
+ std::string _name;
+ bool _has_subtitles;
+ bool _encrypted;
+ bool _kdm_valid;
+};
--- /dev/null
+/*
+ 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 <dcp/subtitle_content.h>
+#include <dcp/raw_convert.h>
+#include "dcp_subtitle_content.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::list;
+using boost::shared_ptr;
+using dcp::raw_convert;
+
+DCPSubtitleContent::DCPSubtitleContent (shared_ptr<const Film> film, boost::filesystem::path path)
+ : Content (film, path)
+ , SubtitleContent (film, path)
+{
+
+}
+
+DCPSubtitleContent::DCPSubtitleContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
+ : Content (film, node)
+ , SubtitleContent (film, node, version)
+ , _length (node->number_child<DCPTime::Type> ("Length"))
+{
+
+}
+
+void
+DCPSubtitleContent::examine (shared_ptr<Job> job)
+{
+ Content::examine (job);
+ dcp::SubtitleContent sc (path (0), false);
+ _length = DCPTime::from_seconds (sc.latest_subtitle_out().to_seconds ());
+}
+
+DCPTime
+DCPSubtitleContent::full_length () const
+{
+ /* XXX: this assumes that the timing of the subtitle file is appropriate
+ for the DCP's frame rate.
+ */
+ return _length;
+}
+
+string
+DCPSubtitleContent::summary () const
+{
+ return path_summary() + " " + _("[subtitles]");
+}
+
+string
+DCPSubtitleContent::technical_summary () const
+{
+ return Content::technical_summary() + " - " + _("DCP XML subtitles");
+}
+
+string
+DCPSubtitleContent::information () const
+{
+
+}
+
+void
+DCPSubtitleContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("DCPSubtitle");
+ Content::as_xml (node);
+ SubtitleContent::as_xml (node);
+ node->add_child("Length")->add_child_text (raw_convert<string> (_length.get ()));
+}
--- /dev/null
+/*
+ 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 "subtitle_content.h"
+
+class DCPSubtitleContent : public SubtitleContent
+{
+public:
+ DCPSubtitleContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+ DCPSubtitleContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
+
+ /* Content */
+ void examine (boost::shared_ptr<Job>);
+ std::string summary () const;
+ std::string technical_summary () const;
+ std::string information () const;
+ void as_xml (xmlpp::Node *) const;
+ DCPTime full_length () const;
+
+ /* SubtitleContent */
+ bool has_subtitles () const {
+ return true;
+ }
+
+private:
+ DCPTime _length;
+};
--- /dev/null
+/*
+ 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 <dcp/subtitle_content.h>
+#include "dcp_subtitle_decoder.h"
+#include "dcp_subtitle_content.h"
+
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+
+DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const DCPSubtitleContent> content)
+ : SubtitleDecoder (content)
+{
+ dcp::SubtitleContent c (content->path (0), false);
+ _subtitles = c.subtitles ();
+ _next = _subtitles.begin ();
+}
+
+void
+DCPSubtitleDecoder::seek (ContentTime time, bool accurate)
+{
+ SubtitleDecoder::seek (time, accurate);
+
+ _next = _subtitles.begin ();
+ list<dcp::SubtitleString>::const_iterator i = _subtitles.begin ();
+ while (i != _subtitles.end() && ContentTime::from_seconds (_next->in().to_seconds()) < time) {
+ ++i;
+ }
+}
+
+bool
+DCPSubtitleDecoder::pass ()
+{
+ if (_next == _subtitles.end ()) {
+ return true;
+ }
+
+ list<dcp::SubtitleString> s;
+ s.push_back (*_next);
+ text_subtitle (s);
+ ++_next;
+
+ return false;
+}
+
+list<ContentTimePeriod>
+DCPSubtitleDecoder::subtitles_during (ContentTimePeriod p, bool starting) const
+{
+ /* XXX: inefficient */
+
+ list<ContentTimePeriod> d;
+
+ for (list<dcp::SubtitleString>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+ ContentTimePeriod period (
+ ContentTime::from_seconds (i->in().to_seconds ()),
+ ContentTime::from_seconds (i->out().to_seconds ())
+ );
+
+ if ((starting && p.contains (period.from)) || (!starting && p.overlaps (period))) {
+ d.push_back (period);
+ }
+ }
+
+ return d;
+}
+
--- /dev/null
+/*
+ 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 "subtitle_decoder.h"
+
+class DCPSubtitleContent;
+
+class DCPSubtitleDecoder : public SubtitleDecoder
+{
+public:
+ DCPSubtitleDecoder (boost::shared_ptr<const DCPSubtitleContent>);
+
+protected:
+ void seek (ContentTime time, bool accurate);
+ bool pass ();
+
+private:
+ std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const;
+
+ std::list<dcp::SubtitleString> _subtitles;
+ std::list<dcp::SubtitleString>::const_iterator _next;
+};
--- /dev/null
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+ Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
+
+ 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/dcp_video_frame.cc
+ * @brief A single frame of video destined for a DCP.
+ *
+ * Given an Image and some settings, this class knows how to encode
+ * the image to J2K either on the local host or on a remote server.
+ *
+ * Objects of this class are used for the queue that we keep
+ * of images that require encoding.
+ */
+
+#include <stdint.h>
+#include <cstring>
+#include <cstdlib>
+#include <stdexcept>
+#include <cstdio>
+#include <iomanip>
+#include <iostream>
+#include <fstream>
+#include <unistd.h>
+#include <errno.h>
+#include <boost/array.hpp>
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/lexical_cast.hpp>
+#include <dcp/gamma_lut.h>
+#include <dcp/xyz_frame.h>
+#include <dcp/rgb_xyz.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
+#include <libcxml/cxml.h>
+#include "film.h"
+#include "dcp_video.h"
+#include "config.h"
+#include "exceptions.h"
+#include "server.h"
+#include "util.h"
+#include "scaler.h"
+#include "image.h"
+#include "log.h"
+#include "cross.h"
+#include "player_video.h"
+#include "encoded_data.h"
+
+#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using dcp::Size;
+using dcp::raw_convert;
+
+#define DCI_COEFFICENT (48.0 / 52.37)
+
+/** Construct a DCP video frame.
+ * @param frame Input frame.
+ * @param index Index of the frame within the DCP.
+ * @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
+ * @param l Log to write to.
+ */
+DCPVideo::DCPVideo (
+ shared_ptr<const PlayerVideo> frame, int index, int dcp_fps, int bw, Resolution r, bool b, shared_ptr<Log> l
+ )
+ : _frame (frame)
+ , _index (index)
+ , _frames_per_second (dcp_fps)
+ , _j2k_bandwidth (bw)
+ , _resolution (r)
+ , _burn_subtitles (b)
+ , _log (l)
+{
+
+}
+
+DCPVideo::DCPVideo (shared_ptr<const PlayerVideo> frame, shared_ptr<const cxml::Node> node, shared_ptr<Log> log)
+ : _frame (frame)
+ , _log (log)
+{
+ _index = node->number_child<int> ("Index");
+ _frames_per_second = node->number_child<int> ("FramesPerSecond");
+ _j2k_bandwidth = node->number_child<int> ("J2KBandwidth");
+ _resolution = Resolution (node->optional_number_child<int>("Resolution").get_value_or (RESOLUTION_2K));
+ _burn_subtitles = node->bool_child ("BurnSubtitles");
+}
+
+/** J2K-encode this frame on the local host.
+ * @return Encoded data.
+ */
+shared_ptr<EncodedData>
+DCPVideo::encode_locally ()
+{
+ shared_ptr<dcp::GammaLUT> in_lut = dcp::GammaLUT::cache.get (
+ 12, _frame->colour_conversion().input_gamma, _frame->colour_conversion().input_gamma_linearised
+ );
+
+ /* XXX: libdcp should probably use boost */
+
+ double matrix[3][3];
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ matrix[i][j] = _frame->colour_conversion().matrix (i, j);
+ }
+ }
+
+ shared_ptr<dcp::XYZFrame> xyz = dcp::rgb_to_xyz (
+ _frame->image (AV_PIX_FMT_RGB48LE, _burn_subtitles),
+ in_lut,
+ dcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma, false),
+ matrix
+ );
+
+ /* Set the max image and component sizes based on frame_rate */
+ int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second;
+ if (_frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT) {
+ /* In 3D we have only half the normal bandwidth per eye */
+ max_cs_len /= 2;
+ }
+ int const max_comp_size = max_cs_len / 1.25;
+
+ /* get a J2K compressor handle */
+ opj_cinfo_t* cinfo = opj_create_compress (CODEC_J2K);
+ if (cinfo == 0) {
+ throw EncodeError (N_("could not create JPEG2000 encoder"));
+ }
+
+ /* Set encoding parameters to default values */
+ opj_cparameters_t parameters;
+ opj_set_default_encoder_parameters (¶meters);
+
+ /* Set default cinema parameters */
+ parameters.tile_size_on = false;
+ parameters.cp_tdx = 1;
+ parameters.cp_tdy = 1;
+
+ /* Tile part */
+ parameters.tp_flag = 'C';
+ parameters.tp_on = 1;
+
+ /* Tile and Image shall be at (0,0) */
+ parameters.cp_tx0 = 0;
+ parameters.cp_ty0 = 0;
+ parameters.image_offset_x0 = 0;
+ parameters.image_offset_y0 = 0;
+
+ /* Codeblock size = 32x32 */
+ parameters.cblockw_init = 32;
+ parameters.cblockh_init = 32;
+ parameters.csty |= 0x01;
+
+ /* The progression order shall be CPRL */
+ parameters.prog_order = CPRL;
+
+ /* No ROI */
+ parameters.roi_compno = -1;
+
+ parameters.subsampling_dx = 1;
+ parameters.subsampling_dy = 1;
+
+ /* 9-7 transform */
+ parameters.irreversible = 1;
+
+ parameters.tcp_rates[0] = 0;
+ parameters.tcp_numlayers++;
+ parameters.cp_disto_alloc = 1;
+ parameters.cp_rsiz = _resolution == RESOLUTION_2K ? CINEMA2K : CINEMA4K;
+ if (_resolution == RESOLUTION_4K) {
+ parameters.numpocs = 2;
+ parameters.POC[0].tile = 1;
+ parameters.POC[0].resno0 = 0;
+ parameters.POC[0].compno0 = 0;
+ parameters.POC[0].layno1 = 1;
+ parameters.POC[0].resno1 = parameters.numresolution - 1;
+ parameters.POC[0].compno1 = 3;
+ parameters.POC[0].prg1 = CPRL;
+ parameters.POC[1].tile = 1;
+ parameters.POC[1].resno0 = parameters.numresolution - 1;
+ parameters.POC[1].compno0 = 0;
+ parameters.POC[1].layno1 = 1;
+ parameters.POC[1].resno1 = parameters.numresolution;
+ parameters.POC[1].compno1 = 3;
+ parameters.POC[1].prg1 = CPRL;
+ }
+
+ parameters.cp_comment = strdup (N_("DCP-o-matic"));
+ parameters.cp_cinema = _resolution == RESOLUTION_2K ? CINEMA2K_24 : CINEMA4K_24;
+
+ /* 3 components, so use MCT */
+ parameters.tcp_mct = 1;
+
+ /* set max image */
+ parameters.max_comp_size = max_comp_size;
+ parameters.tcp_rates[0] = ((float) (3 * xyz->size().width * xyz->size().height * 12)) / (max_cs_len * 8);
+
+ /* Set event manager to null (openjpeg 1.3 bug) */
+ cinfo->event_mgr = 0;
+
+ /* Setup the encoder parameters using the current image and user parameters */
+ opj_setup_encoder (cinfo, ¶meters, xyz->opj_image ());
+
+ opj_cio_t* cio = opj_cio_open ((opj_common_ptr) cinfo, 0, 0);
+ if (cio == 0) {
+ opj_destroy_compress (cinfo);
+ throw EncodeError (N_("could not open JPEG2000 stream"));
+ }
+
+ int const r = opj_encode (cinfo, cio, xyz->opj_image(), 0);
+ if (r == 0) {
+ opj_cio_close (cio);
+ opj_destroy_compress (cinfo);
+ throw EncodeError (N_("JPEG2000 encoding failed"));
+ }
+
+ switch (_frame->eyes()) {
+ case EYES_BOTH:
+ LOG_GENERAL (N_("Finished locally-encoded frame %1 for mono"), _index);
+ break;
+ case EYES_LEFT:
+ LOG_GENERAL (N_("Finished locally-encoded frame %1 for L"), _index);
+ break;
+ case EYES_RIGHT:
+ LOG_GENERAL (N_("Finished locally-encoded frame %1 for R"), _index);
+ break;
+ default:
+ break;
+ }
+
+ shared_ptr<EncodedData> enc (new LocallyEncodedData (cio->buffer, cio_tell (cio)));
+
+ opj_cio_close (cio);
+ free (parameters.cp_comment);
+ opj_destroy_compress (cinfo);
+
+ return enc;
+}
+
+/** Send this frame to a remote server for J2K encoding, then read the result.
+ * @param serv Server to send to.
+ * @return Encoded data.
+ */
+shared_ptr<EncodedData>
+DCPVideo::encode_remotely (ServerDescription serv)
+{
+ boost::asio::io_service io_service;
+ boost::asio::ip::tcp::resolver resolver (io_service);
+ boost::asio::ip::tcp::resolver::query query (serv.host_name(), raw_convert<string> (Config::instance()->server_port_base ()));
+ boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);
+
+ shared_ptr<Socket> socket (new Socket);
+
+ socket->connect (*endpoint_iterator);
+
+ /* Collect all XML metadata */
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("EncodingRequest");
+ root->add_child("Version")->add_child_text (raw_convert<string> (SERVER_LINK_VERSION));
+ add_metadata (root);
+
+ LOG_GENERAL (N_("Sending frame %1 to remote"), _index);
+
+ /* Send XML metadata */
+ string xml = doc.write_to_string ("UTF-8");
+ socket->write (xml.length() + 1);
+ socket->write ((uint8_t *) xml.c_str(), xml.length() + 1);
+
+ /* Send binary data */
+ _frame->send_binary (socket, _burn_subtitles);
+
+ /* Read the response (JPEG2000-encoded data); this blocks until the data
+ is ready and sent back.
+ */
+ shared_ptr<EncodedData> e (new RemotelyEncodedData (socket->read_uint32 ()));
+ socket->read (e->data(), e->size());
+
+ LOG_GENERAL (N_("Finished remotely-encoded frame %1"), _index);
+
+ return e;
+}
+
+void
+DCPVideo::add_metadata (xmlpp::Element* el) const
+{
+ el->add_child("Index")->add_child_text (raw_convert<string> (_index));
+ el->add_child("FramesPerSecond")->add_child_text (raw_convert<string> (_frames_per_second));
+ el->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
+ el->add_child("Resolution")->add_child_text (raw_convert<string> (int (_resolution)));
+ el->add_child("BurnSubtitles")->add_child_text (_burn_subtitles ? "1" : "0");
+ _frame->add_metadata (el, _burn_subtitles);
+}
+
+Eyes
+DCPVideo::eyes () const
+{
+ return _frame->eyes ();
+}
+
+/** @return true if this DCPVideo is definitely the same as another;
+ * (apart from the frame index), false if it is probably not.
+ */
+bool
+DCPVideo::same (shared_ptr<const DCPVideo> other) const
+{
+ if (_frames_per_second != other->_frames_per_second ||
+ _j2k_bandwidth != other->_j2k_bandwidth ||
+ _resolution != other->_resolution ||
+ _burn_subtitles != other->_burn_subtitles) {
+ return false;
+ }
+
+ return _frame->same (other->_frame);
+}
--- /dev/null
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+ Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
+
+ 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 <dcp/picture_mxf_writer.h>
+#include "util.h"
+
+/** @file src/dcp_video_frame.h
+ * @brief A single frame of video destined for a DCP.
+ */
+
+class Film;
+class ServerDescription;
+class Scaler;
+class Image;
+class Log;
+class Subtitle;
+class PlayerVideo;
+class EncodedData;
+
+/** @class DCPVideo
+ * @brief A single frame of video destined for a DCP.
+ *
+ * Given an Image and some settings, this class knows how to encode
+ * the image to J2K either on the local host or on a remote server.
+ *
+ * Objects of this class are used for the queue that we keep
+ * of images that require encoding.
+ */
+class DCPVideo : public boost::noncopyable
+{
+public:
+ DCPVideo (boost::shared_ptr<const PlayerVideo>, int, int, int, Resolution, bool b, boost::shared_ptr<Log>);
+ DCPVideo (boost::shared_ptr<const PlayerVideo>, cxml::ConstNodePtr, boost::shared_ptr<Log>);
+
+ boost::shared_ptr<EncodedData> encode_locally ();
+ boost::shared_ptr<EncodedData> encode_remotely (ServerDescription);
+
+ int index () const {
+ return _index;
+ }
+
+ Eyes eyes () const;
+
+ bool same (boost::shared_ptr<const DCPVideo> other) const;
+
+private:
+
+ void add_metadata (xmlpp::Element *) const;
+
+ boost::shared_ptr<const PlayerVideo> _frame;
+ int _index; ///< frame index within the DCP's intrinsic duration
+ int _frames_per_second; ///< Frames per second that we will use for the DCP
+ int _j2k_bandwidth; ///< J2K bandwidth to use
+ Resolution _resolution; ///< Resolution (2K or 4K)
+ bool _burn_subtitles; ///< true to burn subtitles into the image
+
+ boost::shared_ptr<Log> _log; ///< log
+};
+++ /dev/null
-/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
- Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
-
- 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/dcp_video_frame.cc
- * @brief A single frame of video destined for a DCP.
- *
- * Given an Image and some settings, this class knows how to encode
- * the image to J2K either on the local host or on a remote server.
- *
- * Objects of this class are used for the queue that we keep
- * of images that require encoding.
- */
-
-#include <stdint.h>
-#include <cstring>
-#include <cstdlib>
-#include <stdexcept>
-#include <cstdio>
-#include <iomanip>
-#include <iostream>
-#include <fstream>
-#include <unistd.h>
-#include <errno.h>
-#include <boost/array.hpp>
-#include <boost/asio.hpp>
-#include <boost/filesystem.hpp>
-#include <libdcp/rec709_linearised_gamma_lut.h>
-#include <libdcp/srgb_linearised_gamma_lut.h>
-#include <libdcp/gamma_lut.h>
-#include <libdcp/xyz_frame.h>
-#include <libdcp/rgb_xyz.h>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
-#include <libcxml/cxml.h>
-#include "film.h"
-#include "dcp_video_frame.h"
-#include "config.h"
-#include "exceptions.h"
-#include "server.h"
-#include "util.h"
-#include "scaler.h"
-#include "image.h"
-#include "log.h"
-#include "cross.h"
-#include "player_video_frame.h"
-
-#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
-
-#include "i18n.h"
-
-using std::string;
-using std::cout;
-using boost::shared_ptr;
-using libdcp::Size;
-using libdcp::raw_convert;
-
-#define DCI_COEFFICENT (48.0 / 52.37)
-
-/** Construct a DCP video frame.
- * @param frame Input frame.
- * @param index Index of the frame within the DCP.
- * @param bw J2K bandwidth to use (see Config::j2k_bandwidth ())
- * @param l Log to write to.
- */
-DCPVideoFrame::DCPVideoFrame (
- shared_ptr<const PlayerVideoFrame> frame, int index, int dcp_fps, int bw, Resolution r, shared_ptr<Log> l
- )
- : _frame (frame)
- , _index (index)
- , _frames_per_second (dcp_fps)
- , _j2k_bandwidth (bw)
- , _resolution (r)
- , _log (l)
-{
-
-}
-
-DCPVideoFrame::DCPVideoFrame (shared_ptr<const PlayerVideoFrame> frame, shared_ptr<const cxml::Node> node, shared_ptr<Log> log)
- : _frame (frame)
- , _log (log)
-{
- _index = node->number_child<int> ("Index");
- _frames_per_second = node->number_child<int> ("FramesPerSecond");
- _j2k_bandwidth = node->number_child<int> ("J2KBandwidth");
- _resolution = Resolution (node->optional_number_child<int>("Resolution").get_value_or (RESOLUTION_2K));
-}
-
-/** J2K-encode this frame on the local host.
- * @return Encoded data.
- */
-shared_ptr<EncodedData>
-DCPVideoFrame::encode_locally ()
-{
- shared_ptr<libdcp::LUT> in_lut;
- if (_frame->colour_conversion().input_gamma_linearised) {
- in_lut = libdcp::SRGBLinearisedGammaLUT::cache.get (12, _frame->colour_conversion().input_gamma);
- } else {
- in_lut = libdcp::GammaLUT::cache.get (12, _frame->colour_conversion().input_gamma);
- }
-
- /* XXX: libdcp should probably use boost */
-
- double matrix[3][3];
- for (int i = 0; i < 3; ++i) {
- for (int j = 0; j < 3; ++j) {
- matrix[i][j] = _frame->colour_conversion().matrix (i, j);
- }
- }
-
- shared_ptr<libdcp::XYZFrame> xyz = libdcp::rgb_to_xyz (
- _frame->image(AV_PIX_FMT_RGB48LE),
- in_lut,
- libdcp::GammaLUT::cache.get (16, 1 / _frame->colour_conversion().output_gamma),
- matrix
- );
-
- /* Set the max image and component sizes based on frame_rate */
- int max_cs_len = ((float) _j2k_bandwidth) / 8 / _frames_per_second;
- if (_frame->eyes() == EYES_LEFT || _frame->eyes() == EYES_RIGHT) {
- /* In 3D we have only half the normal bandwidth per eye */
- max_cs_len /= 2;
- }
- int const max_comp_size = max_cs_len / 1.25;
-
- /* get a J2K compressor handle */
- opj_cinfo_t* cinfo = opj_create_compress (CODEC_J2K);
- if (cinfo == 0) {
- throw EncodeError (N_("could not create JPEG2000 encoder"));
- }
-
- /* Set encoding parameters to default values */
- opj_cparameters_t parameters;
- opj_set_default_encoder_parameters (¶meters);
-
- /* Set default cinema parameters */
- parameters.tile_size_on = false;
- parameters.cp_tdx = 1;
- parameters.cp_tdy = 1;
-
- /* Tile part */
- parameters.tp_flag = 'C';
- parameters.tp_on = 1;
-
- /* Tile and Image shall be at (0,0) */
- parameters.cp_tx0 = 0;
- parameters.cp_ty0 = 0;
- parameters.image_offset_x0 = 0;
- parameters.image_offset_y0 = 0;
-
- /* Codeblock size = 32x32 */
- parameters.cblockw_init = 32;
- parameters.cblockh_init = 32;
- parameters.csty |= 0x01;
-
- /* The progression order shall be CPRL */
- parameters.prog_order = CPRL;
-
- /* No ROI */
- parameters.roi_compno = -1;
-
- parameters.subsampling_dx = 1;
- parameters.subsampling_dy = 1;
-
- /* 9-7 transform */
- parameters.irreversible = 1;
-
- parameters.tcp_rates[0] = 0;
- parameters.tcp_numlayers++;
- parameters.cp_disto_alloc = 1;
- parameters.cp_rsiz = _resolution == RESOLUTION_2K ? CINEMA2K : CINEMA4K;
- if (_resolution == RESOLUTION_4K) {
- parameters.numpocs = 2;
- parameters.POC[0].tile = 1;
- parameters.POC[0].resno0 = 0;
- parameters.POC[0].compno0 = 0;
- parameters.POC[0].layno1 = 1;
- parameters.POC[0].resno1 = parameters.numresolution - 1;
- parameters.POC[0].compno1 = 3;
- parameters.POC[0].prg1 = CPRL;
- parameters.POC[1].tile = 1;
- parameters.POC[1].resno0 = parameters.numresolution - 1;
- parameters.POC[1].compno0 = 0;
- parameters.POC[1].layno1 = 1;
- parameters.POC[1].resno1 = parameters.numresolution;
- parameters.POC[1].compno1 = 3;
- parameters.POC[1].prg1 = CPRL;
- }
-
- parameters.cp_comment = strdup (N_("DCP-o-matic"));
- parameters.cp_cinema = _resolution == RESOLUTION_2K ? CINEMA2K_24 : CINEMA4K_24;
-
- /* 3 components, so use MCT */
- parameters.tcp_mct = 1;
-
- /* set max image */
- parameters.max_comp_size = max_comp_size;
- parameters.tcp_rates[0] = ((float) (3 * xyz->size().width * xyz->size().height * 12)) / (max_cs_len * 8);
-
- /* Set event manager to null (openjpeg 1.3 bug) */
- cinfo->event_mgr = 0;
-
- /* Setup the encoder parameters using the current image and user parameters */
- opj_setup_encoder (cinfo, ¶meters, xyz->opj_image ());
-
- opj_cio_t* cio = opj_cio_open ((opj_common_ptr) cinfo, 0, 0);
- if (cio == 0) {
- opj_destroy_compress (cinfo);
- throw EncodeError (N_("could not open JPEG2000 stream"));
- }
-
- int const r = opj_encode (cinfo, cio, xyz->opj_image(), 0);
- if (r == 0) {
- opj_cio_close (cio);
- opj_destroy_compress (cinfo);
- throw EncodeError (N_("JPEG2000 encoding failed"));
- }
-
- switch (_frame->eyes()) {
- case EYES_BOTH:
- LOG_GENERAL (N_("Finished locally-encoded frame %1 for mono"), _index);
- break;
- case EYES_LEFT:
- LOG_GENERAL (N_("Finished locally-encoded frame %1 for L"), _index);
- break;
- case EYES_RIGHT:
- LOG_GENERAL (N_("Finished locally-encoded frame %1 for R"), _index);
- break;
- default:
- break;
- }
-
- shared_ptr<EncodedData> enc (new LocallyEncodedData (cio->buffer, cio_tell (cio)));
-
- opj_cio_close (cio);
- free (parameters.cp_comment);
- opj_destroy_compress (cinfo);
-
- return enc;
-}
-
-/** Send this frame to a remote server for J2K encoding, then read the result.
- * @param serv Server to send to.
- * @return Encoded data.
- */
-shared_ptr<EncodedData>
-DCPVideoFrame::encode_remotely (ServerDescription serv)
-{
- boost::asio::io_service io_service;
- boost::asio::ip::tcp::resolver resolver (io_service);
- boost::asio::ip::tcp::resolver::query query (serv.host_name(), raw_convert<string> (Config::instance()->server_port_base ()));
- boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);
-
- shared_ptr<Socket> socket (new Socket);
-
- socket->connect (*endpoint_iterator);
-
- /* Collect all XML metadata */
- xmlpp::Document doc;
- xmlpp::Element* root = doc.create_root_node ("EncodingRequest");
- root->add_child("Version")->add_child_text (raw_convert<string> (SERVER_LINK_VERSION));
- add_metadata (root);
-
- LOG_GENERAL (N_("Sending frame %1 to remote"), _index);
-
- /* Send XML metadata */
- string xml = doc.write_to_string ("UTF-8");
- socket->write (xml.length() + 1);
- socket->write ((uint8_t *) xml.c_str(), xml.length() + 1);
-
- /* Send binary data */
- _frame->send_binary (socket);
-
- /* Read the response (JPEG2000-encoded data); this blocks until the data
- is ready and sent back.
- */
- shared_ptr<EncodedData> e (new RemotelyEncodedData (socket->read_uint32 ()));
- socket->read (e->data(), e->size());
-
- LOG_GENERAL (N_("Finished remotely-encoded frame %1"), _index);
-
- return e;
-}
-
-void
-DCPVideoFrame::add_metadata (xmlpp::Element* el) const
-{
- el->add_child("Index")->add_child_text (raw_convert<string> (_index));
- el->add_child("FramesPerSecond")->add_child_text (raw_convert<string> (_frames_per_second));
- el->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
- el->add_child("Resolution")->add_child_text (raw_convert<string> (int (_resolution)));
- _frame->add_metadata (el);
-}
-
-Eyes
-DCPVideoFrame::eyes () const
-{
- return _frame->eyes ();
-}
-
-EncodedData::EncodedData (int s)
- : _data (new uint8_t[s])
- , _size (s)
-{
-
-}
-
-EncodedData::EncodedData (boost::filesystem::path file)
-{
- _size = boost::filesystem::file_size (file);
- _data = new uint8_t[_size];
-
- FILE* f = fopen_boost (file, "rb");
- if (!f) {
- throw FileError (_("could not open file for reading"), file);
- }
-
- size_t const r = fread (_data, 1, _size, f);
- if (r != size_t (_size)) {
- fclose (f);
- throw FileError (_("could not read encoded data"), file);
- }
-
- fclose (f);
-}
-
-
-EncodedData::~EncodedData ()
-{
- delete[] _data;
-}
-
-/** Write this data to a J2K file.
- * @param Film Film.
- * @param frame DCP frame index.
- */
-void
-EncodedData::write (shared_ptr<const Film> film, int frame, Eyes eyes) const
-{
- boost::filesystem::path const tmp_j2c = film->j2c_path (frame, eyes, true);
-
- FILE* f = fopen_boost (tmp_j2c, "wb");
-
- if (!f) {
- throw WriteFileError (tmp_j2c, errno);
- }
-
- fwrite (_data, 1, _size, f);
- fclose (f);
-
- boost::filesystem::path const real_j2c = film->j2c_path (frame, eyes, false);
-
- /* Rename the file from foo.j2c.tmp to foo.j2c now that it is complete */
- boost::filesystem::rename (tmp_j2c, real_j2c);
-}
-
-void
-EncodedData::write_info (shared_ptr<const Film> film, int frame, Eyes eyes, libdcp::FrameInfo fin) const
-{
- boost::filesystem::path const info = film->info_path (frame, eyes);
- FILE* h = fopen_boost (info, "w");
- fin.write (h);
- fclose (h);
-}
-
-/** Send this data to a socket.
- * @param socket Socket
- */
-void
-EncodedData::send (shared_ptr<Socket> socket)
-{
- socket->write (_size);
- socket->write (_data, _size);
-}
-
-LocallyEncodedData::LocallyEncodedData (uint8_t* d, int s)
- : EncodedData (s)
-{
- memcpy (_data, d, s);
-}
-
-/** @param s Size of data in bytes */
-RemotelyEncodedData::RemotelyEncodedData (int s)
- : EncodedData (s)
-{
-
-}
+++ /dev/null
-/*
- Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
- Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
-
- 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 <openjpeg.h>
-#include <libdcp/picture_asset.h>
-#include <libdcp/picture_asset_writer.h>
-#include "util.h"
-
-/** @file src/dcp_video_frame.h
- * @brief A single frame of video destined for a DCP.
- */
-
-class Film;
-class ServerDescription;
-class Scaler;
-class Image;
-class Log;
-class Subtitle;
-class PlayerVideoFrame;
-
-/** @class EncodedData
- * @brief Container for J2K-encoded data.
- */
-class EncodedData : public boost::noncopyable
-{
-public:
- /** @param s Size of data, in bytes */
- EncodedData (int s);
-
- EncodedData (boost::filesystem::path);
-
- virtual ~EncodedData ();
-
- void send (boost::shared_ptr<Socket> socket);
- void write (boost::shared_ptr<const Film>, int, Eyes) const;
- void write_info (boost::shared_ptr<const Film>, int, Eyes, libdcp::FrameInfo) const;
-
- /** @return data */
- uint8_t* data () const {
- return _data;
- }
-
- /** @return data size, in bytes */
- int size () const {
- return _size;
- }
-
-protected:
- uint8_t* _data; ///< data
- int _size; ///< data size in bytes
-};
-
-/** @class LocallyEncodedData
- * @brief EncodedData that was encoded locally; this class
- * just keeps a pointer to the data, but does no memory
- * management.
- */
-class LocallyEncodedData : public EncodedData
-{
-public:
- /** @param d Data (which will be copied by this class)
- * @param s Size of data, in bytes.
- */
- LocallyEncodedData (uint8_t* d, int s);
-};
-
-/** @class RemotelyEncodedData
- * @brief EncodedData that is being read from a remote server;
- * this class allocates and manages memory for the data.
- */
-class RemotelyEncodedData : public EncodedData
-{
-public:
- RemotelyEncodedData (int s);
-};
-
-/** @class DCPVideoFrame
- * @brief A single frame of video destined for a DCP.
- *
- * Given an Image and some settings, this class knows how to encode
- * the image to J2K either on the local host or on a remote server.
- *
- * Objects of this class are used for the queue that we keep
- * of images that require encoding.
- */
-class DCPVideoFrame : public boost::noncopyable
-{
-public:
- DCPVideoFrame (boost::shared_ptr<const PlayerVideoFrame>, int, int, int, Resolution, boost::shared_ptr<Log>);
- DCPVideoFrame (boost::shared_ptr<const PlayerVideoFrame>, boost::shared_ptr<const cxml::Node>, boost::shared_ptr<Log>);
-
- boost::shared_ptr<EncodedData> encode_locally ();
- boost::shared_ptr<EncodedData> encode_remotely (ServerDescription);
-
- int index () const {
- return _index;
- }
-
- Eyes eyes () const;
-
-private:
-
- void add_metadata (xmlpp::Element *) const;
-
- boost::shared_ptr<const PlayerVideoFrame> _frame;
- int _index; ///< frame index within the DCP's intrinsic duration
- int _frames_per_second; ///< Frames per second that we will use for the DCP
- int _j2k_bandwidth; ///< J2K bandwidth to use
- Resolution _resolution; ///< Resolution (2K or 4K)
-
- boost::shared_ptr<Log> _log; ///< log
-};
--- /dev/null
+/*
+ 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 "dcpomatic_time.h"
+
+using std::ostream;
+
+ContentTime::ContentTime (DCPTime d, FrameRateChange f)
+ : Time (rint (d.get() * f.speed_up))
+{
+
+}
+
+DCPTime min (DCPTime a, DCPTime b)
+{
+ if (a < b) {
+ return a;
+ }
+
+ return b;
+}
+
+ostream &
+operator<< (ostream& s, ContentTime t)
+{
+ s << "[CONT " << t.get() << " " << t.seconds() << "s]";
+ return s;
+}
+
+ostream &
+operator<< (ostream& s, DCPTime t)
+{
+ s << "[DCP " << t.get() << " " << t.seconds() << "s]";
+ return s;
+}
+
+bool
+ContentTimePeriod::overlaps (ContentTimePeriod const & other) const
+{
+ return (from < other.to && to >= other.from);
+}
+
+bool
+ContentTimePeriod::contains (ContentTime const & other) const
+{
+ return (from <= other && other < to);
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_TIME_H
+#define DCPOMATIC_TIME_H
+
+#include <cmath>
+#include <ostream>
+#include <sstream>
+#include <iomanip>
+#include <stdint.h>
+#include "frame_rate_change.h"
+#include "safe_stringstream.h"
+
+class dcpomatic_round_up_test;
+
+class Time;
+
+/** A time in seconds, expressed as a number scaled up by Time::HZ. */
+class Time
+{
+public:
+ Time ()
+ : _t (0)
+ {}
+
+ typedef int64_t Type;
+
+ explicit Time (Type t)
+ : _t (t)
+ {}
+
+ virtual ~Time () {}
+
+ Type get () const {
+ return _t;
+ }
+
+ double seconds () const {
+ return double (_t) / HZ;
+ }
+
+ template <typename T>
+ int64_t frames (T r) const {
+ return rint (_t * r / HZ);
+ }
+
+ template <typename T>
+ void split (T r, int& h, int& m, int& s, int& f) const
+ {
+ /* Do this calculation with frames so that we can round
+ to a frame boundary at the start rather than the end.
+ */
+ int64_t ff = frames (r);
+
+ h = ff / (3600 * r);
+ ff -= h * 3600 * r;
+ m = ff / (60 * r);
+ ff -= m * 60 * r;
+ s = ff / r;
+ ff -= s * r;
+
+ f = static_cast<int> (ff);
+ }
+
+ template <typename T>
+ std::string timecode (T r) const {
+ int h;
+ int m;
+ int s;
+ int f;
+ split (r, h, m, s, f);
+
+ SafeStringStream o;
+ o.width (2);
+ o.fill ('0');
+ o << std::setw(2) << std::setfill('0') << h << ":"
+ << std::setw(2) << std::setfill('0') << m << ":"
+ << std::setw(2) << std::setfill('0') << s << ":"
+ << std::setw(2) << std::setfill('0') << f;
+ return o.str ();
+ }
+
+protected:
+ friend struct dcptime_round_up_test;
+
+ Type _t;
+ static const int HZ = 96000;
+};
+
+class DCPTime;
+
+class ContentTime : public Time
+{
+public:
+ ContentTime () : Time () {}
+ explicit ContentTime (Type t) : Time (t) {}
+ ContentTime (Type n, Type d) : Time (n * HZ / d) {}
+ ContentTime (DCPTime d, FrameRateChange f);
+
+ bool operator< (ContentTime const & o) const {
+ return _t < o._t;
+ }
+
+ bool operator<= (ContentTime const & o) const {
+ return _t <= o._t;
+ }
+
+ bool operator== (ContentTime const & o) const {
+ return _t == o._t;
+ }
+
+ bool operator!= (ContentTime const & o) const {
+ return _t != o._t;
+ }
+
+ bool operator>= (ContentTime const & o) const {
+ return _t >= o._t;
+ }
+
+ bool operator> (ContentTime const & o) const {
+ return _t > o._t;
+ }
+
+ ContentTime operator+ (ContentTime const & o) const {
+ return ContentTime (_t + o._t);
+ }
+
+ ContentTime & operator+= (ContentTime const & o) {
+ _t += o._t;
+ return *this;
+ }
+
+ ContentTime operator- () const {
+ return ContentTime (-_t);
+ }
+
+ ContentTime operator- (ContentTime const & o) const {
+ return ContentTime (_t - o._t);
+ }
+
+ ContentTime & operator-= (ContentTime const & o) {
+ _t -= o._t;
+ return *this;
+ }
+
+ /** Round up to the nearest sampling interval
+ * at some sampling rate.
+ * @param r Sampling rate.
+ */
+ ContentTime round_up (float r) {
+ Type const n = rint (HZ / r);
+ Type const a = _t + n - 1;
+ return ContentTime (a - (a % n));
+ }
+
+ static ContentTime from_seconds (double s) {
+ return ContentTime (s * HZ);
+ }
+
+ template <class T>
+ static ContentTime from_frames (int64_t f, T r) {
+ assert (r > 0);
+ return ContentTime (f * HZ / r);
+ }
+
+ static ContentTime max () {
+ return ContentTime (INT64_MAX);
+ }
+};
+
+std::ostream& operator<< (std::ostream& s, ContentTime t);
+
+class ContentTimePeriod
+{
+public:
+ ContentTimePeriod () {}
+ ContentTimePeriod (ContentTime f, ContentTime t)
+ : from (f)
+ , to (t)
+ {}
+
+ ContentTime from;
+ ContentTime to;
+
+ ContentTimePeriod operator+ (ContentTime const & o) const {
+ return ContentTimePeriod (from + o, to + o);
+ }
+
+ bool overlaps (ContentTimePeriod const & o) const;
+ bool contains (ContentTime const & o) const;
+};
+
+class DCPTime : public Time
+{
+public:
+ DCPTime () : Time () {}
+ explicit DCPTime (Type t) : Time (t) {}
+ DCPTime (ContentTime t, FrameRateChange c) : Time (rint (t.get() / c.speed_up)) {}
+
+ bool operator< (DCPTime const & o) const {
+ return _t < o._t;
+ }
+
+ bool operator<= (DCPTime const & o) const {
+ return _t <= o._t;
+ }
+
+ bool operator== (DCPTime const & o) const {
+ return _t == o._t;
+ }
+
+ bool operator!= (DCPTime const & o) const {
+ return _t != o._t;
+ }
+
+ bool operator>= (DCPTime const & o) const {
+ return _t >= o._t;
+ }
+
+ bool operator> (DCPTime const & o) const {
+ return _t > o._t;
+ }
+
+ DCPTime operator+ (DCPTime const & o) const {
+ return DCPTime (_t + o._t);
+ }
+
+ DCPTime & operator+= (DCPTime const & o) {
+ _t += o._t;
+ return *this;
+ }
+
+ DCPTime operator- () const {
+ return DCPTime (-_t);
+ }
+
+ DCPTime operator- (DCPTime const & o) const {
+ return DCPTime (_t - o._t);
+ }
+
+ DCPTime & operator-= (DCPTime const & o) {
+ _t -= o._t;
+ return *this;
+ }
+
+ /** Round up to the nearest sampling interval
+ * at some sampling rate.
+ * @param r Sampling rate.
+ */
+ DCPTime round_up (float r) {
+ Type const n = rint (HZ / r);
+ Type const a = _t + n - 1;
+ return DCPTime (a - (a % n));
+ }
+
+ DCPTime abs () const {
+ return DCPTime (std::abs (_t));
+ }
+
+ static DCPTime from_seconds (double s) {
+ return DCPTime (s * HZ);
+ }
+
+ template <class T>
+ static DCPTime from_frames (int64_t f, T r) {
+ assert (r > 0);
+ return DCPTime (f * HZ / r);
+ }
+
+ static DCPTime delta () {
+ return DCPTime (1);
+ }
+
+ static DCPTime max () {
+ return DCPTime (INT64_MAX);
+ }
+};
+
+DCPTime min (DCPTime a, DCPTime b);
+std::ostream& operator<< (std::ostream& s, DCPTime t);
+
+#endif
+++ /dev/null
-/*
- 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/decoder.cc
- * @brief Parent class for decoders of content.
- */
-
-#include "film.h"
-#include "decoder.h"
-
-#include "i18n.h"
-
-using boost::shared_ptr;
-
-/** @param f Film.
- * @param o Decode options.
- */
-Decoder::Decoder (shared_ptr<const Film> f)
- : _film (f)
-{
-
-}
*/
/** @file src/decoder.h
- * @brief Parent class for decoders of content.
+ * @brief Decoder class.
*/
#ifndef DCPOMATIC_DECODER_H
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/utility.hpp>
+#include "types.h"
+#include "dcpomatic_time.h"
-class Film;
+class Decoded;
/** @class Decoder.
* @brief Parent class for decoders of content.
class Decoder : public boost::noncopyable
{
public:
- Decoder (boost::shared_ptr<const Film>);
virtual ~Decoder () {}
- /** Perform one decode pass of the content, which may or may not
- * cause the object to emit some data.
+protected:
+ /** Seek so that the next pass() will yield the next thing
+ * (video/sound frame, subtitle etc.) at or after the requested
+ * time. Pass accurate = true to try harder to ensure that, at worst,
+ * the next thing we yield comes before `time'. This may entail
+ * seeking some way before `time' to be on the safe side.
+ * Alternatively, if seeking is 100% accurate for this decoder,
+ * it may seek to just the right spot.
*/
- virtual void pass () = 0;
- virtual bool done () const = 0;
-
-protected:
-
- virtual void flush () {};
-
- /** The Film that we are decoding in */
- boost::weak_ptr<const Film> _film;
+ virtual void seek (ContentTime time, bool accurate) = 0;
+ virtual bool pass () = 0;
};
#endif
using namespace std;
DolbyCP750::DolbyCP750 ()
- : SoundProcessor ("dolby_cp750", _("Dolby CP650 and CP750"))
+ : CinemaSoundProcessor ("dolby_cp750", _("Dolby CP650 and CP750"))
{
}
*/
-#include "sound_processor.h"
+#include "cinema_sound_processor.h"
-class DolbyCP750 : public SoundProcessor
+class DolbyCP750 : public CinemaSoundProcessor
{
public:
DolbyCP750 ();
--- /dev/null
+/*
+ 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 "encoded_data.h"
+#include "cross.h"
+#include "exceptions.h"
+#include "film.h"
+
+#include "i18n.h"
+
+using boost::shared_ptr;
+
+EncodedData::EncodedData (int s)
+ : _data (new uint8_t[s])
+ , _size (s)
+{
+
+}
+
+EncodedData::EncodedData (uint8_t const * d, int s)
+ : _data (new uint8_t[s])
+ , _size (s)
+{
+ memcpy (_data, d, s);
+}
+
+EncodedData::EncodedData (boost::filesystem::path file)
+{
+ _size = boost::filesystem::file_size (file);
+ _data = new uint8_t[_size];
+
+ FILE* f = fopen_boost (file, "rb");
+ if (!f) {
+ throw FileError (_("could not open file for reading"), file);
+ }
+
+ size_t const r = fread (_data, 1, _size, f);
+ if (r != size_t (_size)) {
+ fclose (f);
+ throw FileError (_("could not read encoded data"), file);
+ }
+
+ fclose (f);
+}
+
+
+EncodedData::~EncodedData ()
+{
+ delete[] _data;
+}
+
+/** Write this data to a J2K file.
+ * @param Film Film.
+ * @param frame DCP frame index.
+ */
+void
+EncodedData::write (shared_ptr<const Film> film, int frame, Eyes eyes) const
+{
+ boost::filesystem::path const tmp_j2c = film->j2c_path (frame, eyes, true);
+
+ FILE* f = fopen_boost (tmp_j2c, "wb");
+
+ if (!f) {
+ throw WriteFileError (tmp_j2c, errno);
+ }
+
+ fwrite (_data, 1, _size, f);
+ fclose (f);
+
+ boost::filesystem::path const real_j2c = film->j2c_path (frame, eyes, false);
+
+ /* Rename the file from foo.j2c.tmp to foo.j2c now that it is complete */
+ boost::filesystem::rename (tmp_j2c, real_j2c);
+}
+
+void
+EncodedData::write_info (shared_ptr<const Film> film, int frame, Eyes eyes, dcp::FrameInfo fin) const
+{
+ boost::filesystem::path const info = film->info_path (frame, eyes);
+ FILE* h = fopen_boost (info, "w");
+ fin.write (h);
+ fclose (h);
+}
+
+/** Send this data to a socket.
+ * @param socket Socket
+ */
+void
+EncodedData::send (shared_ptr<Socket> socket)
+{
+ socket->write (_size);
+ socket->write (_data, _size);
+}
+
+LocallyEncodedData::LocallyEncodedData (uint8_t* d, int s)
+ : EncodedData (s)
+{
+ memcpy (_data, d, s);
+}
+
+/** @param s Size of data in bytes */
+RemotelyEncodedData::RemotelyEncodedData (int s)
+ : EncodedData (s)
+{
+
+}
--- /dev/null
+/*
+ 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/noncopyable.hpp>
+#include <boost/filesystem.hpp>
+#include <dcp/picture_mxf_writer.h>
+#include "types.h"
+
+class Socket;
+class Film;
+
+/** @class EncodedData
+ * @brief Container for J2K-encoded data.
+ */
+class EncodedData : public boost::noncopyable
+{
+public:
+ /** @param s Size of data, in bytes */
+ EncodedData (int s);
+ EncodedData (uint8_t const * d, int s);
+
+ EncodedData (boost::filesystem::path);
+
+ virtual ~EncodedData ();
+
+ void send (boost::shared_ptr<Socket> socket);
+ void write (boost::shared_ptr<const Film>, int, Eyes) const;
+ void write_info (boost::shared_ptr<const Film>, int, Eyes, dcp::FrameInfo) const;
+
+ /** @return data */
+ uint8_t* data () const {
+ return _data;
+ }
+
+ /** @return data size, in bytes */
+ int size () const {
+ return _size;
+ }
+
+protected:
+ uint8_t* _data; ///< data
+ int _size; ///< data size in bytes
+};
+
+/** @class LocallyEncodedData
+ * @brief EncodedData that was encoded locally; this class
+ * just keeps a pointer to the data, but does no memory
+ * management.
+ */
+class LocallyEncodedData : public EncodedData
+{
+public:
+ /** @param d Data (which will be copied by this class)
+ * @param s Size of data, in bytes.
+ */
+ LocallyEncodedData (uint8_t* d, int s);
+};
+
+/** @class RemotelyEncodedData
+ * @brief EncodedData that is being read from a remote server;
+ * this class allocates and manages memory for the data.
+ */
+class RemotelyEncodedData : public EncodedData
+{
+public:
+ RemotelyEncodedData (int s);
+};
#include "film.h"
#include "log.h"
#include "config.h"
-#include "dcp_video_frame.h"
+#include "dcp_video.h"
#include "server.h"
#include "cross.h"
#include "writer.h"
#include "server_finder.h"
#include "player.h"
-#include "player_video_frame.h"
+#include "player_video.h"
#include "i18n.h"
int const Encoder::_history_size = 25;
/** @param f Film that we are encoding */
-Encoder::Encoder (shared_ptr<const Film> f, weak_ptr<Job> j)
+Encoder::Encoder (shared_ptr<const Film> f, weak_ptr<Job> j, shared_ptr<Writer> writer)
: _film (f)
, _job (j)
, _video_frames_out (0)
, _terminate (false)
+ , _writer (writer)
{
- _have_a_real_frame[EYES_BOTH] = false;
- _have_a_real_frame[EYES_LEFT] = false;
- _have_a_real_frame[EYES_RIGHT] = false;
+
}
Encoder::~Encoder ()
}
void
-Encoder::process_begin ()
+Encoder::begin ()
{
for (int i = 0; i < Config::instance()->num_local_encoding_threads (); ++i) {
_threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, optional<ServerDescription> ())));
}
- _writer.reset (new Writer (_film, _job));
ServerFinder::instance()->connect (boost::bind (&Encoder::server_found, this, _1));
}
void
-Encoder::process_end ()
+Encoder::end ()
{
boost::mutex::scoped_lock lock (_mutex);
So just mop up anything left in the queue here.
*/
- for (list<shared_ptr<DCPVideoFrame> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
+ for (list<shared_ptr<DCPVideo> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
LOG_GENERAL (N_("Encode left-over frame %1"), (*i)->index ());
try {
_writer->write ((*i)->encode_locally(), (*i)->index (), (*i)->eyes ());
LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
}
}
-
- _writer->finish ();
- _writer.reset ();
}
/** @return an estimate of the current number of frames we are encoding per second,
}
}
+/** Called in order, so each time this is called the supplied frame is the one
+ * after the previous one.
+ */
void
-Encoder::process_video (shared_ptr<PlayerVideoFrame> pvf, bool same)
+Encoder::enqueue (shared_ptr<PlayerVideo> pv)
{
_waker.nudge ();
rethrow ();
if (_writer->can_fake_write (_video_frames_out)) {
- _writer->fake_write (_video_frames_out, pvf->eyes ());
- _have_a_real_frame[pvf->eyes()] = false;
- frame_done ();
- } else if (same && _have_a_real_frame[pvf->eyes()]) {
- /* Use the last frame that we encoded. */
- _writer->repeat (_video_frames_out, pvf->eyes());
+ /* We can fake-write this frame */
+ _writer->fake_write (_video_frames_out, pv->eyes ());
frame_done ();
+ } else if (pv->has_j2k ()) {
+ /* This frame already has JPEG2000 data, so just write it */
+ _writer->write (pv->j2k(), _video_frames_out, pv->eyes ());
} else {
/* Queue this new frame for encoding */
LOG_TIMING ("adding to queue of %1", _queue.size ());
- _queue.push_back (shared_ptr<DCPVideoFrame> (
- new DCPVideoFrame (
- pvf, _video_frames_out, _film->video_frame_rate(),
- _film->j2k_bandwidth(), _film->resolution(), _film->log()
+ _queue.push_back (shared_ptr<DCPVideo> (
+ new DCPVideo (
+ pv,
+ _video_frames_out,
+ _film->video_frame_rate(),
+ _film->j2k_bandwidth(),
+ _film->resolution(),
+ _film->burn_subtitles(),
+ _film->log()
)
));
waiting on that.
*/
_empty_condition.notify_all ();
- _have_a_real_frame[pvf->eyes()] = true;
}
- if (pvf->eyes() != EYES_LEFT) {
+ if (pv->eyes() != EYES_LEFT) {
++_video_frames_out;
}
}
-void
-Encoder::process_audio (shared_ptr<const AudioBuffers> data)
-{
- _writer->write (data);
-}
-
void
Encoder::terminate_threads ()
{
encodings.
*/
int remote_backoff = 0;
+ shared_ptr<DCPVideo> last_dcp_video;
+ shared_ptr<EncodedData> last_encoded;
while (true) {
}
LOG_TIMING ("[%1] encoder thread wakes with queue of %2", boost::this_thread::get_id(), _queue.size());
- shared_ptr<DCPVideoFrame> vf = _queue.front ();
+ shared_ptr<DCPVideo> vf = _queue.front ();
LOG_TIMING ("[%1] encoder thread pops frame %2 (%3) from queue", boost::this_thread::get_id(), vf->index(), vf->eyes ());
_queue.pop_front ();
shared_ptr<EncodedData> encoded;
- if (server) {
- try {
- encoded = vf->encode_remotely (server.get ());
-
- if (remote_backoff > 0) {
- LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
+ if (last_dcp_video && vf->same (last_dcp_video)) {
+ /* We already have encoded data for the same input as this one, so take a short-cut */
+ encoded = last_encoded;
+ } else {
+ /* We need to encode this input */
+ if (server) {
+ try {
+ encoded = vf->encode_remotely (server.get ());
+
+ if (remote_backoff > 0) {
+ LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
+ }
+
+ /* This job succeeded, so remove any backoff */
+ remote_backoff = 0;
+
+ } catch (std::exception& e) {
+ if (remote_backoff < 60) {
+ /* back off more */
+ remote_backoff += 10;
+ }
+ LOG_ERROR (
+ N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
+ vf->index(), server->host_name(), e.what(), remote_backoff
+ );
}
- /* This job succeeded, so remove any backoff */
- remote_backoff = 0;
-
- } catch (std::exception& e) {
- if (remote_backoff < 60) {
- /* back off more */
- remote_backoff += 10;
+ } else {
+ try {
+ LOG_TIMING ("[%1] encoder thread begins local encode of %2", boost::this_thread::get_id(), vf->index());
+ encoded = vf->encode_locally ();
+ LOG_TIMING ("[%1] encoder thread finishes local encode of %2", boost::this_thread::get_id(), vf->index());
+ } catch (std::exception& e) {
+ LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
}
- LOG_ERROR (
- N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
- vf->index(), server->host_name(), e.what(), remote_backoff
- );
- }
-
- } else {
- try {
- LOG_TIMING ("[%1] encoder thread begins local encode of %2", boost::this_thread::get_id(), vf->index());
- encoded = vf->encode_locally ();
- LOG_TIMING ("[%1] encoder thread finishes local encode of %2", boost::this_thread::get_id(), vf->index());
- } catch (std::exception& e) {
- LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
}
}
+ last_dcp_video = vf;
+ last_encoded = encoded;
+
if (encoded) {
_writer->write (encoded, vf->index (), vf->eyes ());
frame_done ();
#include "util.h"
#include "config.h"
#include "cross.h"
+#include "exceptions.h"
class Image;
class AudioBuffers;
class Film;
class ServerDescription;
-class DCPVideoFrame;
+class DCPVideo;
class EncodedData;
class Writer;
class Job;
class ServerFinder;
-class PlayerVideoFrame;
+class PlayerVideo;
/** @class Encoder
- * @brief Encoder to J2K and WAV for DCP.
+ * @brief Class to manage encoding to JPEG2000.
*
- * Video is supplied to process_video as RGB frames, and audio
- * is supplied as uncompressed PCM in blocks of various sizes.
+ * This class keeps a queue of frames to be encoded and distributes
+ * the work around threads and encoding servers.
*/
class Encoder : public boost::noncopyable, public ExceptionStore
{
public:
- Encoder (boost::shared_ptr<const Film> f, boost::weak_ptr<Job>);
+ Encoder (boost::shared_ptr<const Film> f, boost::weak_ptr<Job>, boost::shared_ptr<Writer>);
virtual ~Encoder ();
/** Called to indicate that a processing run is about to begin */
- void process_begin ();
+ void begin ();
/** Call with a frame of video.
- * @param pvf Video frame image.
- * @param same true if pvf is the same as the last time we were called.
+ * @param f Video frame.
*/
- void process_video (boost::shared_ptr<PlayerVideoFrame> pvf, bool same);
-
- /** Call with some audio data */
- void process_audio (boost::shared_ptr<const AudioBuffers>);
+ void enqueue (boost::shared_ptr<PlayerVideo> f);
/** Called when a processing run has finished */
- void process_end ();
+ void end ();
float current_encoding_rate () const;
int video_frames_out () const;
/** Number of video frames written for the DCP so far */
int _video_frames_out;
- bool _have_a_real_frame[EYES_COUNT];
bool _terminate;
- std::list<boost::shared_ptr<DCPVideoFrame> > _queue;
+ std::list<boost::shared_ptr<DCPVideo> > _queue;
std::list<boost::thread *> _threads;
mutable boost::mutex _mutex;
/** condition to manage thread wakeups when we have nothing to do */
/*
- 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
}
-PixelFormatError::PixelFormatError (std::string o, AVPixelFormat f)
+PixelFormatError::PixelFormatError (string o, AVPixelFormat f)
: StringError (String::compose (_("Cannot handle pixel format %1 during %2"), f, o))
{
}
+
+SubRipError::SubRipError (string saw, string expecting, boost::filesystem::path f)
+ : FileError (String::compose (_("Error in SubRip file: saw %1 while expecting %2"), saw.empty() ? "[nothing]" : saw, expecting), f)
+{
+
+}
+
+InvalidSignerError::InvalidSignerError ()
+ : StringError (_("The certificate chain for signing is invalid"))
+{
+
+}
/*
- 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 DCPOMATIC_EXCEPTIONS_H
-#define DCPOMATIC_EXCEPTIONS_H
-
-/** @file src/exceptions.h
+/** @file src/lib/exceptions.h
* @brief Our exceptions.
*/
+#ifndef DCPOMATIC_EXCEPTIONS_H
+#define DCPOMATIC_EXCEPTIONS_H
+
#include <stdexcept>
#include <cstring>
#include <boost/exception/all.hpp>
{}
};
-/** @class NetworkError.
+/** @class NetworkError
* @brief Indicates some problem with communication on the network.
*/
class NetworkError : public StringError
{}
};
+/** @class KDMError
+ * @brief A problem with a KDM.
+ */
class KDMError : public StringError
{
public:
{}
};
+/** @class PixelFormatError
+ * @brief A problem with an unsupported pixel format.
+ */
class PixelFormatError : public StringError
{
public:
PixelFormatError (std::string o, AVPixelFormat f);
};
-/** A parent class for classes which have a need to catch and
- * re-throw exceptions. This is intended for classes
- * which run their own thread; they should do something like
+/** @class SubRipError
+ * @brief An error that occurs while parsing a SubRip file.
+ */
+class SubRipError : public FileError
+{
+public:
+ SubRipError (std::string, std::string, boost::filesystem::path);
+};
+
+class DCPError : public StringError
+{
+public:
+ DCPError (std::string s)
+ : StringError (s)
+ {}
+};
+
+class InvalidSignerError : public StringError
+{
+public:
+ InvalidSignerError ();
+};
+
+/** @class ExceptionStore
+ * @brief A parent class for classes which have a need to catch and
+ * re-throw exceptions.
+ *
+ * This is intended for classes which run their own thread; they should do
+ * something like
*
* void my_thread ()
* try {
mutable boost::mutex _mutex;
};
-
-
#endif
/*
- 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 <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "ffmpeg.h"
#include "ffmpeg_content.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
#include "exceptions.h"
#include "util.h"
using std::string;
using std::cout;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
boost::mutex FFmpeg::_mutex;
, _video_stream (-1)
{
setup_general ();
- setup_video ();
- setup_audio ();
+ setup_decoders ();
}
FFmpeg::~FFmpeg ()
boost::mutex::scoped_lock lm (_mutex);
for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
- AVCodecContext* context = _format_context->streams[i]->codec;
- if (context->codec_type == AVMEDIA_TYPE_VIDEO || context->codec_type == AVMEDIA_TYPE_AUDIO) {
- avcodec_close (context);
- }
+ avcodec_close (_format_context->streams[i]->codec);
}
av_frame_free (&_frame);
-
avformat_close_input (&_format_context);
}
}
void
-FFmpeg::setup_video ()
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- assert (_video_stream >= 0);
- AVCodecContext* context = _format_context->streams[_video_stream]->codec;
- AVCodec* codec = avcodec_find_decoder (context->codec_id);
-
- if (codec == 0) {
- throw DecodeError (_("could not find video decoder"));
- }
-
- if (avcodec_open2 (context, codec, 0) < 0) {
- throw DecodeError (N_("could not open video decoder"));
- }
-}
-
-void
-FFmpeg::setup_audio ()
+FFmpeg::setup_decoders ()
{
boost::mutex::scoped_lock lm (_mutex);
for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
AVCodecContext* context = _format_context->streams[i]->codec;
- if (context->codec_type != AVMEDIA_TYPE_AUDIO) {
- continue;
- }
AVCodec* codec = avcodec_find_decoder (context->codec_id);
- if (codec == 0) {
- throw DecodeError (_("could not find audio decoder"));
- }
-
- if (avcodec_open2 (context, codec, 0) < 0) {
- throw DecodeError (N_("could not open audio decoder"));
+ if (codec) {
+ if (avcodec_open2 (context, codec, 0) < 0) {
+ throw DecodeError (N_("could not open decoder"));
+ }
}
+
+ /* We are silently ignoring any failures to find suitable decoders here */
}
}
-
AVCodecContext *
FFmpeg::video_codec_context () const
{
AVCodecContext *
FFmpeg::audio_codec_context () const
{
+ if (!_ffmpeg_content->audio_stream ()) {
+ return 0;
+ }
+
return _ffmpeg_content->audio_stream()->stream(_format_context)->codec;
}
+AVCodecContext *
+FFmpeg::subtitle_codec_context () const
+{
+ if (!_ffmpeg_content->subtitle_stream ()) {
+ return 0;
+ }
+
+ return _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
+}
+
int
FFmpeg::avio_read (uint8_t* buffer, int const amount)
{
protected:
AVCodecContext* video_codec_context () const;
AVCodecContext* audio_codec_context () const;
+ AVCodecContext* subtitle_codec_context () const;
boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
private:
void setup_general ();
- void setup_video ();
- void setup_audio ();
+ void setup_decoders ();
};
#endif
--- /dev/null
+/*
+ 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 <libxml++/libxml++.h>
+#include <libcxml/cxml.h>
+#include <dcp/raw_convert.h>
+#include "ffmpeg_audio_stream.h"
+
+using std::string;
+using dcp::raw_convert;
+
+FFmpegAudioStream::FFmpegAudioStream (cxml::ConstNodePtr node, int version)
+ : FFmpegStream (node)
+ , _frame_rate (node->number_child<int> ("FrameRate"))
+ , _channels (node->number_child<int64_t> ("Channels"))
+ , _mapping (node->node_child ("Mapping"), version)
+{
+ first_audio = node->optional_number_child<double> ("FirstAudio");
+}
+
+void
+FFmpegAudioStream::as_xml (xmlpp::Node* root) const
+{
+ FFmpegStream::as_xml (root);
+ root->add_child("FrameRate")->add_child_text (raw_convert<string> (_frame_rate));
+ root->add_child("Channels")->add_child_text (raw_convert<string> (_channels));
+ if (first_audio) {
+ root->add_child("FirstAudio")->add_child_text (raw_convert<string> (first_audio.get().get()));
+ }
+ _mapping.as_xml (root->add_child("Mapping"));
+}
--- /dev/null
+/*
+ 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 "ffmpeg_stream.h"
+#include "audio_mapping.h"
+#include "dcpomatic_time.h"
+
+struct ffmpeg_pts_offset_test;
+
+class FFmpegAudioStream : public FFmpegStream
+{
+public:
+ FFmpegAudioStream (std::string n, int i, int f, int c)
+ : FFmpegStream (n, i)
+ , _frame_rate (f)
+ , _channels (c)
+ , _mapping (c)
+ {
+ _mapping.make_default ();
+ }
+
+ FFmpegAudioStream (cxml::ConstNodePtr, int);
+
+ void as_xml (xmlpp::Node *) const;
+
+ int frame_rate () const {
+ return _frame_rate;
+ }
+
+ int channels () const {
+ return _channels;
+ }
+
+ AudioMapping mapping () const {
+ return _mapping;
+ }
+
+ void set_mapping (AudioMapping m) {
+ _mapping = m;
+ }
+
+ boost::optional<ContentTime> first_audio;
+
+private:
+ friend struct ffmpeg_pts_offset_test;
+
+ /* Constructor for tests */
+ FFmpegAudioStream ()
+ : FFmpegStream ("", 0)
+ , _frame_rate (0)
+ , _channels (0)
+ , _mapping (1)
+ {}
+
+ int _frame_rate;
+ int _channels;
+ AudioMapping _mapping;
+};
#include <libavformat/avformat.h>
}
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "ffmpeg_content.h"
#include "ffmpeg_examiner.h"
+#include "ffmpeg_subtitle_stream.h"
+#include "ffmpeg_audio_stream.h"
#include "compose.hpp"
#include "job.h"
#include "util.h"
using std::pair;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
int const FFmpegContentProperty::SUBTITLE_STREAMS = 100;
int const FFmpegContentProperty::SUBTITLE_STREAM = 101;
}
-FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version, list<string>& notes)
+FFmpegContent::FFmpegContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version, list<string>& notes)
: Content (f, node)
, VideoContent (f, node, version)
, AudioContent (f, node)
for (size_t i = 0; i < c.size(); ++i) {
shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c[i]);
- if (f->with_subtitles() && *(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) {
+ if (fc->use_subtitles() && *(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) {
throw JoinError (_("Content to be joined must use the same subtitle stream."));
}
}
if (_first_video) {
- node->add_child("FirstVideo")->add_child_text (raw_convert<string> (_first_video.get ()));
+ node->add_child("FirstVideo")->add_child_text (raw_convert<string> (_first_video.get().get()));
}
}
Content::examine (job);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (shared_from_this ()));
+ take_from_video_examiner (examiner);
- VideoContent::Frame video_length = 0;
- video_length = examiner->video_length ();
- LOG_GENERAL ("Video length obtained from header as %1 frames", video_length);
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
{
boost::mutex::scoped_lock lm (_mutex);
- _video_length = video_length;
-
_subtitle_streams = examiner->subtitle_streams ();
if (!_subtitle_streams.empty ()) {
_subtitle_stream = _subtitle_streams.front ();
_first_video = examiner->first_video ();
}
- take_from_video_examiner (examiner);
-
- signal_changed (ContentProperty::LENGTH);
signal_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
signal_changed (FFmpegContentProperty::AUDIO_STREAMS);
string
FFmpegContent::information () const
{
- if (video_length() == 0 || video_frame_rate() == 0) {
+ if (video_length() == ContentTime (0) || video_frame_rate() == 0) {
return "";
}
SafeStringStream s;
- s << String::compose (_("%1 frames; %2 frames per second"), video_length_after_3d_combine(), video_frame_rate()) << "\n";
+ s << String::compose (_("%1 frames; %2 frames per second"), video_length_after_3d_combine().frames (video_frame_rate()), video_frame_rate()) << "\n";
s << VideoContent::information ();
return s.str ();
signal_changed (FFmpegContentProperty::AUDIO_STREAM);
}
-AudioContent::Frame
+ContentTime
FFmpegContent::audio_length () const
{
- int const cafr = content_audio_frame_rate ();
- float const vfr = video_frame_rate ();
- VideoContent::Frame const vl = video_length_after_3d_combine ();
-
- boost::mutex::scoped_lock lm (_mutex);
- if (!_audio_stream) {
- return 0;
+ if (!audio_stream ()) {
+ return ContentTime ();
}
-
- return video_frames_to_audio_frames (vl, cafr, vfr);
+
+ return video_length ();
}
int
return 0;
}
- return _audio_stream->channels;
+ return _audio_stream->channels ();
}
int
-FFmpegContent::content_audio_frame_rate () const
+FFmpegContent::audio_frame_rate () const
{
boost::mutex::scoped_lock lm (_mutex);
return 0;
}
- return _audio_stream->frame_rate;
+ return _audio_stream->frame_rate ();
}
bool
return a._id != b._id;
}
-FFmpegStream::FFmpegStream (shared_ptr<const cxml::Node> node)
- : name (node->string_child ("Name"))
- , _id (node->number_child<int> ("Id"))
-{
-
-}
-
-void
-FFmpegStream::as_xml (xmlpp::Node* root) const
-{
- root->add_child("Name")->add_child_text (name);
- root->add_child("Id")->add_child_text (raw_convert<string> (_id));
-}
-
-FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node, int version)
- : FFmpegStream (node)
- , mapping (node->node_child ("Mapping"), version)
-{
- frame_rate = node->number_child<int> ("FrameRate");
- channels = node->number_child<int64_t> ("Channels");
- first_audio = node->optional_number_child<double> ("FirstAudio");
-}
-
-void
-FFmpegAudioStream::as_xml (xmlpp::Node* root) const
-{
- FFmpegStream::as_xml (root);
- root->add_child("FrameRate")->add_child_text (raw_convert<string> (frame_rate));
- root->add_child("Channels")->add_child_text (raw_convert<string> (channels));
- if (first_audio) {
- root->add_child("FirstAudio")->add_child_text (raw_convert<string> (first_audio.get ()));
- }
- mapping.as_xml (root->add_child("Mapping"));
-}
-
-bool
-FFmpegStream::uses_index (AVFormatContext const * fc, int index) const
-{
- size_t i = 0;
- while (i < fc->nb_streams) {
- if (fc->streams[i]->id == _id) {
- return int (i) == index;
- }
- ++i;
- }
-
- return false;
-}
-
-AVStream *
-FFmpegStream::stream (AVFormatContext const * fc) const
-{
- size_t i = 0;
- while (i < fc->nb_streams) {
- if (fc->streams[i]->id == _id) {
- return fc->streams[i];
- }
- ++i;
- }
-
- assert (false);
- return 0;
-}
-
-/** Construct a SubtitleStream from a value returned from to_string().
- * @param t String returned from to_string().
- * @param v State file version.
- */
-FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node)
- : FFmpegStream (node)
-{
-
-}
-
-void
-FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
-{
- FFmpegStream::as_xml (root);
-}
-
-Time
+DCPTime
FFmpegContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate (), film->video_frame_rate ());
- return video_length_after_3d_combine() * frc.factor() * TIME_HZ / film->video_frame_rate ();
+ return DCPTime (video_length_after_3d_combine(), FrameRateChange (video_frame_rate (), film->video_frame_rate ()));
}
AudioMapping
return AudioMapping ();
}
- return _audio_stream->mapping;
+ return _audio_stream->mapping ();
}
void
void
FFmpegContent::set_audio_mapping (AudioMapping m)
{
- audio_stream()->mapping = m;
- signal_changed (AudioContentProperty::AUDIO_MAPPING);
+ audio_stream()->set_mapping (m);
+ AudioContent::set_audio_mapping (m);
}
string
p /= name;
return p;
}
+
+list<ContentTimePeriod>
+FFmpegContent::subtitles_during (ContentTimePeriod period, bool starting) const
+{
+ list<ContentTimePeriod> d;
+
+ shared_ptr<FFmpegSubtitleStream> stream = subtitle_stream ();
+ if (!stream) {
+ return d;
+ }
+
+ /* XXX: inefficient */
+ for (vector<ContentTimePeriod>::const_iterator i = stream->periods.begin(); i != stream->periods.end(); ++i) {
+ if ((starting && period.contains (i->from)) || (!starting && period.overlaps (*i))) {
+ d.push_back (*i);
+ }
+ }
+
+ return d;
+}
+
+bool
+FFmpegContent::has_subtitles () const
+{
+ return !subtitle_streams().empty ();
+}
/*
- 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
#define DCPOMATIC_FFMPEG_CONTENT_H
#include <boost/enable_shared_from_this.hpp>
+#include <boost/lexical_cast.hpp>
#include "video_content.h"
#include "audio_content.h"
#include "subtitle_content.h"
struct AVStream;
class Filter;
-class ffmpeg_pts_offset_test;
-
-class FFmpegStream
-{
-public:
- FFmpegStream (std::string n, int i)
- : name (n)
- , _id (i)
- {}
-
- FFmpegStream (boost::shared_ptr<const cxml::Node>);
-
- void as_xml (xmlpp::Node *) const;
-
- /** @param c An AVFormatContext.
- * @param index A stream index within the AVFormatContext.
- * @return true if this FFmpegStream uses the given stream index.
- */
- bool uses_index (AVFormatContext const * c, int index) const;
- AVStream* stream (AVFormatContext const * c) const;
-
- std::string technical_summary () const {
- return "id " + boost::lexical_cast<std::string> (_id);
- }
-
- std::string identifier () const {
- return boost::lexical_cast<std::string> (_id);
- }
-
- std::string name;
-
- friend bool operator== (FFmpegStream const & a, FFmpegStream const & b);
- friend bool operator!= (FFmpegStream const & a, FFmpegStream const & b);
-
-private:
- int _id;
-};
-
-class FFmpegAudioStream : public FFmpegStream
-{
-public:
- FFmpegAudioStream (std::string n, int i, int f, int c)
- : FFmpegStream (n, i)
- , frame_rate (f)
- , channels (c)
- , mapping (c)
- {
- mapping.make_default ();
- }
-
- FFmpegAudioStream (boost::shared_ptr<const cxml::Node>, int);
-
- void as_xml (xmlpp::Node *) const;
-
- int frame_rate;
- int channels;
- AudioMapping mapping;
- boost::optional<double> first_audio;
-
-private:
- friend class ffmpeg_pts_offset_test;
-
- /* Constructor for tests */
- FFmpegAudioStream ()
- : FFmpegStream ("", 0)
- , frame_rate (0)
- , channels (0)
- , mapping (1)
- {}
-};
-
-class FFmpegSubtitleStream : public FFmpegStream
-{
-public:
- FFmpegSubtitleStream (std::string n, int i)
- : FFmpegStream (n, i)
- {}
-
- FFmpegSubtitleStream (boost::shared_ptr<const cxml::Node>);
-
- void as_xml (xmlpp::Node *) const;
-};
+class FFmpegSubtitleStream;
+class FFmpegAudioStream;
+struct ffmpeg_pts_offset_test;
class FFmpegContentProperty : public VideoContentProperty
{
{
public:
FFmpegContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- FFmpegContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int version, std::list<std::string> &);
+ FFmpegContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int version, std::list<std::string> &);
FFmpegContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
boost::shared_ptr<FFmpegContent> shared_from_this () {
std::string technical_summary () const;
std::string information () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
std::string identifier () const;
/* AudioContent */
int audio_channels () const;
- AudioContent::Frame audio_length () const;
- int content_audio_frame_rate () const;
+ ContentTime audio_length () const;
+ int audio_frame_rate () const;
AudioMapping audio_mapping () const;
void set_audio_mapping (AudioMapping);
boost::filesystem::path audio_analysis_path () const;
+ /* SubtitleContent */
+ bool has_subtitles () const;
+
void set_filters (std::vector<Filter const *> const &);
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
void set_subtitle_stream (boost::shared_ptr<FFmpegSubtitleStream>);
void set_audio_stream (boost::shared_ptr<FFmpegAudioStream>);
- boost::optional<double> first_video () const {
+ boost::optional<ContentTime> first_video () const {
boost::mutex::scoped_lock lm (_mutex);
return _first_video;
}
+ std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const;
+
private:
- friend class ffmpeg_pts_offset_test;
+ friend struct ffmpeg_pts_offset_test;
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
boost::shared_ptr<FFmpegSubtitleStream> _subtitle_stream;
std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
boost::shared_ptr<FFmpegAudioStream> _audio_stream;
- boost::optional<double> _first_video;
+ boost::optional<ContentTime> _first_video;
/** Video filters that should be used when generating DCPs */
std::vector<Filter const *> _filters;
};
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
-#include "film.h"
#include "filter.h"
#include "exceptions.h"
#include "image.h"
#include "util.h"
#include "log.h"
#include "ffmpeg_decoder.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
#include "filter_graph.h"
#include "audio_buffers.h"
#include "ffmpeg_content.h"
-#include "image_proxy.h"
+#include "raw_image_proxy.h"
+#include "film.h"
+#include "timer.h"
#include "i18n.h"
-#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
-#define LOG_ERROR(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
-#define LOG_WARNING(...) film->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+#define LOG_GENERAL(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_WARNING(...) _video_content->film()->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
using std::cout;
using std::string;
using std::list;
using std::min;
using std::pair;
+using std::make_pair;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
-using libdcp::Size;
+using dcp::Size;
-FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio)
- : Decoder (f)
- , VideoDecoder (f, c)
- , AudioDecoder (f, c)
- , SubtitleDecoder (f)
+FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log> log)
+ : VideoDecoder (c)
+ , AudioDecoder (c)
+ , SubtitleDecoder (c)
, FFmpeg (c)
- , _subtitle_codec_context (0)
- , _subtitle_codec (0)
- , _decode_video (video)
- , _decode_audio (audio)
- , _pts_offset (0)
- , _just_sought (false)
+ , _log (log)
{
- setup_subtitle ();
-
/* Audio and video frame PTS values may not start with 0. We want
to fiddle them so that:
Then we remove big initial gaps in PTS and we allow our
insertion of black frames to work.
- We will do:
- audio_pts_to_use = audio_pts_from_ffmpeg + pts_offset;
- video_pts_to_use = video_pts_from_ffmpeg + pts_offset;
+ We will do pts_to_use = pts_from_ffmpeg + pts_offset;
*/
- bool const have_video = video && c->first_video();
- bool const have_audio = audio && c->audio_stream() && c->audio_stream()->first_audio;
+ bool const have_video = c->first_video();
+ bool const have_audio = c->audio_stream () && c->audio_stream()->first_audio;
/* First, make one of them start at 0 */
/* Now adjust both so that the video pts starts on a frame */
if (have_video && have_audio) {
- double first_video = c->first_video().get() + _pts_offset;
- double const old_first_video = first_video;
-
- /* Round the first video up to a frame boundary */
- if (fabs (rint (first_video * c->video_frame_rate()) - first_video * c->video_frame_rate()) > 1e-6) {
- first_video = ceil (first_video * c->video_frame_rate()) / c->video_frame_rate ();
- }
-
- _pts_offset += first_video - old_first_video;
- }
-}
-
-FFmpegDecoder::~FFmpegDecoder ()
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- if (_subtitle_codec_context) {
- avcodec_close (_subtitle_codec_context);
+ ContentTime first_video = c->first_video().get() + _pts_offset;
+ ContentTime const old_first_video = first_video;
+ _pts_offset += first_video.round_up (c->video_frame_rate ()) - old_first_video;
}
}
/* XXX: should we reset _packet.data and size after each *_decode_* call? */
- if (_decode_video) {
- while (decode_video_packet ()) {}
- }
+ while (decode_video_packet ()) {}
- if (_ffmpeg_content->audio_stream() && _decode_audio) {
+ if (_ffmpeg_content->audio_stream()) {
decode_audio_packet ();
+ AudioDecoder::flush ();
}
-
- /* Stop us being asked for any more data */
- _video_position = _ffmpeg_content->video_length_after_3d_combine ();
- _audio_position = _ffmpeg_content->audio_length ();
}
-void
+bool
FFmpegDecoder::pass ()
{
int r = av_read_frame (_format_context, &_packet);
/* Maybe we should fail here, but for now we'll just finish off instead */
char buf[256];
av_strerror (r, buf, sizeof(buf));
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r);
}
flush ();
- return;
+ return true;
}
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
int const si = _packet.stream_index;
-
- if (si == _video_stream && _decode_video) {
+
+ if (si == _video_stream) {
decode_video_packet ();
- } else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si) && _decode_audio) {
+ } else if (_ffmpeg_content->audio_stream() && _ffmpeg_content->audio_stream()->uses_index (_format_context, si)) {
decode_audio_packet ();
- } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si) && film->with_subtitles ()) {
+ } else if (_ffmpeg_content->subtitle_stream() && _ffmpeg_content->subtitle_stream()->uses_index (_format_context, si)) {
decode_subtitle_packet ();
}
av_free_packet (&_packet);
+ return false;
}
/** @param data pointer to array of pointers to buffers.
}
void
-FFmpegDecoder::seek (VideoContent::Frame frame, bool accurate)
+FFmpegDecoder::seek (ContentTime time, bool accurate)
{
- double const time_base = av_q2d (_format_context->streams[_video_stream]->time_base);
-
- /* If we are doing an accurate seek, our initial shot will be 5 frames (5 being
- a number plucked from the air) earlier than we want to end up. The loop below
- will hopefully then step through to where we want to be.
+ VideoDecoder::seek (time, accurate);
+ AudioDecoder::seek (time, accurate);
+
+ /* If we are doing an `accurate' seek, we need to use pre-roll, as
+ we don't really know what the seek will give us.
*/
- int initial = frame;
- if (accurate) {
- initial -= 5;
- }
+ ContentTime pre_roll = accurate ? ContentTime::from_seconds (2) : ContentTime (0);
+ time -= pre_roll;
- if (initial < 0) {
- initial = 0;
- }
-
- /* Initial seek time in the stream's timebase */
- int64_t const initial_vt = ((initial / _ffmpeg_content->original_video_frame_rate()) - _pts_offset) / time_base;
-
- av_seek_frame (_format_context, _video_stream, initial_vt, AVSEEK_FLAG_BACKWARD);
-
- avcodec_flush_buffers (video_codec_context());
- if (_subtitle_codec_context) {
- avcodec_flush_buffers (_subtitle_codec_context);
- }
-
- /* This !accurate is piling hack upon hack; setting _just_sought to true
- even with accurate == true defeats our attempt to align the start
- of the video and audio. Here we disable that defeat when accurate == true
- i.e. when we are making a DCP rather than just previewing one.
- Ewww. This should be gone in 2.0.
+ /* XXX: it seems debatable whether PTS should be used here...
+ http://www.mjbshaw.com/2012/04/seeking-in-ffmpeg-know-your-timestamp.html
*/
- if (!accurate) {
- _just_sought = true;
- }
-
- _video_position = frame;
- if (frame == 0 || !accurate) {
- /* We're already there, or we're as close as we need to be */
- return;
- }
+ ContentTime const u = time - _pts_offset;
+ int64_t s = u.seconds() / av_q2d (_format_context->streams[_video_stream]->time_base);
- while (true) {
- int r = av_read_frame (_format_context, &_packet);
- if (r < 0) {
- return;
- }
+ if (_ffmpeg_content->audio_stream ()) {
+ s = min (
+ s, int64_t (u.seconds() / av_q2d (_ffmpeg_content->audio_stream()->stream(_format_context)->time_base))
+ );
+ }
- if (_packet.stream_index != _video_stream) {
- av_free_packet (&_packet);
- continue;
- }
-
- int finished = 0;
- r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
- if (r >= 0 && finished) {
- _video_position = rint (
- (av_frame_get_best_effort_timestamp (_frame) * time_base + _pts_offset) * _ffmpeg_content->original_video_frame_rate()
- );
+ av_seek_frame (_format_context, _video_stream, s, 0);
- if (_video_position >= (frame - 1)) {
- av_free_packet (&_packet);
- break;
- }
- }
-
- av_free_packet (&_packet);
+ avcodec_flush_buffers (video_codec_context());
+ if (audio_codec_context ()) {
+ avcodec_flush_buffers (audio_codec_context ());
+ }
+ if (subtitle_codec_context ()) {
+ avcodec_flush_buffers (subtitle_codec_context ());
}
-
- /* _video_position should be the next thing to be emitted, which will the one after the thing
- we just saw.
- */
- _video_position++;
}
void
int frame_finished;
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet);
+
if (decode_result < 0) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
LOG_ERROR ("avcodec_decode_audio4 failed (%1)", decode_result);
return;
}
if (frame_finished) {
-
- if (_audio_position == 0) {
- /* Where we are in the source, in seconds */
- double const pts = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base)
- * av_frame_get_best_effort_timestamp(_frame) + _pts_offset;
-
- if (pts > 0) {
- /* Emit some silence */
- int64_t frames = pts * _ffmpeg_content->content_audio_frame_rate ();
- while (frames > 0) {
- int64_t const this_time = min (frames, (int64_t) _ffmpeg_content->content_audio_frame_rate() / 2);
-
- shared_ptr<AudioBuffers> silence (
- new AudioBuffers (_ffmpeg_content->audio_channels(), this_time)
- );
-
- silence->make_silent ();
- audio (silence, _audio_position);
- frames -= this_time;
- }
- }
- }
+ ContentTime const ct = ContentTime::from_seconds (
+ av_frame_get_best_effort_timestamp (_frame) *
+ av_q2d (_ffmpeg_content->audio_stream()->stream (_format_context)->time_base))
+ + _pts_offset;
int const data_size = av_samples_get_buffer_size (
0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1
);
-
- audio (deinterleave_audio (_frame->data, data_size), _audio_position);
+
+ audio (deinterleave_audio (_frame->data, data_size), ct);
}
copy_packet.data += decode_result;
shared_ptr<FilterGraph> graph;
list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
- while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
+ while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
++i;
}
if (i == _filter_graphs.end ()) {
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- graph.reset (new FilterGraph (_ffmpeg_content, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
+ graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
_filter_graphs.push_back (graph);
-
LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
} else {
graph = *i;
list<pair<shared_ptr<Image>, int64_t> > images = graph->process (_frame);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
for (list<pair<shared_ptr<Image>, int64_t> >::iterator i = images.begin(); i != images.end(); ++i) {
shared_ptr<Image> image = i->first;
if (i->second != AV_NOPTS_VALUE) {
-
- double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset;
-
- if (_just_sought) {
- /* We just did a seek, so disable any attempts to correct for where we
- are / should be.
- */
- _video_position = rint (pts * _ffmpeg_content->original_video_frame_rate ());
- _just_sought = false;
- }
-
- double const next = _video_position / _ffmpeg_content->original_video_frame_rate();
- double const one_frame = 1 / _ffmpeg_content->original_video_frame_rate ();
- double delta = pts - next;
-
- while (delta > one_frame) {
- /* This PTS is more than one frame forward in time of where we think we should be; emit
- a black frame.
- */
-
- /* XXX: I think this should be a copy of the last frame... */
- boost::shared_ptr<Image> black (
- new Image (
- static_cast<AVPixelFormat> (_frame->format),
- libdcp::Size (video_codec_context()->width, video_codec_context()->height),
- true
- )
- );
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- black->make_black ();
- video (shared_ptr<ImageProxy> (new RawImageProxy (image, film->log())), false, _video_position);
- delta -= one_frame;
- }
-
- if (delta > -one_frame) {
- /* This PTS is within a frame of being right; emit this (otherwise it will be dropped) */
- video (shared_ptr<ImageProxy> (new RawImageProxy (image, film->log())), false, _video_position);
- }
-
+ double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds ();
+ video (
+ shared_ptr<ImageProxy> (new RawImageProxy (image, _video_content->film()->log())),
+ rint (pts * _ffmpeg_content->video_frame_rate ())
+ );
} else {
LOG_WARNING ("Dropping frame without PTS");
}
return true;
}
-
-
-void
-FFmpegDecoder::setup_subtitle ()
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- if (!_ffmpeg_content->subtitle_stream()) {
- return;
- }
-
- _subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
- if (_subtitle_codec_context == 0) {
- throw DecodeError (N_("could not find subtitle stream"));
- }
-
- _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
-
- if (_subtitle_codec == 0) {
- throw DecodeError (N_("could not find subtitle decoder"));
- }
-
- if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
- throw DecodeError (N_("could not open subtitle decoder"));
- }
-}
-
-bool
-FFmpegDecoder::done () const
-{
- bool const vd = !_decode_video || (_video_position >= _ffmpeg_content->video_length());
- bool const ad = !_decode_audio || !_ffmpeg_content->audio_stream() || (_audio_position >= _ffmpeg_content->audio_length());
- return vd && ad;
-}
void
FFmpegDecoder::decode_subtitle_packet ()
{
int got_subtitle;
AVSubtitle sub;
- if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) {
+ if (avcodec_decode_subtitle2 (subtitle_codec_context(), &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) {
return;
}
indicate that the previous subtitle should stop.
*/
if (sub.num_rects <= 0) {
- subtitle (shared_ptr<Image> (), dcpomatic::Rect<double> (), 0, 0);
+ image_subtitle (ContentTimePeriod (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
return;
} else if (sub.num_rects > 1) {
throw DecodeError (_("multi-part subtitles not yet supported"));
}
- /* Subtitle PTS in seconds (within the source, not taking into account any of the
+ /* Subtitle PTS (within the source, not taking into account any of the
source that we may have chopped off for the DCP)
*/
- double const packet_time = (static_cast<double> (sub.pts ) / AV_TIME_BASE) + _pts_offset;
-
- /* hence start time for this sub */
- Time const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ;
- Time const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ;
+ ContentTimePeriod period = subtitle_period (sub) + _pts_offset;
AVSubtitleRect const * rect = sub.rects[0];
if (rect->type != SUBTITLE_BITMAP) {
- throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ /* XXX */
+ // throw DecodeError (_("non-bitmap subtitles not yet supported"));
+ return;
}
/* Note RGBA is expressed little-endian, so the first byte in the word is R, second
G, third B, fourth A.
*/
- shared_ptr<Image> image (new Image (PIX_FMT_RGBA, libdcp::Size (rect->w, rect->h), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true));
/* Start of the first line in the subtitle */
uint8_t* sub_p = rect->pict.data[0];
out_p += image->stride()[0] / sizeof (uint32_t);
}
- libdcp::Size const vs = _ffmpeg_content->video_size ();
+ dcp::Size const vs = _ffmpeg_content->video_size ();
- subtitle (
+ image_subtitle (
+ period,
image,
dcpomatic::Rect<double> (
static_cast<double> (rect->x) / vs.width,
static_cast<double> (rect->y) / vs.height,
static_cast<double> (rect->w) / vs.width,
static_cast<double> (rect->h) / vs.height
- ),
- from,
- to
+ )
);
-
avsubtitle_free (&sub);
}
+
+list<ContentTimePeriod>
+FFmpegDecoder::subtitles_during (ContentTimePeriod p, bool starting) const
+{
+ return _ffmpeg_content->subtitles_during (p, starting);
+}
/*
- 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 "subtitle_decoder.h"
#include "ffmpeg.h"
-class Film;
+class Log;
class FilterGraph;
class ffmpeg_pts_offset_test;
class FFmpegDecoder : public VideoDecoder, public AudioDecoder, public SubtitleDecoder, public FFmpeg
{
public:
- FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const FFmpegContent>, bool video, bool audio);
- ~FFmpegDecoder ();
-
- void pass ();
- void seek (VideoContent::Frame, bool);
- bool done () const;
+ FFmpegDecoder (boost::shared_ptr<const FFmpegContent>, boost::shared_ptr<Log>);
private:
- friend class ::ffmpeg_pts_offset_test;
-
- static double compute_pts_offset (double, double, float);
+ friend struct ::ffmpeg_pts_offset_test;
+ void seek (ContentTime time, bool);
+ bool pass ();
void flush ();
- void setup_subtitle ();
-
AVSampleFormat audio_sample_format () const;
int bytes_per_audio_sample () const;
void maybe_add_subtitle ();
boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t** data, int size);
- AVCodecContext* _subtitle_codec_context; ///< may be 0 if there is no subtitle
- AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle
+ std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const;
+
+ boost::shared_ptr<Log> _log;
std::list<boost::shared_ptr<FilterGraph> > _filter_graphs;
boost::mutex _filter_graphs_mutex;
- bool _decode_video;
- bool _decode_audio;
-
- double _pts_offset;
- bool _just_sought;
+ ContentTime _pts_offset;
};
/*
- 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 "ffmpeg_examiner.h"
#include "ffmpeg_content.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
+#include "util.h"
#include "safe_stringstream.h"
#include "i18n.h"
}
}
- /* Run through until we find the first audio (for each stream) and video */
-
+ /* Run through until we find:
+ * - the first video.
+ * - the first audio for each stream.
+ * - the subtitle periods for each stream.
+ *
+ * We have to note subtitle periods as otherwise we have no way of knowing
+ * where we should look for subtitles (video and audio are always present,
+ * so they are ok).
+ */
while (true) {
int r = av_read_frame (_format_context, &_packet);
if (r < 0) {
break;
}
- int frame_finished;
-
AVCodecContext* context = _format_context->streams[_packet.stream_index]->codec;
- if (_packet.stream_index == _video_stream && !_first_video) {
- if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _first_video = frame_time (_format_context->streams[_video_stream]);
- }
- } else {
- for (size_t i = 0; i < _audio_streams.size(); ++i) {
- if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index) && !_audio_streams[i]->first_audio) {
- if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
- _audio_streams[i]->first_audio = frame_time (_audio_streams[i]->stream (_format_context));
- }
- }
+ if (_packet.stream_index == _video_stream) {
+ video_packet (context);
+ }
+
+ for (size_t i = 0; i < _audio_streams.size(); ++i) {
+ if (_audio_streams[i]->uses_index (_format_context, _packet.stream_index)) {
+ audio_packet (context, _audio_streams[i]);
}
}
- bool have_all_audio = true;
- size_t i = 0;
- while (i < _audio_streams.size() && have_all_audio) {
- have_all_audio = _audio_streams[i]->first_audio;
- ++i;
+ for (size_t i = 0; i < _subtitle_streams.size(); ++i) {
+ if (_subtitle_streams[i]->uses_index (_format_context, _packet.stream_index)) {
+ subtitle_packet (context, _subtitle_streams[i]);
+ }
}
av_free_packet (&_packet);
-
- if (_first_video && have_all_audio) {
- break;
+ }
+}
+
+void
+FFmpegExaminer::video_packet (AVCodecContext* context)
+{
+ if (_first_video) {
+ return;
+ }
+
+ int frame_finished;
+ if (avcodec_decode_video2 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ _first_video = frame_time (_format_context->streams[_video_stream]);
+ }
+}
+
+void
+FFmpegExaminer::audio_packet (AVCodecContext* context, shared_ptr<FFmpegAudioStream> stream)
+{
+ if (stream->first_audio) {
+ return;
+ }
+
+ int frame_finished;
+ if (avcodec_decode_audio4 (context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
+ stream->first_audio = frame_time (stream->stream (_format_context));
+ }
+}
+
+void
+FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr<FFmpegSubtitleStream> stream)
+{
+ int frame_finished;
+ AVSubtitle sub;
+ if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) {
+ ContentTimePeriod const period = subtitle_period (sub);
+ if (sub.num_rects == 0 && !stream->periods.empty () && stream->periods.back().to > period.from) {
+ /* Finish the last subtitle */
+ stream->periods.back().to = period.from;
+ } else if (sub.num_rects == 1) {
+ stream->periods.push_back (period);
}
}
}
-optional<double>
+optional<ContentTime>
FFmpegExaminer::frame_time (AVStream* s) const
{
- optional<double> t;
+ optional<ContentTime> t;
int64_t const bet = av_frame_get_best_effort_timestamp (_frame);
if (bet != AV_NOPTS_VALUE) {
- t = bet * av_q2d (s->time_base);
+ t = ContentTime::from_seconds (bet * av_q2d (s->time_base));
}
return t;
float
FFmpegExaminer::video_frame_rate () const
{
- AVStream* s = _format_context->streams[_video_stream];
-
- if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
- return av_q2d (s->avg_frame_rate);
- }
-
- return av_q2d (s->r_frame_rate);
+ /* This use of r_frame_rate is debateable; there's a few different
+ * frame rates in the format context, but this one seems to be the most
+ * reliable.
+ */
+ return av_q2d (av_stream_get_r_frame_rate (_format_context->streams[_video_stream]));
}
-libdcp::Size
+dcp::Size
FFmpegExaminer::video_size () const
{
- return libdcp::Size (video_codec_context()->width, video_codec_context()->height);
+ return dcp::Size (video_codec_context()->width, video_codec_context()->height);
}
-/** @return Length (in video frames) according to our content's header */
-VideoContent::Frame
+/** @return Length according to our content's header */
+ContentTime
FFmpegExaminer::video_length () const
{
- VideoContent::Frame const length = (double (_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
- return max (1, length);
+ ContentTime const length = ContentTime::from_seconds (double (_format_context->duration) / AV_TIME_BASE);
+ return ContentTime (max (ContentTime::Type (1), length.get ()));
}
string
FFmpegExaminer (boost::shared_ptr<const FFmpegContent>);
float video_frame_rate () const;
- libdcp::Size video_size () const;
- VideoContent::Frame video_length () const;
+ dcp::Size video_size () const;
+ ContentTime video_length () const;
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
return _subtitle_streams;
return _audio_streams;
}
- boost::optional<double> first_video () const {
+ boost::optional<ContentTime> first_video () const {
return _first_video;
}
private:
+ void video_packet (AVCodecContext *);
+ void audio_packet (AVCodecContext *, boost::shared_ptr<FFmpegAudioStream>);
+ void subtitle_packet (AVCodecContext *, boost::shared_ptr<FFmpegSubtitleStream>);
+
std::string stream_name (AVStream* s) const;
std::string audio_stream_name (AVStream* s) const;
std::string subtitle_stream_name (AVStream* s) const;
- boost::optional<double> frame_time (AVStream* s) const;
+ boost::optional<ContentTime> frame_time (AVStream* s) const;
std::vector<boost::shared_ptr<FFmpegSubtitleStream> > _subtitle_streams;
std::vector<boost::shared_ptr<FFmpegAudioStream> > _audio_streams;
- boost::optional<double> _first_video;
+ boost::optional<ContentTime> _first_video;
};
--- /dev/null
+/*
+ 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.
+
+*/
+
+extern "C" {
+#include <libavformat/avformat.h>
+}
+#include <libxml++/libxml++.h>
+#include <dcp/raw_convert.h>
+#include "ffmpeg_stream.h"
+
+using std::string;
+using dcp::raw_convert;
+
+FFmpegStream::FFmpegStream (cxml::ConstNodePtr node)
+ : name (node->string_child ("Name"))
+ , _id (node->number_child<int> ("Id"))
+{
+
+}
+
+void
+FFmpegStream::as_xml (xmlpp::Node* root) const
+{
+ root->add_child("Name")->add_child_text (name);
+ root->add_child("Id")->add_child_text (raw_convert<string> (_id));
+}
+
+bool
+FFmpegStream::uses_index (AVFormatContext const * fc, int index) const
+{
+ size_t i = 0;
+ while (i < fc->nb_streams) {
+ if (fc->streams[i]->id == _id) {
+ return int (i) == index;
+ }
+ ++i;
+ }
+
+ return false;
+}
+
+AVStream *
+FFmpegStream::stream (AVFormatContext const * fc) const
+{
+ size_t i = 0;
+ while (i < fc->nb_streams) {
+ if (fc->streams[i]->id == _id) {
+ return fc->streams[i];
+ }
+ ++i;
+ }
+
+ assert (false);
+ return 0;
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_FFMPEG_STREAM_H
+#define DCPOMATIC_FFMPEG_STREAM_H
+
+#include <boost/lexical_cast.hpp>
+#include <libcxml/cxml.h>
+
+struct AVFormatContext;
+struct AVStream;
+
+class FFmpegStream
+{
+public:
+ FFmpegStream (std::string n, int i)
+ : name (n)
+ , _id (i)
+ {}
+
+ FFmpegStream (cxml::ConstNodePtr);
+
+ void as_xml (xmlpp::Node *) const;
+
+ /** @param c An AVFormatContext.
+ * @param index A stream index within the AVFormatContext.
+ * @return true if this FFmpegStream uses the given stream index.
+ */
+ bool uses_index (AVFormatContext const * c, int index) const;
+ AVStream* stream (AVFormatContext const * c) const;
+
+ std::string technical_summary () const {
+ return "id " + boost::lexical_cast<std::string> (_id);
+ }
+
+ std::string identifier () const {
+ return boost::lexical_cast<std::string> (_id);
+ }
+
+ std::string name;
+
+ friend bool operator== (FFmpegStream const & a, FFmpegStream const & b);
+ friend bool operator!= (FFmpegStream const & a, FFmpegStream const & b);
+
+private:
+ int _id;
+};
+
+#endif
--- /dev/null
+/*
+ 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 "ffmpeg_subtitle_stream.h"
+
+/** Construct a SubtitleStream from a value returned from to_string().
+ * @param t String returned from to_string().
+ * @param v State file version.
+ */
+FFmpegSubtitleStream::FFmpegSubtitleStream (cxml::ConstNodePtr node)
+ : FFmpegStream (node)
+{
+
+}
+
+void
+FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
+{
+ FFmpegStream::as_xml (root);
+}
--- /dev/null
+/*
+ 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 "dcpomatic_time.h"
+#include "ffmpeg_stream.h"
+
+class FFmpegSubtitleStream : public FFmpegStream
+{
+public:
+ FFmpegSubtitleStream (std::string n, int i)
+ : FFmpegStream (n, i)
+ {}
+
+ FFmpegSubtitleStream (cxml::ConstNodePtr);
+
+ void as_xml (xmlpp::Node *) const;
+
+ std::vector<ContentTimePeriod> periods;
+};
+
/*
- 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/lib/file_group.cc
+ * @brief FileGroup class.
+ */
+
#include <cstdio>
#include <sndfile.h>
#include "file_group.h"
using std::vector;
using std::cout;
+/** Construct a FileGroup with no files */
FileGroup::FileGroup ()
: _current_path (0)
, _current_file (0)
}
+/** Construct a FileGroup with a single file */
FileGroup::FileGroup (boost::filesystem::path p)
: _current_path (0)
, _current_file (0)
{
_paths.push_back (p);
+ ensure_open_path (0);
seek (0, SEEK_SET);
}
+/** Construct a FileGroup with multiple files */
FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
: _paths (p)
, _current_path (0)
seek (0, SEEK_SET);
}
+/** Destroy a FileGroup, closing any open file */
FileGroup::~FileGroup ()
{
if (_current_file) {
return read;
}
+/** @return Combined length of all the files */
int64_t
FileGroup::length () const
{
/*
- 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/lib/file_group.h
+ * @brief FileGroup class.
+ */
+
#ifndef DCPOMATIC_FILE_GROUP_H
#define DCPOMATIC_FILE_GROUP_H
#include <vector>
#include <boost/filesystem.hpp>
+/** @class FileGroup
+ * @brief A class to make a list of files behave like they were concatenated.
+ */
class FileGroup
{
public:
#include <unistd.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
-#include <boost/date_time.hpp>
+#include <boost/lexical_cast.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/cpl.h>
-#include <libdcp/signer.h>
-#include <libdcp/util.h>
-#include <libdcp/kdm.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/cpl.h>
+#include <dcp/signer.h>
+#include <dcp/util.h>
+#include <dcp/local_time.h>
+#include <dcp/raw_convert.h>
#include "film.h"
#include "job.h"
#include "util.h"
using boost::starts_with;
using boost::optional;
using boost::is_any_of;
-using libdcp::Size;
-using libdcp::Signer;
-using libdcp::raw_convert;
+using dcp::Size;
+using dcp::Signer;
+using dcp::raw_convert;
+using dcp::raw_convert;
#define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
#define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
* 7 -> 8
* Use <Scale> tag in <VideoContent> rather than <Ratio>.
* 8 -> 9
- * DCI -> ISDCF.
+ * DCI -> ISDCF
* 9 -> 10
* Subtitle X and Y scale.
+ *
+ * Bumped to 32 for 2.0 branch; some times are expressed in Times rather
+ * than frames now.
*/
-int const Film::current_state_version = 10;
+int const Film::current_state_version = 32;
/** Construct a Film object in a given directory.
*
, _container (Config::instance()->default_container ())
, _resolution (RESOLUTION_2K)
, _scaler (Scaler::from_id ("bicubic"))
- , _with_subtitles (false)
, _signed (true)
, _encrypted (false)
, _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
, _three_d (false)
, _sequence_video (true)
, _interop (false)
+ , _burn_subtitles (false)
, _state_version (current_state_version)
, _dirty (false)
{
s << "_S";
}
- if (_three_d) {
- s << "_3D";
+ if (_burn_subtitles) {
+ s << "_B";
}
- if (_with_subtitles) {
- s << "_WS";
+ if (_three_d) {
+ s << "_3D";
}
return s.str ();
return filename_safe_name() + "_audio.mxf";
}
+boost::filesystem::path
+Film::subtitle_xml_filename () const
+{
+ return filename_safe_name() + "_subtitle.xml";
+}
+
string
Film::filename_safe_name () const
{
root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
root->add_child("Scaler")->add_child_text (_scaler->id ());
- root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
_isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0");
root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
+ root->add_child("BurnSubtitles")->add_child_text (_burn_subtitles ? "1" : "0");
root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
root->add_child("Key")->add_child_text (_key.hex ());
_resolution = string_to_resolution (f.string_child ("Resolution"));
_scaler = Scaler::from_id (f.string_child ("Scaler"));
- _with_subtitles = f.bool_child ("WithSubtitles");
_j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
_video_frame_rate = f.number_child<int> ("VideoFrameRate");
_signed = f.optional_bool_child("Signed").get_value_or (true);
_sequence_video = f.bool_child ("SequenceVideo");
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
- _key = libdcp::Key (f.string_child ("Key"));
+ if (_state_version >= 32) {
+ _burn_subtitles = f.bool_child ("BurnSubtitles");
+ }
+ _key = dcp::Key (f.string_child ("Key"));
list<string> notes;
/* This method is the only one that can return notes (so far) */
/* XXX: this uses the first bit of content only */
/* The standard says we don't do this for trailers, for some strange reason */
- if (dcp_content_type() && dcp_content_type()->libdcp_kind() != libdcp::TRAILER) {
- Ratio const * content_ratio = 0;
+ if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
ContentList cl = content ();
+ Ratio const * content_ratio = 0;
for (ContentList::iterator i = cl.begin(); i != cl.end(); ++i) {
shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
if (vc) {
return name();
}
-
void
Film::set_directory (boost::filesystem::path d)
{
signal_changed (SCALER);
}
-void
-Film::set_with_subtitles (bool w)
-{
- _with_subtitles = w;
- signal_changed (WITH_SUBTITLES);
-}
-
void
Film::set_j2k_bandwidth (int b)
{
signal_changed (INTEROP);
}
+void
+Film::set_burn_subtitles (bool b)
+{
+ _burn_subtitles = b;
+ signal_changed (BURN_SUBTITLES);
+}
+
void
Film::signal_changed (Property p)
{
return file (p);
}
-/** Find all the DCPs in our directory that can be libdcp::DCP::read() and return details of their CPLs */
+/** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
vector<CPLSummary>
Film::cpls () const
{
) {
try {
- libdcp::DCP dcp (*i);
+ dcp::DCP dcp (*i);
dcp.read ();
out.push_back (
CPLSummary (
- i->path().leaf().string(), dcp.cpls().front()->id(), dcp.cpls().front()->name(), dcp.cpls().front()->filename()
+ i->path().leaf().string(),
+ dcp.cpls().front()->id(),
+ dcp.cpls().front()->annotation_text(),
+ dcp.cpls().front()->file()
)
);
} catch (...) {
return _playlist->content ();
}
+void
+Film::examine_content (shared_ptr<Content> c)
+{
+ shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
+ JobManager::instance()->add (j);
+}
+
void
Film::examine_and_add_content (shared_ptr<Content> c)
{
_playlist->move_later (c);
}
-Time
+DCPTime
Film::length () const
{
return _playlist->length ();
}
-bool
-Film::has_subtitles () const
+int
+Film::best_video_frame_rate () const
{
- return _playlist->has_subtitles ();
+ return _playlist->best_dcp_frame_rate ();
}
-OutputVideoFrame
-Film::best_video_frame_rate () const
+FrameRateChange
+Film::active_frame_rate_change (DCPTime t) const
{
- return _playlist->best_dcp_frame_rate ();
+ return _playlist->active_frame_rate_change (t, video_frame_rate ());
}
void
signal_changed (CONTENT);
}
-OutputAudioFrame
-Film::time_to_audio_frames (Time t) const
-{
- return divide_with_round (t * audio_frame_rate (), TIME_HZ);
-}
-
-OutputVideoFrame
-Film::time_to_video_frames (Time t) const
-{
- return divide_with_round (t * video_frame_rate (), TIME_HZ);
-}
-
-Time
-Film::audio_frames_to_time (OutputAudioFrame f) const
-{
- return divide_with_round (f * TIME_HZ, audio_frame_rate ());
-}
-
-Time
-Film::video_frames_to_time (OutputVideoFrame f) const
-{
- return divide_with_round (f * TIME_HZ, video_frame_rate ());
-}
-
-OutputAudioFrame
+int
Film::audio_frame_rate () const
{
/* XXX */
}
/** @return Size of the largest possible image in whatever resolution we are using */
-libdcp::Size
+dcp::Size
Film::full_frame () const
{
switch (_resolution) {
case RESOLUTION_2K:
- return libdcp::Size (2048, 1080);
+ return dcp::Size (2048, 1080);
case RESOLUTION_4K:
- return libdcp::Size (4096, 2160);
+ return dcp::Size (4096, 2160);
}
assert (false);
- return libdcp::Size ();
+ return dcp::Size ();
}
/** @return Size of the frame */
-libdcp::Size
+dcp::Size
Film::frame_size () const
{
- return fit_ratio_within (container()->ratio(), full_frame ());
+ return fit_ratio_within (container()->ratio(), full_frame (), 1);
}
-/** @param from KDM from time in local time.
- * @param to KDM to time in local time.
- */
-libdcp::KDM
+dcp::EncryptedKDM
Film::make_kdm (
- shared_ptr<libdcp::Certificate> target,
+ dcp::Certificate target,
boost::filesystem::path cpl_file,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime until,
+ dcp::Formulation formulation
) const
{
- shared_ptr<const Signer> signer = make_signer ();
-
- time_t now = time (0);
- struct tm* tm = localtime (&now);
- string const issue_date = libdcp::tm_to_string (tm);
+ shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
+ shared_ptr<const dcp::Signer> signer = Config::instance()->signer();
+ if (!signer->valid ()) {
+ throw InvalidSignerError ();
+ }
- return libdcp::KDM (cpl_file, signer, target, key (), from, until, "DCP-o-matic", issue_date, formulation);
+ return dcp::DecryptedKDM (
+ cpl, key(), from, until, "DCP-o-matic", cpl->content_title_text(), dcp::LocalTime().as_string()
+ ).encrypt (signer, target, formulation);
}
-list<libdcp::KDM>
+list<dcp::EncryptedKDM>
Film::make_kdms (
list<shared_ptr<Screen> > screens,
boost::filesystem::path dcp,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime until,
+ dcp::Formulation formulation
) const
{
- list<libdcp::KDM> kdms;
+ list<dcp::EncryptedKDM> kdms;
for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
- kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until, formulation));
+ if ((*i)->certificate) {
+ kdms.push_back (make_kdm ((*i)->certificate.get(), dcp, from, until, formulation));
+ }
}
return kdms;
uint64_t
Film::required_disk_space () const
{
- return uint64_t (j2k_bandwidth() / 8) * length() / TIME_HZ;
+ return uint64_t (j2k_bandwidth() / 8) * length().seconds();
}
/** This method checks the disk that the Film is on and tries to decide whether or not
available = double (s.available) / 1073741824.0f;
return (available - required) > 1;
}
-
-FrameRateChange
-Film::active_frame_rate_change (Time t) const
-{
- return _playlist->active_frame_rate_change (t, video_frame_rate ());
-}
-
#include <boost/signals2.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/key.h>
-#include <libdcp/kdm.h>
+#include <dcp/key.h>
+#include <dcp/decrypted_kdm.h>
+#include <dcp/encrypted_kdm.h>
#include "util.h"
#include "types.h"
#include "isdcf_metadata.h"
class AudioContent;
class Scaler;
class Screen;
-class isdcf_name_test;
+struct isdcf_name_test;
/** @class Film
*
boost::filesystem::path video_mxf_filename () const;
boost::filesystem::path audio_mxf_filename () const;
+ boost::filesystem::path subtitle_xml_filename () const;
void send_dcp_to_tms ();
void make_dcp ();
return _dirty;
}
- libdcp::Size full_frame () const;
- libdcp::Size frame_size () const;
+ dcp::Size full_frame () const;
+ dcp::Size frame_size () const;
std::vector<CPLSummary> cpls () const;
boost::shared_ptr<Player> make_player () const;
boost::shared_ptr<Playlist> playlist () const;
- OutputAudioFrame audio_frame_rate () const;
-
- OutputAudioFrame time_to_audio_frames (Time) const;
- OutputVideoFrame time_to_video_frames (Time) const;
- Time video_frames_to_time (OutputVideoFrame) const;
- Time audio_frames_to_time (OutputAudioFrame) const;
+ int audio_frame_rate () const;
uint64_t required_disk_space () const;
bool should_be_enough_disk_space (double &, double &) const;
/* Proxies for some Playlist methods */
ContentList content () const;
- Time length () const;
- bool has_subtitles () const;
- OutputVideoFrame best_video_frame_rate () const;
- FrameRateChange active_frame_rate_change (Time) const;
+ DCPTime length () const;
+ int best_video_frame_rate () const;
+ FrameRateChange active_frame_rate_change (DCPTime) const;
- libdcp::KDM
+ dcp::EncryptedKDM
make_kdm (
- boost::shared_ptr<libdcp::Certificate> target,
+ dcp::Certificate target,
boost::filesystem::path cpl_file,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime until,
+ dcp::Formulation formulation
) const;
- std::list<libdcp::KDM> make_kdms (
+ std::list<dcp::EncryptedKDM> make_kdms (
std::list<boost::shared_ptr<Screen> >,
boost::filesystem::path cpl_file,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime until,
+ dcp::Formulation formulation
) const;
- libdcp::Key key () const {
+ dcp::Key key () const {
return _key;
}
CONTAINER,
RESOLUTION,
SCALER,
- WITH_SUBTITLES,
SIGNED,
ENCRYPTED,
J2K_BANDWIDTH,
ISDCF_METADATA,
VIDEO_FRAME_RATE,
AUDIO_CHANNELS,
- /** The setting of _three_d has been changed */
+ /** The setting of _three_d has changed */
THREE_D,
SEQUENCE_VIDEO,
INTEROP,
+ /** The setting of _burn_subtitles has changed */
+ BURN_SUBTITLES,
};
return _scaler;
}
- bool with_subtitles () const {
- return _with_subtitles;
- }
-
/* signed is a reserved word */
bool is_signed () const {
return _signed;
bool interop () const {
return _interop;
}
+
+ bool burn_subtitles () const {
+ return _burn_subtitles;
+ }
/* SET */
void set_directory (boost::filesystem::path);
void set_name (std::string);
void set_use_isdcf_name (bool);
+ void examine_content (boost::shared_ptr<Content>);
void examine_and_add_content (boost::shared_ptr<Content>);
void add_content (boost::shared_ptr<Content>);
void remove_content (boost::shared_ptr<Content>);
void set_container (Ratio const *);
void set_resolution (Resolution);
void set_scaler (Scaler const *);
- void set_with_subtitles (bool);
void set_signed (bool);
void set_encrypted (bool);
void set_j2k_bandwidth (int);
void set_isdcf_date_today ();
void set_sequence_video (bool);
void set_interop (bool);
+ void set_burn_subtitles (bool);
/** Emitted when some property has of the Film has changed */
mutable boost::signals2::signal<void (Property)> Changed;
private:
- friend class ::isdcf_name_test;
+ friend struct ::isdcf_name_test;
void signal_changed (Property);
std::string video_identifier () const;
Resolution _resolution;
/** Scaler algorithm to use */
Scaler const * _scaler;
- /** True if subtitles should be shown for this film */
- bool _with_subtitles;
bool _signed;
bool _encrypted;
/** bandwidth for J2K files in bits per second */
bool _three_d;
bool _sequence_video;
bool _interop;
- libdcp::Key _key;
+ bool _burn_subtitles;
+ dcp::Key _key;
int _state_version;
/** true if our state has changed since we last saved it */
mutable bool _dirty;
- friend class paths_test;
- friend class film_metadata_test;
+ friend struct paths_test;
+ friend struct film_metadata_test;
};
#endif
using std::cout;
using boost::shared_ptr;
using boost::weak_ptr;
-using libdcp::Size;
+using dcp::Size;
/** Construct a FilterGraph for the settings in a piece of content.
* @param content Content.
* @param s Size of the images to process.
* @param p Pixel format of the images to process.
*/
-FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, libdcp::Size s, AVPixelFormat p)
- : _buffer_src_context (0)
+FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, dcp::Size s, AVPixelFormat p)
+ : _copy (false)
+ , _buffer_src_context (0)
, _buffer_sink_context (0)
, _size (s)
, _pixel_format (p)
+ , _frame (0)
{
- _frame = av_frame_alloc ();
-
- string filters = Filter::ffmpeg_string (content->filters());
+ string const filters = Filter::ffmpeg_string (content->filters());
if (filters.empty ()) {
- filters = "copy";
+ _copy = true;
+ return;
}
+ _frame = av_frame_alloc ();
+
AVFilterGraph* graph = avfilter_graph_alloc();
if (graph == 0) {
throw DecodeError (N_("could not create filter graph."));
throw DecodeError (N_("could not configure filter graph."));
}
- /* XXX: leaking `inputs' / `outputs' ? */
+ avfilter_inout_free (&inputs);
+ avfilter_inout_free (&outputs);
}
FilterGraph::~FilterGraph ()
{
- av_frame_free (&_frame);
+ if (_frame) {
+ av_frame_free (&_frame);
+ }
}
/** Take an AVFrame and process it using our configured filters, returning a
{
list<pair<shared_ptr<Image>, int64_t> > images;
- if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) {
- throw DecodeError (N_("could not push buffer into filter chain."));
- }
-
- while (true) {
- if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) {
- break;
+ if (_copy) {
+ images.push_back (make_pair (shared_ptr<Image> (new Image (frame)), av_frame_get_best_effort_timestamp (frame)));
+ } else {
+ if (av_buffersrc_write_frame (_buffer_src_context, frame) < 0) {
+ throw DecodeError (N_("could not push buffer into filter chain."));
+ }
+
+ while (true) {
+ if (av_buffersink_get_frame (_buffer_sink_context, _frame) < 0) {
+ break;
+ }
+
+ images.push_back (make_pair (shared_ptr<Image> (new Image (_frame)), av_frame_get_best_effort_timestamp (_frame)));
+ av_frame_unref (_frame);
}
-
- images.push_back (make_pair (shared_ptr<Image> (new Image (_frame)), av_frame_get_best_effort_timestamp (_frame)));
- av_frame_unref (_frame);
}
-
+
return images;
}
* @return true if this chain can process images with `s' and `p', otherwise false.
*/
bool
-FilterGraph::can_process (libdcp::Size s, AVPixelFormat p) const
+FilterGraph::can_process (dcp::Size s, AVPixelFormat p) const
{
return (_size == s && _pixel_format == p);
}
class FilterGraph : public boost::noncopyable
{
public:
- FilterGraph (boost::shared_ptr<const FFmpegContent> content, libdcp::Size s, AVPixelFormat p);
+ FilterGraph (boost::shared_ptr<const FFmpegContent> content, dcp::Size s, AVPixelFormat p);
~FilterGraph ();
- bool can_process (libdcp::Size s, AVPixelFormat p) const;
+ bool can_process (dcp::Size s, AVPixelFormat p) const;
std::list<std::pair<boost::shared_ptr<Image>, int64_t> > process (AVFrame * frame);
private:
+ /** true if this graph has no filters in, so it just copies stuff straight through */
+ bool _copy;
AVFilterContext* _buffer_src_context;
AVFilterContext* _buffer_sink_context;
- libdcp::Size _size; ///< size of the images that this chain can process
+ dcp::Size _size; ///< size of the images that this chain can process
AVPixelFormat _pixel_format; ///< pixel format of the images that this chain can process
AVFrame* _frame;
};
/*
- 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 "image.h"
#include "exceptions.h"
#include "scaler.h"
+#include "timer.h"
+#include "rect.h"
#include "md5_digester.h"
#include "i18n.h"
using std::min;
using std::cout;
using std::cerr;
+using std::list;
using boost::shared_ptr;
-using libdcp::Size;
+using dcp::Size;
int
Image::line_factor (int n) const
/** Crop this image, scale it to `inter_size' and then place it in a black frame of `out_size' */
shared_ptr<Image>
-Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
+Image::crop_scale_window (Crop crop, dcp::Size inter_size, dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
{
assert (scaler);
/* Empirical testing suggests that sws_scale() will crash if
out->make_black ();
/* Size of the image after any crop */
- libdcp::Size const cropped_size = crop.apply (size ());
+ dcp::Size const cropped_size = crop.apply (size ());
/* Scale context for a scale from cropped_size to inter_size */
struct SwsContext* scale_context = sws_getContext (
- cropped_size.width, cropped_size.height, pixel_format(),
- inter_size.width, inter_size.height, out_format,
- scaler->ffmpeg_id (), 0, 0, 0
+ cropped_size.width, cropped_size.height, pixel_format(),
+ inter_size.width, inter_size.height, out_format,
+ scaler->ffmpeg_id (), 0, 0, 0
);
if (!scale_context) {
}
shared_ptr<Image>
-Image::scale (libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
+Image::scale (dcp::Size out_size, Scaler const * scaler, AVPixelFormat out_format, bool out_aligned) const
{
assert (scaler);
/* Empirical testing suggests that sws_scale() will crash if
shared_ptr<Image>
Image::crop (Crop crop, bool aligned) const
{
- libdcp::Size cropped_size = crop.apply (size ());
+ dcp::Size cropped_size = crop.apply (size ());
shared_ptr<Image> out (new Image (pixel_format(), cropped_size, aligned));
for (int c = 0; c < components(); ++c) {
}
}
+void
+Image::make_transparent ()
+{
+ if (_pixel_format != PIX_FMT_RGBA) {
+ throw PixelFormatError ("make_transparent()", _pixel_format);
+ }
+
+ memset (data()[0], 0, lines(0) * stride()[0]);
+}
+
void
Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
{
- /* Only implemented for RGBA onto RGB24 so far */
- assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA);
+ assert (other->pixel_format() == PIX_FMT_RGBA);
+ int const other_bpp = 4;
+
+ int this_bpp = 0;
+ switch (_pixel_format) {
+ case PIX_FMT_BGRA:
+ case PIX_FMT_RGBA:
+ this_bpp = 4;
+ break;
+ case PIX_FMT_RGB24:
+ this_bpp = 3;
+ break;
+ default:
+ assert (false);
+ }
int start_tx = position.x;
int start_ox = 0;
}
for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) {
- uint8_t* tp = data()[0] + ty * stride()[0] + position.x * 3;
+ uint8_t* tp = data()[0] + ty * stride()[0] + start_tx * this_bpp;
uint8_t* op = other->data()[0] + oy * other->stride()[0];
for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) {
float const alpha = float (op[3]) / 255;
- tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha;
- tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha;
- tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha;
- tp += 3;
- op += 4;
+ tp[0] = op[0] + (tp[0] * (1 - alpha));
+ tp[1] = op[1] + (tp[1] * (1 - alpha));
+ tp[2] = op[2] + (tp[2] * (1 - alpha));
+ tp[3] = op[3] + (tp[3] * (1 - alpha));
+
+ tp += this_bpp;
+ op += other_bpp;
}
}
}
* @param p Pixel format.
* @param s Size in pixels.
*/
-Image::Image (AVPixelFormat p, libdcp::Size s, bool aligned)
- : libdcp::Image (s)
+Image::Image (AVPixelFormat p, dcp::Size s, bool aligned)
+ : dcp::Image (s)
, _pixel_format (p)
, _aligned (aligned)
{
}
Image::Image (Image const & other)
- : libdcp::Image (other)
+ : dcp::Image (other)
, _pixel_format (other._pixel_format)
, _aligned (other._aligned)
{
}
Image::Image (AVFrame* frame)
- : libdcp::Image (libdcp::Size (frame->width, frame->height))
+ : dcp::Image (dcp::Size (frame->width, frame->height))
, _pixel_format (static_cast<AVPixelFormat> (frame->format))
, _aligned (true)
{
}
Image::Image (shared_ptr<const Image> other, bool aligned)
- : libdcp::Image (other)
+ : dcp::Image (other)
, _pixel_format (other->_pixel_format)
, _aligned (aligned)
{
void
Image::swap (Image & other)
{
- libdcp::Image::swap (other);
+ dcp::Image::swap (other);
std::swap (_pixel_format, other._pixel_format);
return _stride;
}
-libdcp::Size
+dcp::Size
Image::size () const
{
return _size;
return _aligned;
}
+PositionImage
+merge (list<PositionImage> images)
+{
+ if (images.empty ()) {
+ return PositionImage ();
+ }
+
+ if (images.size() == 1) {
+ return images.front ();
+ }
+
+ dcpomatic::Rect<int> all (images.front().position, images.front().image->size().width, images.front().image->size().height);
+ for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
+ all.extend (dcpomatic::Rect<int> (i->position, i->image->size().width, i->image->size().height));
+ }
+
+ shared_ptr<Image> merged (new Image (images.front().image->pixel_format (), dcp::Size (all.width, all.height), true));
+ merged->make_transparent ();
+ for (list<PositionImage>::const_iterator i = images.begin(); i != images.end(); ++i) {
+ merged->alpha_blend (i->image, i->position - all.position());
+ }
+
+ return PositionImage (merged, all.position ());
+}
+
string
Image::digest () const
{
return digester.get ();
}
-
+
+bool
+operator== (Image const & a, Image const & b)
+{
+ if (a.components() != b.components() || a.pixel_format() != b.pixel_format() || a.aligned() != b.aligned()) {
+ return false;
+ }
+
+ for (int c = 0; c < a.components(); ++c) {
+ if (a.lines(c) != b.lines(c) || a.line_size()[c] != b.line_size()[c] || a.stride()[c] != b.stride()[c]) {
+ return false;
+ }
+
+ uint8_t* p = a.data()[c];
+ uint8_t* q = b.data()[c];
+ for (int y = 0; y < a.lines(c); ++y) {
+ if (memcmp (p, q, a.line_size()[c]) != 0) {
+ return false;
+ }
+
+ p += a.stride()[c];
+ q += b.stride()[c];
+ }
+ }
+
+ return true;
+}
+
+void
+Image::fade (float f)
+{
+ switch (_pixel_format) {
+ case PIX_FMT_YUV420P:
+ case PIX_FMT_YUV422P:
+ case PIX_FMT_YUV444P:
+ case PIX_FMT_YUV411P:
+ case PIX_FMT_YUVJ420P:
+ case PIX_FMT_YUVJ422P:
+ case PIX_FMT_YUVJ444P:
+ case PIX_FMT_RGB24:
+ case PIX_FMT_ARGB:
+ case PIX_FMT_RGBA:
+ case PIX_FMT_ABGR:
+ case PIX_FMT_BGRA:
+ case PIX_FMT_RGB555LE:
+ /* 8-bit */
+ for (int c = 0; c < 3; ++c) {
+ uint8_t* p = data()[c];
+ for (int y = 0; y < lines(c); ++y) {
+ uint8_t* q = p;
+ for (int x = 0; x < line_size()[c]; ++x) {
+ *q = int (float (*q) * f);
+ ++q;
+ }
+ p += stride()[c];
+ }
+ }
+ break;
+
+ case PIX_FMT_YUV422P9LE:
+ case PIX_FMT_YUV444P9LE:
+ case PIX_FMT_YUV422P10LE:
+ case PIX_FMT_YUV444P10LE:
+ case PIX_FMT_YUV422P16LE:
+ case PIX_FMT_YUV444P16LE:
+ case AV_PIX_FMT_YUVA420P9LE:
+ case AV_PIX_FMT_YUVA422P9LE:
+ case AV_PIX_FMT_YUVA444P9LE:
+ case AV_PIX_FMT_YUVA420P10LE:
+ case AV_PIX_FMT_YUVA422P10LE:
+ case AV_PIX_FMT_YUVA444P10LE:
+ /* 16-bit little-endian */
+ for (int c = 0; c < 3; ++c) {
+ int const stride_pixels = stride()[c] / 2;
+ int const line_size_pixels = line_size()[c] / 2;
+ uint16_t* p = reinterpret_cast<uint16_t*> (data()[c]);
+ for (int y = 0; y < lines(c); ++y) {
+ uint16_t* q = p;
+ for (int x = 0; x < line_size_pixels; ++x) {
+ *q = int (float (*q) * f);
+ ++q;
+ }
+ p += stride_pixels;
+ }
+ }
+ break;
+
+ case PIX_FMT_YUV422P9BE:
+ case PIX_FMT_YUV444P9BE:
+ case PIX_FMT_YUV444P10BE:
+ case PIX_FMT_YUV422P10BE:
+ case AV_PIX_FMT_YUVA420P9BE:
+ case AV_PIX_FMT_YUVA422P9BE:
+ case AV_PIX_FMT_YUVA444P9BE:
+ case AV_PIX_FMT_YUVA420P10BE:
+ case AV_PIX_FMT_YUVA422P10BE:
+ case AV_PIX_FMT_YUVA444P10BE:
+ case AV_PIX_FMT_YUVA420P16BE:
+ case AV_PIX_FMT_YUVA422P16BE:
+ case AV_PIX_FMT_YUVA444P16BE:
+ /* 16-bit big-endian */
+ for (int c = 0; c < 3; ++c) {
+ int const stride_pixels = stride()[c] / 2;
+ int const line_size_pixels = line_size()[c] / 2;
+ uint16_t* p = reinterpret_cast<uint16_t*> (data()[c]);
+ for (int y = 0; y < lines(c); ++y) {
+ uint16_t* q = p;
+ for (int x = 0; x < line_size_pixels; ++x) {
+ *q = swap_16 (int (float (swap_16 (*q)) * f));
+ ++q;
+ }
+ p += stride_pixels;
+ }
+ }
+ break;
+
+ case PIX_FMT_UYVY422:
+ {
+ int const Y = lines(0);
+ int const X = line_size()[0];
+ uint8_t* p = data()[0];
+ for (int y = 0; y < Y; ++y) {
+ for (int x = 0; x < X; ++x) {
+ *p = int (float (*p) * f);
+ ++p;
+ }
+ }
+ break;
+ }
+
+ default:
+ throw PixelFormatError ("fade()", _pixel_format);
+ }
+}
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
}
-#include <libdcp/image.h>
+#include <dcp/image.h>
#include "util.h"
#include "position.h"
+#include "position_image.h"
class Scaler;
-class Image : public libdcp::Image
+class Image : public dcp::Image
{
public:
- Image (AVPixelFormat, libdcp::Size, bool);
+ Image (AVPixelFormat, dcp::Size, bool);
Image (AVFrame *);
Image (Image const &);
Image (boost::shared_ptr<const Image>, bool);
uint8_t ** data () const;
int * line_size () const;
int * stride () const;
- libdcp::Size size () const;
+ dcp::Size size () const;
bool aligned () const;
int components () const;
int line_factor (int) const;
int lines (int) const;
- boost::shared_ptr<Image> scale (libdcp::Size, Scaler const *, AVPixelFormat, bool aligned) const;
+ boost::shared_ptr<Image> scale (dcp::Size, Scaler const *, AVPixelFormat, bool aligned) const;
boost::shared_ptr<Image> crop (Crop c, bool aligned) const;
- boost::shared_ptr<Image> crop_scale_window (Crop c, libdcp::Size, libdcp::Size, Scaler const *, AVPixelFormat, bool aligned) const;
+ boost::shared_ptr<Image> crop_scale_window (Crop c, dcp::Size, dcp::Size, Scaler const *, AVPixelFormat, bool aligned) const;
void make_black ();
+ void make_transparent ();
void alpha_blend (boost::shared_ptr<const Image> image, Position<int> pos);
void copy (boost::shared_ptr<const Image> image, Position<int> pos);
+ void fade (float);
void read_from_socket (boost::shared_ptr<Socket>);
void write_to_socket (boost::shared_ptr<Socket>) const;
std::string digest () const;
private:
- friend class pixel_formats_test;
+ friend struct pixel_formats_test;
void allocate ();
void swap (Image &);
bool _aligned;
};
+extern PositionImage merge (std::list<PositionImage> images);
+extern bool operator== (Image const & a, Image const & b);
+
#endif
#include <libcxml/cxml.h>
#include "image_content.h"
#include "image_examiner.h"
-#include "config.h"
#include "compose.hpp"
#include "film.h"
#include "job.h"
#include "frame_rate_change.h"
+#include "exceptions.h"
#include "safe_stringstream.h"
#include "i18n.h"
}
-ImageContent::ImageContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+ImageContent::ImageContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
, VideoContent (f, node, version)
{
assert (film);
shared_ptr<ImageExaminer> examiner (new ImageExaminer (film, shared_from_this(), job));
-
take_from_video_examiner (examiner);
- set_video_length (examiner->video_length ());
}
void
-ImageContent::set_video_length (VideoContent::Frame len)
+ImageContent::set_video_length (ContentTime len)
{
{
boost::mutex::scoped_lock lm (_mutex);
signal_changed (ContentProperty::LENGTH);
}
-Time
+DCPTime
ImageContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate(), film->video_frame_rate ());
- return video_length_after_3d_combine() * frc.factor() * TIME_HZ / video_frame_rate();
+ return DCPTime (video_length_after_3d_combine(), FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
string
{
SafeStringStream s;
s << VideoContent::identifier ();
- s << "_" << video_length();
+ s << "_" << video_length().get();
return s.str ();
}
{
public:
ImageContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- ImageContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
+ ImageContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
boost::shared_ptr<ImageContent> shared_from_this () {
return boost::dynamic_pointer_cast<ImageContent> (Content::shared_from_this ());
std::string summary () const;
std::string technical_summary () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
+ DCPTime full_length () const;
std::string identifier () const;
- void set_video_length (VideoContent::Frame);
+ void set_video_length (ContentTime);
bool still () const;
void set_video_frame_rate (float);
};
#include "image_content.h"
#include "image_decoder.h"
#include "image.h"
-#include "image_proxy.h"
+#include "magick_image_proxy.h"
#include "film.h"
#include "exceptions.h"
using std::cout;
using boost::shared_ptr;
-using libdcp::Size;
+using dcp::Size;
-ImageDecoder::ImageDecoder (shared_ptr<const Film> f, shared_ptr<const ImageContent> c)
- : Decoder (f)
- , VideoDecoder (f, c)
+ImageDecoder::ImageDecoder (shared_ptr<const ImageContent> c)
+ : VideoDecoder (c)
, _image_content (c)
{
}
-void
+bool
ImageDecoder::pass ()
{
- if (_video_position >= _image_content->video_length ()) {
- return;
+ if (_video_position >= _image_content->video_length().frames (_image_content->video_frame_rate ())) {
+ return true;
}
- if (_image && _image_content->still ()) {
- video (_image, true, _video_position);
- return;
+ if (!_image_content->still() || !_image) {
+ /* Either we need an image or we are using moving images, so load one */
+ _image.reset (new MagickImageProxy (_image_content->path (_image_content->still() ? 0 : _video_position), _image_content->film()->log ()));
}
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- _image.reset (new MagickImageProxy (_image_content->path (_image_content->still() ? 0 : _video_position), film->log ()));
- video (_image, false, _video_position);
+
+ video (_image, _video_position);
+ ++_video_position;
+ return false;
}
void
-ImageDecoder::seek (VideoContent::Frame frame, bool)
-{
- _video_position = frame;
-}
-
-bool
-ImageDecoder::done () const
+ImageDecoder::seek (ContentTime time, bool accurate)
{
- return _video_position >= _image_content->video_length ();
+ VideoDecoder::seek (time, accurate);
+ _video_position = time.frames (_image_content->video_frame_rate ());
}
class ImageDecoder : public VideoDecoder
{
public:
- ImageDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageContent>);
+ ImageDecoder (boost::shared_ptr<const ImageContent> c);
boost::shared_ptr<const ImageContent> content () {
return _image_content;
}
- /* Decoder */
-
- void pass ();
- void seek (VideoContent::Frame, bool);
- bool done () const;
+ void seek (ContentTime, bool);
private:
+ bool pass ();
+
boost::shared_ptr<const ImageContent> _image_content;
boost::shared_ptr<ImageProxy> _image;
+ VideoFrame _video_position;
};
ImageExaminer::ImageExaminer (shared_ptr<const Film> film, shared_ptr<const ImageContent> content, shared_ptr<Job>)
: _film (film)
, _image_content (content)
- , _video_length (0)
{
#ifdef DCPOMATIC_IMAGE_MAGICK
using namespace MagickCore;
#endif
Magick::Image* image = new Magick::Image (content->path(0).string());
- _video_size = libdcp::Size (image->columns(), image->rows());
+ _video_size = dcp::Size (image->columns(), image->rows());
delete image;
if (content->still ()) {
- _video_length = Config::instance()->default_still_length() * video_frame_rate();
+ _video_length = ContentTime::from_seconds (Config::instance()->default_still_length());
} else {
- _video_length = _image_content->number_of_paths ();
+ _video_length = ContentTime::from_frames (_image_content->number_of_paths (), video_frame_rate ());
}
}
-libdcp::Size
+dcp::Size
ImageExaminer::video_size () const
{
return _video_size.get ();
ImageExaminer (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageContent>, boost::shared_ptr<Job>);
float video_frame_rate () const;
- libdcp::Size video_size () const;
- VideoContent::Frame video_length () const {
+ dcp::Size video_size () const;
+ ContentTime video_length () const {
return _video_length;
}
private:
boost::weak_ptr<const Film> _film;
boost::shared_ptr<const ImageContent> _image_content;
- boost::optional<libdcp::Size> _video_size;
- VideoContent::Frame _video_length;
+ boost::optional<dcp::Size> _video_size;
+ ContentTime _video_length;
};
*/
-#include <Magick++.h>
-#include <libdcp/util.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/util.h>
+#include <dcp/raw_convert.h>
#include "image_proxy.h"
+#include "raw_image_proxy.h"
+#include "magick_image_proxy.h"
+#include "j2k_image_proxy.h"
#include "image.h"
#include "exceptions.h"
#include "cross.h"
}
-RawImageProxy::RawImageProxy (shared_ptr<Image> image, shared_ptr<Log> log)
- : ImageProxy (log)
- , _image (image)
-{
-
-}
-
-RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
- : ImageProxy (log)
-{
- libdcp::Size size (
- xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
- );
-
- _image.reset (new Image (static_cast<AVPixelFormat> (xml->number_child<int> ("PixelFormat")), size, true));
- _image->read_from_socket (socket);
-}
-
-shared_ptr<Image>
-RawImageProxy::image () const
-{
- return _image;
-}
-
-void
-RawImageProxy::add_metadata (xmlpp::Node* node) const
-{
- node->add_child("Type")->add_child_text (N_("Raw"));
- node->add_child("Width")->add_child_text (libdcp::raw_convert<string> (_image->size().width));
- node->add_child("Height")->add_child_text (libdcp::raw_convert<string> (_image->size().height));
- node->add_child("PixelFormat")->add_child_text (libdcp::raw_convert<string> (_image->pixel_format ()));
-}
-
-void
-RawImageProxy::send_binary (shared_ptr<Socket> socket) const
-{
- _image->write_to_socket (socket);
-}
-
-MagickImageProxy::MagickImageProxy (boost::filesystem::path path, shared_ptr<Log> log)
- : ImageProxy (log)
-{
- /* Read the file into a Blob */
-
- boost::uintmax_t const size = boost::filesystem::file_size (path);
- FILE* f = fopen_boost (path, "rb");
- if (!f) {
- throw OpenFileError (path);
- }
-
- uint8_t* data = new uint8_t[size];
- if (fread (data, 1, size, f) != size) {
- delete[] data;
- throw ReadFileError (path);
- }
-
- fclose (f);
- _blob.update (data, size);
- delete[] data;
-}
-
-MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket, shared_ptr<Log> log)
- : ImageProxy (log)
-{
- uint32_t const size = socket->read_uint32 ();
- uint8_t* data = new uint8_t[size];
- socket->read (data, size);
- _blob.update (data, size);
- delete[] data;
-}
-
-shared_ptr<Image>
-MagickImageProxy::image () const
-{
- if (_image) {
- return _image;
- }
-
- LOG_TIMING ("[%1] MagickImageProxy begins decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
-
- Magick::Image* magick_image = 0;
- string error;
- try {
- magick_image = new Magick::Image (_blob);
- } catch (Magick::Exception& e) {
- error = e.what ();
- }
-
- if (!magick_image) {
- /* ImageMagick cannot auto-detect Targa files, it seems, so try here with an
- explicit format. I can't find it documented that passing a (0, 0) geometry
- is allowed, but it seems to work.
- */
- try {
- magick_image = new Magick::Image (_blob, Magick::Geometry (0, 0), "TGA");
- } catch (...) {
-
- }
- }
-
- if (!magick_image) {
- /* If we failed both an auto-detect and a forced-Targa we give the error from
- the auto-detect.
- */
- throw DecodeError (String::compose (_("Could not decode image file (%1)"), error));
- }
-
- LOG_TIMING ("[%1] MagickImageProxy decode finished", boost::this_thread::get_id ());
-
- libdcp::Size size (magick_image->columns(), magick_image->rows());
-
- _image.reset (new Image (PIX_FMT_RGB24, size, true));
-
- /* Write line-by-line here as _image must be aligned, and write() cannot be told about strides */
- uint8_t* p = _image->data()[0];
- for (int i = 0; i < size.height; ++i) {
-#ifdef DCPOMATIC_IMAGE_MAGICK
- using namespace MagickCore;
-#else
- using namespace MagickLib;
-#endif
- magick_image->write (0, i, size.width, 1, "RGB", CharPixel, p);
- p += _image->stride()[0];
- }
-
- delete magick_image;
-
- LOG_TIMING ("[%1] MagickImageProxy completes decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
-
- return _image;
-}
-
-void
-MagickImageProxy::add_metadata (xmlpp::Node* node) const
-{
- node->add_child("Type")->add_child_text (N_("Magick"));
-}
-
-void
-MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
-{
- socket->write (_blob.length ());
- socket->write ((uint8_t *) _blob.data (), _blob.length ());
-}
-
shared_ptr<ImageProxy>
image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
{
return shared_ptr<ImageProxy> (new RawImageProxy (xml, socket, log));
} else if (xml->string_child("Type") == N_("Magick")) {
return shared_ptr<MagickImageProxy> (new MagickImageProxy (xml, socket, log));
+ } else if (xml->string_child("Type") == N_("J2K")) {
+ return shared_ptr<J2KImageProxy> (new J2KImageProxy (xml, socket, log));
}
throw NetworkError (_("Unexpected image type received by server"));
*/
+#ifndef DCPOMATIC_IMAGE_PROXY_H
+#define DCPOMATIC_IMAGE_PROXY_H
+
/** @file src/lib/image_proxy.h
* @brief ImageProxy and subclasses.
*/
class Node;
}
+namespace dcp {
+ class MonoPictureFrame;
+ class StereoPictureFrame;
+}
+
/** @class ImageProxy
* @brief A class which holds an Image, and can produce it on request.
*
* of happening in a single-threaded decoder.
*
* For example, large TIFFs are slow to decode, so this class will keep
- * the TIFF data TIFF until such a time that the actual image is needed.
+ * the TIFF data compressed until the decompressed image is needed.
* At this point, the class decodes the TIFF to an Image.
*/
class ImageProxy : public boost::noncopyable
virtual boost::shared_ptr<Image> image () const = 0;
virtual void add_metadata (xmlpp::Node *) const = 0;
virtual void send_binary (boost::shared_ptr<Socket>) const = 0;
+ /** @return true if our image is definitely the same as another, false if it is probably not */
+ virtual bool same (boost::shared_ptr<const ImageProxy>) const {
+ return false;
+ }
protected:
boost::shared_ptr<Log> _log;
};
-class RawImageProxy : public ImageProxy
-{
-public:
- RawImageProxy (boost::shared_ptr<Image>, boost::shared_ptr<Log> log);
- RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
-
- boost::shared_ptr<Image> image () const;
- void add_metadata (xmlpp::Node *) const;
- void send_binary (boost::shared_ptr<Socket>) const;
-
-private:
- boost::shared_ptr<Image> _image;
-};
-
-class MagickImageProxy : public ImageProxy
-{
-public:
- MagickImageProxy (boost::filesystem::path, boost::shared_ptr<Log> log);
- MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
-
- boost::shared_ptr<Image> image () const;
- void add_metadata (xmlpp::Node *) const;
- void send_binary (boost::shared_ptr<Socket>) const;
-
-private:
- Magick::Blob _blob;
- mutable boost::shared_ptr<Image> _image;
-};
-
boost::shared_ptr<ImageProxy> image_proxy_factory (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
+
+#endif
--- /dev/null
+/*
+ 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 DCPOMATIC_IMAGE_SUBTITLE_H
+#define DCPOMATIC_IMAGE_SUBTITLE_H
+
+#include "rect.h"
+
+class Image;
+
+class ImageSubtitle
+{
+public:
+ ImageSubtitle (boost::shared_ptr<Image> i, dcpomatic::Rect<double> r)
+ : image (i)
+ , rectangle (r)
+ {}
+
+ boost::shared_ptr<Image> image;
+ /** Area that the subtitle covers on its corresponding video, expressed in
+ * proportions of the image size; e.g. rectangle.x = 0.5 would mean that
+ * the rectangle starts half-way across the video.
+ *
+ * This rectangle may or may not have had a SubtitleContent's offsets and
+ * scale applied to it, depending on context.
+ */
+ dcpomatic::Rect<double> rectangle;
+};
+
+#endif
#include <iostream>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "isdcf_metadata.h"
#include "i18n.h"
using std::string;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
-ISDCFMetadata::ISDCFMetadata (shared_ptr<const cxml::Node> node)
+ISDCFMetadata::ISDCFMetadata (cxml::ConstNodePtr node)
{
content_version = node->number_child<int> ("ContentVersion");
audio_language = node->string_child ("AudioLanguage");
#include <string>
#include <libxml++/libxml++.h>
-
-namespace cxml {
- class Node;
-}
+#include <libcxml/cxml.h>
class ISDCFMetadata
{
, two_d_version_of_three_d (false)
{}
- ISDCFMetadata (boost::shared_ptr<const cxml::Node>);
+ ISDCFMetadata (cxml::ConstNodePtr);
void as_xml (xmlpp::Node *) const;
void read_old_metadata (std::string, std::string);
--- /dev/null
+/*
+ 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 <libcxml/cxml.h>
+#include <dcp/raw_convert.h>
+#include <dcp/mono_picture_frame.h>
+#include <dcp/stereo_picture_frame.h>
+#include "j2k_image_proxy.h"
+#include "util.h"
+#include "image.h"
+#include "encoded_data.h"
+
+#include "i18n.h"
+
+using std::string;
+using boost::shared_ptr;
+
+J2KImageProxy::J2KImageProxy (shared_ptr<const dcp::MonoPictureFrame> frame, dcp::Size size, shared_ptr<Log> log)
+ : ImageProxy (log)
+ , _mono (frame)
+ , _size (size)
+{
+
+}
+
+J2KImageProxy::J2KImageProxy (shared_ptr<const dcp::StereoPictureFrame> frame, dcp::Size size, dcp::Eye eye, shared_ptr<Log> log)
+ : ImageProxy (log)
+ , _stereo (frame)
+ , _size (size)
+ , _eye (eye)
+{
+
+}
+
+J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
+ : ImageProxy (log)
+{
+ _size = dcp::Size (xml->number_child<int> ("Width"), xml->number_child<int> ("Height"));
+ if (xml->optional_number_child<int> ("Eye")) {
+ _eye = static_cast<dcp::Eye> (xml->number_child<int> ("Eye"));
+ int const left_size = xml->number_child<int> ("LeftSize");
+ int const right_size = xml->number_child<int> ("RightSize");
+ shared_ptr<dcp::StereoPictureFrame> f (new dcp::StereoPictureFrame ());
+ socket->read (f->left_j2k_data(), left_size);
+ socket->read (f->right_j2k_data(), right_size);
+ _stereo = f;
+ } else {
+ int const size = xml->number_child<int> ("Size");
+ shared_ptr<dcp::MonoPictureFrame> f (new dcp::MonoPictureFrame ());
+ socket->read (f->j2k_data (), size);
+ _mono = f;
+ }
+}
+
+shared_ptr<Image>
+J2KImageProxy::image () const
+{
+ shared_ptr<Image> image (new Image (PIX_FMT_RGB24, _size, false));
+
+ if (_mono) {
+ _mono->rgb_frame (image->data()[0]);
+ } else {
+ _stereo->rgb_frame (_eye, image->data()[0]);
+ }
+
+ return shared_ptr<Image> (new Image (image, true));
+}
+
+void
+J2KImageProxy::add_metadata (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text (N_("J2K"));
+ node->add_child("Width")->add_child_text (dcp::raw_convert<string> (_size.width));
+ node->add_child("Height")->add_child_text (dcp::raw_convert<string> (_size.height));
+ if (_stereo) {
+ node->add_child("Eye")->add_child_text (dcp::raw_convert<string> (_eye));
+ node->add_child("LeftSize")->add_child_text (dcp::raw_convert<string> (_stereo->left_j2k_size ()));
+ node->add_child("RightSize")->add_child_text (dcp::raw_convert<string> (_stereo->right_j2k_size ()));
+ } else {
+ node->add_child("Size")->add_child_text (dcp::raw_convert<string> (_mono->j2k_size ()));
+ }
+}
+
+void
+J2KImageProxy::send_binary (shared_ptr<Socket> socket) const
+{
+ if (_mono) {
+ socket->write (_mono->j2k_data(), _mono->j2k_size ());
+ } else {
+ socket->write (_stereo->left_j2k_data(), _stereo->left_j2k_size ());
+ socket->write (_stereo->right_j2k_data(), _stereo->right_j2k_size ());
+ }
+}
+
+shared_ptr<EncodedData>
+J2KImageProxy::j2k () const
+{
+ if (_mono) {
+ return shared_ptr<EncodedData> (new EncodedData (_mono->j2k_data(), _mono->j2k_size()));
+ } else {
+ if (_eye == dcp::EYE_LEFT) {
+ return shared_ptr<EncodedData> (new EncodedData (_stereo->left_j2k_data(), _stereo->left_j2k_size()));
+ } else {
+ return shared_ptr<EncodedData> (new EncodedData (_stereo->right_j2k_data(), _stereo->right_j2k_size()));
+ }
+ }
+}
--- /dev/null
+/*
+ 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 <dcp/util.h>
+#include "image_proxy.h"
+
+class EncodedData;
+
+class J2KImageProxy : public ImageProxy
+{
+public:
+ J2KImageProxy (boost::shared_ptr<const dcp::MonoPictureFrame> frame, dcp::Size, boost::shared_ptr<Log> log);
+ J2KImageProxy (boost::shared_ptr<const dcp::StereoPictureFrame> frame, dcp::Size, dcp::Eye, boost::shared_ptr<Log> log);
+ J2KImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
+
+ boost::shared_ptr<Image> image () const;
+ void add_metadata (xmlpp::Node *) const;
+ void send_binary (boost::shared_ptr<Socket>) const;
+
+ boost::shared_ptr<EncodedData> j2k () const;
+ dcp::Size size () const {
+ return _size;
+ }
+
+private:
+ boost::shared_ptr<const dcp::MonoPictureFrame> _mono;
+ boost::shared_ptr<const dcp::StereoPictureFrame> _stereo;
+ dcp::Size _size;
+ dcp::Eye _eye;
+};
/*
- 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 <boost/thread.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/exceptions.h>
+#include <dcp/exceptions.h>
#include "job.h"
#include "util.h"
#include "cross.h"
#include "ui_signaller.h"
#include "exceptions.h"
-#include "safe_stringstream.h"
+#include "film.h"
+#include "log.h"
#include "i18n.h"
run ();
- } catch (libdcp::FileError& e) {
-
+ } catch (dcp::FileError& e) {
+
string m = String::compose (_("An error occurred whilst handling the file %1."), boost::filesystem::path (e.filename()).leaf());
try {
}
}
-/** @return Time (in seconds) that this sub-job has been running */
+/** @return DCPTime (in seconds) that this sub-job has been running */
int
Job::elapsed_time () const
{
void
Job::set_error (string s, string d)
{
+ _film->log()->log (String::compose ("Error in job: %1 (%2)", s, d), Log::TYPE_ERROR);
boost::mutex::scoped_lock lm (_state_mutex);
_error_summary = s;
_error_details = d;
#include <boost/shared_ptr.hpp>
#include <quickmail.h>
#include <zip.h>
-#include <libdcp/kdm.h>
+#include <dcp/encrypted_kdm.h>
+#include <dcp/types.h>
#include "kdm.h"
#include "cinema.h"
#include "exceptions.h"
struct ScreenKDM
{
- ScreenKDM (shared_ptr<Screen> s, libdcp::KDM k)
+ ScreenKDM (shared_ptr<Screen> s, dcp::EncryptedKDM k)
: screen (s)
, kdm (k)
{}
shared_ptr<Screen> screen;
- libdcp::KDM kdm;
+ dcp::EncryptedKDM kdm;
};
static string
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
)
{
- list<libdcp::KDM> kdms = film->make_kdms (screens, cpl, from, to, formulation);
+ list<dcp::EncryptedKDM> kdms = film->make_kdms (screens, cpl, from, to, formulation);
list<ScreenKDM> screen_kdms;
list<shared_ptr<Screen> >::iterator i = screens.begin ();
- list<libdcp::KDM>::iterator j = kdms.begin ();
+ list<dcp::EncryptedKDM>::iterator j = kdms.begin ();
while (i != screens.end() && j != kdms.end ()) {
screen_kdms.push_back (ScreenKDM (*i, *j));
++i;
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
)
{
list<ScreenKDM> screen_kdms = make_screen_kdms (film, screens, cpl, from, to, formulation);
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation,
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation,
boost::filesystem::path directory
)
{
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation,
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation,
boost::filesystem::path directory
)
{
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
)
{
list<CinemaKDMs> cinema_kdms = make_cinema_kdms (film, screens, cpl, from, to, formulation);
boost::shared_ptr<const Film> film,
std::list<boost::shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation,
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation,
boost::filesystem::path directory
);
boost::shared_ptr<const Film> film,
std::list<boost::shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation,
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation,
boost::filesystem::path directory
);
boost::shared_ptr<const Film> film,
std::list<boost::shared_ptr<Screen> > screens,
boost::filesystem::path cpl,
- boost::posix_time::ptime from,
- boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::LocalTime from,
+ dcp::LocalTime to,
+ dcp::Formulation formulation
);
--- /dev/null
+/*
+ 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 <Magick++.h>
+#include "magick_image_proxy.h"
+#include "cross.h"
+#include "exceptions.h"
+#include "util.h"
+#include "log.h"
+#include "image.h"
+#include "log.h"
+
+#include "i18n.h"
+
+#define LOG_TIMING(...) _log->microsecond_log (String::compose (__VA_ARGS__), Log::TYPE_TIMING);
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+MagickImageProxy::MagickImageProxy (boost::filesystem::path path, shared_ptr<Log> log)
+ : ImageProxy (log)
+{
+ /* Read the file into a Blob */
+
+ boost::uintmax_t const size = boost::filesystem::file_size (path);
+ FILE* f = fopen_boost (path, "rb");
+ if (!f) {
+ throw OpenFileError (path);
+ }
+
+ uint8_t* data = new uint8_t[size];
+ if (fread (data, 1, size, f) != size) {
+ delete[] data;
+ throw ReadFileError (path);
+ }
+
+ fclose (f);
+ _blob.update (data, size);
+ delete[] data;
+}
+
+MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket, shared_ptr<Log> log)
+ : ImageProxy (log)
+{
+ uint32_t const size = socket->read_uint32 ();
+ uint8_t* data = new uint8_t[size];
+ socket->read (data, size);
+ _blob.update (data, size);
+ delete[] data;
+}
+
+shared_ptr<Image>
+MagickImageProxy::image () const
+{
+ if (_image) {
+ return _image;
+ }
+
+ LOG_TIMING ("[%1] MagickImageProxy begins decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
+
+ Magick::Image* magick_image = 0;
+ string error;
+ try {
+ magick_image = new Magick::Image (_blob);
+ } catch (Magick::Exception& e) {
+ error = e.what ();
+ }
+
+ if (!magick_image) {
+ /* ImageMagick cannot auto-detect Targa files, it seems, so try here with an
+ explicit format. I can't find it documented that passing a (0, 0) geometry
+ is allowed, but it seems to work.
+ */
+ try {
+ magick_image = new Magick::Image (_blob, Magick::Geometry (0, 0), "TGA");
+ } catch (...) {
+
+ }
+ }
+
+ if (!magick_image) {
+ /* If we failed both an auto-detect and a forced-Targa we give the error from
+ the auto-detect.
+ */
+ throw DecodeError (String::compose (_("Could not decode image file (%1)"), error));
+ }
+
+ dcp::Size size (magick_image->columns(), magick_image->rows());
+ LOG_TIMING ("[%1] MagickImageProxy decode finished", boost::this_thread::get_id ());
+
+ _image.reset (new Image (PIX_FMT_RGB24, size, true));
+
+ /* Write line-by-line here as _image must be aligned, and write() cannot be told about strides */
+ uint8_t* p = _image->data()[0];
+ for (int i = 0; i < size.height; ++i) {
+ using namespace MagickCore;
+ magick_image->write (0, i, size.width, 1, "RGB", CharPixel, p);
+ p += _image->stride()[0];
+ }
+
+ delete magick_image;
+
+ LOG_TIMING ("[%1] MagickImageProxy completes decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
+
+ return _image;
+}
+
+void
+MagickImageProxy::add_metadata (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text (N_("Magick"));
+}
+
+void
+MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
+{
+ socket->write (_blob.length ());
+ socket->write ((uint8_t *) _blob.data (), _blob.length ());
+}
+
+bool
+MagickImageProxy::same (shared_ptr<const ImageProxy> other) const
+{
+ shared_ptr<const MagickImageProxy> mp = dynamic_pointer_cast<const MagickImageProxy> (other);
+ if (!mp) {
+ return false;
+ }
+
+ if (_blob.length() != mp->_blob.length()) {
+ return false;
+ }
+
+ return memcmp (_blob.data(), mp->_blob.data(), _blob.length()) == 0;
+}
--- /dev/null
+/*
+ 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 "image_proxy.h"
+
+class MagickImageProxy : public ImageProxy
+{
+public:
+ MagickImageProxy (boost::filesystem::path, boost::shared_ptr<Log> log);
+ MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
+
+ boost::shared_ptr<Image> image () const;
+ void add_metadata (xmlpp::Node *) const;
+ void send_binary (boost::shared_ptr<Socket>) const;
+ bool same (boost::shared_ptr<const ImageProxy> other) const;
+
+private:
+ Magick::Blob _blob;
+ mutable boost::shared_ptr<Image> _image;
+};
--- /dev/null
+/*
+ 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 "mid_side_decoder.h"
+#include "audio_buffers.h"
+
+#include "i18n.h"
+
+using std::string;
+using boost::shared_ptr;
+
+string
+MidSideDecoder::name () const
+{
+ return _("Mid-side decoder");
+}
+
+string
+MidSideDecoder::id () const
+{
+ return N_("mid-side-decoder");
+}
+
+ChannelCount
+MidSideDecoder::in_channels () const
+{
+ return ChannelCount (2);
+}
+
+int
+MidSideDecoder::out_channels (int) const
+{
+ return 3;
+}
+
+shared_ptr<AudioProcessor>
+MidSideDecoder::clone (int) const
+{
+ return shared_ptr<AudioProcessor> (new MidSideDecoder ());
+}
+
+shared_ptr<AudioBuffers>
+MidSideDecoder::run (shared_ptr<const AudioBuffers> in)
+{
+ shared_ptr<AudioBuffers> out (new AudioBuffers (3, in->frames ()));
+ for (int i = 0; i < in->frames(); ++i) {
+ float const left = in->data()[0][i];
+ float const right = in->data()[1][i];
+ float const mid = (left + right) / 2;
+ out->data()[0][i] = left - mid;
+ out->data()[1][i] = right - mid;
+ out->data()[2][i] = mid;
+ }
+
+ return out;
+}
--- /dev/null
+/*
+ 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 "audio_processor.h"
+
+class MidSideDecoder : public AudioProcessor
+{
+public:
+ std::string name () const;
+ std::string id () const;
+ ChannelCount in_channels () const;
+ int out_channels (int) const;
+ boost::shared_ptr<AudioProcessor> clone (int) const;
+ boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+};
+
+
+++ /dev/null
-/*
- 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 "piece.h"
-#include "player.h"
-
-using boost::shared_ptr;
-
-Piece::Piece (shared_ptr<Content> c)
- : content (c)
- , video_position (c->position ())
- , audio_position (c->position ())
- , repeat_to_do (0)
- , repeat_done (0)
-{
-
-}
-
-Piece::Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
- : content (c)
- , decoder (d)
- , video_position (c->position ())
- , audio_position (c->position ())
- , repeat_to_do (0)
- , repeat_done (0)
-{
-
-}
-
-/** Set this piece to repeat a video frame a given number of times */
-void
-Piece::set_repeat (IncomingVideo video, int num)
-{
- repeat_video = video;
- repeat_to_do = num;
- repeat_done = 0;
-}
-
-void
-Piece::reset_repeat ()
-{
- repeat_video.image.reset ();
- repeat_to_do = 0;
- repeat_done = 0;
-}
-
-bool
-Piece::repeating () const
-{
- return repeat_done != repeat_to_do;
-}
-
-void
-Piece::repeat (Player* player)
-{
- player->process_video (
- repeat_video.weak_piece,
- repeat_video.image,
- repeat_video.eyes,
- repeat_video.part,
- repeat_done > 0,
- repeat_video.frame,
- (repeat_done + 1) * (TIME_HZ / player->_film->video_frame_rate ())
- );
-
- ++repeat_done;
-}
-
class Content;
class Decoder;
-class Piece;
-class ImageProxy;
-class Player;
-
-struct IncomingVideo
-{
-public:
- boost::weak_ptr<Piece> weak_piece;
- boost::shared_ptr<const ImageProxy> image;
- Eyes eyes;
- Part part;
- bool same;
- VideoContent::Frame frame;
- Time extra;
-};
class Piece
{
public:
- Piece (boost::shared_ptr<Content> c);
- Piece (boost::shared_ptr<Content> c, boost::shared_ptr<Decoder> d);
- void set_repeat (IncomingVideo video, int num);
- void reset_repeat ();
- bool repeating () const;
- void repeat (Player* player);
-
+ Piece (boost::shared_ptr<Content> c, boost::shared_ptr<Decoder> d, FrameRateChange f)
+ : content (c)
+ , decoder (d)
+ , frc (f)
+ {}
+
boost::shared_ptr<Content> content;
boost::shared_ptr<Decoder> decoder;
- /** Time of the last video we emitted relative to the start of the DCP */
- Time video_position;
- /** Time of the last audio we emitted relative to the start of the DCP */
- Time audio_position;
-
- IncomingVideo repeat_video;
- int repeat_to_do;
- int repeat_done;
+ FrameRateChange frc;
};
#endif
*/
-#include <stdint.h>
#include "player.h"
#include "film.h"
#include "ffmpeg_decoder.h"
+#include "audio_buffers.h"
#include "ffmpeg_content.h"
#include "image_decoder.h"
#include "image_content.h"
#include "sndfile_decoder.h"
#include "sndfile_content.h"
#include "subtitle_content.h"
+#include "subrip_decoder.h"
+#include "subrip_content.h"
+#include "dcp_content.h"
#include "playlist.h"
#include "job.h"
#include "image.h"
-#include "image_proxy.h"
+#include "raw_image_proxy.h"
#include "ratio.h"
-#include "resampler.h"
#include "log.h"
#include "scaler.h"
-#include "player_video_frame.h"
+#include "render_subtitles.h"
+#include "config.h"
+#include "content_video.h"
+#include "player_video.h"
#include "frame_rate_change.h"
+#include "dcp_content.h"
+#include "dcp_decoder.h"
+#include "dcp_subtitle_content.h"
+#include "dcp_subtitle_decoder.h"
+#include <boost/foreach.hpp>
+#include <stdint.h>
+#include <algorithm>
#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
using std::cout;
using std::min;
using std::max;
+using std::min;
using std::vector;
using std::pair;
using std::map;
+using std::make_pair;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
+using boost::optional;
Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
: _film (f)
, _playlist (p)
- , _video (true)
- , _audio (true)
, _have_valid_pieces (false)
- , _video_position (0)
- , _audio_position (0)
- , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
- , _last_emit_was_black (false)
+ , _approximate_size (false)
{
_playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
_playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
}
void
-Player::disable_video ()
-{
- _video = false;
-}
-
-void
-Player::disable_audio ()
+Player::setup_pieces ()
{
- _audio = false;
-}
+ list<shared_ptr<Piece> > old_pieces = _pieces;
+ _pieces.clear ();
-bool
-Player::pass ()
-{
- if (!_have_valid_pieces) {
- setup_pieces ();
- }
+ ContentList content = _playlist->content ();
- Time earliest_t = TIME_MAX;
- shared_ptr<Piece> earliest;
- enum {
- VIDEO,
- AUDIO
- } type = VIDEO;
+ for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- if ((*i)->decoder->done () || (*i)->content->length_after_trim() == 0) {
+ if (!(*i)->paths_valid ()) {
continue;
}
-
- shared_ptr<VideoDecoder> vd = dynamic_pointer_cast<VideoDecoder> ((*i)->decoder);
- shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
-
- if (_video && vd) {
- if ((*i)->video_position < earliest_t) {
- earliest_t = (*i)->video_position;
- earliest = *i;
- type = VIDEO;
+
+ shared_ptr<Decoder> decoder;
+ optional<FrameRateChange> frc;
+
+ /* Work out a FrameRateChange for the best overlap video for this content, in case we need it below */
+ DCPTime best_overlap_t;
+ shared_ptr<VideoContent> best_overlap;
+ for (ContentList::iterator j = content.begin(); j != content.end(); ++j) {
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*j);
+ if (!vc) {
+ continue;
+ }
+
+ DCPTime const overlap = max (vc->position(), (*i)->position()) - min (vc->end(), (*i)->end());
+ if (overlap > best_overlap_t) {
+ best_overlap = vc;
+ best_overlap_t = overlap;
}
}
- if (_audio && ad && ad->has_audio ()) {
- if ((*i)->audio_position < earliest_t) {
- earliest_t = (*i)->audio_position;
- earliest = *i;
- type = AUDIO;
- }
+ optional<FrameRateChange> best_overlap_frc;
+ if (best_overlap) {
+ best_overlap_frc = FrameRateChange (best_overlap->video_frame_rate(), _film->video_frame_rate ());
+ } else {
+ /* No video overlap; e.g. if the DCP is just audio */
+ best_overlap_frc = FrameRateChange (_film->video_frame_rate(), _film->video_frame_rate ());
}
- }
- if (!earliest) {
- flush ();
- return true;
- }
+ /* FFmpeg */
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+ if (fc) {
+ decoder.reset (new FFmpegDecoder (fc, _film->log()));
+ frc = FrameRateChange (fc->video_frame_rate(), _film->video_frame_rate());
+ }
- switch (type) {
- case VIDEO:
- if (earliest_t > _video_position) {
- emit_black ();
- } else {
- if (earliest->repeating ()) {
- earliest->repeat (this);
- } else {
- earliest->decoder->pass ();
- }
+ shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (*i);
+ if (dc) {
+ decoder.reset (new DCPDecoder (dc, _film->log ()));
+ frc = FrameRateChange (dc->video_frame_rate(), _film->video_frame_rate());
}
- break;
- case AUDIO:
- if (earliest_t > _audio_position) {
- emit_silence (_film->time_to_audio_frames (earliest_t - _audio_position));
- } else {
- earliest->decoder->pass ();
-
- if (earliest->decoder->done()) {
- shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (earliest->content);
- assert (ac);
- shared_ptr<Resampler> re = resampler (ac, false);
- if (re) {
- shared_ptr<const AudioBuffers> b = re->flush ();
- if (b->frames ()) {
- process_audio (
- earliest,
- b,
- ac->audio_length() * ac->output_audio_frame_rate() / ac->content_audio_frame_rate(),
- true
- );
- }
+ /* ImageContent */
+ shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
+ if (ic) {
+ /* See if we can re-use an old ImageDecoder */
+ for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
+ shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
+ if (imd && imd->content() == ic) {
+ decoder = imd;
}
}
- }
- break;
- }
- if (_audio) {
- boost::optional<Time> audio_done_up_to;
- for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- if ((*i)->decoder->done ()) {
- continue;
+ if (!decoder) {
+ decoder.reset (new ImageDecoder (ic));
}
- shared_ptr<AudioDecoder> ad = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
- if (ad && ad->has_audio ()) {
- audio_done_up_to = min (audio_done_up_to.get_value_or (TIME_MAX), (*i)->audio_position);
- }
+ frc = FrameRateChange (ic->video_frame_rate(), _film->video_frame_rate());
}
- if (audio_done_up_to) {
- TimedAudioBuffers<Time> tb = _audio_merger.pull (audio_done_up_to.get ());
- Audio (tb.audio, tb.time);
- _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
+ /* SndfileContent */
+ shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
+ if (sc) {
+ decoder.reset (new SndfileDecoder (sc));
+ frc = best_overlap_frc;
}
- }
-
- return false;
-}
-/** @param extra Amount of extra time to add to the content frame's time (for repeat) */
-void
-Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const ImageProxy> image, Eyes eyes, Part part, bool same, VideoContent::Frame frame, Time extra)
-{
- /* Keep a note of what came in so that we can repeat it if required */
- _last_incoming_video.weak_piece = weak_piece;
- _last_incoming_video.image = image;
- _last_incoming_video.eyes = eyes;
- _last_incoming_video.part = part;
- _last_incoming_video.same = same;
- _last_incoming_video.frame = frame;
- _last_incoming_video.extra = extra;
-
- shared_ptr<Piece> piece = weak_piece.lock ();
- if (!piece) {
- return;
- }
-
- shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
- assert (content);
-
- FrameRateChange frc (content->video_frame_rate(), _film->video_frame_rate());
- if (frc.skip && (frame % 2) == 1) {
- return;
- }
-
- Time const relative_time = (frame * frc.factor() * TIME_HZ / _film->video_frame_rate());
- if (content->trimmed (relative_time)) {
- return;
- }
-
- Time const time = content->position() + relative_time + extra - content->trim_start ();
- libdcp::Size const image_size = content->scale().size (content, _video_container_size, _film->frame_size ());
-
- shared_ptr<PlayerVideoFrame> pi (
- new PlayerVideoFrame (
- image,
- content->crop(),
- image_size,
- _video_container_size,
- _film->scaler(),
- eyes,
- part,
- content->colour_conversion()
- )
- );
-
- if (_film->with_subtitles ()) {
- for (list<Subtitle>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
- if (i->covers (time)) {
- /* This may be true for more than one of _subtitles, but the last (latest-starting)
- one is the one we want to use, so that's ok.
- */
- Position<int> const container_offset (
- (_video_container_size.width - image_size.width) / 2,
- (_video_container_size.height - image_size.width) / 2
- );
-
- pi->set_subtitle (i->out_image(), i->out_position() + container_offset);
- }
+ /* SubRipContent */
+ shared_ptr<const SubRipContent> rc = dynamic_pointer_cast<const SubRipContent> (*i);
+ if (rc) {
+ decoder.reset (new SubRipDecoder (rc));
+ frc = best_overlap_frc;
}
- }
- /* Clear out old subtitles */
- for (list<Subtitle>::iterator i = _subtitles.begin(); i != _subtitles.end(); ) {
- list<Subtitle>::iterator j = i;
- ++j;
-
- if (i->ends_before (time)) {
- _subtitles.erase (i);
+ /* DCPSubtitleContent */
+ shared_ptr<const DCPSubtitleContent> dsc = dynamic_pointer_cast<const DCPSubtitleContent> (*i);
+ if (dsc) {
+ decoder.reset (new DCPSubtitleDecoder (dsc));
+ frc = best_overlap_frc;
}
- i = j;
+ _pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder, frc.get ())));
}
-#ifdef DCPOMATIC_DEBUG
- _last_video = piece->content;
-#endif
+ _have_valid_pieces = true;
+}
- Video (pi, same, time);
+void
+Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
+{
+ shared_ptr<Content> c = w.lock ();
+ if (!c) {
+ return;
+ }
- _last_emit_was_black = false;
- _video_position = piece->video_position = (time + TIME_HZ / _film->video_frame_rate());
+ if (
+ property == ContentProperty::POSITION ||
+ property == ContentProperty::LENGTH ||
+ property == ContentProperty::TRIM_START ||
+ property == ContentProperty::TRIM_END ||
+ property == ContentProperty::PATH ||
+ property == VideoContentProperty::VIDEO_FRAME_TYPE ||
+ property == DCPContentProperty::CAN_BE_PLAYED
+ ) {
+
+ _have_valid_pieces = false;
+ Changed (frequent);
- if (frc.repeat > 1 && !piece->repeating ()) {
- piece->set_repeat (_last_incoming_video, frc.repeat - 1);
+ } else if (
+ property == SubtitleContentProperty::USE_SUBTITLES ||
+ property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
+ property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
+ property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
+ property == SubtitleContentProperty::SUBTITLE_Y_SCALE ||
+ property == VideoContentProperty::VIDEO_CROP ||
+ property == VideoContentProperty::VIDEO_SCALE ||
+ property == VideoContentProperty::VIDEO_FRAME_RATE
+ ) {
+
+ Changed (frequent);
}
}
/** @param already_resampled true if this data has already been through the chain up to the resampler */
void
-Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers> audio, AudioContent::Frame frame, bool already_resampled)
+Player::playlist_changed ()
{
- shared_ptr<Piece> piece = weak_piece.lock ();
- if (!piece) {
- return;
- }
+ _have_valid_pieces = false;
+ Changed (false);
+}
- shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
- assert (content);
+void
+Player::set_video_container_size (dcp::Size s)
+{
+ _video_container_size = s;
- if (!already_resampled) {
- /* Gain */
- if (content->audio_gain() != 0) {
- shared_ptr<AudioBuffers> gain (new AudioBuffers (audio));
- gain->apply_gain (content->audio_gain ());
- audio = gain;
- }
-
- /* Resample */
- if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
- shared_ptr<Resampler> r = resampler (content, true);
- pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> ro = r->run (audio, frame);
- audio = ro.first;
- frame = ro.second;
- }
- }
-
- Time const relative_time = _film->audio_frames_to_time (frame);
+ _black_image.reset (new Image (PIX_FMT_RGB24, _video_container_size, true));
+ _black_image->make_black ();
+}
- if (content->trimmed (relative_time)) {
- return;
- }
+void
+Player::film_changed (Film::Property p)
+{
+ /* Here we should notice Film properties that affect our output, and
+ alert listeners that our output now would be different to how it was
+ last time we were run.
+ */
- Time time = content->position() + (content->audio_delay() * TIME_HZ / 1000) + relative_time - content->trim_start ();
-
- /* Remap channels */
- shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), audio->frames()));
- dcp_mapped->make_silent ();
-
- AudioMapping map = content->audio_mapping ();
- for (int i = 0; i < map.content_channels(); ++i) {
- for (int j = 0; j < _film->audio_channels(); ++j) {
- if (map.get (i, static_cast<libdcp::Channel> (j)) > 0) {
- dcp_mapped->accumulate_channel (
- audio.get(),
- i,
- static_cast<libdcp::Channel> (j),
- map.get (i, static_cast<libdcp::Channel> (j))
- );
- }
- }
+ if (p == Film::SCALER || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
+ Changed (false);
}
+}
- audio = dcp_mapped;
-
- /* We must cut off anything that comes before the start of all time */
- if (time < 0) {
- int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
- if (frames >= audio->frames ()) {
- return;
+list<PositionImage>
+Player::transform_image_subtitles (list<ImageSubtitle> subs) const
+{
+ list<PositionImage> all;
+
+ for (list<ImageSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
+ if (!i->image) {
+ continue;
}
- shared_ptr<AudioBuffers> trimmed (new AudioBuffers (audio->channels(), audio->frames() - frames));
- trimmed->copy_from (audio.get(), audio->frames() - frames, frames, 0);
-
- audio = trimmed;
- time = 0;
+ /* We will scale the subtitle up to fit _video_container_size */
+ dcp::Size scaled_size (i->rectangle.width * _video_container_size.width, i->rectangle.height * _video_container_size.height);
+
+ /* Then we need a corrective translation, consisting of two parts:
+ *
+ * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be
+ * rect.x * _video_container_size.width and rect.y * _video_container_size.height.
+ *
+ * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be
+ * (width_before_subtitle_scale * (1 - subtitle_x_scale) / 2) and
+ * (height_before_subtitle_scale * (1 - subtitle_y_scale) / 2).
+ *
+ * Combining these two translations gives these expressions.
+ */
+
+ all.push_back (
+ PositionImage (
+ i->image->scale (
+ scaled_size,
+ Scaler::from_id ("bicubic"),
+ i->image->pixel_format (),
+ true
+ ),
+ Position<int> (
+ rint (_video_container_size.width * i->rectangle.x),
+ rint (_video_container_size.height * i->rectangle.y)
+ )
+ )
+ );
}
- _audio_merger.push (audio, time);
- piece->audio_position += _film->audio_frames_to_time (audio->frames ());
+ return all;
}
void
-Player::flush ()
+Player::set_approximate_size ()
{
- TimedAudioBuffers<Time> tb = _audio_merger.flush ();
- if (_audio && tb.audio) {
- Audio (tb.audio, tb.time);
- _audio_position += _film->audio_frames_to_time (tb.audio->frames ());
- }
-
- while (_video && _video_position < _audio_position) {
- emit_black ();
- }
+ _approximate_size = true;
+}
- while (_audio && _audio_position < _video_position) {
- emit_silence (_film->time_to_audio_frames (_video_position - _audio_position));
- }
-
+shared_ptr<PlayerVideo>
+Player::black_player_video_frame (DCPTime time) const
+{
+ return shared_ptr<PlayerVideo> (
+ new PlayerVideo (
+ shared_ptr<const ImageProxy> (new RawImageProxy (_black_image, _film->log ())),
+ time,
+ Crop (),
+ optional<float> (),
+ _video_container_size,
+ _video_container_size,
+ Scaler::from_id ("bicubic"),
+ EYES_BOTH,
+ PART_WHOLE,
+ Config::instance()->colour_conversions().front().conversion
+ )
+ );
}
-/** Seek so that the next pass() will yield (approximately) the requested frame.
- * Pass accurate = true to try harder to get close to the request.
- * @return true on error
- */
-void
-Player::seek (Time t, bool accurate)
+/** @return All PlayerVideos at the given time (there may be two frames for 3D) */
+list<shared_ptr<PlayerVideo> >
+Player::get_video (DCPTime time, bool accurate)
{
if (!_have_valid_pieces) {
setup_pieces ();
}
+
+ list<shared_ptr<Piece> > ov = overlaps<VideoContent> (
+ time,
+ time + DCPTime::from_frames (1, _film->video_frame_rate ())
+ );
- if (_pieces.empty ()) {
- return;
- }
+ list<shared_ptr<PlayerVideo> > pvf;
- for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> ((*i)->content);
- if (!vc) {
- continue;
+ if (ov.empty ()) {
+ /* No video content at this time */
+ pvf.push_back (black_player_video_frame (time));
+ } else {
+ /* Create a PlayerVideo from the content's video at this time */
+
+ shared_ptr<Piece> piece = ov.back ();
+ shared_ptr<VideoDecoder> decoder = dynamic_pointer_cast<VideoDecoder> (piece->decoder);
+ assert (decoder);
+ shared_ptr<VideoContent> content = dynamic_pointer_cast<VideoContent> (piece->content);
+ assert (content);
+
+ list<ContentVideo> content_video = decoder->get_video (dcp_to_content_video (piece, time), accurate);
+ if (content_video.empty ()) {
+ pvf.push_back (black_player_video_frame (time));
+ return pvf;
}
+
+ dcp::Size image_size = content->scale().size (content, _video_container_size, _film->frame_size (), _approximate_size ? 4 : 1);
+ if (_approximate_size) {
+ image_size.width &= ~3;
+ image_size.height &= ~3;
+ }
+
+ for (list<ContentVideo>::const_iterator i = content_video.begin(); i != content_video.end(); ++i) {
+ pvf.push_back (
+ shared_ptr<PlayerVideo> (
+ new PlayerVideo (
+ i->image,
+ content_video_to_dcp (piece, i->frame),
+ content->crop (),
+ content->fade (i->frame),
+ image_size,
+ _video_container_size,
+ _film->scaler(),
+ i->eyes,
+ i->part,
+ content->colour_conversion ()
+ )
+ )
+ );
+ }
+ }
- /* s is the offset of t from the start position of this content */
- Time s = t - vc->position ();
- s = max (static_cast<Time> (0), s);
- s = min (vc->length_after_trim(), s);
-
- /* Hence set the piece positions to the `global' time */
- (*i)->video_position = (*i)->audio_position = vc->position() + s;
+ /* Add subtitles (for possible burn-in) to whatever PlayerVideos we got */
- /* And seek the decoder */
- dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (
- vc->time_to_content_video_frames (s + vc->trim_start ()), accurate
- );
+ PlayerSubtitles ps = get_subtitles (time, DCPTime::from_frames (1, _film->video_frame_rate ()), false);
- (*i)->reset_repeat ();
- }
+ list<PositionImage> sub_images;
- _video_position = _audio_position = t;
+ /* Image subtitles */
+ list<PositionImage> c = transform_image_subtitles (ps.image);
+ copy (c.begin(), c.end(), back_inserter (sub_images));
- /* XXX: don't seek audio because we don't need to... */
+ /* Text subtitles (rendered to images) */
+ sub_images.push_back (render_subtitles (ps.text, _video_container_size));
+
+ if (!sub_images.empty ()) {
+ for (list<shared_ptr<PlayerVideo> >::const_iterator i = pvf.begin(); i != pvf.end(); ++i) {
+ (*i)->set_subtitle (merge (sub_images));
+ }
+ }
+
+ return pvf;
}
-void
-Player::setup_pieces ()
+shared_ptr<AudioBuffers>
+Player::get_audio (DCPTime time, DCPTime length, bool accurate)
{
- list<shared_ptr<Piece> > old_pieces = _pieces;
+ if (!_have_valid_pieces) {
+ setup_pieces ();
+ }
- _pieces.clear ();
+ AudioFrame const length_frames = length.frames (_film->audio_frame_rate ());
- ContentList content = _playlist->content ();
- sort (content.begin(), content.end(), ContentSorter ());
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (_film->audio_channels(), length_frames));
+ audio->make_silent ();
+
+ list<shared_ptr<Piece> > ov = overlaps<AudioContent> (time, time + length);
+ if (ov.empty ()) {
+ return audio;
+ }
- for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) {
- if (!(*i)->paths_valid ()) {
+ shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content);
+ assert (content);
+ shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder);
+ assert (decoder);
+
+ if (content->audio_frame_rate() == 0) {
+ /* This AudioContent has no audio (e.g. if it is an FFmpegContent with no
+ * audio stream).
+ */
continue;
}
- shared_ptr<Piece> piece (new Piece (*i));
+ /* The time that we should request from the content */
+ DCPTime request = time - DCPTime::from_seconds (content->audio_delay() / 1000.0);
+ DCPTime offset;
+ if (request < DCPTime ()) {
+ /* We went off the start of the content, so we will need to offset
+ the stuff we get back.
+ */
+ offset = -request;
+ request = DCPTime ();
+ }
- /* XXX: into content? */
+ AudioFrame const content_frame = dcp_to_content_audio (*i, request);
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
- if (fc) {
- shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (_film, fc, _video, _audio));
-
- fd->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, _5, 0));
- fd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2, false));
- fd->Subtitle.connect (bind (&Player::process_subtitle, this, weak_ptr<Piece> (piece), _1, _2, _3, _4));
+ /* Audio from this piece's decoder (which might be more or less than what we asked for) */
+ shared_ptr<ContentAudio> all = decoder->get_audio (content_frame, length_frames, accurate);
- fd->seek (fc->time_to_content_video_frames (fc->trim_start ()), true);
- piece->decoder = fd;
+ /* Gain */
+ if (content->audio_gain() != 0) {
+ shared_ptr<AudioBuffers> gain (new AudioBuffers (all->audio));
+ gain->apply_gain (content->audio_gain ());
+ all->audio = gain;
}
-
- shared_ptr<const ImageContent> ic = dynamic_pointer_cast<const ImageContent> (*i);
- if (ic) {
- bool reusing = false;
-
- /* See if we can re-use an old ImageDecoder */
- for (list<shared_ptr<Piece> >::const_iterator j = old_pieces.begin(); j != old_pieces.end(); ++j) {
- shared_ptr<ImageDecoder> imd = dynamic_pointer_cast<ImageDecoder> ((*j)->decoder);
- if (imd && imd->content() == ic) {
- piece = *j;
- reusing = true;
- }
- }
- if (!reusing) {
- shared_ptr<ImageDecoder> id (new ImageDecoder (_film, ic));
- id->Video.connect (bind (&Player::process_video, this, weak_ptr<Piece> (piece), _1, _2, _3, _4, _5, 0));
- piece->decoder = id;
+ /* Remap channels */
+ shared_ptr<AudioBuffers> dcp_mapped (new AudioBuffers (_film->audio_channels(), all->audio->frames()));
+ dcp_mapped->make_silent ();
+ AudioMapping map = content->audio_mapping ();
+ for (int i = 0; i < map.content_channels(); ++i) {
+ for (int j = 0; j < _film->audio_channels(); ++j) {
+ if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
+ dcp_mapped->accumulate_channel (
+ all->audio.get(),
+ i,
+ j,
+ map.get (i, static_cast<dcp::Channel> (j))
+ );
+ }
}
}
+
+ all->audio = dcp_mapped;
- shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
- if (sc) {
- shared_ptr<AudioDecoder> sd (new SndfileDecoder (_film, sc));
- sd->Audio.connect (bind (&Player::process_audio, this, weak_ptr<Piece> (piece), _1, _2, false));
-
- piece->decoder = sd;
- }
-
- _pieces.push_back (piece);
+ audio->accumulate_frames (
+ all->audio.get(),
+ content_frame - all->frame,
+ offset.frames (_film->audio_frame_rate()),
+ min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
+ );
}
- _have_valid_pieces = true;
+ return audio;
}
-void
-Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
+VideoFrame
+Player::dcp_to_content_video (shared_ptr<const Piece> piece, DCPTime t) const
{
- shared_ptr<Content> c = w.lock ();
- if (!c) {
- return;
- }
+ /* s is the offset of t from the start position of this content */
+ DCPTime s = t - piece->content->position ();
+ s = DCPTime (max (DCPTime::Type (0), s.get ()));
+ s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
- if (
- property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
- property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
- property == VideoContentProperty::VIDEO_FRAME_TYPE
- ) {
-
- _have_valid_pieces = false;
- Changed (frequent);
-
- } else if (
- property == SubtitleContentProperty::SUBTITLE_X_OFFSET ||
- property == SubtitleContentProperty::SUBTITLE_Y_OFFSET ||
- property == SubtitleContentProperty::SUBTITLE_X_SCALE ||
- property == SubtitleContentProperty::SUBTITLE_Y_SCALE
- ) {
-
- for (list<Subtitle>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
- i->update (_film, _video_container_size);
- }
-
- Changed (frequent);
-
- } else if (
- property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_SCALE ||
- property == VideoContentProperty::VIDEO_FRAME_RATE
- ) {
-
- Changed (frequent);
-
- } else if (property == ContentProperty::PATH) {
-
- _have_valid_pieces = false;
- Changed (frequent);
- }
+ /* Convert this to the content frame */
+ return DCPTime (s + piece->content->trim_start()).frames (_film->video_frame_rate()) * piece->frc.factor ();
}
-void
-Player::playlist_changed ()
+DCPTime
+Player::content_video_to_dcp (shared_ptr<const Piece> piece, VideoFrame f) const
{
- _have_valid_pieces = false;
- Changed (false);
+ DCPTime t = DCPTime::from_frames (f / piece->frc.factor (), _film->video_frame_rate()) - piece->content->trim_start () + piece->content->position ();
+ if (t < DCPTime ()) {
+ t = DCPTime ();
+ }
+
+ return t;
}
-void
-Player::set_video_container_size (libdcp::Size s)
+AudioFrame
+Player::dcp_to_content_audio (shared_ptr<const Piece> piece, DCPTime t) const
{
- _video_container_size = s;
+ /* s is the offset of t from the start position of this content */
+ DCPTime s = t - piece->content->position ();
+ s = DCPTime (max (DCPTime::Type (0), s.get ()));
+ s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
- shared_ptr<Image> im (new Image (PIX_FMT_RGB24, _video_container_size, true));
- im->make_black ();
-
- _black_frame.reset (
- new PlayerVideoFrame (
- shared_ptr<ImageProxy> (new RawImageProxy (im, _film->log ())),
- Crop(),
- _video_container_size,
- _video_container_size,
- Scaler::from_id ("bicubic"),
- EYES_BOTH,
- PART_WHOLE,
- ColourConversion ()
- )
- );
+ /* Convert this to the content frame */
+ return DCPTime (s + piece->content->trim_start()).frames (_film->audio_frame_rate());
}
-shared_ptr<Resampler>
-Player::resampler (shared_ptr<AudioContent> c, bool create)
+ContentTime
+Player::dcp_to_content_subtitle (shared_ptr<const Piece> piece, DCPTime t) const
{
- map<shared_ptr<AudioContent>, shared_ptr<Resampler> >::iterator i = _resamplers.find (c);
- if (i != _resamplers.end ()) {
- return i->second;
- }
-
- if (!create) {
- return shared_ptr<Resampler> ();
- }
-
- LOG_GENERAL (
- "Creating new resampler for %1 to %2 with %3 channels", c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()
- );
+ /* s is the offset of t from the start position of this content */
+ DCPTime s = t - piece->content->position ();
+ s = DCPTime (max (DCPTime::Type (0), s.get ()));
+ s = DCPTime (min (piece->content->length_after_trim().get(), s.get()));
- shared_ptr<Resampler> r (new Resampler (c->content_audio_frame_rate(), c->output_audio_frame_rate(), c->audio_channels()));
- _resamplers[c] = r;
- return r;
+ return ContentTime (s + piece->content->trim_start(), piece->frc);
}
void
-Player::emit_black ()
+PlayerStatistics::dump (shared_ptr<Log> log) const
{
-#ifdef DCPOMATIC_DEBUG
- _last_video.reset ();
-#endif
-
- Video (_black_frame, _last_emit_was_black, _video_position);
- _video_position += _film->video_frames_to_time (1);
- _last_emit_was_black = true;
+ log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
+ log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
}
-void
-Player::emit_silence (OutputAudioFrame most)
+PlayerStatistics const &
+Player::statistics () const
{
- if (most == 0) {
- return;
- }
-
- OutputAudioFrame N = min (most, _film->audio_frame_rate() / 2);
- shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), N));
- silence->make_silent ();
- Audio (silence, _audio_position);
- _audio_position += _film->audio_frames_to_time (N);
+ return _statistics;
}
-void
-Player::film_changed (Film::Property p)
+PlayerSubtitles
+Player::get_subtitles (DCPTime time, DCPTime length, bool starting)
{
- /* Here we should notice Film properties that affect our output, and
- alert listeners that our output now would be different to how it was
- last time we were run.
- */
+ list<shared_ptr<Piece> > subs = overlaps<SubtitleContent> (time, time + length);
- if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER || p == Film::VIDEO_FRAME_RATE) {
- Changed (false);
- }
-}
+ PlayerSubtitles ps (time, length);
-void
-Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
-{
- if (!image) {
- /* A null image means that we should stop any current subtitles at `from' */
- for (list<Subtitle>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
- i->set_stop (from);
+ for (list<shared_ptr<Piece> >::const_iterator j = subs.begin(); j != subs.end(); ++j) {
+ shared_ptr<SubtitleContent> subtitle_content = dynamic_pointer_cast<SubtitleContent> ((*j)->content);
+ if (!subtitle_content->use_subtitles ()) {
+ continue;
}
- } else {
- _subtitles.push_back (Subtitle (_film, _video_container_size, weak_piece, image, rect, from, to));
- }
-}
-/** Re-emit the last frame that was emitted, using current settings for crop, ratio, scaler and subtitles.
- * @return false if this could not be done.
- */
-bool
-Player::repeat_last_video ()
-{
- if (!_last_incoming_video.image || !_have_valid_pieces) {
- return false;
- }
+ shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder);
+ ContentTime const from = dcp_to_content_subtitle (*j, time);
+ /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */
+ ContentTime const to = from + ContentTime::from_frames (1, _film->video_frame_rate ());
- process_video (
- _last_incoming_video.weak_piece,
- _last_incoming_video.image,
- _last_incoming_video.eyes,
- _last_incoming_video.part,
- _last_incoming_video.same,
- _last_incoming_video.frame,
- _last_incoming_video.extra
- );
+ list<ContentImageSubtitle> image = subtitle_decoder->get_image_subtitles (ContentTimePeriod (from, to), starting);
+ for (list<ContentImageSubtitle>::iterator i = image.begin(); i != image.end(); ++i) {
+
+ /* Apply content's subtitle offsets */
+ i->sub.rectangle.x += subtitle_content->subtitle_x_offset ();
+ i->sub.rectangle.y += subtitle_content->subtitle_y_offset ();
+
+ /* Apply content's subtitle scale */
+ i->sub.rectangle.width *= subtitle_content->subtitle_x_scale ();
+ i->sub.rectangle.height *= subtitle_content->subtitle_y_scale ();
+
+ /* Apply a corrective translation to keep the subtitle centred after that scale */
+ i->sub.rectangle.x -= i->sub.rectangle.width * (subtitle_content->subtitle_x_scale() - 1);
+ i->sub.rectangle.y -= i->sub.rectangle.height * (subtitle_content->subtitle_y_scale() - 1);
+
+ ps.image.push_back (i->sub);
+ }
+
+ list<ContentTextSubtitle> text = subtitle_decoder->get_text_subtitles (ContentTimePeriod (from, to), starting);
+ BOOST_FOREACH (ContentTextSubtitle& ts, text) {
+ BOOST_FOREACH (dcp::SubtitleString& s, ts.subs) {
+ s.set_v_position (s.v_position() + subtitle_content->subtitle_y_offset ());
+ s.set_size (s.size() * max (subtitle_content->subtitle_x_scale(), subtitle_content->subtitle_y_scale()));
+ ps.text.push_back (s);
+ }
+ }
+ }
- return true;
+ return ps;
}
#include "content.h"
#include "film.h"
#include "rect.h"
-#include "audio_merger.h"
#include "audio_content.h"
+#include "dcpomatic_time.h"
+#include "content_subtitle.h"
+#include "position_image.h"
#include "piece.h"
-#include "subtitle.h"
+#include "content_video.h"
+#include "player_subtitles.h"
class Job;
class Film;
class AudioContent;
class Piece;
class Image;
+class Decoder;
class Resampler;
-class PlayerVideoFrame;
+class PlayerVideo;
class ImageProxy;
+class PlayerStatistics
+{
+public:
+ struct Video {
+ Video ()
+ : black (0)
+ , repeat (0)
+ , good (0)
+ , skip (0)
+ {}
+
+ int black;
+ int repeat;
+ int good;
+ int skip;
+ } video;
+
+ struct Audio {
+ Audio ()
+ : silence (0)
+ , good (0)
+ , skip (0)
+ {}
+
+ DCPTime silence;
+ int64_t good;
+ int64_t skip;
+ } audio;
+
+ void dump (boost::shared_ptr<Log>) const;
+};
+
/** @class Player
- * @brief A class which can `play' a Playlist; emitting its audio and video.
+ * @brief A class which can `play' a Playlist.
*/
class Player : public boost::enable_shared_from_this<Player>, public boost::noncopyable
{
public:
Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>);
- void disable_video ();
- void disable_audio ();
-
- bool pass ();
- void seek (Time, bool);
+ std::list<boost::shared_ptr<PlayerVideo> > get_video (DCPTime time, bool accurate);
+ boost::shared_ptr<AudioBuffers> get_audio (DCPTime time, DCPTime length, bool accurate);
+ PlayerSubtitles get_subtitles (DCPTime time, DCPTime length, bool starting);
- Time video_position () const {
- return _video_position;
- }
-
- void set_video_container_size (libdcp::Size);
-
- bool repeat_last_video ();
+ void set_video_container_size (dcp::Size);
+ void set_approximate_size ();
- /** Emitted when a video frame is ready.
- * First parameter is the video image.
- * Second parameter is true if the frame is the same as the last one that was emitted.
- * Third parameter is the time.
- */
- boost::signals2::signal<void (boost::shared_ptr<PlayerVideoFrame>, bool, Time)> Video;
+ PlayerStatistics const & statistics () const;
- /** Emitted when some audio data is ready */
- boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, Time)> Audio;
-
/** Emitted when something has changed such that if we went back and emitted
* the last frame again it would look different. This is not emitted after
* a seek.
private:
friend class PlayerWrapper;
friend class Piece;
+ friend struct player_overlaps_test;
- void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const ImageProxy>, Eyes, Part, bool, VideoContent::Frame, Time);
- void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame, bool);
- void process_subtitle (boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
void setup_pieces ();
void playlist_changed ();
void content_changed (boost::weak_ptr<Content>, int, bool);
- void do_seek (Time, bool);
void flush ();
- void emit_black ();
- void emit_silence (OutputAudioFrame);
- boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>, bool);
void film_changed (Film::Property);
- void update_subtitle ();
-
+ std::list<PositionImage> transform_image_subtitles (std::list<ImageSubtitle>) const;
+ void update_subtitle_from_text ();
+ VideoFrame dcp_to_content_video (boost::shared_ptr<const Piece> piece, DCPTime t) const;
+ DCPTime content_video_to_dcp (boost::shared_ptr<const Piece> piece, VideoFrame f) const;
+ AudioFrame dcp_to_content_audio (boost::shared_ptr<const Piece> piece, DCPTime t) const;
+ ContentTime dcp_to_content_subtitle (boost::shared_ptr<const Piece> piece, DCPTime t) const;
+ boost::shared_ptr<PlayerVideo> black_player_video_frame (DCPTime) const;
+
+ /** @return Pieces of content type C that overlap a specified time range in the DCP */
+ template<class C>
+ std::list<boost::shared_ptr<Piece> >
+ overlaps (DCPTime from, DCPTime to)
+ {
+ if (!_have_valid_pieces) {
+ setup_pieces ();
+ }
+
+ std::list<boost::shared_ptr<Piece> > overlaps;
+ for (typename std::list<boost::shared_ptr<Piece> >::const_iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
+ if (!boost::dynamic_pointer_cast<C> ((*i)->content)) {
+ continue;
+ }
+
+ if ((*i)->content->position() <= to && (*i)->content->end() >= from) {
+ overlaps.push_back (*i);
+ }
+ }
+
+ return overlaps;
+ }
+
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
-
- bool _video;
- bool _audio;
/** Our pieces are ready to go; if this is false the pieces must be (re-)created before they are used */
bool _have_valid_pieces;
std::list<boost::shared_ptr<Piece> > _pieces;
- /** The time after the last video that we emitted */
- Time _video_position;
- /** The time after the last audio that we emitted */
- Time _audio_position;
-
- AudioMerger<Time, AudioContent::Frame> _audio_merger;
-
- libdcp::Size _video_container_size;
- boost::shared_ptr<PlayerVideoFrame> _black_frame;
- std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers;
-
- std::list<Subtitle> _subtitles;
-
-#ifdef DCPOMATIC_DEBUG
- boost::shared_ptr<Content> _last_video;
-#endif
+ dcp::Size _video_container_size;
+ boost::shared_ptr<Image> _black_image;
- bool _last_emit_was_black;
+ bool _approximate_size;
+ bool _burn_subtitles;
- IncomingVideo _last_incoming_video;
+ PlayerStatistics _statistics;
boost::signals2::scoped_connection _playlist_changed_connection;
boost::signals2::scoped_connection _playlist_content_changed_connection;
--- /dev/null
+/*
+ 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 DCPOMATIC_PLAYER_SUBTITLES_H
+#define DCPOMATIC_PLAYER_SUBTITLES_H
+
+#include <dcp/subtitle_string.h>
+#include "image_subtitle.h"
+
+class PlayerSubtitles
+{
+public:
+ PlayerSubtitles (DCPTime f, DCPTime t)
+ : from (f)
+ , to (t)
+ {}
+
+ DCPTime from;
+ DCPTime to;
+
+ /** ImageSubtitles, with their rectangles transformed as specified by their content */
+ std::list<ImageSubtitle> image;
+ std::list<dcp::SubtitleString> text;
+};
+
+#endif
--- /dev/null
+/*
+ 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 <dcp/raw_convert.h>
+#include "player_video.h"
+#include "image.h"
+#include "image_proxy.h"
+#include "j2k_image_proxy.h"
+#include "scaler.h"
+
+using std::string;
+using std::cout;
+using dcp::raw_convert;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+PlayerVideo::PlayerVideo (
+ shared_ptr<const ImageProxy> in,
+ DCPTime time,
+ Crop crop,
+ boost::optional<float> fade,
+ dcp::Size inter_size,
+ dcp::Size out_size,
+ Scaler const * scaler,
+ Eyes eyes,
+ Part part,
+ ColourConversion colour_conversion
+ )
+ : _in (in)
+ , _time (time)
+ , _crop (crop)
+ , _fade (fade)
+ , _inter_size (inter_size)
+ , _out_size (out_size)
+ , _scaler (scaler)
+ , _eyes (eyes)
+ , _part (part)
+ , _colour_conversion (colour_conversion)
+{
+
+}
+
+PlayerVideo::PlayerVideo (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket, shared_ptr<Log> log)
+{
+ _time = DCPTime (node->number_child<DCPTime::Type> ("Time"));
+ _crop = Crop (node);
+ _fade = node->optional_number_child<float> ("Fade");
+
+ _inter_size = dcp::Size (node->number_child<int> ("InterWidth"), node->number_child<int> ("InterHeight"));
+ _out_size = dcp::Size (node->number_child<int> ("OutWidth"), node->number_child<int> ("OutHeight"));
+ _scaler = Scaler::from_id (node->string_child ("Scaler"));
+ _eyes = (Eyes) node->number_child<int> ("Eyes");
+ _part = (Part) node->number_child<int> ("Part");
+ _colour_conversion = ColourConversion (node);
+
+ _in = image_proxy_factory (node->node_child ("In"), socket, log);
+
+ if (node->optional_number_child<int> ("SubtitleX")) {
+
+ _subtitle.position = Position<int> (node->number_child<int> ("SubtitleX"), node->number_child<int> ("SubtitleY"));
+
+ _subtitle.image.reset (
+ new Image (PIX_FMT_RGBA, dcp::Size (node->number_child<int> ("SubtitleWidth"), node->number_child<int> ("SubtitleHeight")), true)
+ );
+
+ _subtitle.image->read_from_socket (socket);
+ }
+}
+
+void
+PlayerVideo::set_subtitle (PositionImage image)
+{
+ _subtitle = image;
+}
+
+shared_ptr<Image>
+PlayerVideo::image (AVPixelFormat pixel_format, bool burn_subtitle) const
+{
+ shared_ptr<Image> im = _in->image ();
+
+ Crop total_crop = _crop;
+ switch (_part) {
+ case PART_LEFT_HALF:
+ total_crop.right += im->size().width / 2;
+ break;
+ case PART_RIGHT_HALF:
+ total_crop.left += im->size().width / 2;
+ break;
+ case PART_TOP_HALF:
+ total_crop.bottom += im->size().height / 2;
+ break;
+ case PART_BOTTOM_HALF:
+ total_crop.top += im->size().height / 2;
+ break;
+ default:
+ break;
+ }
+
+ shared_ptr<Image> out = im->crop_scale_window (total_crop, _inter_size, _out_size, _scaler, pixel_format, true);
+
+ if (burn_subtitle && _subtitle.image) {
+ out->alpha_blend (_subtitle.image, _subtitle.position);
+ }
+
+ if (_fade) {
+ out->fade (_fade.get ());
+ }
+
+ return out;
+}
+
+void
+PlayerVideo::add_metadata (xmlpp::Node* node, bool send_subtitles) const
+{
+ node->add_child("Time")->add_child_text (raw_convert<string> (_time.get ()));
+ _crop.as_xml (node);
+ if (_fade) {
+ node->add_child("Fade")->add_child_text (raw_convert<string> (_fade.get ()));
+ }
+ _in->add_metadata (node->add_child ("In"));
+ node->add_child("InterWidth")->add_child_text (raw_convert<string> (_inter_size.width));
+ node->add_child("InterHeight")->add_child_text (raw_convert<string> (_inter_size.height));
+ node->add_child("OutWidth")->add_child_text (raw_convert<string> (_out_size.width));
+ node->add_child("OutHeight")->add_child_text (raw_convert<string> (_out_size.height));
+ node->add_child("Scaler")->add_child_text (_scaler->id ());
+ node->add_child("Eyes")->add_child_text (raw_convert<string> (_eyes));
+ node->add_child("Part")->add_child_text (raw_convert<string> (_part));
+ _colour_conversion.as_xml (node);
+ if (send_subtitles && _subtitle.image) {
+ node->add_child ("SubtitleWidth")->add_child_text (raw_convert<string> (_subtitle.image->size().width));
+ node->add_child ("SubtitleHeight")->add_child_text (raw_convert<string> (_subtitle.image->size().height));
+ node->add_child ("SubtitleX")->add_child_text (raw_convert<string> (_subtitle.position.x));
+ node->add_child ("SubtitleY")->add_child_text (raw_convert<string> (_subtitle.position.y));
+ }
+}
+
+void
+PlayerVideo::send_binary (shared_ptr<Socket> socket, bool send_subtitles) const
+{
+ _in->send_binary (socket);
+ if (send_subtitles && _subtitle.image) {
+ _subtitle.image->write_to_socket (socket);
+ }
+}
+
+bool
+PlayerVideo::has_j2k () const
+{
+ /* XXX: burnt-in subtitle; maybe other things */
+
+ shared_ptr<const J2KImageProxy> j2k = dynamic_pointer_cast<const J2KImageProxy> (_in);
+ if (!j2k) {
+ return false;
+ }
+
+ return _crop == Crop () && _inter_size == j2k->size();
+}
+
+shared_ptr<EncodedData>
+PlayerVideo::j2k () const
+{
+ shared_ptr<const J2KImageProxy> j2k = dynamic_pointer_cast<const J2KImageProxy> (_in);
+ assert (j2k);
+ return j2k->j2k ();
+}
+
+Position<int>
+PlayerVideo::inter_position () const
+{
+ return Position<int> ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.height) / 2);
+}
+
+/** @return true if this PlayerVideo is definitely the same as another
+ * (apart from _time), false if it is probably not
+ */
+bool
+PlayerVideo::same (shared_ptr<const PlayerVideo> other) const
+{
+ if (_in != other->_in ||
+ _crop != other->_crop ||
+ _fade.get_value_or(0) != other->_fade.get_value_or(0) ||
+ _inter_size != other->_inter_size ||
+ _out_size != other->_out_size ||
+ _scaler != other->_scaler ||
+ _eyes != other->_eyes ||
+ _part != other->_part ||
+ _colour_conversion != other->_colour_conversion ||
+ !_subtitle.same (other->_subtitle)) {
+ return false;
+ }
+
+ return _in->same (other->_in);
+}
--- /dev/null
+/*
+ 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 <boost/shared_ptr.hpp>
+extern "C" {
+#include <libavutil/pixfmt.h>
+}
+#include "types.h"
+#include "position.h"
+#include "colour_conversion.h"
+#include "position_image.h"
+
+class Image;
+class ImageProxy;
+class Scaler;
+class Socket;
+class Log;
+class EncodedData;
+
+/** Everything needed to describe a video frame coming out of the player, but with the
+ * bits still their raw form. We may want to combine the bits on a remote machine,
+ * or maybe not even bother to combine them at all.
+ */
+class PlayerVideo
+{
+public:
+ PlayerVideo (
+ boost::shared_ptr<const ImageProxy>,
+ DCPTime,
+ Crop,
+ boost::optional<float>,
+ dcp::Size,
+ dcp::Size,
+ Scaler const *,
+ Eyes,
+ Part,
+ ColourConversion
+ );
+
+ PlayerVideo (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>, boost::shared_ptr<Log>);
+
+ void set_subtitle (PositionImage);
+
+ boost::shared_ptr<Image> image (AVPixelFormat pix_fmt, bool burn_subtitle) const;
+
+ void add_metadata (xmlpp::Node* node, bool send_subtitles) const;
+ void send_binary (boost::shared_ptr<Socket> socket, bool send_subtitles) const;
+
+ bool has_j2k () const;
+ boost::shared_ptr<EncodedData> j2k () const;
+
+ DCPTime time () const {
+ return _time;
+ }
+
+ Eyes eyes () const {
+ return _eyes;
+ }
+
+ ColourConversion colour_conversion () const {
+ return _colour_conversion;
+ }
+
+ /** @return Position of the content within the overall image once it has been scaled up */
+ Position<int> inter_position () const;
+
+ /** @return Size of the content within the overall image once it has been scaled up */
+ dcp::Size inter_size () const {
+ return _inter_size;
+ }
+
+ bool same (boost::shared_ptr<const PlayerVideo> other) const;
+
+private:
+ boost::shared_ptr<const ImageProxy> _in;
+ DCPTime _time;
+ Crop _crop;
+ boost::optional<float> _fade;
+ dcp::Size _inter_size;
+ dcp::Size _out_size;
+ Scaler const * _scaler;
+ Eyes _eyes;
+ Part _part;
+ ColourConversion _colour_conversion;
+ PositionImage _subtitle;
+};
+++ /dev/null
-/*
- 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 <boost/shared_ptr.hpp>
-extern "C" {
-#include <libavutil/pixfmt.h>
-}
-#include "types.h"
-#include "position.h"
-#include "colour_conversion.h"
-
-class Image;
-class ImageProxy;
-class Scaler;
-class Socket;
-class Log;
-
-/** Everything needed to describe a video frame coming out of the player, but with the
- * bits still their raw form. We may want to combine the bits on a remote machine,
- * or maybe not even bother to combine them at all.
- */
-class PlayerVideoFrame
-{
-public:
- PlayerVideoFrame (boost::shared_ptr<const ImageProxy>, Crop, libdcp::Size, libdcp::Size, Scaler const *, Eyes, Part, ColourConversion);
- PlayerVideoFrame (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>, boost::shared_ptr<Log>);
-
- void set_subtitle (boost::shared_ptr<const Image>, Position<int>);
-
- boost::shared_ptr<Image> image (AVPixelFormat) const;
-
- void add_metadata (xmlpp::Node* node) const;
- void send_binary (boost::shared_ptr<Socket> socket) const;
-
- Eyes eyes () const {
- return _eyes;
- }
-
- ColourConversion colour_conversion () const {
- return _colour_conversion;
- }
-
-private:
- boost::shared_ptr<const ImageProxy> _in;
- Crop _crop;
- libdcp::Size _inter_size;
- libdcp::Size _out_size;
- Scaler const * _scaler;
- Eyes _eyes;
- Part _part;
- ColourConversion _colour_conversion;
- boost::shared_ptr<const Image> _subtitle_image;
- Position<int> _subtitle_position;
-};
_sequencing_video = true;
ContentList cl = _content;
- Time next_left = 0;
- Time next_right = 0;
+ DCPTime next_left;
+ DCPTime next_right;
for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
if (!vc) {
continue;
}
-
+
if (vc->video_frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
vc->set_position (next_right);
- next_right = vc->end() + 1;
+ next_right = vc->end() + DCPTime::delta ();
} else {
vc->set_position (next_left);
- next_left = vc->end() + 1;
+ next_left = vc->end() + DCPTime::delta ();
}
}
/** @param node <Playlist> node */
void
-Playlist::set_from_xml (shared_ptr<const Film> film, shared_ptr<const cxml::Node> node, int version, list<string>& notes)
+Playlist::set_from_xml (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version, list<string>& notes)
{
list<cxml::NodePtr> c = node->node_children ("Content");
for (list<cxml::NodePtr>::iterator i = c.begin(); i != c.end(); ++i) {
Changed ();
}
-bool
-Playlist::has_subtitles () const
-{
- for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
- shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
- if (fc && !fc->subtitle_streams().empty()) {
- return true;
- }
- }
-
- return false;
-}
-
class FrameRateCandidate
{
public:
return best->dcp;
}
-Time
+DCPTime
Playlist::length () const
{
- Time len = 0;
+ DCPTime len;
for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
- len = max (len, (*i)->end() + 1);
+ len = max (len, (*i)->end() + DCPTime::delta ());
}
return len;
}
}
-Time
+DCPTime
Playlist::video_end () const
{
- Time end = 0;
+ DCPTime end;
for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
if (dynamic_pointer_cast<const VideoContent> (*i)) {
end = max (end, (*i)->end ());
return end;
}
+FrameRateChange
+Playlist::active_frame_rate_change (DCPTime t, int dcp_video_frame_rate) const
+{
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (*i);
+ if (!vc) {
+ continue;
+ }
+
+ if (vc->position() >= t && t < vc->end()) {
+ return FrameRateChange (vc->video_frame_rate(), dcp_video_frame_rate);
+ }
+ }
+
+ return FrameRateChange (dcp_video_frame_rate, dcp_video_frame_rate);
+}
+
void
Playlist::set_sequence_video (bool s)
{
void
Playlist::repeat (ContentList c, int n)
{
- pair<Time, Time> range (TIME_MAX, 0);
+ pair<DCPTime, DCPTime> range (DCPTime::max (), DCPTime ());
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
range.first = min (range.first, (*i)->position ());
range.second = max (range.second, (*i)->position ());
range.second = max (range.second, (*i)->end ());
}
- Time pos = range.second;
+ DCPTime pos = range.second;
for (int i = 0; i < n; ++i) {
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<Content> copy = (*i)->clone ();
}
- Time const p = (*previous)->position ();
+ DCPTime const p = (*previous)->position ();
(*previous)->set_position (p + c->length_after_trim ());
c->set_position (p);
sort (_content.begin(), _content.end(), ContentSorter ());
c->set_position (c->position() + c->length_after_trim ());
sort (_content.begin(), _content.end(), ContentSorter ());
}
-
-FrameRateChange
-Playlist::active_frame_rate_change (Time t, int dcp_video_frame_rate) const
-{
- for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
- shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (*i);
- if (!vc) {
- continue;
- }
-
- if (vc->position() >= t && t < vc->end()) {
- return FrameRateChange (vc->video_frame_rate(), dcp_video_frame_rate);
- }
- }
-
- return FrameRateChange (dcp_video_frame_rate, dcp_video_frame_rate);
-}
#include <boost/enable_shared_from_this.hpp>
#include "ffmpeg_content.h"
#include "audio_mapping.h"
+#include "util.h"
#include "frame_rate_change.h"
class Content;
class Film;
class Region;
-/** @class Playlist
- * @brief A set of content files (video and audio), with knowledge of how they should be arranged into
- * a DCP.
- *
- * This class holds Content objects, and it knows how they should be arranged.
- */
-
struct ContentSorter
{
bool operator() (boost::shared_ptr<Content> a, boost::shared_ptr<Content> b);
};
+/** @class Playlist
+ * @brief A set of Content objects with knowledge of how they should be arranged into
+ * a DCP.
+ */
class Playlist : public boost::noncopyable
{
public:
~Playlist ();
void as_xml (xmlpp::Node *);
- void set_from_xml (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int, std::list<std::string> &);
+ void set_from_xml (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int, std::list<std::string> &);
void add (boost::shared_ptr<Content>);
void remove (boost::shared_ptr<Content>);
void move_earlier (boost::shared_ptr<Content>);
void move_later (boost::shared_ptr<Content>);
- bool has_subtitles () const;
-
ContentList content () const;
std::string video_identifier () const;
- Time length () const;
+ DCPTime length () const;
int best_dcp_frame_rate () const;
- Time video_end () const;
- FrameRateChange active_frame_rate_change (Time, int dcp_frame_rate) const;
+ DCPTime video_end () const;
+ FrameRateChange active_frame_rate_change (DCPTime, int dcp_frame_rate) const;
void set_sequence_video (bool);
void maybe_sequence_video ();
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-07-13 02:32+0100\n"
"Last-Translator: Carsten Kurz\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"X-Generator: Poedit 1.6.5\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/lib/sndfile_content.cc:61
+#: src/lib/dcp_content.cc:106
+msgid "%1 [DCP]"
+msgstr ""
+
+#: src/lib/sndfile_content.cc:64
msgid "%1 [audio]"
msgstr "%1 [Ton]"
-#: src/lib/ffmpeg_content.cc:211
+#: src/lib/ffmpeg_content.cc:205
msgid "%1 [movie]"
msgstr "%1 [Film]"
-#: src/lib/sndfile_content.cc:82
+#: src/lib/sndfile_content.cc:85
msgid "%1 channels, %2kHz, %3 samples"
msgstr "%1 Kanäle, %2kHz, %3 samples"
-#: src/lib/ffmpeg_content.cc:246
+#: src/lib/ffmpeg_content.cc:240
msgid "%1 frames; %2 frames per second"
msgstr "%1 Bilder; %2 Bilder pro Sekunde"
-#: src/lib/video_content.cc:211
+#: src/lib/video_content.cc:235
msgid "%1x%2 pixels (%3:1)"
msgstr "%1x%2 pixel (%3:1)"
msgid "Advertisement"
msgstr "Werbung"
-#: src/lib/job.cc:71
+#: src/lib/job.cc:72
msgid "An error occurred whilst handling the file %1."
msgstr "Beim Bearbeiten der Datei %1 trat ein Fehler auf."
-#: src/lib/analyse_audio_job.cc:48
+#: src/lib/analyse_audio_job.cc:49
msgid "Analyse audio"
msgstr "Audio analysieren"
msgid "Bilinear"
msgstr "Bi-Linear"
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
msgid "Cancelled"
msgstr "Abgebrochen"
msgid "Cannot handle pixel format %1 during %2"
msgstr "Kann dieses Pixelformat %1 während %2 nicht bearbeiten"
-#: src/lib/util.cc:786
+#: src/lib/util.cc:775
msgid "Centre"
msgstr "Center"
-#: src/lib/writer.cc:83
+#: src/lib/writer.cc:90
msgid "Checking existing image data"
msgstr "Überprüfe bestehende Bilddateien"
-#: src/lib/writer.cc:475
+#: src/lib/writer.cc:490
msgid "Computing audio digest"
msgstr "Tonübersicht berechnen"
msgid "Computing digest"
msgstr "Zusammenfassung berechnen"
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
msgid "Computing image digest"
msgstr "Bildübersicht berechnen"
msgid "Content and DCP have the same rate.\n"
msgstr "Quelle und DCP haben dieselbe Bildrate. Gut ;-)\n"
-#: src/lib/audio_content.cc:83
+#: src/lib/subtitle_content.cc:103
+#, fuzzy
+msgid "Content to be joined must have the same 'use subtitles' setting."
+msgstr "Zu verbindende Inhalte müssen die gleiche Skalierung verwenden."
+
+#: src/lib/audio_content.cc:100
msgid "Content to be joined must have the same audio delay."
msgstr "Zu verbindende Inhalte müssen die gleiche Tonverzögerung verwenden."
-#: src/lib/audio_content.cc:79
+#: src/lib/audio_content.cc:96
msgid "Content to be joined must have the same audio gain."
msgstr ""
"Zu verbindende Inhalte müssen die gleichen Tonpegeleinstellungen verwenden."
-#: src/lib/video_content.cc:146
+#: src/lib/video_content.cc:158
msgid "Content to be joined must have the same colour conversion."
msgstr "Zu verbindende Inhalte müssen die gleiche Farbumwandlung verwenden."
-#: src/lib/video_content.cc:138
+#: src/lib/video_content.cc:150
msgid "Content to be joined must have the same crop."
msgstr "Zu verbindende Inhalte müssen gleichen Beschnitt verwenden."
-#: src/lib/video_content.cc:126
+#: src/lib/video_content.cc:162
+#, fuzzy
+msgid "Content to be joined must have the same fades."
+msgstr "Zu verbindende Inhalte müssen gleichen Beschnitt verwenden."
+
+#: src/lib/video_content.cc:138
msgid "Content to be joined must have the same picture size."
msgstr "Zu verbindende Inhalte müssen die gleiche Bildgröße haben."
-#: src/lib/video_content.cc:142
+#: src/lib/video_content.cc:154
msgid "Content to be joined must have the same scale setting."
msgstr "Zu verbindende Inhalte müssen die gleiche Skalierung verwenden."
-#: src/lib/subtitle_content.cc:81
+#: src/lib/subtitle_content.cc:107
msgid "Content to be joined must have the same subtitle X offset."
msgstr ""
"Zu verbindende Inhalte müssen den gleichen horizontalen Untertitelversatz "
"verwenden."
-#: src/lib/subtitle_content.cc:89
+#: src/lib/subtitle_content.cc:115
#, fuzzy
msgid "Content to be joined must have the same subtitle X scale."
msgstr "Zu verbindende Inhalte müssen die gleiche Untertitelgröße verwenden."
-#: src/lib/subtitle_content.cc:85
+#: src/lib/subtitle_content.cc:111
msgid "Content to be joined must have the same subtitle Y offset."
msgstr ""
"Zu verbindende Inhalte müssen den gleichen vertikalen Untertitelversatz "
"verwenden."
-#: src/lib/subtitle_content.cc:93
+#: src/lib/subtitle_content.cc:119
#, fuzzy
msgid "Content to be joined must have the same subtitle Y scale."
msgstr "Zu verbindende Inhalte müssen die gleiche Untertitelgröße verwenden."
-#: src/lib/video_content.cc:130
+#: src/lib/video_content.cc:142
msgid "Content to be joined must have the same video frame rate."
msgstr "Zu verbindende Inhalte müssen die gleiche Bildrate haben."
-#: src/lib/video_content.cc:134
+#: src/lib/video_content.cc:146
msgid "Content to be joined must have the same video frame type."
msgstr "Zu verbindende Inhalte müssen den gleichen Bildtyp (z.B. 2D) haben."
-#: src/lib/ffmpeg_content.cc:116
+#: src/lib/ffmpeg_content.cc:118
msgid "Content to be joined must use the same audio stream."
msgstr ""
"Zu verbindende Inhalte müssen die gleiche Tonspurkonfiguration verwenden."
-#: src/lib/ffmpeg_content.cc:112
+#: src/lib/ffmpeg_content.cc:114
msgid "Content to be joined must use the same subtitle stream."
msgstr "Zu verbindende Inhalte müssen die gleiche Untertitelspur verwenden."
msgid "Could not create remote directory %1 (%2)"
msgstr "Konnte entfernten Ordner %1 (%2) nicht erstellen."
-#: src/lib/image_proxy.cc:147
+#: src/lib/magick_image_proxy.cc:103
#, fuzzy
msgid "Could not decode image file (%1)"
msgstr "Bilddatei konnte nicht dekodiert werden"
-#: src/lib/job.cc:90
+#: src/lib/job.cc:91
msgid "Could not open %1"
msgstr "%1 konnte nicht geöffnet werden."
msgid "Could not write to remote file (%1)"
msgstr "Entfernte Datei (%1) konnte nicht gespeichert werden"
+#: src/lib/dcp_subtitle_content.cc:72
+msgid "DCP XML subtitles"
+msgstr ""
+
#: src/lib/frame_rate_change.cc:98
msgid "DCP will run at %1%% of the content speed.\n"
msgstr "DCP läuft mit %1% der Original Geschwindigkeit der Quelle.\n"
msgid "DCP will use every other frame of the content.\n"
msgstr "DCP verwendet nur jedes zweite Bild des Quelle.\n"
-#: src/lib/job.cc:91
+#: src/lib/job.cc:92
msgid ""
"DCP-o-matic could not open the file %1. Perhaps it does not exist or is in "
"an unexpected format."
"DCP-o-matic konnte die Datei %1 nicht öffnen. Vielleicht existiert sie nicht "
"oder ist in einem unerwarteten Format."
-#: src/lib/ffmpeg_content.cc:93
+#: src/lib/ffmpeg_content.cc:95
msgid ""
"DCP-o-matic no longer supports the `%1' filter, so it has been turned off."
msgstr ""
msgid "De-interlacing"
msgstr "De-Interlacer"
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
msgid ""
"Dear Projectionist\n"
"\n"
msgid "Email KDMs for %1"
msgstr "Email KDMs für %1"
-#: src/lib/writer.cc:126
+#: src/lib/writer.cc:136
msgid "Encoding image data"
msgstr "Kodiere Bilddaten"
-#: src/lib/job.cc:320
+#: src/lib/job.cc:322
msgid "Error (%1)"
msgstr "Fehler (%1)"
+#: src/lib/exceptions.cc:66
+msgid "Error in SubRip file: saw %1 while expecting %2"
+msgstr ""
+
#: src/lib/examine_content_job.cc:46
msgid "Examine content"
msgstr "Inhalt wird überprüft"
msgid "Gradient debander"
msgstr "Gradient Glätter"
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
msgid "Hearing impaired"
msgstr "HI"
msgid "High quality 3D denoiser"
msgstr "3D Rauschunterdrückung hoher Qualität"
-#: src/lib/job.cc:111 src/lib/job.cc:121
+#: src/lib/job.cc:112 src/lib/job.cc:122
msgid ""
"It is not known what caused this error. Please report the problem to the "
"DCP-o-matic author (carl@dcpomatic.com)."
"Ein unbekannter Fehler ist aufgetreten. Bitte schicken Sie eine Nachricht an "
"den DCP-o-matic Autor (carl@dcpomatic.com)."
-#: src/lib/config.cc:199
+#: src/lib/config.cc:212
msgid "KDM delivery: $CPL_NAME"
msgstr "KDM Zustellung: $CPL_NAME"
msgid "Lanczos"
msgstr "Lanczos"
-#: src/lib/util.cc:784
+#: src/lib/util.cc:773
msgid "Left"
msgstr "Links"
-#: src/lib/util.cc:792
+#: src/lib/util.cc:781
msgid "Left centre"
msgstr "Center links"
-#: src/lib/util.cc:794
+#: src/lib/util.cc:783
msgid "Left rear surround"
msgstr "Surround hinten links"
-#: src/lib/util.cc:788
+#: src/lib/util.cc:777
msgid "Left surround"
msgstr "Surround links"
-#: src/lib/util.cc:787
+#: src/lib/util.cc:776
msgid "Lfe (sub)"
msgstr "LFE (Subwoofer)"
+#: src/lib/mid_side_decoder.cc:31
+msgid "Mid-side decoder"
+msgstr ""
+
#: src/lib/filter.cc:68 src/lib/filter.cc:69 src/lib/filter.cc:72
msgid "Misc"
msgstr "Verschiedenes"
+#: src/lib/dcp_examiner.cc:93
+msgid "Mismatched audio channel counts in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:99
+msgid "Mismatched audio frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:74
+msgid "Mismatched frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:81
+msgid "Mismatched video sizes in DCP"
+msgstr ""
+
#: src/lib/filter.cc:65
msgid "Motion compensating deinterlacer"
msgstr "Bewegungskompensierender De-Interlacer"
msgid "Noise reduction"
msgstr "Rauschunterdrückung"
-#: src/lib/job.cc:318
+#: src/lib/job.cc:320
msgid "OK (ran for %1)"
msgstr "OK (Dauer %1)"
-#: src/lib/content.cc:102
+#: src/lib/content.cc:106
msgid "Only the first piece of content to be joined can have a start trim."
msgstr ""
"Nur das erste Segment der zu verbindenden Inhalte kann vom Anfang her "
"beschnitten werden."
-#: src/lib/content.cc:106
+#: src/lib/content.cc:110
msgid "Only the last piece of content to be joined can have an end trim."
msgstr ""
"Nur das letzte Segment der zu verbindenden Inhalte kann vom Ende her "
"beschnitten werden."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "Out of memory"
msgstr "Zuwenig Speicher"
msgid "Rating"
msgstr "Freigabehinweis"
-#: src/lib/config.cc:86 src/lib/config.cc:183
+#: src/lib/config.cc:92 src/lib/config.cc:196
msgid "Rec. 709"
msgstr "Rec. 709"
-#: src/lib/util.cc:785
+#: src/lib/util.cc:774
msgid "Right"
msgstr "Rechts"
-#: src/lib/util.cc:793
+#: src/lib/util.cc:782
msgid "Right centre"
msgstr "Center rechts"
-#: src/lib/util.cc:795
+#: src/lib/util.cc:784
msgid "Right rear surround"
msgstr "Surround hinten rechts"
-#: src/lib/util.cc:789
+#: src/lib/util.cc:778
msgid "Right surround"
msgstr "Surround rechts"
msgid "Spline"
msgstr "Spline"
+#: src/lib/upmixer_a.cc:42
+msgid "Stereo to 5.1 up-mixer A"
+msgstr ""
+
+#: src/lib/subrip_content.cc:73
+msgid "SubRip subtitles"
+msgstr ""
+
#: src/lib/dcp_content_type.cc:50
msgid "Teaser"
msgstr "Teaser"
msgid "Test"
msgstr "Test"
-#: src/lib/job.cc:77
+#: src/lib/dcp_examiner.cc:133
+msgid ""
+"The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"
+msgstr ""
+
+#: src/lib/exceptions.cc:72
+msgid "The certificate chain for signing is invalid"
+msgstr ""
+
+#: src/lib/job.cc:78
msgid ""
"The drive that the film is stored on is low in disc space. Free some more "
"space and try again."
"Das Laufwerk, auf dem der Film gespeichert werden soll, hat zu wenig freien "
"Speicher. Bitte Speicher freigeben und nochmal versuchen."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "There was not enough memory to do this."
msgstr "Zu wenig Speicher für diese Operation."
-#: src/lib/film.cc:414
+#: src/lib/film.cc:423
msgid ""
"This film was created with a newer version of DCP-o-matic, and it cannot be "
"loaded into this version. Sorry!"
"kann leider nicht mit dieser älteren Version geladen werden. Sie müssen den "
"Film neu erstellen. Sorry!"
-#: src/lib/film.cc:406
+#: src/lib/film.cc:415
msgid ""
"This film was created with an older version of DCP-o-matic, and "
"unfortunately it cannot be loaded into this version. You will need to "
msgid "Trailer"
msgstr "Trailer"
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
msgid "Transcode %1"
msgstr "Wandle %1 um"
msgid "Unexpected ZIP file contents"
msgstr "Ungültiger ZIP Inhalt"
-#: src/lib/image_proxy.cc:193
+#: src/lib/image_proxy.cc:56
msgid "Unexpected image type received by server"
msgstr "Ungültiges Bildformat vom Server erhalten"
-#: src/lib/job.cc:120
+#: src/lib/job.cc:121
msgid "Unknown error"
msgstr "Unbekannter Fehler"
-#: src/lib/ffmpeg_decoder.cc:293
+#: src/lib/ffmpeg_decoder.cc:263
msgid "Unrecognised audio sample format (%1)"
msgstr "Ton Sample Format (%1) nicht erkannt."
msgid "Untitled"
msgstr "Unbenannt"
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
msgid "Visually impaired"
msgstr "VI"
msgid "Yet Another Deinterlacing Filter"
msgstr "Und ein weiterer De-Interlacer..."
-#: src/lib/film.cc:311
+#: src/lib/film.cc:320
msgid "You must add some content to the DCP before creating it"
msgstr "Sie müssen erst Inhalte hinzufügen bevor Sie ein DCP erstellen können!"
msgid "[still]"
msgstr "[Standbild]"
-#: src/lib/film.cc:259
+#: src/lib/dcp_subtitle_content.cc:66 src/lib/subrip_content.cc:67
+msgid "[subtitles]"
+msgstr ""
+
+#: src/lib/film.cc:268
msgid "cannot contain slashes"
msgstr "Darf keine Schrägstriche enthalten"
-#: src/lib/util.cc:565
+#: src/lib/util.cc:554
msgid "connect timed out"
msgstr "Zeit für Verbindung abgelaufen"
msgid "connecting"
msgstr "verbinde..."
-#: src/lib/film.cc:307
+#: src/lib/film.cc:316
msgid "container"
msgstr "Containerformat"
-#: src/lib/film.cc:315
+#: src/lib/film.cc:324
msgid "content type"
msgstr "Inhaltsbeschreibung"
msgid "could not create file %1"
msgstr "Datei %1 konnte nicht erstellt werden."
-#: src/lib/ffmpeg.cc:176
-msgid "could not find audio decoder"
-msgstr "Ton Dekoder nicht gefunden."
-
-#: src/lib/ffmpeg.cc:105
+#: src/lib/ffmpeg.cc:102
msgid "could not find stream information"
msgstr "Keine Spur-Information gefunden"
-#: src/lib/ffmpeg.cc:155
-msgid "could not find video decoder"
-msgstr "Bild-Dekoder nicht gefunden"
-
-#: src/lib/writer.cc:439
+#: src/lib/writer.cc:430
msgid "could not move audio MXF into the DCP (%1)"
msgstr "Ton MXF kann nicht in das DCP verschoben werden (%1)"
-#: src/lib/sndfile_decoder.cc:56
+#: src/lib/sndfile_decoder.cc:54
msgid "could not open audio file for reading"
msgstr "Tondatei kann nicht zum Lesen geöffnet werden."
msgid "could not open file %1"
msgstr "Datei %1 konnte nicht geöffnet werden."
-#: src/lib/dcp_video_frame.cc:331
+#: src/lib/encoded_data.cc:50
msgid "could not open file for reading"
msgstr "Datei konnte nicht zum Lesen geöffnet werden."
-#: src/lib/dcp_video_frame.cc:337
+#: src/lib/encoded_data.cc:56
msgid "could not read encoded data"
msgstr "Kodierte Daten nicht gefunden."
msgid "could not read from file %1 (%2)"
msgstr "Datei %1 konnte nicht gelesen werden (%2)"
-#: src/lib/resampler.cc:98
+#: src/lib/resampler.cc:96
msgid "could not run sample-rate converter"
msgstr "Sample-Rate konnte nicht gewandelt werden"
-#: src/lib/resampler.cc:79
+#: src/lib/resampler.cc:77
msgid "could not run sample-rate converter for %1 samples (%2) (%3)"
msgstr "Sample-Rate für %1 samples konnte nicht gewandelt werden (%2)(%3)"
msgid "could not write to file %1 (%2)"
msgstr "Datei %1 konnte nicht geschrieben werden (%2)"
-#: src/lib/util.cc:585
+#: src/lib/util.cc:574
msgid "error during async_accept (%1)"
msgstr "error during async_accept (%1)"
-#: src/lib/util.cc:561
+#: src/lib/util.cc:550
msgid "error during async_connect (%1)"
msgstr "error during async_connect (%1)"
-#: src/lib/util.cc:634
+#: src/lib/util.cc:623
msgid "error during async_read (%1)"
msgstr "error during async_read (%1)"
-#: src/lib/util.cc:606
+#: src/lib/util.cc:595
msgid "error during async_write (%1)"
msgstr "error during async_write (%1)"
-#: src/lib/transcode_job.cc:97
+#: src/lib/transcode_job.cc:98
msgid "frames per second"
msgstr "Bilder pro Sekunde"
-#: src/lib/util.cc:156
+#: src/lib/util.cc:158
msgid "hour"
msgstr "Stunde"
-#: src/lib/util.cc:152 src/lib/util.cc:158
+#: src/lib/util.cc:154 src/lib/util.cc:160
msgid "hours"
msgstr "Stunden"
-#: src/lib/util.cc:174
+#: src/lib/util.cc:176
msgid "minute"
msgstr "Minute"
-#: src/lib/util.cc:170 src/lib/util.cc:176
+#: src/lib/util.cc:172 src/lib/util.cc:178
msgid "minutes"
msgstr "Minuten"
msgid "moving"
msgstr "wird verschoben"
-#: src/lib/ffmpeg_decoder.cc:589
+#: src/lib/ffmpeg_decoder.cc:420
msgid "multi-part subtitles not yet supported"
msgstr "Mehr-Segment Untertitel werden noch nicht unterstützt"
-#: src/lib/film.cc:259 src/lib/film.cc:319
+#: src/lib/film.cc:268 src/lib/film.cc:328
msgid "name"
msgstr "Name"
-#: src/lib/ffmpeg_decoder.cc:604
-msgid "non-bitmap subtitles not yet supported"
-msgstr "Nur Bitmap Untertitel werden unterstützt"
-
#. / TRANSLATORS: remaining here follows an amount of time that is remaining
#. / on an operation.
-#: src/lib/job.cc:315
+#: src/lib/job.cc:317
msgid "remaining"
msgstr "verbleibend"
-#: src/lib/config.cc:84 src/lib/video_content.cc:179
+#: src/lib/config.cc:90 src/lib/video_content.cc:197
msgid "sRGB"
msgstr "sRGB"
-#: src/lib/config.cc:85
+#: src/lib/config.cc:91
msgid "sRGB non-linearised"
msgstr "sRGB nicht linearisiert"
-#: src/lib/util.cc:189
+#: src/lib/util.cc:191
#, fuzzy
msgid "second"
msgstr "Sekunden"
-#: src/lib/util.cc:191
+#: src/lib/util.cc:193
msgid "seconds"
msgstr "Sekunden"
msgid "still"
msgstr "Standbild"
-#: src/lib/ffmpeg_examiner.cc:168
+#: src/lib/ffmpeg_examiner.cc:207
msgid "unknown"
msgstr "unbekannt"
+#~ msgid "could not find audio decoder"
+#~ msgstr "Ton Dekoder nicht gefunden."
+
+#~ msgid "could not find video decoder"
+#~ msgstr "Bild-Dekoder nicht gefunden"
+
+#~ msgid "non-bitmap subtitles not yet supported"
+#~ msgstr "Nur Bitmap Untertitel werden unterstützt"
+
#~ msgid "Could not read DCP to make KDM for"
#~ msgstr "DCP konnte nicht zur Schlüsselerstellung geöffnet werden"
msgstr ""
"Project-Id-Version: LIBDCPOMATIC\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-04-20 10:12-0500\n"
"Last-Translator: Manuel AC <manuel.acevedo@civantos.>\n"
"Language-Team: Manuel AC <manuel.acevedo@civantos.com>\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.4\n"
-#: src/lib/sndfile_content.cc:61
+#: src/lib/dcp_content.cc:106
+msgid "%1 [DCP]"
+msgstr ""
+
+#: src/lib/sndfile_content.cc:64
msgid "%1 [audio]"
msgstr "%1 [audio]"
-#: src/lib/ffmpeg_content.cc:211
+#: src/lib/ffmpeg_content.cc:205
msgid "%1 [movie]"
msgstr "%1 [pelÃcula]"
-#: src/lib/sndfile_content.cc:82
+#: src/lib/sndfile_content.cc:85
msgid "%1 channels, %2kHz, %3 samples"
msgstr "%1 canales, %2kHz, %3 muestras"
-#: src/lib/ffmpeg_content.cc:246
+#: src/lib/ffmpeg_content.cc:240
msgid "%1 frames; %2 frames per second"
msgstr "%1 imágenes; %2 imágenes per second"
-#: src/lib/video_content.cc:211
+#: src/lib/video_content.cc:235
msgid "%1x%2 pixels (%3:1)"
msgstr "%1x%2 pixels (%3:1)"
msgid "Advertisement"
msgstr "Publicidad"
-#: src/lib/job.cc:71
+#: src/lib/job.cc:72
msgid "An error occurred whilst handling the file %1."
msgstr "Ha ocurrido un error con el fichero %1."
-#: src/lib/analyse_audio_job.cc:48
+#: src/lib/analyse_audio_job.cc:49
msgid "Analyse audio"
msgstr "Analizar audio"
msgid "Bilinear"
msgstr "Bilineal"
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
msgid "Cancelled"
msgstr "Cancelado"
msgid "Cannot handle pixel format %1 during %2"
msgstr "No se puede usar el formato de pixel %1 para %2"
-#: src/lib/util.cc:786
+#: src/lib/util.cc:775
msgid "Centre"
msgstr "Centro"
-#: src/lib/writer.cc:83
+#: src/lib/writer.cc:90
msgid "Checking existing image data"
msgstr "Comprobando las imágenes existentes"
-#: src/lib/writer.cc:475
+#: src/lib/writer.cc:490
msgid "Computing audio digest"
msgstr "Calculando la firma resumen del audio"
msgid "Computing digest"
msgstr "Calculando la firma resumen"
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
msgid "Computing image digest"
msgstr "Calculando la firma resumen de imagen"
msgid "Content and DCP have the same rate.\n"
msgstr "La fuente y el DCP tienen la misma velocidad.\n"
-#: src/lib/audio_content.cc:83
+#: src/lib/subtitle_content.cc:103
+#, fuzzy
+msgid "Content to be joined must have the same 'use subtitles' setting."
+msgstr "Para unir contenido debe tener la misma redimensión."
+
+#: src/lib/audio_content.cc:100
msgid "Content to be joined must have the same audio delay."
msgstr "Para unir contenido debe tener el mismo retardo de audio."
-#: src/lib/audio_content.cc:79
+#: src/lib/audio_content.cc:96
msgid "Content to be joined must have the same audio gain."
msgstr "Para unir contenido debe tener la misma ganancia de audio."
-#: src/lib/video_content.cc:146
+#: src/lib/video_content.cc:158
msgid "Content to be joined must have the same colour conversion."
msgstr "Para unir contenido debe tener la misma conversión de color."
-#: src/lib/video_content.cc:138
+#: src/lib/video_content.cc:150
msgid "Content to be joined must have the same crop."
msgstr "Para unir contenido debe tener el mismo recorte."
-#: src/lib/video_content.cc:126
+#: src/lib/video_content.cc:162
+#, fuzzy
+msgid "Content to be joined must have the same fades."
+msgstr "Para unir contenido debe tener el mismo recorte."
+
+#: src/lib/video_content.cc:138
msgid "Content to be joined must have the same picture size."
msgstr "Para unir contenido debe tener el mismo tamaño de imagen."
-#: src/lib/video_content.cc:142
+#: src/lib/video_content.cc:154
msgid "Content to be joined must have the same scale setting."
msgstr "Para unir contenido debe tener la misma redimensión."
-#: src/lib/subtitle_content.cc:81
+#: src/lib/subtitle_content.cc:107
msgid "Content to be joined must have the same subtitle X offset."
msgstr ""
"Para unir contenido debe tener el mismo desplazamiento de subtÃtulo en X."
-#: src/lib/subtitle_content.cc:89
+#: src/lib/subtitle_content.cc:115
#, fuzzy
msgid "Content to be joined must have the same subtitle X scale."
msgstr "Para unir contenido debe tener el mismo tamaño de subtÃtulo."
-#: src/lib/subtitle_content.cc:85
+#: src/lib/subtitle_content.cc:111
msgid "Content to be joined must have the same subtitle Y offset."
msgstr ""
"Para unir contenido debe tener el mismo desplazamiento de subtÃtulo en Y."
-#: src/lib/subtitle_content.cc:93
+#: src/lib/subtitle_content.cc:119
#, fuzzy
msgid "Content to be joined must have the same subtitle Y scale."
msgstr "Para unir contenido debe tener el mismo tamaño de subtÃtulo."
-#: src/lib/video_content.cc:130
+#: src/lib/video_content.cc:142
msgid "Content to be joined must have the same video frame rate."
msgstr "Para unir contenido debe tener la misma velocidad de imagen."
-#: src/lib/video_content.cc:134
+#: src/lib/video_content.cc:146
msgid "Content to be joined must have the same video frame type."
msgstr "Para unir contenido debe tener el mismo tamaño de imagen."
-#: src/lib/ffmpeg_content.cc:116
+#: src/lib/ffmpeg_content.cc:118
msgid "Content to be joined must use the same audio stream."
msgstr "Para unir contenido debe usar el mismo tipo de audio."
-#: src/lib/ffmpeg_content.cc:112
+#: src/lib/ffmpeg_content.cc:114
msgid "Content to be joined must use the same subtitle stream."
msgstr "Para unir contenido debe tener el mismo tipo de subtÃtulos."
msgid "Could not create remote directory %1 (%2)"
msgstr "No se pudo crear la carpeta remota %1 (%2)"
-#: src/lib/image_proxy.cc:147
+#: src/lib/magick_image_proxy.cc:103
#, fuzzy
msgid "Could not decode image file (%1)"
msgstr "No se pudo crear el fichero (%1)"
-#: src/lib/job.cc:90
+#: src/lib/job.cc:91
msgid "Could not open %1"
msgstr "No se pudo abrir %1"
msgid "Could not write to remote file (%1)"
msgstr "No se pudo escribir el fichero remoto (%1)"
+#: src/lib/dcp_subtitle_content.cc:72
+msgid "DCP XML subtitles"
+msgstr ""
+
#: src/lib/frame_rate_change.cc:98
msgid "DCP will run at %1%% of the content speed.\n"
msgstr "El DCP se reproducirá al %1%% de la velocidad de la fuente.\n"
msgid "DCP will use every other frame of the content.\n"
msgstr "El DCP usará imágenes alternas de la fuente.\n"
-#: src/lib/job.cc:91
+#: src/lib/job.cc:92
msgid ""
"DCP-o-matic could not open the file %1. Perhaps it does not exist or is in "
"an unexpected format."
"DCP-o-matic no pudo abrir el fichero %1. Quizás no existe o está en un "
"formato inesperado."
-#: src/lib/ffmpeg_content.cc:93
+#: src/lib/ffmpeg_content.cc:95
msgid ""
"DCP-o-matic no longer supports the `%1' filter, so it has been turned off."
msgstr "DCP-o-matic ya no ofrece el filtro `%1', asà que ha sido desactivado."
msgid "De-interlacing"
msgstr "Desentrelazado"
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
#, fuzzy
msgid ""
"Dear Projectionist\n"
msgid "Email KDMs for %1"
msgstr "Enviar por email las KDM para %1"
-#: src/lib/writer.cc:126
+#: src/lib/writer.cc:136
msgid "Encoding image data"
msgstr "Codificando imagen"
-#: src/lib/job.cc:320
+#: src/lib/job.cc:322
msgid "Error (%1)"
msgstr "Error (%1)"
+#: src/lib/exceptions.cc:66
+msgid "Error in SubRip file: saw %1 while expecting %2"
+msgstr ""
+
#: src/lib/examine_content_job.cc:46
msgid "Examine content"
msgstr "Examinar contenido"
msgid "Gradient debander"
msgstr "Gradient debander"
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
msgid "Hearing impaired"
msgstr "Sordos"
msgid "High quality 3D denoiser"
msgstr "Reductor de ruido 3D de alta calidad"
-#: src/lib/job.cc:111 src/lib/job.cc:121
+#: src/lib/job.cc:112 src/lib/job.cc:122
#, fuzzy
msgid ""
"It is not known what caused this error. Please report the problem to the "
"Error desconocido. La mejor idea es informar del problema a la lista de "
"correo de DCP-o-matic (carl@dcpomatic.com)"
-#: src/lib/config.cc:199
+#: src/lib/config.cc:212
msgid "KDM delivery: $CPL_NAME"
msgstr ""
msgid "Lanczos"
msgstr "Lanczos"
-#: src/lib/util.cc:784
+#: src/lib/util.cc:773
msgid "Left"
msgstr "Izquierda"
-#: src/lib/util.cc:792
+#: src/lib/util.cc:781
msgid "Left centre"
msgstr "Centro izquierda"
-#: src/lib/util.cc:794
+#: src/lib/util.cc:783
msgid "Left rear surround"
msgstr "Surround trasero izquierda"
-#: src/lib/util.cc:788
+#: src/lib/util.cc:777
msgid "Left surround"
msgstr "Surround izquierda"
-#: src/lib/util.cc:787
+#: src/lib/util.cc:776
msgid "Lfe (sub)"
msgstr "Lfe (bajos)"
+#: src/lib/mid_side_decoder.cc:31
+msgid "Mid-side decoder"
+msgstr ""
+
#: src/lib/filter.cc:68 src/lib/filter.cc:69 src/lib/filter.cc:72
msgid "Misc"
msgstr "Miscelánea"
+#: src/lib/dcp_examiner.cc:93
+msgid "Mismatched audio channel counts in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:99
+msgid "Mismatched audio frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:74
+msgid "Mismatched frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:81
+msgid "Mismatched video sizes in DCP"
+msgstr ""
+
#: src/lib/filter.cc:65
msgid "Motion compensating deinterlacer"
msgstr "Motion compensating deinterlacer"
msgid "Noise reduction"
msgstr "Reducción de ruido"
-#: src/lib/job.cc:318
+#: src/lib/job.cc:320
msgid "OK (ran for %1)"
msgstr "OK (ejecución %1)"
-#: src/lib/content.cc:102
+#: src/lib/content.cc:106
msgid "Only the first piece of content to be joined can have a start trim."
msgstr ""
"Solo la primera pieza a ser unida puede tener un recorte en su comienzo."
-#: src/lib/content.cc:106
+#: src/lib/content.cc:110
msgid "Only the last piece of content to be joined can have an end trim."
msgstr "Solo la última pieza a ser unida puede tener un recorte en su final."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "Out of memory"
msgstr "Falta de memoria"
msgid "Rating"
msgstr "Clasificación"
-#: src/lib/config.cc:86 src/lib/config.cc:183
+#: src/lib/config.cc:92 src/lib/config.cc:196
msgid "Rec. 709"
msgstr "Rec. 709"
-#: src/lib/util.cc:785
+#: src/lib/util.cc:774
msgid "Right"
msgstr "Derecha"
-#: src/lib/util.cc:793
+#: src/lib/util.cc:782
msgid "Right centre"
msgstr "Centro derecha"
-#: src/lib/util.cc:795
+#: src/lib/util.cc:784
msgid "Right rear surround"
msgstr "Surround trasero derecha"
-#: src/lib/util.cc:789
+#: src/lib/util.cc:778
msgid "Right surround"
msgstr "Surround derecha"
msgid "Spline"
msgstr "Spline"
+#: src/lib/upmixer_a.cc:42
+msgid "Stereo to 5.1 up-mixer A"
+msgstr ""
+
+#: src/lib/subrip_content.cc:73
+msgid "SubRip subtitles"
+msgstr ""
+
#: src/lib/dcp_content_type.cc:50
msgid "Teaser"
msgstr "Teaser"
msgid "Test"
msgstr "Test"
-#: src/lib/job.cc:77
+#: src/lib/dcp_examiner.cc:133
+msgid ""
+"The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"
+msgstr ""
+
+#: src/lib/exceptions.cc:72
+msgid "The certificate chain for signing is invalid"
+msgstr ""
+
+#: src/lib/job.cc:78
msgid ""
"The drive that the film is stored on is low in disc space. Free some more "
"space and try again."
"En el dispositivo donde se encuentra la pelÃcula queda poco espacio. Libere "
"espacio en el disco y pruebe de nuevo."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "There was not enough memory to do this."
msgstr "No hubo suficiente memoria para hacer esto."
-#: src/lib/film.cc:414
+#: src/lib/film.cc:423
msgid ""
"This film was created with a newer version of DCP-o-matic, and it cannot be "
"loaded into this version. Sorry!"
"desgraciadamente no s puede cargar. Necesitas crear una nueva pelÃcula, "
"volver a añadir y configurar ton contenido. ¡Lo siento!"
-#: src/lib/film.cc:406
+#: src/lib/film.cc:415
msgid ""
"This film was created with an older version of DCP-o-matic, and "
"unfortunately it cannot be loaded into this version. You will need to "
msgid "Trailer"
msgstr "Trailer"
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
msgid "Transcode %1"
msgstr "Codificar %1"
msgid "Unexpected ZIP file contents"
msgstr "Contenidos inesperados del fichero ZIP"
-#: src/lib/image_proxy.cc:193
+#: src/lib/image_proxy.cc:56
msgid "Unexpected image type received by server"
msgstr ""
-#: src/lib/job.cc:120
+#: src/lib/job.cc:121
msgid "Unknown error"
msgstr "Error desconocido"
-#: src/lib/ffmpeg_decoder.cc:293
+#: src/lib/ffmpeg_decoder.cc:263
msgid "Unrecognised audio sample format (%1)"
msgstr "Formato de audio desconocido (%1)"
msgid "Untitled"
msgstr "Sin tÃtulo"
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
msgid "Visually impaired"
msgstr "Ciegos"
msgid "Yet Another Deinterlacing Filter"
msgstr "Yet Another Deinterlacing Filter"
-#: src/lib/film.cc:311
+#: src/lib/film.cc:320
msgid "You must add some content to the DCP before creating it"
msgstr "Tienes que añadir contenido al DCP antes de crearlo."
msgid "[still]"
msgstr "[imagen fija]"
-#: src/lib/film.cc:259
+#: src/lib/dcp_subtitle_content.cc:66 src/lib/subrip_content.cc:67
+msgid "[subtitles]"
+msgstr ""
+
+#: src/lib/film.cc:268
msgid "cannot contain slashes"
msgstr "no puede contener barras"
-#: src/lib/util.cc:565
+#: src/lib/util.cc:554
msgid "connect timed out"
msgstr "tiempo de conexión agotado"
msgid "connecting"
msgstr "conectando"
-#: src/lib/film.cc:307
+#: src/lib/film.cc:316
msgid "container"
msgstr "continente"
-#: src/lib/film.cc:315
+#: src/lib/film.cc:324
msgid "content type"
msgstr "tipo de contenido"
msgid "could not create file %1"
msgstr "No se pudo crear el fichero (%1)"
-#: src/lib/ffmpeg.cc:176
-msgid "could not find audio decoder"
-msgstr "no se encontró el decodificador de audio"
-
-#: src/lib/ffmpeg.cc:105
+#: src/lib/ffmpeg.cc:102
msgid "could not find stream information"
msgstr "no se pudo encontrar información del flujo"
-#: src/lib/ffmpeg.cc:155
-msgid "could not find video decoder"
-msgstr "no se pudo encontrar decodificador de vÃdeo"
-
-#: src/lib/writer.cc:439
+#: src/lib/writer.cc:430
msgid "could not move audio MXF into the DCP (%1)"
msgstr "no s puedo mover el audio MXF en el DCP (%1)"
-#: src/lib/sndfile_decoder.cc:56
+#: src/lib/sndfile_decoder.cc:54
msgid "could not open audio file for reading"
msgstr "no se pudo abrir el fichero de audio para lectura"
msgid "could not open file %1"
msgstr "no se pudo abrir el fichero %1"
-#: src/lib/dcp_video_frame.cc:331
+#: src/lib/encoded_data.cc:50
msgid "could not open file for reading"
msgstr "no se pudo abrir el fichero para lectura"
-#: src/lib/dcp_video_frame.cc:337
+#: src/lib/encoded_data.cc:56
msgid "could not read encoded data"
msgstr "no se pudo leer la información codificada"
msgid "could not read from file %1 (%2)"
msgstr "No se pudo leer del fichero %1 (%2)"
-#: src/lib/resampler.cc:98
+#: src/lib/resampler.cc:96
msgid "could not run sample-rate converter"
msgstr "no se pudo ejecutar el conversor de velocidad"
-#: src/lib/resampler.cc:79
+#: src/lib/resampler.cc:77
msgid "could not run sample-rate converter for %1 samples (%2) (%3)"
msgstr "no se pudo ejecutar el conversor de velocidad"
msgid "could not write to file %1 (%2)"
msgstr "No se pudo escribir en el fichero (%1)"
-#: src/lib/util.cc:585
+#: src/lib/util.cc:574
msgid "error during async_accept (%1)"
msgstr "error durante async_accept (%1)"
-#: src/lib/util.cc:561
+#: src/lib/util.cc:550
msgid "error during async_connect (%1)"
msgstr "error durante async_connect (%1)"
-#: src/lib/util.cc:634
+#: src/lib/util.cc:623
msgid "error during async_read (%1)"
msgstr "error durante async_read (%1)"
-#: src/lib/util.cc:606
+#: src/lib/util.cc:595
msgid "error during async_write (%1)"
msgstr "error durante async_write (%1)"
-#: src/lib/transcode_job.cc:97
+#: src/lib/transcode_job.cc:98
msgid "frames per second"
msgstr "imágenes por segundo"
-#: src/lib/util.cc:156
+#: src/lib/util.cc:158
msgid "hour"
msgstr "hora"
-#: src/lib/util.cc:152 src/lib/util.cc:158
+#: src/lib/util.cc:154 src/lib/util.cc:160
msgid "hours"
msgstr "horas"
-#: src/lib/util.cc:174
+#: src/lib/util.cc:176
msgid "minute"
msgstr "minuto"
-#: src/lib/util.cc:170 src/lib/util.cc:176
+#: src/lib/util.cc:172 src/lib/util.cc:178
msgid "minutes"
msgstr "minutos"
msgid "moving"
msgstr "moviendo"
-#: src/lib/ffmpeg_decoder.cc:589
+#: src/lib/ffmpeg_decoder.cc:420
msgid "multi-part subtitles not yet supported"
msgstr "todavÃa no se soportan subtÃtulos en múltiples partes"
-#: src/lib/film.cc:259 src/lib/film.cc:319
+#: src/lib/film.cc:268 src/lib/film.cc:328
msgid "name"
msgstr "nombre"
-#: src/lib/ffmpeg_decoder.cc:604
-msgid "non-bitmap subtitles not yet supported"
-msgstr "todavÃa no se soportan subtÃtulos que no son en mapas de bits"
-
#. / TRANSLATORS: remaining here follows an amount of time that is remaining
#. / on an operation.
-#: src/lib/job.cc:315
+#: src/lib/job.cc:317
msgid "remaining"
msgstr "pendiente"
-#: src/lib/config.cc:84 src/lib/video_content.cc:179
+#: src/lib/config.cc:90 src/lib/video_content.cc:197
msgid "sRGB"
msgstr "sRGB"
-#: src/lib/config.cc:85
+#: src/lib/config.cc:91
msgid "sRGB non-linearised"
msgstr "sRGB no-lineal"
-#: src/lib/util.cc:189
+#: src/lib/util.cc:191
#, fuzzy
msgid "second"
msgstr "segundos"
-#: src/lib/util.cc:191
+#: src/lib/util.cc:193
msgid "seconds"
msgstr "segundos"
msgid "still"
msgstr "imagen fija"
-#: src/lib/ffmpeg_examiner.cc:168
+#: src/lib/ffmpeg_examiner.cc:207
msgid "unknown"
msgstr "desconocido"
+#~ msgid "could not find audio decoder"
+#~ msgstr "no se encontró el decodificador de audio"
+
+#~ msgid "could not find video decoder"
+#~ msgstr "no se pudo encontrar decodificador de vÃdeo"
+
+#~ msgid "non-bitmap subtitles not yet supported"
+#~ msgstr "todavÃa no se soportan subtÃtulos que no son en mapas de bits"
+
#~ msgid "Could not read DCP to make KDM for"
#~ msgstr "No se pudo leer el DCP para hacer el KDM"
msgstr ""
"Project-Id-Version: DCP-o-matic FRENCH\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-07-14 12:04+0100\n"
"Last-Translator: Grégoire AUSINA <gregoire@gisele-productions.eu>\n"
"Language-Team: \n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.6\n"
-#: src/lib/sndfile_content.cc:61
+#: src/lib/dcp_content.cc:106
+msgid "%1 [DCP]"
+msgstr ""
+
+#: src/lib/sndfile_content.cc:64
msgid "%1 [audio]"
msgstr "%1 [audio]"
-#: src/lib/ffmpeg_content.cc:211
+#: src/lib/ffmpeg_content.cc:205
msgid "%1 [movie]"
msgstr "%1 [vidéo]"
-#: src/lib/sndfile_content.cc:82
+#: src/lib/sndfile_content.cc:85
msgid "%1 channels, %2kHz, %3 samples"
msgstr "%1 canaux, %2kHz, %3 échantillons"
-#: src/lib/ffmpeg_content.cc:246
+#: src/lib/ffmpeg_content.cc:240
msgid "%1 frames; %2 frames per second"
msgstr "%1 images ; %2 images par seconde"
-#: src/lib/video_content.cc:211
+#: src/lib/video_content.cc:235
msgid "%1x%2 pixels (%3:1)"
msgstr "%1x%2 pixels (%3:1)"
msgid "Advertisement"
msgstr "Advertisement"
-#: src/lib/job.cc:71
+#: src/lib/job.cc:72
msgid "An error occurred whilst handling the file %1."
msgstr "Une erreur s'est produite lors du traitement du fichier %1."
-#: src/lib/analyse_audio_job.cc:48
+#: src/lib/analyse_audio_job.cc:49
msgid "Analyse audio"
msgstr "Analyse audio"
msgid "Bilinear"
msgstr "Bilinéaire"
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
msgid "Cancelled"
msgstr "Annulé"
msgid "Cannot handle pixel format %1 during %2"
msgstr "Format du pixel %1 non géré par %2"
-#: src/lib/util.cc:786
+#: src/lib/util.cc:775
msgid "Centre"
msgstr "Centre"
-#: src/lib/writer.cc:83
+#: src/lib/writer.cc:90
msgid "Checking existing image data"
msgstr "Recherche de données images existantes"
-#: src/lib/writer.cc:475
+#: src/lib/writer.cc:490
msgid "Computing audio digest"
msgstr "Fabrication rendu audio"
msgid "Computing digest"
msgstr "fabrication rendu"
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
msgid "Computing image digest"
msgstr "Fabrication rendu image"
msgid "Content and DCP have the same rate.\n"
msgstr "Le DCP et la source ont la même cadence image.\n"
-#: src/lib/audio_content.cc:83
+#: src/lib/subtitle_content.cc:103
+#, fuzzy
+msgid "Content to be joined must have the same 'use subtitles' setting."
+msgstr ""
+"Le contenu à ajouter doit avoir les mêmes paramètres de mise à l'échelle"
+
+#: src/lib/audio_content.cc:100
msgid "Content to be joined must have the same audio delay."
msgstr "Le contenu à ajouter doit présenter le même délais audio"
-#: src/lib/audio_content.cc:79
+#: src/lib/audio_content.cc:96
msgid "Content to be joined must have the same audio gain."
msgstr "Le contenu à ajouter doit avoir le même gain audio"
-#: src/lib/video_content.cc:146
+#: src/lib/video_content.cc:158
msgid "Content to be joined must have the same colour conversion."
msgstr "Le contenu à ajouter doit avoir le même type de conversion couleur"
-#: src/lib/video_content.cc:138
+#: src/lib/video_content.cc:150
msgid "Content to be joined must have the same crop."
msgstr "le contenu à ajouter doit avoir les mêmes valeurs de rognage"
-#: src/lib/video_content.cc:126
+#: src/lib/video_content.cc:162
+#, fuzzy
+msgid "Content to be joined must have the same fades."
+msgstr "le contenu à ajouter doit avoir les mêmes valeurs de rognage"
+
+#: src/lib/video_content.cc:138
msgid "Content to be joined must have the same picture size."
msgstr "Le contenu à ajouter doit avoir la même taille d'image"
-#: src/lib/video_content.cc:142
+#: src/lib/video_content.cc:154
msgid "Content to be joined must have the same scale setting."
msgstr ""
"Le contenu à ajouter doit avoir les mêmes paramètres de mise à l'échelle"
-#: src/lib/subtitle_content.cc:81
+#: src/lib/subtitle_content.cc:107
msgid "Content to be joined must have the same subtitle X offset."
msgstr ""
"Le contenu à ajouter doit avoir le même positionnement horizontal des sous-"
"titres"
-#: src/lib/subtitle_content.cc:89
+#: src/lib/subtitle_content.cc:115
#, fuzzy
msgid "Content to be joined must have the same subtitle X scale."
msgstr "le contenu à ajouter doit avoir le même positionnement de sous-titre"
-#: src/lib/subtitle_content.cc:85
+#: src/lib/subtitle_content.cc:111
msgid "Content to be joined must have the same subtitle Y offset."
msgstr ""
"Le contenu à ajouter doit avoir le même positionnement vertical des sous-"
"titres"
-#: src/lib/subtitle_content.cc:93
+#: src/lib/subtitle_content.cc:119
#, fuzzy
msgid "Content to be joined must have the same subtitle Y scale."
msgstr "le contenu à ajouter doit avoir le même positionnement de sous-titre"
-#: src/lib/video_content.cc:130
+#: src/lib/video_content.cc:142
msgid "Content to be joined must have the same video frame rate."
msgstr "Le contenu à ajouter doit avoir la même cadence d'images"
-#: src/lib/video_content.cc:134
+#: src/lib/video_content.cc:146
msgid "Content to be joined must have the same video frame type."
msgstr "Le contenu à ajouter doit avoir le même type de trame vidéo"
-#: src/lib/ffmpeg_content.cc:116
+#: src/lib/ffmpeg_content.cc:118
msgid "Content to be joined must use the same audio stream."
msgstr "Le contenu à ajouter doit avoir le même flux audio"
-#: src/lib/ffmpeg_content.cc:112
+#: src/lib/ffmpeg_content.cc:114
msgid "Content to be joined must use the same subtitle stream."
msgstr "Le contenu à ajouter doit avoir le même flux sous titre"
msgid "Could not create remote directory %1 (%2)"
msgstr "Création du dossier distant %1 (%2) impossible"
-#: src/lib/image_proxy.cc:147
+#: src/lib/magick_image_proxy.cc:103
#, fuzzy
msgid "Could not decode image file (%1)"
msgstr "Impossible de décoder le ficher image"
-#: src/lib/job.cc:90
+#: src/lib/job.cc:91
msgid "Could not open %1"
msgstr "lecture du fichier %1 impossible"
msgid "Could not write to remote file (%1)"
msgstr "Écriture vers fichier distant (%1) impossible"
+#: src/lib/dcp_subtitle_content.cc:72
+msgid "DCP XML subtitles"
+msgstr ""
+
#: src/lib/frame_rate_change.cc:98
msgid "DCP will run at %1%% of the content speed.\n"
msgstr "Le DCP sera lu à %1%% de la vitesse du contenu source.\n"
msgid "DCP will use every other frame of the content.\n"
msgstr "Le DCP utilisera les autres images de la source.\n"
-#: src/lib/job.cc:91
+#: src/lib/job.cc:92
msgid ""
"DCP-o-matic could not open the file %1. Perhaps it does not exist or is in "
"an unexpected format."
"DCP-o-matic ne peut pas ouvrir le fichier %1. Soit il n'existe pas, soit il "
"n'est pas au bon format."
-#: src/lib/ffmpeg_content.cc:93
+#: src/lib/ffmpeg_content.cc:95
msgid ""
"DCP-o-matic no longer supports the `%1' filter, so it has been turned off."
msgstr "DCP-o-matic ne gère plus le filtre `%1'. Celui-ci a été désactivé."
msgid "De-interlacing"
msgstr "Désentrelacement"
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
msgid ""
"Dear Projectionist\n"
"\n"
msgid "Email KDMs for %1"
msgstr "Envoyer KDM par email pour %1"
-#: src/lib/writer.cc:126
+#: src/lib/writer.cc:136
msgid "Encoding image data"
msgstr "encodage des données image"
-#: src/lib/job.cc:320
+#: src/lib/job.cc:322
msgid "Error (%1)"
msgstr "Erreur (%1)"
+#: src/lib/exceptions.cc:66
+msgid "Error in SubRip file: saw %1 while expecting %2"
+msgstr ""
+
#: src/lib/examine_content_job.cc:46
msgid "Examine content"
msgstr "Examen du contenu"
msgid "Gradient debander"
msgstr "Corrections des bandes par dégradé"
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
msgid "Hearing impaired"
msgstr "Déficients Auditifs"
msgid "High quality 3D denoiser"
msgstr "Débruiteur 3D haute qualité"
-#: src/lib/job.cc:111 src/lib/job.cc:121
+#: src/lib/job.cc:112 src/lib/job.cc:122
msgid ""
"It is not known what caused this error. Please report the problem to the "
"DCP-o-matic author (carl@dcpomatic.com)."
"Erreur indéterminée. Merci de rapporter le problème à l'auteur de DCP-o-"
"matic (carl@dcpomatic.com)"
-#: src/lib/config.cc:199
+#: src/lib/config.cc:212
msgid "KDM delivery: $CPL_NAME"
msgstr ""
msgid "Lanczos"
msgstr "Lanczos"
-#: src/lib/util.cc:784
+#: src/lib/util.cc:773
msgid "Left"
msgstr "Gauche"
-#: src/lib/util.cc:792
+#: src/lib/util.cc:781
msgid "Left centre"
msgstr "Centre Gauche"
-#: src/lib/util.cc:794
+#: src/lib/util.cc:783
msgid "Left rear surround"
msgstr "Surround arrière gauche"
-#: src/lib/util.cc:788
+#: src/lib/util.cc:777
msgid "Left surround"
msgstr "Arrière gauche"
-#: src/lib/util.cc:787
+#: src/lib/util.cc:776
msgid "Lfe (sub)"
msgstr "Basses fréquences"
+#: src/lib/mid_side_decoder.cc:31
+msgid "Mid-side decoder"
+msgstr ""
+
#: src/lib/filter.cc:68 src/lib/filter.cc:69 src/lib/filter.cc:72
msgid "Misc"
msgstr "Divers"
+#: src/lib/dcp_examiner.cc:93
+msgid "Mismatched audio channel counts in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:99
+msgid "Mismatched audio frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:74
+msgid "Mismatched frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:81
+msgid "Mismatched video sizes in DCP"
+msgstr ""
+
#: src/lib/filter.cc:65
msgid "Motion compensating deinterlacer"
msgstr "Désentrelaceur par compensation de mouvement"
msgid "Noise reduction"
msgstr "Réduction de bruit"
-#: src/lib/job.cc:318
+#: src/lib/job.cc:320
msgid "OK (ran for %1)"
msgstr "OK (processus %1)"
-#: src/lib/content.cc:102
+#: src/lib/content.cc:106
msgid "Only the first piece of content to be joined can have a start trim."
msgstr "Seul le premier contenu à ajouter peut être rogné au point d'entrée."
-#: src/lib/content.cc:106
+#: src/lib/content.cc:110
msgid "Only the last piece of content to be joined can have an end trim."
msgstr "Seul le dernier contenu à ajouter peut être rogné au point de sortie."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "Out of memory"
msgstr "Hors capacité mémoire"
msgid "Rating"
msgstr "Classification"
-#: src/lib/config.cc:86 src/lib/config.cc:183
+#: src/lib/config.cc:92 src/lib/config.cc:196
msgid "Rec. 709"
msgstr "Rec. 709"
-#: src/lib/util.cc:785
+#: src/lib/util.cc:774
msgid "Right"
msgstr "Droite"
-#: src/lib/util.cc:793
+#: src/lib/util.cc:782
msgid "Right centre"
msgstr "Centre Droit"
-#: src/lib/util.cc:795
+#: src/lib/util.cc:784
msgid "Right rear surround"
msgstr "Surround arrière droite"
-#: src/lib/util.cc:789
+#: src/lib/util.cc:778
msgid "Right surround"
msgstr "Arrière droite"
msgid "Spline"
msgstr "Spline"
+#: src/lib/upmixer_a.cc:42
+msgid "Stereo to 5.1 up-mixer A"
+msgstr ""
+
+#: src/lib/subrip_content.cc:73
+msgid "SubRip subtitles"
+msgstr ""
+
#: src/lib/dcp_content_type.cc:50
msgid "Teaser"
msgstr "Teaser"
msgid "Test"
msgstr "Test"
-#: src/lib/job.cc:77
+#: src/lib/dcp_examiner.cc:133
+msgid ""
+"The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"
+msgstr ""
+
+#: src/lib/exceptions.cc:72
+msgid "The certificate chain for signing is invalid"
+msgstr ""
+
+#: src/lib/job.cc:78
msgid ""
"The drive that the film is stored on is low in disc space. Free some more "
"space and try again."
"Le disque contenant le film est presque plein. Libérez de l'espace et "
"essayez à nouveau."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "There was not enough memory to do this."
msgstr "Il n'y avait pas assez de mémoire pour faire cela."
-#: src/lib/film.cc:414
+#: src/lib/film.cc:423
msgid ""
"This film was created with a newer version of DCP-o-matic, and it cannot be "
"loaded into this version. Sorry!"
"Ce film a été créé avec une nouvelle version de DCP-o-matic et il ne peut "
"être ouvert dans cette version du programme. Désolé!"
-#: src/lib/film.cc:406
+#: src/lib/film.cc:415
msgid ""
"This film was created with an older version of DCP-o-matic, and "
"unfortunately it cannot be loaded into this version. You will need to "
msgid "Trailer"
msgstr "Trailer"
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
msgid "Transcode %1"
msgstr "Transcodage %1"
msgid "Unexpected ZIP file contents"
msgstr "Contenu de fichier ZIP non géré."
-#: src/lib/image_proxy.cc:193
+#: src/lib/image_proxy.cc:56
msgid "Unexpected image type received by server"
msgstr "Type d'image non conforme reçu par le serveur"
-#: src/lib/job.cc:120
+#: src/lib/job.cc:121
msgid "Unknown error"
msgstr "Erreur inconnue"
-#: src/lib/ffmpeg_decoder.cc:293
+#: src/lib/ffmpeg_decoder.cc:263
msgid "Unrecognised audio sample format (%1)"
msgstr "Échantillonnage audio (%1) inconnu"
msgid "Untitled"
msgstr "Sans titre"
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
msgid "Visually impaired"
msgstr "Déficients Visuels"
msgid "Yet Another Deinterlacing Filter"
msgstr "Un autre filtre de désentrelacement"
-#: src/lib/film.cc:311
+#: src/lib/film.cc:320
msgid "You must add some content to the DCP before creating it"
msgstr "Ajoutez un contenu pour créer le DCP"
msgid "[still]"
msgstr "[restant]"
-#: src/lib/film.cc:259
+#: src/lib/dcp_subtitle_content.cc:66 src/lib/subrip_content.cc:67
+msgid "[subtitles]"
+msgstr ""
+
+#: src/lib/film.cc:268
msgid "cannot contain slashes"
msgstr "slash interdit"
-#: src/lib/util.cc:565
+#: src/lib/util.cc:554
msgid "connect timed out"
msgstr "temps de connexion expiré"
msgid "connecting"
msgstr "connexion"
-#: src/lib/film.cc:307
+#: src/lib/film.cc:316
msgid "container"
msgstr "conteneur"
-#: src/lib/film.cc:315
+#: src/lib/film.cc:324
msgid "content type"
msgstr "type de contenu"
msgid "could not create file %1"
msgstr "Écriture vers fichier distant (%1) impossible"
-#: src/lib/ffmpeg.cc:176
-msgid "could not find audio decoder"
-msgstr "décodeur audio introuvable"
-
-#: src/lib/ffmpeg.cc:105
+#: src/lib/ffmpeg.cc:102
msgid "could not find stream information"
msgstr "information du flux introuvable"
-#: src/lib/ffmpeg.cc:155
-msgid "could not find video decoder"
-msgstr "décodeur vidéo introuvable"
-
-#: src/lib/writer.cc:439
+#: src/lib/writer.cc:430
msgid "could not move audio MXF into the DCP (%1)"
msgstr "ne peut déplacer un MXF son dans le DCP (%1)"
-#: src/lib/sndfile_decoder.cc:56
+#: src/lib/sndfile_decoder.cc:54
msgid "could not open audio file for reading"
msgstr "lecture du fichier audio impossible"
msgid "could not open file %1"
msgstr "lecture du fichier (%1) impossible"
-#: src/lib/dcp_video_frame.cc:331
+#: src/lib/encoded_data.cc:50
msgid "could not open file for reading"
msgstr "lecture du fichier impossible"
-#: src/lib/dcp_video_frame.cc:337
+#: src/lib/encoded_data.cc:56
msgid "could not read encoded data"
msgstr "lecture des données encodées impossible"
msgid "could not read from file %1 (%2)"
msgstr "lecture du fichier impossible %1 (%2)"
-#: src/lib/resampler.cc:98
+#: src/lib/resampler.cc:96
msgid "could not run sample-rate converter"
msgstr "conversion de la fréquence d'échantillonnage impossible"
-#: src/lib/resampler.cc:79
+#: src/lib/resampler.cc:77
msgid "could not run sample-rate converter for %1 samples (%2) (%3)"
msgstr ""
"n'a pas pu convertir la fréquence d'échantillonnage pour %1 échantillons "
msgid "could not write to file %1 (%2)"
msgstr "Écriture vers fichier distant (%1) impossible (%2)"
-#: src/lib/util.cc:585
+#: src/lib/util.cc:574
msgid "error during async_accept (%1)"
msgstr "erreur pendant async_accept (%1)"
-#: src/lib/util.cc:561
+#: src/lib/util.cc:550
msgid "error during async_connect (%1)"
msgstr "erreur pendant async_connect (%1)"
-#: src/lib/util.cc:634
+#: src/lib/util.cc:623
msgid "error during async_read (%1)"
msgstr "erreur pendant async_read (%1)"
-#: src/lib/util.cc:606
+#: src/lib/util.cc:595
msgid "error during async_write (%1)"
msgstr "erreur pendant async_write (%1)"
-#: src/lib/transcode_job.cc:97
+#: src/lib/transcode_job.cc:98
msgid "frames per second"
msgstr "images par seconde"
-#: src/lib/util.cc:156
+#: src/lib/util.cc:158
msgid "hour"
msgstr "heure"
-#: src/lib/util.cc:152 src/lib/util.cc:158
+#: src/lib/util.cc:154 src/lib/util.cc:160
msgid "hours"
msgstr "heures"
-#: src/lib/util.cc:174
+#: src/lib/util.cc:176
msgid "minute"
msgstr "minute"
-#: src/lib/util.cc:170 src/lib/util.cc:176
+#: src/lib/util.cc:172 src/lib/util.cc:178
msgid "minutes"
msgstr "minutes"
msgid "moving"
msgstr "déplacement"
-#: src/lib/ffmpeg_decoder.cc:589
+#: src/lib/ffmpeg_decoder.cc:420
msgid "multi-part subtitles not yet supported"
msgstr "sous-titres en plusieurs parties non supportés"
-#: src/lib/film.cc:259 src/lib/film.cc:319
+#: src/lib/film.cc:268 src/lib/film.cc:328
msgid "name"
msgstr "nom"
-#: src/lib/ffmpeg_decoder.cc:604
-msgid "non-bitmap subtitles not yet supported"
-msgstr "sous-titres non-bitmap non supportés actuellement"
-
#. / TRANSLATORS: remaining here follows an amount of time that is remaining
#. / on an operation.
-#: src/lib/job.cc:315
+#: src/lib/job.cc:317
msgid "remaining"
msgstr "restant"
-#: src/lib/config.cc:84 src/lib/video_content.cc:179
+#: src/lib/config.cc:90 src/lib/video_content.cc:197
msgid "sRGB"
msgstr "sRGB"
-#: src/lib/config.cc:85
+#: src/lib/config.cc:91
msgid "sRGB non-linearised"
msgstr "sRGB non linéarisé"
-#: src/lib/util.cc:189
+#: src/lib/util.cc:191
#, fuzzy
msgid "second"
msgstr "secondes"
-#: src/lib/util.cc:191
+#: src/lib/util.cc:193
msgid "seconds"
msgstr "secondes"
msgid "still"
msgstr "%1 [restant]"
-#: src/lib/ffmpeg_examiner.cc:168
+#: src/lib/ffmpeg_examiner.cc:207
msgid "unknown"
msgstr "Inconnu"
+#~ msgid "could not find audio decoder"
+#~ msgstr "décodeur audio introuvable"
+
+#~ msgid "could not find video decoder"
+#~ msgstr "décodeur vidéo introuvable"
+
+#~ msgid "non-bitmap subtitles not yet supported"
+#~ msgstr "sous-titres non-bitmap non supportés actuellement"
+
#~ msgid "Could not read DCP to make KDM for"
#~ msgstr "DCP illisible pour fabrication de KDM"
msgstr ""
"Project-Id-Version: IT VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-02-03 10:48+0100\n"
"Last-Translator: William Fanelli <william.f@impronte.com>\n"
"Language-Team: \n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.3\n"
-#: src/lib/sndfile_content.cc:61
+#: src/lib/dcp_content.cc:106
+msgid "%1 [DCP]"
+msgstr ""
+
+#: src/lib/sndfile_content.cc:64
msgid "%1 [audio]"
msgstr ""
-#: src/lib/ffmpeg_content.cc:211
+#: src/lib/ffmpeg_content.cc:205
msgid "%1 [movie]"
msgstr ""
-#: src/lib/sndfile_content.cc:82
+#: src/lib/sndfile_content.cc:85
msgid "%1 channels, %2kHz, %3 samples"
msgstr ""
-#: src/lib/ffmpeg_content.cc:246
+#: src/lib/ffmpeg_content.cc:240
msgid "%1 frames; %2 frames per second"
msgstr "%1 fotogrammi; %2 fotogrammi al secondo"
-#: src/lib/video_content.cc:211
+#: src/lib/video_content.cc:235
msgid "%1x%2 pixels (%3:1)"
msgstr ""
msgid "Advertisement"
msgstr "Pubblicità "
-#: src/lib/job.cc:71
+#: src/lib/job.cc:72
msgid "An error occurred whilst handling the file %1."
msgstr "Errore durante l'elaborazione del file %1."
-#: src/lib/analyse_audio_job.cc:48
+#: src/lib/analyse_audio_job.cc:49
#, fuzzy
msgid "Analyse audio"
msgstr "Analizzo l'audio di %1"
msgid "Bilinear"
msgstr "Bilineare"
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
msgid "Cancelled"
msgstr "Cancellato"
msgid "Cannot handle pixel format %1 during %2"
msgstr "Non posso gestire il formato di pixel %1 durante %2"
-#: src/lib/util.cc:786
+#: src/lib/util.cc:775
msgid "Centre"
msgstr "Centro"
-#: src/lib/writer.cc:83
+#: src/lib/writer.cc:90
msgid "Checking existing image data"
msgstr ""
-#: src/lib/writer.cc:475
+#: src/lib/writer.cc:490
msgid "Computing audio digest"
msgstr ""
msgid "Computing digest"
msgstr ""
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
msgid "Computing image digest"
msgstr ""
msgid "Content and DCP have the same rate.\n"
msgstr "Il DCP e il sorgente hanno la stessa frequenza.\n"
-#: src/lib/audio_content.cc:83
+#: src/lib/subtitle_content.cc:103
+#, fuzzy
+msgid "Content to be joined must have the same 'use subtitles' setting."
+msgstr ""
+"Il contenuto da unire deve avere lo stesso spostamento X dei sottotitoli."
+
+#: src/lib/audio_content.cc:100
msgid "Content to be joined must have the same audio delay."
msgstr ""
-#: src/lib/audio_content.cc:79
+#: src/lib/audio_content.cc:96
msgid "Content to be joined must have the same audio gain."
msgstr ""
-#: src/lib/video_content.cc:146
+#: src/lib/video_content.cc:158
msgid "Content to be joined must have the same colour conversion."
msgstr ""
-#: src/lib/video_content.cc:138
+#: src/lib/video_content.cc:150
msgid "Content to be joined must have the same crop."
msgstr ""
-#: src/lib/video_content.cc:126
+#: src/lib/video_content.cc:162
+#, fuzzy
+msgid "Content to be joined must have the same fades."
+msgstr ""
+"Il contenuto da unire deve avere lo stesso spostamento X dei sottotitoli."
+
+#: src/lib/video_content.cc:138
msgid "Content to be joined must have the same picture size."
msgstr ""
-#: src/lib/video_content.cc:142
+#: src/lib/video_content.cc:154
#, fuzzy
msgid "Content to be joined must have the same scale setting."
msgstr ""
"Il contenuto da unire deve avere lo stesso spostamento X dei sottotitoli."
-#: src/lib/subtitle_content.cc:81
+#: src/lib/subtitle_content.cc:107
msgid "Content to be joined must have the same subtitle X offset."
msgstr ""
"Il contenuto da unire deve avere lo stesso spostamento X dei sottotitoli."
-#: src/lib/subtitle_content.cc:89
+#: src/lib/subtitle_content.cc:115
#, fuzzy
msgid "Content to be joined must have the same subtitle X scale."
msgstr ""
"Il contenuto da unire deve avere lo stesso spostamento X dei sottotitoli."
-#: src/lib/subtitle_content.cc:85
+#: src/lib/subtitle_content.cc:111
msgid "Content to be joined must have the same subtitle Y offset."
msgstr ""
"Il contenuto da unire deve avere lo stesso spostamento Y dei sottotitoli."
-#: src/lib/subtitle_content.cc:93
+#: src/lib/subtitle_content.cc:119
#, fuzzy
msgid "Content to be joined must have the same subtitle Y scale."
msgstr ""
"Il contenuto da unire deve avere lo stesso spostamento Y dei sottotitoli."
-#: src/lib/video_content.cc:130
+#: src/lib/video_content.cc:142
msgid "Content to be joined must have the same video frame rate."
msgstr ""
-#: src/lib/video_content.cc:134
+#: src/lib/video_content.cc:146
msgid "Content to be joined must have the same video frame type."
msgstr ""
-#: src/lib/ffmpeg_content.cc:116
+#: src/lib/ffmpeg_content.cc:118
msgid "Content to be joined must use the same audio stream."
msgstr ""
-#: src/lib/ffmpeg_content.cc:112
+#: src/lib/ffmpeg_content.cc:114
msgid "Content to be joined must use the same subtitle stream."
msgstr ""
msgid "Could not create remote directory %1 (%2)"
msgstr "Non posso creare la directory remota %1 (%2)"
-#: src/lib/image_proxy.cc:147
+#: src/lib/magick_image_proxy.cc:103
#, fuzzy
msgid "Could not decode image file (%1)"
msgstr "Non posso scrivere il file remoto (%1)"
-#: src/lib/job.cc:90
+#: src/lib/job.cc:91
msgid "Could not open %1"
msgstr "Non riesco ad aprire %1"
msgid "Could not write to remote file (%1)"
msgstr "Non posso scrivere il file remoto (%1)"
+#: src/lib/dcp_subtitle_content.cc:72
+msgid "DCP XML subtitles"
+msgstr ""
+
#: src/lib/frame_rate_change.cc:98
#, fuzzy
msgid "DCP will run at %1%% of the content speed.\n"
msgid "DCP will use every other frame of the content.\n"
msgstr "Il DCP userà ogni altro fotogramma del sorgente.\n"
-#: src/lib/job.cc:91
+#: src/lib/job.cc:92
msgid ""
"DCP-o-matic could not open the file %1. Perhaps it does not exist or is in "
"an unexpected format."
"DCP-o-matic non può aprire il file %1. Non esiste oppure è in un formato non "
"riconosciuto."
-#: src/lib/ffmpeg_content.cc:93
+#: src/lib/ffmpeg_content.cc:95
msgid ""
"DCP-o-matic no longer supports the `%1' filter, so it has been turned off."
msgstr ""
msgid "De-interlacing"
msgstr "De-interlacciamento"
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
#, fuzzy
msgid ""
"Dear Projectionist\n"
msgid "Email KDMs for %1"
msgstr ""
-#: src/lib/writer.cc:126
+#: src/lib/writer.cc:136
msgid "Encoding image data"
msgstr ""
-#: src/lib/job.cc:320
+#: src/lib/job.cc:322
msgid "Error (%1)"
msgstr "Errore (%1)"
+#: src/lib/exceptions.cc:66
+msgid "Error in SubRip file: saw %1 while expecting %2"
+msgstr ""
+
#: src/lib/examine_content_job.cc:46
msgid "Examine content"
msgstr "Esamino il contenuto"
msgid "Gradient debander"
msgstr "Gradiente debander"
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
msgid "Hearing impaired"
msgstr ""
msgid "High quality 3D denoiser"
msgstr "Riduttore di rumore 3D di alta qualità "
-#: src/lib/job.cc:111 src/lib/job.cc:121
+#: src/lib/job.cc:112 src/lib/job.cc:122
#, fuzzy
msgid ""
"It is not known what caused this error. Please report the problem to the "
"Non sappiamo cosa ha causato questo errore. La cosa migliore è inviare un "
"report del problema alla mailing list di DCP-o-matic (carl@dcpomatic.com)"
-#: src/lib/config.cc:199
+#: src/lib/config.cc:212
msgid "KDM delivery: $CPL_NAME"
msgstr ""
msgid "Lanczos"
msgstr "Lanczos"
-#: src/lib/util.cc:784
+#: src/lib/util.cc:773
msgid "Left"
msgstr "Sinistro"
-#: src/lib/util.cc:792
+#: src/lib/util.cc:781
msgid "Left centre"
msgstr ""
-#: src/lib/util.cc:794
+#: src/lib/util.cc:783
#, fuzzy
msgid "Left rear surround"
msgstr "Surround sinistro"
-#: src/lib/util.cc:788
+#: src/lib/util.cc:777
msgid "Left surround"
msgstr "Surround sinistro"
-#: src/lib/util.cc:787
+#: src/lib/util.cc:776
msgid "Lfe (sub)"
msgstr "Lfe(sub)"
+#: src/lib/mid_side_decoder.cc:31
+msgid "Mid-side decoder"
+msgstr ""
+
#: src/lib/filter.cc:68 src/lib/filter.cc:69 src/lib/filter.cc:72
msgid "Misc"
msgstr "Varie"
+#: src/lib/dcp_examiner.cc:93
+msgid "Mismatched audio channel counts in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:99
+msgid "Mismatched audio frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:74
+msgid "Mismatched frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:81
+msgid "Mismatched video sizes in DCP"
+msgstr ""
+
#: src/lib/filter.cc:65
msgid "Motion compensating deinterlacer"
msgstr "Dinterlacciatore con compensazione di movimento"
msgid "Noise reduction"
msgstr "Riduzione del rumore"
-#: src/lib/job.cc:318
+#: src/lib/job.cc:320
msgid "OK (ran for %1)"
msgstr "OK (eseguito in %1)"
-#: src/lib/content.cc:102
+#: src/lib/content.cc:106
msgid "Only the first piece of content to be joined can have a start trim."
msgstr ""
-#: src/lib/content.cc:106
+#: src/lib/content.cc:110
msgid "Only the last piece of content to be joined can have an end trim."
msgstr ""
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "Out of memory"
msgstr ""
msgid "Rating"
msgstr "Punteggio"
-#: src/lib/config.cc:86 src/lib/config.cc:183
+#: src/lib/config.cc:92 src/lib/config.cc:196
#, fuzzy
msgid "Rec. 709"
msgstr "Rec 709"
-#: src/lib/util.cc:785
+#: src/lib/util.cc:774
msgid "Right"
msgstr "Destro"
-#: src/lib/util.cc:793
+#: src/lib/util.cc:782
msgid "Right centre"
msgstr ""
-#: src/lib/util.cc:795
+#: src/lib/util.cc:784
#, fuzzy
msgid "Right rear surround"
msgstr "Surround destro"
-#: src/lib/util.cc:789
+#: src/lib/util.cc:778
msgid "Right surround"
msgstr "Surround destro"
msgid "Spline"
msgstr "Spline"
+#: src/lib/upmixer_a.cc:42
+msgid "Stereo to 5.1 up-mixer A"
+msgstr ""
+
+#: src/lib/subrip_content.cc:73
+msgid "SubRip subtitles"
+msgstr ""
+
#: src/lib/dcp_content_type.cc:50
msgid "Teaser"
msgstr "Teaser"
msgid "Test"
msgstr "Prova"
-#: src/lib/job.cc:77
+#: src/lib/dcp_examiner.cc:133
+msgid ""
+"The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"
+msgstr ""
+
+#: src/lib/exceptions.cc:72
+msgid "The certificate chain for signing is invalid"
+msgstr ""
+
+#: src/lib/job.cc:78
msgid ""
"The drive that the film is stored on is low in disc space. Free some more "
"space and try again."
"Sul disco dove è memorizzato il film non c'è abbastanza spazio. Liberare "
"altro spazio e riprovare."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "There was not enough memory to do this."
msgstr ""
-#: src/lib/film.cc:414
+#: src/lib/film.cc:423
#, fuzzy
msgid ""
"This film was created with a newer version of DCP-o-matic, and it cannot be "
"un nuovo film, ri-aggiungere i tuoi contenuti e configurarlo di nuovo. Ci "
"dispiace!"
-#: src/lib/film.cc:406
+#: src/lib/film.cc:415
msgid ""
"This film was created with an older version of DCP-o-matic, and "
"unfortunately it cannot be loaded into this version. You will need to "
msgid "Trailer"
msgstr "Prossimamente"
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
msgid "Transcode %1"
msgstr "Transcodifica %1"
msgid "Unexpected ZIP file contents"
msgstr ""
-#: src/lib/image_proxy.cc:193
+#: src/lib/image_proxy.cc:56
msgid "Unexpected image type received by server"
msgstr ""
-#: src/lib/job.cc:120
+#: src/lib/job.cc:121
msgid "Unknown error"
msgstr "Errore sconosciuto"
-#: src/lib/ffmpeg_decoder.cc:293
+#: src/lib/ffmpeg_decoder.cc:263
msgid "Unrecognised audio sample format (%1)"
msgstr "Formato di campionamento audio non riconosciuto (%1)"
msgid "Untitled"
msgstr "Senza titolo"
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
msgid "Visually impaired"
msgstr ""
msgid "Yet Another Deinterlacing Filter"
msgstr "Altro filtro di deinterlacciamento"
-#: src/lib/film.cc:311
+#: src/lib/film.cc:320
msgid "You must add some content to the DCP before creating it"
msgstr "Devi aggiungere dei contenuti al DCP prima di crearlo"
msgid "[still]"
msgstr "ancora"
-#: src/lib/film.cc:259
+#: src/lib/dcp_subtitle_content.cc:66 src/lib/subrip_content.cc:67
+msgid "[subtitles]"
+msgstr ""
+
+#: src/lib/film.cc:268
msgid "cannot contain slashes"
msgstr "non può contenere barre"
-#: src/lib/util.cc:565
+#: src/lib/util.cc:554
msgid "connect timed out"
msgstr "connessione scaduta"
msgid "connecting"
msgstr "mi sto connettendo"
-#: src/lib/film.cc:307
+#: src/lib/film.cc:316
msgid "container"
msgstr "contenitore"
-#: src/lib/film.cc:315
+#: src/lib/film.cc:324
msgid "content type"
msgstr "tipo di contenuto"
msgid "could not create file %1"
msgstr "Non posso scrivere il file remoto (%1)"
-#: src/lib/ffmpeg.cc:176
-msgid "could not find audio decoder"
-msgstr "non riesco a trovare il decoder audio"
-
-#: src/lib/ffmpeg.cc:105
+#: src/lib/ffmpeg.cc:102
msgid "could not find stream information"
msgstr "non riesco a trovare informazioni sullo streaming"
-#: src/lib/ffmpeg.cc:155
-msgid "could not find video decoder"
-msgstr "non riesco a trovare il decoder video"
-
-#: src/lib/writer.cc:439
+#: src/lib/writer.cc:430
msgid "could not move audio MXF into the DCP (%1)"
msgstr ""
-#: src/lib/sndfile_decoder.cc:56
+#: src/lib/sndfile_decoder.cc:54
msgid "could not open audio file for reading"
msgstr "non riesco ad aprire il file in lettura"
msgid "could not open file %1"
msgstr "non riesco ad aprire %1"
-#: src/lib/dcp_video_frame.cc:331
+#: src/lib/encoded_data.cc:50
msgid "could not open file for reading"
msgstr "non riesco ad aprire il file per leggerlo"
-#: src/lib/dcp_video_frame.cc:337
+#: src/lib/encoded_data.cc:56
#, fuzzy
msgid "could not read encoded data"
msgstr "non riesco a trovare il decoder audio"
msgid "could not read from file %1 (%2)"
msgstr "non posso leggere dal file %1 (%2)"
-#: src/lib/resampler.cc:98
+#: src/lib/resampler.cc:96
msgid "could not run sample-rate converter"
msgstr "non riesco a eseguire il convertitore della frequenza di campionamento"
-#: src/lib/resampler.cc:79
+#: src/lib/resampler.cc:77
#, fuzzy
msgid "could not run sample-rate converter for %1 samples (%2) (%3)"
msgstr "non riesco a eseguire il convertitore della frequenza di campionamento"
msgid "could not write to file %1 (%2)"
msgstr "non posso scrivere il file (%1)"
-#: src/lib/util.cc:585
+#: src/lib/util.cc:574
msgid "error during async_accept (%1)"
msgstr ""
-#: src/lib/util.cc:561
+#: src/lib/util.cc:550
msgid "error during async_connect (%1)"
msgstr ""
-#: src/lib/util.cc:634
+#: src/lib/util.cc:623
msgid "error during async_read (%1)"
msgstr ""
-#: src/lib/util.cc:606
+#: src/lib/util.cc:595
msgid "error during async_write (%1)"
msgstr ""
-#: src/lib/transcode_job.cc:97
+#: src/lib/transcode_job.cc:98
msgid "frames per second"
msgstr "fotogrammi al secondo"
-#: src/lib/util.cc:156
+#: src/lib/util.cc:158
msgid "hour"
msgstr "ora"
-#: src/lib/util.cc:152 src/lib/util.cc:158
+#: src/lib/util.cc:154 src/lib/util.cc:160
msgid "hours"
msgstr "ore"
-#: src/lib/util.cc:174
+#: src/lib/util.cc:176
msgid "minute"
msgstr "minuto"
-#: src/lib/util.cc:170 src/lib/util.cc:176
+#: src/lib/util.cc:172 src/lib/util.cc:178
msgid "minutes"
msgstr "minuti"
msgid "moving"
msgstr ""
-#: src/lib/ffmpeg_decoder.cc:589
+#: src/lib/ffmpeg_decoder.cc:420
msgid "multi-part subtitles not yet supported"
msgstr "sottotitoli multi-part non ancora supportati"
-#: src/lib/film.cc:259 src/lib/film.cc:319
+#: src/lib/film.cc:268 src/lib/film.cc:328
msgid "name"
msgstr "nome"
-#: src/lib/ffmpeg_decoder.cc:604
-msgid "non-bitmap subtitles not yet supported"
-msgstr "sottotitoli non-bitmap non ancora supportati"
-
#. / TRANSLATORS: remaining here follows an amount of time that is remaining
#. / on an operation.
-#: src/lib/job.cc:315
+#: src/lib/job.cc:317
msgid "remaining"
msgstr "restano"
-#: src/lib/config.cc:84 src/lib/video_content.cc:179
+#: src/lib/config.cc:90 src/lib/video_content.cc:197
msgid "sRGB"
msgstr "sRGB"
-#: src/lib/config.cc:85
+#: src/lib/config.cc:91
msgid "sRGB non-linearised"
msgstr "sRGB non linearizzato"
-#: src/lib/util.cc:189
+#: src/lib/util.cc:191
#, fuzzy
msgid "second"
msgstr "secondi"
-#: src/lib/util.cc:191
+#: src/lib/util.cc:193
msgid "seconds"
msgstr "secondi"
msgid "still"
msgstr "ancora"
-#: src/lib/ffmpeg_examiner.cc:168
+#: src/lib/ffmpeg_examiner.cc:207
#, fuzzy
msgid "unknown"
msgstr "Errore sconosciuto"
+#~ msgid "could not find audio decoder"
+#~ msgstr "non riesco a trovare il decoder audio"
+
+#~ msgid "could not find video decoder"
+#~ msgstr "non riesco a trovare il decoder video"
+
+#~ msgid "non-bitmap subtitles not yet supported"
+#~ msgstr "sottotitoli non-bitmap non ancora supportati"
+
#~ msgid "Cubic interpolating deinterlacer"
#~ msgstr "Deinterlacciatore cubico interpolato"
msgstr ""
"Project-Id-Version: DCP-o-matic\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-09-04 20:34+0100\n"
"Last-Translator: Cherif Ben Brahim <firehc@mac.com>\n"
"Language-Team: UniversalDV <Tkooijmans@universaldv.nl>\n"
"X-Generator: Poedit 1.6.9\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/lib/sndfile_content.cc:61
+#: src/lib/dcp_content.cc:106
+msgid "%1 [DCP]"
+msgstr ""
+
+#: src/lib/sndfile_content.cc:64
msgid "%1 [audio]"
msgstr "%1 [audio]"
-#: src/lib/ffmpeg_content.cc:211
+#: src/lib/ffmpeg_content.cc:205
msgid "%1 [movie]"
msgstr "%1 [film]"
-#: src/lib/sndfile_content.cc:82
+#: src/lib/sndfile_content.cc:85
msgid "%1 channels, %2kHz, %3 samples"
msgstr "%1 kanalen, %2kHz, %3 samples"
-#: src/lib/ffmpeg_content.cc:246
+#: src/lib/ffmpeg_content.cc:240
msgid "%1 frames; %2 frames per second"
msgstr "%1 frames; %2 frames per seconde"
-#: src/lib/video_content.cc:211
+#: src/lib/video_content.cc:235
msgid "%1x%2 pixels (%3:1)"
msgstr "%1x%2 pixels (%3:1)"
msgid "Advertisement"
msgstr "Advertentie"
-#: src/lib/job.cc:71
+#: src/lib/job.cc:72
msgid "An error occurred whilst handling the file %1."
msgstr "Er is een fout opgetreden met bestand %1."
-#: src/lib/analyse_audio_job.cc:48
+#: src/lib/analyse_audio_job.cc:49
msgid "Analyse audio"
msgstr "Analyseer audio"
msgid "Bilinear"
msgstr "Bilinear"
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
msgid "Cancelled"
msgstr "Afgebroken"
msgid "Cannot handle pixel format %1 during %2"
msgstr "Fout met pixel formaat %1 tijdens %2"
-#: src/lib/util.cc:786
+#: src/lib/util.cc:775
msgid "Centre"
msgstr "Midden"
-#: src/lib/writer.cc:83
+#: src/lib/writer.cc:90
msgid "Checking existing image data"
msgstr "Controleer bestaande videodata"
-#: src/lib/writer.cc:475
+#: src/lib/writer.cc:490
msgid "Computing audio digest"
msgstr "Verwerk audio data"
msgid "Computing digest"
msgstr "Verwerken..."
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
msgid "Computing image digest"
msgstr "Verwerk video data"
msgid "Content and DCP have the same rate.\n"
msgstr "Inhoud en DCP hebben dezelfde framerate .\n"
-#: src/lib/audio_content.cc:83
+#: src/lib/subtitle_content.cc:103
+#, fuzzy
+msgid "Content to be joined must have the same 'use subtitles' setting."
+msgstr "Toegevoegde bestanden moeten dezelfde schaal hebben."
+
+#: src/lib/audio_content.cc:100
msgid "Content to be joined must have the same audio delay."
msgstr "Toegevoegde bestanden moeten dezelfde audio vertraging hebben."
-#: src/lib/audio_content.cc:79
+#: src/lib/audio_content.cc:96
msgid "Content to be joined must have the same audio gain."
msgstr "Toegevoegde bestanden moeten dezelfde audio gain hebben."
-#: src/lib/video_content.cc:146
+#: src/lib/video_content.cc:158
msgid "Content to be joined must have the same colour conversion."
msgstr "Toegevoegde bestanden moeten dezelfde kleurindeling hebben."
-#: src/lib/video_content.cc:138
+#: src/lib/video_content.cc:150
msgid "Content to be joined must have the same crop."
msgstr "Toegevoegde bestanden moeten dezelfde crop hebben."
-#: src/lib/video_content.cc:126
+#: src/lib/video_content.cc:162
+#, fuzzy
+msgid "Content to be joined must have the same fades."
+msgstr "Toegevoegde bestanden moeten dezelfde crop hebben."
+
+#: src/lib/video_content.cc:138
msgid "Content to be joined must have the same picture size."
msgstr "Toegevoegde bestanden moeten dezelfde grootte hebben."
-#: src/lib/video_content.cc:142
+#: src/lib/video_content.cc:154
msgid "Content to be joined must have the same scale setting."
msgstr "Toegevoegde bestanden moeten dezelfde schaal hebben."
-#: src/lib/subtitle_content.cc:81
+#: src/lib/subtitle_content.cc:107
msgid "Content to be joined must have the same subtitle X offset."
msgstr "Toegevoegde bestanden moeten dezelfde ondertitel X offset hebben."
-#: src/lib/subtitle_content.cc:89
+#: src/lib/subtitle_content.cc:115
#, fuzzy
msgid "Content to be joined must have the same subtitle X scale."
msgstr "Toegevoegde bestanden moeten dezelfde ondertitel grootte hebben."
-#: src/lib/subtitle_content.cc:85
+#: src/lib/subtitle_content.cc:111
msgid "Content to be joined must have the same subtitle Y offset."
msgstr "Toegevoegde bestanden moeten dezelfde ondertitel Y offset hebben."
-#: src/lib/subtitle_content.cc:93
+#: src/lib/subtitle_content.cc:119
#, fuzzy
msgid "Content to be joined must have the same subtitle Y scale."
msgstr "Toegevoegde bestanden moeten dezelfde ondertitel grootte hebben."
-#: src/lib/video_content.cc:130
+#: src/lib/video_content.cc:142
msgid "Content to be joined must have the same video frame rate."
msgstr "Toegevoegde bestanden moeten dezelfde video frame rate hebben."
-#: src/lib/video_content.cc:134
+#: src/lib/video_content.cc:146
msgid "Content to be joined must have the same video frame type."
msgstr "Toegevoegde bestanden moeten dezelfde video formaat hebben."
-#: src/lib/ffmpeg_content.cc:116
+#: src/lib/ffmpeg_content.cc:118
msgid "Content to be joined must use the same audio stream."
msgstr "Toegevoegde bestanden moeten dezelfde audio stream gebruiken."
-#: src/lib/ffmpeg_content.cc:112
+#: src/lib/ffmpeg_content.cc:114
msgid "Content to be joined must use the same subtitle stream."
msgstr "Toegevoegde bestanden moeten dezelfde ondertitel stream gebruiken."
msgid "Could not create remote directory %1 (%2)"
msgstr "Kan geen remote map maken %1 (%2)"
-#: src/lib/image_proxy.cc:147
+#: src/lib/magick_image_proxy.cc:103
#, fuzzy
msgid "Could not decode image file (%1)"
msgstr "Kan beeldbestand niet decoderen"
-#: src/lib/job.cc:90
+#: src/lib/job.cc:91
msgid "Could not open %1"
msgstr "Kan niet openen %1"
msgid "Could not write to remote file (%1)"
msgstr "Kan niet schrijven naar bestand op FTP server"
+#: src/lib/dcp_subtitle_content.cc:72
+msgid "DCP XML subtitles"
+msgstr ""
+
#: src/lib/frame_rate_change.cc:98
msgid "DCP will run at %1%% of the content speed.\n"
msgstr "DCP renderd met %1%% van de content framerate.\n"
msgid "DCP will use every other frame of the content.\n"
msgstr "DCP zal alleen elk ander frame van de content gebruiken.\n"
-#: src/lib/job.cc:91
+#: src/lib/job.cc:92
msgid ""
"DCP-o-matic could not open the file %1. Perhaps it does not exist or is in "
"an unexpected format."
"DCP-o-matic kan bestand niet openen %1 . Misschien bestaat het niet of wordt "
"het formaat niet ondersteund."
-#: src/lib/ffmpeg_content.cc:93
+#: src/lib/ffmpeg_content.cc:95
msgid ""
"DCP-o-matic no longer supports the `%1' filter, so it has been turned off."
msgstr ""
msgid "De-interlacing"
msgstr "De-interlacing"
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
msgid ""
"Dear Projectionist\n"
"\n"
msgid "Email KDMs for %1"
msgstr "Email KDMs voor %1"
-#: src/lib/writer.cc:126
+#: src/lib/writer.cc:136
msgid "Encoding image data"
msgstr "Encoding bestandsdata"
-#: src/lib/job.cc:320
+#: src/lib/job.cc:322
msgid "Error (%1)"
msgstr "Fout (%1)"
+#: src/lib/exceptions.cc:66
+msgid "Error in SubRip file: saw %1 while expecting %2"
+msgstr ""
+
#: src/lib/examine_content_job.cc:46
msgid "Examine content"
msgstr "Controleer content"
msgid "Gradient debander"
msgstr "Gradient debander"
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
msgid "Hearing impaired"
msgstr "Slechthorenden"
msgid "High quality 3D denoiser"
msgstr "High quality 3D denoiser"
-#: src/lib/job.cc:111 src/lib/job.cc:121
+#: src/lib/job.cc:112 src/lib/job.cc:122
msgid ""
"It is not known what caused this error. Please report the problem to the "
"DCP-o-matic author (carl@dcpomatic.com)."
"Dit is een onbekende fout. U kunt deze het beste melden bij de DCP-o-matic "
"maker (carl@dcpomatic.com)"
-#: src/lib/config.cc:199
+#: src/lib/config.cc:212
msgid "KDM delivery: $CPL_NAME"
msgstr "KDM levering: $CPL_NAME"
msgid "Lanczos"
msgstr "Lanczos"
-#: src/lib/util.cc:784
+#: src/lib/util.cc:773
msgid "Left"
msgstr "Links"
-#: src/lib/util.cc:792
+#: src/lib/util.cc:781
msgid "Left centre"
msgstr "Links midden"
-#: src/lib/util.cc:794
+#: src/lib/util.cc:783
msgid "Left rear surround"
msgstr "Links achter surround"
-#: src/lib/util.cc:788
+#: src/lib/util.cc:777
msgid "Left surround"
msgstr "links surround"
-#: src/lib/util.cc:787
+#: src/lib/util.cc:776
msgid "Lfe (sub)"
msgstr "Lfe (sub)"
+#: src/lib/mid_side_decoder.cc:31
+msgid "Mid-side decoder"
+msgstr ""
+
#: src/lib/filter.cc:68 src/lib/filter.cc:69 src/lib/filter.cc:72
msgid "Misc"
msgstr "Diverse"
+#: src/lib/dcp_examiner.cc:93
+msgid "Mismatched audio channel counts in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:99
+msgid "Mismatched audio frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:74
+msgid "Mismatched frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:81
+msgid "Mismatched video sizes in DCP"
+msgstr ""
+
#: src/lib/filter.cc:65
msgid "Motion compensating deinterlacer"
msgstr "Motion compensating deinterlacer"
msgid "Noise reduction"
msgstr "Ruis reductie"
-#: src/lib/job.cc:318
+#: src/lib/job.cc:320
msgid "OK (ran for %1)"
msgstr "OK (bezig.. %1)"
-#: src/lib/content.cc:102
+#: src/lib/content.cc:106
msgid "Only the first piece of content to be joined can have a start trim."
msgstr ""
"Alleen het eerste deel van de toegevoegde content kan een start trim "
"bevatten."
-#: src/lib/content.cc:106
+#: src/lib/content.cc:110
msgid "Only the last piece of content to be joined can have an end trim."
msgstr ""
"Alleen het laatste deel van de toegevoegde content kan een eind trim "
"bevatten."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "Out of memory"
msgstr "Te weinig geheugen"
msgid "Rating"
msgstr "Beoordeling"
-#: src/lib/config.cc:86 src/lib/config.cc:183
+#: src/lib/config.cc:92 src/lib/config.cc:196
msgid "Rec. 709"
msgstr "Rec. 709"
-#: src/lib/util.cc:785
+#: src/lib/util.cc:774
msgid "Right"
msgstr "Rechts"
-#: src/lib/util.cc:793
+#: src/lib/util.cc:782
msgid "Right centre"
msgstr "Rechts midden"
-#: src/lib/util.cc:795
+#: src/lib/util.cc:784
msgid "Right rear surround"
msgstr "Rechtsachter surround"
-#: src/lib/util.cc:789
+#: src/lib/util.cc:778
msgid "Right surround"
msgstr "Rechts surround"
msgid "Spline"
msgstr "Spline"
+#: src/lib/upmixer_a.cc:42
+msgid "Stereo to 5.1 up-mixer A"
+msgstr ""
+
+#: src/lib/subrip_content.cc:73
+msgid "SubRip subtitles"
+msgstr ""
+
#: src/lib/dcp_content_type.cc:50
msgid "Teaser"
msgstr "Teaser"
msgid "Test"
msgstr "Test"
-#: src/lib/job.cc:77
+#: src/lib/dcp_examiner.cc:133
+msgid ""
+"The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"
+msgstr ""
+
+#: src/lib/exceptions.cc:72
+msgid "The certificate chain for signing is invalid"
+msgstr ""
+
+#: src/lib/job.cc:78
msgid ""
"The drive that the film is stored on is low in disc space. Free some more "
"space and try again."
"De harddisk waar de film is opgeslagen heeft te weinig ruimte. Maak rumte "
"en probeer opnieuw."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "There was not enough memory to do this."
msgstr "Er was niet genoeg geheugen om dit uit te voeren."
-#: src/lib/film.cc:414
+#: src/lib/film.cc:423
msgid ""
"This film was created with a newer version of DCP-o-matic, and it cannot be "
"loaded into this version. Sorry!"
"Deze film is gemaakt met een nieuwere versie van DCP-o-matic en kan niet "
"geopend worden. Sorry!"
-#: src/lib/film.cc:406
+#: src/lib/film.cc:415
msgid ""
"This film was created with an older version of DCP-o-matic, and "
"unfortunately it cannot be loaded into this version. You will need to "
msgid "Trailer"
msgstr "Trailer"
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
msgid "Transcode %1"
msgstr "Omzetten %1"
msgid "Unexpected ZIP file contents"
msgstr "Onverwachte ZIP file inhoud"
-#: src/lib/image_proxy.cc:193
+#: src/lib/image_proxy.cc:56
msgid "Unexpected image type received by server"
msgstr "Onverwacht beeldtype ontvangen door server"
-#: src/lib/job.cc:120
+#: src/lib/job.cc:121
msgid "Unknown error"
msgstr "Onbekende fout"
-#: src/lib/ffmpeg_decoder.cc:293
+#: src/lib/ffmpeg_decoder.cc:263
msgid "Unrecognised audio sample format (%1)"
msgstr "Niet herkenbaar audio sample formaat (%1)"
msgid "Untitled"
msgstr "Niet benoemd"
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
msgid "Visually impaired"
msgstr "Slechtzienden"
msgid "Yet Another Deinterlacing Filter"
msgstr "Yet Another Deinterlacing Filter"
-#: src/lib/film.cc:311
+#: src/lib/film.cc:320
msgid "You must add some content to the DCP before creating it"
msgstr "U moet wat content toevoegen voor het maken van de DCP"
msgid "[still]"
msgstr "[still]"
-#: src/lib/film.cc:259
+#: src/lib/dcp_subtitle_content.cc:66 src/lib/subrip_content.cc:67
+msgid "[subtitles]"
+msgstr ""
+
+#: src/lib/film.cc:268
msgid "cannot contain slashes"
msgstr "er mag geen '\" gebruikt worden"
-#: src/lib/util.cc:565
+#: src/lib/util.cc:554
msgid "connect timed out"
msgstr "verbinding timeout"
msgid "connecting"
msgstr "verbinden"
-#: src/lib/film.cc:307
+#: src/lib/film.cc:316
msgid "container"
msgstr "container"
-#: src/lib/film.cc:315
+#: src/lib/film.cc:324
msgid "content type"
msgstr "content type"
msgid "could not create file %1"
msgstr "kan geen bestand maken %1"
-#: src/lib/ffmpeg.cc:176
-msgid "could not find audio decoder"
-msgstr "kan geen audio decoder vinden"
-
-#: src/lib/ffmpeg.cc:105
+#: src/lib/ffmpeg.cc:102
msgid "could not find stream information"
msgstr "kan geen stream informatie vinden"
-#: src/lib/ffmpeg.cc:155
-msgid "could not find video decoder"
-msgstr "kan geen videodecoder vinden"
-
-#: src/lib/writer.cc:439
+#: src/lib/writer.cc:430
msgid "could not move audio MXF into the DCP (%1)"
msgstr "kan MXF audio niet plaatsen in DCP (%1)"
-#: src/lib/sndfile_decoder.cc:56
+#: src/lib/sndfile_decoder.cc:54
msgid "could not open audio file for reading"
msgstr "kan audio bestand niet openen om te lezen"
msgid "could not open file %1"
msgstr "kan bestand niet openen %1"
-#: src/lib/dcp_video_frame.cc:331
+#: src/lib/encoded_data.cc:50
msgid "could not open file for reading"
msgstr "kan bestand niet openen om te lezen"
-#: src/lib/dcp_video_frame.cc:337
+#: src/lib/encoded_data.cc:56
msgid "could not read encoded data"
msgstr "kan encoded data niet lezen"
msgid "could not read from file %1 (%2)"
msgstr "kan bestand niet lezen %1 (%2)"
-#: src/lib/resampler.cc:98
+#: src/lib/resampler.cc:96
msgid "could not run sample-rate converter"
msgstr "kan sample-rate converter niet starten"
-#: src/lib/resampler.cc:79
+#: src/lib/resampler.cc:77
msgid "could not run sample-rate converter for %1 samples (%2) (%3)"
msgstr "kan sample-rate converter niet starten gedurende %1 samples (%2) (%3)"
msgid "could not write to file %1 (%2)"
msgstr "kan niet schrijven naar bestand %1 (%2)"
-#: src/lib/util.cc:585
+#: src/lib/util.cc:574
msgid "error during async_accept (%1)"
msgstr "fout met async_accepteren (FTP) (%1)"
-#: src/lib/util.cc:561
+#: src/lib/util.cc:550
msgid "error during async_connect (%1)"
msgstr "fout met async_verbinden (FTP) (%1)"
-#: src/lib/util.cc:634
+#: src/lib/util.cc:623
msgid "error during async_read (%1)"
msgstr "fout met async_lezen (FTP) (%1)"
-#: src/lib/util.cc:606
+#: src/lib/util.cc:595
msgid "error during async_write (%1)"
msgstr "fout met async_schrijven (FTP) (%1)"
-#: src/lib/transcode_job.cc:97
+#: src/lib/transcode_job.cc:98
msgid "frames per second"
msgstr "frames per seconde"
-#: src/lib/util.cc:156
+#: src/lib/util.cc:158
msgid "hour"
msgstr "uur"
-#: src/lib/util.cc:152 src/lib/util.cc:158
+#: src/lib/util.cc:154 src/lib/util.cc:160
msgid "hours"
msgstr "uren"
-#: src/lib/util.cc:174
+#: src/lib/util.cc:176
msgid "minute"
msgstr "minuut"
-#: src/lib/util.cc:170 src/lib/util.cc:176
+#: src/lib/util.cc:172 src/lib/util.cc:178
msgid "minutes"
msgstr "minuten"
msgid "moving"
msgstr "bewegend"
-#: src/lib/ffmpeg_decoder.cc:589
+#: src/lib/ffmpeg_decoder.cc:420
msgid "multi-part subtitles not yet supported"
msgstr "ondertitels met meerdere delen worden nog niet ondersteund."
-#: src/lib/film.cc:259 src/lib/film.cc:319
+#: src/lib/film.cc:268 src/lib/film.cc:328
msgid "name"
msgstr "naam"
-#: src/lib/ffmpeg_decoder.cc:604
-msgid "non-bitmap subtitles not yet supported"
-msgstr "non-bitmap ondertitels worden nog niet ondersteund"
-
#. / TRANSLATORS: remaining here follows an amount of time that is remaining
#. / on an operation.
-#: src/lib/job.cc:315
+#: src/lib/job.cc:317
msgid "remaining"
msgstr "resterend"
-#: src/lib/config.cc:84 src/lib/video_content.cc:179
+#: src/lib/config.cc:90 src/lib/video_content.cc:197
msgid "sRGB"
msgstr "sRGB"
-#: src/lib/config.cc:85
+#: src/lib/config.cc:91
msgid "sRGB non-linearised"
msgstr "sRGB non-linearised"
-#: src/lib/util.cc:189
+#: src/lib/util.cc:191
msgid "second"
msgstr "secondes"
-#: src/lib/util.cc:191
+#: src/lib/util.cc:193
msgid "seconds"
msgstr "secondes"
msgid "still"
msgstr "still"
-#: src/lib/ffmpeg_examiner.cc:168
+#: src/lib/ffmpeg_examiner.cc:207
msgid "unknown"
msgstr "onbekend"
+#~ msgid "could not find audio decoder"
+#~ msgstr "kan geen audio decoder vinden"
+
+#~ msgid "could not find video decoder"
+#~ msgstr "kan geen videodecoder vinden"
+
+#~ msgid "non-bitmap subtitles not yet supported"
+#~ msgstr "non-bitmap ondertitels worden nog niet ondersteund"
+
#~ msgid "Could not read DCP to make KDM for"
#~ msgstr "Kan DCP niet lezen om KDM te maken"
msgstr ""
"Project-Id-Version: DCP-o-matic\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-01-19 08:59+0100\n"
"Last-Translator: Adam Klotblixt <adam.klotblixt@gmail.com>\n"
"Language-Team: \n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.3\n"
-#: src/lib/sndfile_content.cc:61
+#: src/lib/dcp_content.cc:106
+msgid "%1 [DCP]"
+msgstr ""
+
+#: src/lib/sndfile_content.cc:64
msgid "%1 [audio]"
msgstr "%1 [ljud]"
-#: src/lib/ffmpeg_content.cc:211
+#: src/lib/ffmpeg_content.cc:205
msgid "%1 [movie]"
msgstr "%1 [film]"
-#: src/lib/sndfile_content.cc:82
+#: src/lib/sndfile_content.cc:85
msgid "%1 channels, %2kHz, %3 samples"
msgstr "%1 kanaler, %2kHz, %3 sampel"
-#: src/lib/ffmpeg_content.cc:246
+#: src/lib/ffmpeg_content.cc:240
msgid "%1 frames; %2 frames per second"
msgstr "%1 bilder; %2 bilder per sekund"
-#: src/lib/video_content.cc:211
+#: src/lib/video_content.cc:235
msgid "%1x%2 pixels (%3:1)"
msgstr "%1x%2 pixlar (%3:1)"
msgid "Advertisement"
msgstr "Advertisement"
-#: src/lib/job.cc:71
+#: src/lib/job.cc:72
msgid "An error occurred whilst handling the file %1."
msgstr "Ett fel inträffade vid hantering av filen %1"
-#: src/lib/analyse_audio_job.cc:48
+#: src/lib/analyse_audio_job.cc:49
msgid "Analyse audio"
msgstr "Analysera audio"
msgid "Bilinear"
msgstr "Bilinjär"
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
msgid "Cancelled"
msgstr "Avbruten"
msgid "Cannot handle pixel format %1 during %2"
msgstr "Kan inte hantera pixelformat %1 under %2"
-#: src/lib/util.cc:786
+#: src/lib/util.cc:775
msgid "Centre"
msgstr "Center"
-#: src/lib/writer.cc:83
+#: src/lib/writer.cc:90
msgid "Checking existing image data"
msgstr "Kontrollerar befintligt bilddata"
-#: src/lib/writer.cc:475
+#: src/lib/writer.cc:490
msgid "Computing audio digest"
msgstr "Beräknar audiosammanfattning"
msgid "Computing digest"
msgstr "Beräknar sammanfattning"
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
msgid "Computing image digest"
msgstr "Beräknar bildsammanfattning"
msgid "Content and DCP have the same rate.\n"
msgstr "Källa och DCP har samma bildfrekvens.\n"
-#: src/lib/audio_content.cc:83
+#: src/lib/subtitle_content.cc:103
+#, fuzzy
+msgid "Content to be joined must have the same 'use subtitles' setting."
+msgstr "Innehåll som ska sammanfogas måste använda samma bildförhållande."
+
+#: src/lib/audio_content.cc:100
msgid "Content to be joined must have the same audio delay."
msgstr "Innehåll som ska sammanfogas måste använda samma audiofördröjning."
-#: src/lib/audio_content.cc:79
+#: src/lib/audio_content.cc:96
msgid "Content to be joined must have the same audio gain."
msgstr "Innehåll som ska sammanfogas måste använda samma audioförstärkning."
-#: src/lib/video_content.cc:146
+#: src/lib/video_content.cc:158
msgid "Content to be joined must have the same colour conversion."
msgstr "Innehåll som ska sammanfogas måste använda samma färgkonvertering."
-#: src/lib/video_content.cc:138
+#: src/lib/video_content.cc:150
msgid "Content to be joined must have the same crop."
msgstr "Innehåll som ska sammanfogas måste använda samma beskärning."
-#: src/lib/video_content.cc:126
+#: src/lib/video_content.cc:162
+#, fuzzy
+msgid "Content to be joined must have the same fades."
+msgstr "Innehåll som ska sammanfogas måste använda samma beskärning."
+
+#: src/lib/video_content.cc:138
msgid "Content to be joined must have the same picture size."
msgstr "Innehåll som ska sammanfogas måste använda samma bildstorlek."
-#: src/lib/video_content.cc:142
+#: src/lib/video_content.cc:154
#, fuzzy
msgid "Content to be joined must have the same scale setting."
msgstr "Innehåll som ska sammanfogas måste använda samma bildförhållande."
-#: src/lib/subtitle_content.cc:81
+#: src/lib/subtitle_content.cc:107
#, fuzzy
msgid "Content to be joined must have the same subtitle X offset."
msgstr ""
"Innehåll som ska sammanfogas måste använda samma förskjutning på undertexten."
-#: src/lib/subtitle_content.cc:89
+#: src/lib/subtitle_content.cc:115
#, fuzzy
msgid "Content to be joined must have the same subtitle X scale."
msgstr "Innehåll som ska sammanfogas måste använda samma skala på undertexten."
-#: src/lib/subtitle_content.cc:85
+#: src/lib/subtitle_content.cc:111
#, fuzzy
msgid "Content to be joined must have the same subtitle Y offset."
msgstr ""
"Innehåll som ska sammanfogas måste använda samma förskjutning på undertexten."
-#: src/lib/subtitle_content.cc:93
+#: src/lib/subtitle_content.cc:119
#, fuzzy
msgid "Content to be joined must have the same subtitle Y scale."
msgstr "Innehåll som ska sammanfogas måste använda samma skala på undertexten."
-#: src/lib/video_content.cc:130
+#: src/lib/video_content.cc:142
msgid "Content to be joined must have the same video frame rate."
msgstr "Innehåll som ska sammanfogas måste använda samma videobildhastighet."
-#: src/lib/video_content.cc:134
+#: src/lib/video_content.cc:146
msgid "Content to be joined must have the same video frame type."
msgstr "Innehåll som ska sammanfogas måste använda samma videobildtyp."
-#: src/lib/ffmpeg_content.cc:116
+#: src/lib/ffmpeg_content.cc:118
msgid "Content to be joined must use the same audio stream."
msgstr "Innehåll som ska sammanfogas måste använda samma audioström."
-#: src/lib/ffmpeg_content.cc:112
+#: src/lib/ffmpeg_content.cc:114
msgid "Content to be joined must use the same subtitle stream."
msgstr "Innehåll som ska sammanfogas måste använda samma undertextström."
msgid "Could not create remote directory %1 (%2)"
msgstr "Kunde inte skapa fjärrkatalog %1 (%2)"
-#: src/lib/image_proxy.cc:147
+#: src/lib/magick_image_proxy.cc:103
#, fuzzy
msgid "Could not decode image file (%1)"
msgstr "kunde inte skapa fil %1"
-#: src/lib/job.cc:90
+#: src/lib/job.cc:91
msgid "Could not open %1"
msgstr "Kunde inte öppna %1"
msgid "Could not write to remote file (%1)"
msgstr "Kunde inte skriva till fjärrfil (%1)"
+#: src/lib/dcp_subtitle_content.cc:72
+msgid "DCP XML subtitles"
+msgstr ""
+
#: src/lib/frame_rate_change.cc:98
msgid "DCP will run at %1%% of the content speed.\n"
msgstr "DCP kommer att köras på %1%% av källans hastighet.\n"
msgid "DCP will use every other frame of the content.\n"
msgstr "DCP kommer att använda varannan bild från källan.\n"
-#: src/lib/job.cc:91
+#: src/lib/job.cc:92
msgid ""
"DCP-o-matic could not open the file %1. Perhaps it does not exist or is in "
"an unexpected format."
"DCP-o-matic kunde inte öppna filen %1. Saknas den, eller har den ett "
"oförväntat format?"
-#: src/lib/ffmpeg_content.cc:93
+#: src/lib/ffmpeg_content.cc:95
msgid ""
"DCP-o-matic no longer supports the `%1' filter, so it has been turned off."
msgstr ""
msgstr "Avflätning"
# svåröversatt
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
#, fuzzy
msgid ""
"Dear Projectionist\n"
msgid "Email KDMs for %1"
msgstr "E-posta KDM:er för %1"
-#: src/lib/writer.cc:126
+#: src/lib/writer.cc:136
msgid "Encoding image data"
msgstr "Kodar bild-data"
-#: src/lib/job.cc:320
+#: src/lib/job.cc:322
msgid "Error (%1)"
msgstr "Fel (%1)"
+#: src/lib/exceptions.cc:66
+msgid "Error in SubRip file: saw %1 while expecting %2"
+msgstr ""
+
#: src/lib/examine_content_job.cc:46
msgid "Examine content"
msgstr "Undersök innehållet"
msgid "Gradient debander"
msgstr "Gradientutjämnare"
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
msgid "Hearing impaired"
msgstr ""
msgid "High quality 3D denoiser"
msgstr "Högkvalitets 3D-brusreducering"
-#: src/lib/job.cc:111 src/lib/job.cc:121
+#: src/lib/job.cc:112 src/lib/job.cc:122
#, fuzzy
msgid ""
"It is not known what caused this error. Please report the problem to the "
"Det är inte känt vad som orsakade detta fel. Bästa sättet att rapportera "
"problemet är till DCP-o-matics mejl-lista (carl@dcpomatic.com)"
-#: src/lib/config.cc:199
+#: src/lib/config.cc:212
msgid "KDM delivery: $CPL_NAME"
msgstr ""
msgid "Lanczos"
msgstr "Lanczos"
-#: src/lib/util.cc:784
+#: src/lib/util.cc:773
msgid "Left"
msgstr "Vänster"
-#: src/lib/util.cc:792
+#: src/lib/util.cc:781
msgid "Left centre"
msgstr ""
-#: src/lib/util.cc:794
+#: src/lib/util.cc:783
#, fuzzy
msgid "Left rear surround"
msgstr "Vänster surround"
-#: src/lib/util.cc:788
+#: src/lib/util.cc:777
msgid "Left surround"
msgstr "Vänster surround"
-#: src/lib/util.cc:787
+#: src/lib/util.cc:776
msgid "Lfe (sub)"
msgstr "Lfe (sub)"
+#: src/lib/mid_side_decoder.cc:31
+msgid "Mid-side decoder"
+msgstr ""
+
#: src/lib/filter.cc:68 src/lib/filter.cc:69 src/lib/filter.cc:72
msgid "Misc"
msgstr "Diverse"
+#: src/lib/dcp_examiner.cc:93
+msgid "Mismatched audio channel counts in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:99
+msgid "Mismatched audio frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:74
+msgid "Mismatched frame rates in DCP"
+msgstr ""
+
+#: src/lib/dcp_examiner.cc:81
+msgid "Mismatched video sizes in DCP"
+msgstr ""
+
#: src/lib/filter.cc:65
msgid "Motion compensating deinterlacer"
msgstr "Rörelsekompenserande avflätare"
msgid "Noise reduction"
msgstr "Brusreducering"
-#: src/lib/job.cc:318
+#: src/lib/job.cc:320
msgid "OK (ran for %1)"
msgstr "OK (kördes %1)"
-#: src/lib/content.cc:102
+#: src/lib/content.cc:106
msgid "Only the first piece of content to be joined can have a start trim."
msgstr ""
"Endast den första delen av innehållet som läggs ihop kan start-trimmas."
-#: src/lib/content.cc:106
+#: src/lib/content.cc:110
msgid "Only the last piece of content to be joined can have an end trim."
msgstr "Endast den sista delen av innehållet som läggs ihop kan slut-trimmas."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "Out of memory"
msgstr ""
msgid "Rating"
msgstr "Rating"
-#: src/lib/config.cc:86 src/lib/config.cc:183
+#: src/lib/config.cc:92 src/lib/config.cc:196
msgid "Rec. 709"
msgstr "Rec. 709"
-#: src/lib/util.cc:785
+#: src/lib/util.cc:774
msgid "Right"
msgstr "Höger"
-#: src/lib/util.cc:793
+#: src/lib/util.cc:782
msgid "Right centre"
msgstr ""
-#: src/lib/util.cc:795
+#: src/lib/util.cc:784
#, fuzzy
msgid "Right rear surround"
msgstr "Höger surround"
-#: src/lib/util.cc:789
+#: src/lib/util.cc:778
msgid "Right surround"
msgstr "Höger surround"
msgid "Spline"
msgstr "Spline"
+#: src/lib/upmixer_a.cc:42
+msgid "Stereo to 5.1 up-mixer A"
+msgstr ""
+
+#: src/lib/subrip_content.cc:73
+msgid "SubRip subtitles"
+msgstr ""
+
#: src/lib/dcp_content_type.cc:50
msgid "Teaser"
msgstr "Teaser"
msgid "Test"
msgstr "Test"
-#: src/lib/job.cc:77
+#: src/lib/dcp_examiner.cc:133
+msgid ""
+"The KDM does not decrypt the DCP. Perhaps it is targeted at the wrong CPL"
+msgstr ""
+
+#: src/lib/exceptions.cc:72
+msgid "The certificate chain for signing is invalid"
+msgstr ""
+
+#: src/lib/job.cc:78
msgid ""
"The drive that the film is stored on is low in disc space. Free some more "
"space and try again."
"Enheten som filmen lagras på har för lite ledigt utrymme. Frigör utrymme och "
"försök igen."
-#: src/lib/job.cc:103
+#: src/lib/job.cc:104
msgid "There was not enough memory to do this."
msgstr ""
-#: src/lib/film.cc:414
+#: src/lib/film.cc:423
#, fuzzy
msgid ""
"This film was created with a newer version of DCP-o-matic, and it cannot be "
"inte öppnas i denna version. Du måste skapa en ny Film, lägga till ditt "
"innehåll och konfigurera allt igen. Ursäkta!"
-#: src/lib/film.cc:406
+#: src/lib/film.cc:415
msgid ""
"This film was created with an older version of DCP-o-matic, and "
"unfortunately it cannot be loaded into this version. You will need to "
msgid "Trailer"
msgstr "Trailer"
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
msgid "Transcode %1"
msgstr "Konvertera %1"
msgid "Unexpected ZIP file contents"
msgstr ""
-#: src/lib/image_proxy.cc:193
+#: src/lib/image_proxy.cc:56
msgid "Unexpected image type received by server"
msgstr ""
-#: src/lib/job.cc:120
+#: src/lib/job.cc:121
msgid "Unknown error"
msgstr "Okänt fel"
# Svengelska
-#: src/lib/ffmpeg_decoder.cc:293
+#: src/lib/ffmpeg_decoder.cc:263
msgid "Unrecognised audio sample format (%1)"
msgstr "Okänt audio-sampelformat (%1)"
msgid "Untitled"
msgstr "Utan titel"
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
msgid "Visually impaired"
msgstr ""
msgid "Yet Another Deinterlacing Filter"
msgstr "Yet Another Deinterlacing Filter"
-#: src/lib/film.cc:311
+#: src/lib/film.cc:320
msgid "You must add some content to the DCP before creating it"
msgstr "Du måste lägga till något innehåll till DCP:n innan du skapar den"
msgid "[still]"
msgstr "[stillbild]"
-#: src/lib/film.cc:259
+#: src/lib/dcp_subtitle_content.cc:66 src/lib/subrip_content.cc:67
+msgid "[subtitles]"
+msgstr ""
+
+#: src/lib/film.cc:268
msgid "cannot contain slashes"
msgstr "får inte innehålla snedstreck"
# Svengelska
-#: src/lib/util.cc:565
+#: src/lib/util.cc:554
msgid "connect timed out"
msgstr "uppkopplingen tajmade ur"
msgid "connecting"
msgstr "kopplar upp"
-#: src/lib/film.cc:307
+#: src/lib/film.cc:316
msgid "container"
msgstr "behållare"
-#: src/lib/film.cc:315
+#: src/lib/film.cc:324
msgid "content type"
msgstr "innehållstyp"
msgid "could not create file %1"
msgstr "kunde inte skapa fil %1"
-#: src/lib/ffmpeg.cc:176
-msgid "could not find audio decoder"
-msgstr "kunde inte hitta audio-avkodare"
-
-#: src/lib/ffmpeg.cc:105
+#: src/lib/ffmpeg.cc:102
msgid "could not find stream information"
msgstr "kunde inte hitta information om strömmen"
-#: src/lib/ffmpeg.cc:155
-msgid "could not find video decoder"
-msgstr "kunde inte hitta video-avkodare"
-
-#: src/lib/writer.cc:439
+#: src/lib/writer.cc:430
msgid "could not move audio MXF into the DCP (%1)"
msgstr "kunde inte flytta audio-MXF in i DCP:n (%1)"
-#: src/lib/sndfile_decoder.cc:56
+#: src/lib/sndfile_decoder.cc:54
msgid "could not open audio file for reading"
msgstr "kunde inte öppna audio-fil för läsning"
msgid "could not open file %1"
msgstr "kunde inte öppna fil %1"
-#: src/lib/dcp_video_frame.cc:331
+#: src/lib/encoded_data.cc:50
msgid "could not open file for reading"
msgstr "kunde inte öppna fil för läsning"
-#: src/lib/dcp_video_frame.cc:337
+#: src/lib/encoded_data.cc:56
msgid "could not read encoded data"
msgstr "kunde inte läsa kodat data"
msgid "could not read from file %1 (%2)"
msgstr "kunde inte läsa från fil %1 (%2)"
-#: src/lib/resampler.cc:98
+#: src/lib/resampler.cc:96
msgid "could not run sample-rate converter"
msgstr "kunde inte köra sampelhastighetskonverteraren"
-#: src/lib/resampler.cc:79
+#: src/lib/resampler.cc:77
msgid "could not run sample-rate converter for %1 samples (%2) (%3)"
msgstr ""
"kunde inte köra sampelhastighetskonverteraren under %1 sampel (%2) (%3)"
msgid "could not write to file %1 (%2)"
msgstr "kunde inte skriva till fil %1 (%2)"
-#: src/lib/util.cc:585
+#: src/lib/util.cc:574
msgid "error during async_accept (%1)"
msgstr "fel vid async_accept (%1)"
-#: src/lib/util.cc:561
+#: src/lib/util.cc:550
msgid "error during async_connect (%1)"
msgstr "fel vid async_connect (%1)"
-#: src/lib/util.cc:634
+#: src/lib/util.cc:623
msgid "error during async_read (%1)"
msgstr "fel vid async_read (%1)"
-#: src/lib/util.cc:606
+#: src/lib/util.cc:595
msgid "error during async_write (%1)"
msgstr "fel vid async_write (%1)"
-#: src/lib/transcode_job.cc:97
+#: src/lib/transcode_job.cc:98
msgid "frames per second"
msgstr "bilder per sekund"
-#: src/lib/util.cc:156
+#: src/lib/util.cc:158
msgid "hour"
msgstr "timme"
-#: src/lib/util.cc:152 src/lib/util.cc:158
+#: src/lib/util.cc:154 src/lib/util.cc:160
msgid "hours"
msgstr "timmar"
-#: src/lib/util.cc:174
+#: src/lib/util.cc:176
msgid "minute"
msgstr "minut"
-#: src/lib/util.cc:170 src/lib/util.cc:176
+#: src/lib/util.cc:172 src/lib/util.cc:178
msgid "minutes"
msgstr "minuter"
msgid "moving"
msgstr "rörlig"
-#: src/lib/ffmpeg_decoder.cc:589
+#: src/lib/ffmpeg_decoder.cc:420
msgid "multi-part subtitles not yet supported"
msgstr "undertexter i flera delar stöds inte ännu"
-#: src/lib/film.cc:259 src/lib/film.cc:319
+#: src/lib/film.cc:268 src/lib/film.cc:328
msgid "name"
msgstr "namn"
-#: src/lib/ffmpeg_decoder.cc:604
-msgid "non-bitmap subtitles not yet supported"
-msgstr "icke-rastergrafiska undertexter stöds inte ännu"
-
#. / TRANSLATORS: remaining here follows an amount of time that is remaining
#. / on an operation.
-#: src/lib/job.cc:315
+#: src/lib/job.cc:317
msgid "remaining"
msgstr "återstående tid"
-#: src/lib/config.cc:84 src/lib/video_content.cc:179
+#: src/lib/config.cc:90 src/lib/video_content.cc:197
msgid "sRGB"
msgstr "sRGB"
-#: src/lib/config.cc:85
+#: src/lib/config.cc:91
msgid "sRGB non-linearised"
msgstr "sRGB icke-linjär"
-#: src/lib/util.cc:189
+#: src/lib/util.cc:191
#, fuzzy
msgid "second"
msgstr "sekunder"
-#: src/lib/util.cc:191
+#: src/lib/util.cc:193
msgid "seconds"
msgstr "sekunder"
msgid "still"
msgstr "stillbild"
-#: src/lib/ffmpeg_examiner.cc:168
+#: src/lib/ffmpeg_examiner.cc:207
msgid "unknown"
msgstr "okänd"
+#~ msgid "could not find audio decoder"
+#~ msgstr "kunde inte hitta audio-avkodare"
+
+#~ msgid "could not find video decoder"
+#~ msgstr "kunde inte hitta video-avkodare"
+
+#~ msgid "non-bitmap subtitles not yet supported"
+#~ msgstr "icke-rastergrafiska undertexter stöds inte ännu"
+
#~ msgid "Could not read DCP to make KDM for"
#~ msgstr "Kunde inte läsa DCP för att skapa KDM"
/*
- 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
#define DCPOMATIC_POSITION_H
/** @struct Position
- * @brief A position.
+ * @brief A position (x and y coordinates)
*/
template <class T>
class Position
return Position<T> (a.x + b.x, a.y + b.y);
}
+template<class T>
+Position<T>
+operator- (Position<T> const & a, Position<T> const & b)
+{
+ return Position<T> (a.x - b.x, a.y - b.y);
+}
+
+template<class T>
+bool
+operator== (Position<T> const & a, Position<T> const & b)
+{
+ return a.x == b.x && a.y == b.y;
+}
+
+template<class T>
+bool
+operator!= (Position<T> const & a, Position<T> const & b)
+{
+ return a.x != b.x || a.y != b.y;
+}
+
#endif
--- /dev/null
+/*
+ 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 "position_image.h"
+#include "image.h"
+
+using std::cout;
+
+bool
+PositionImage::same (PositionImage const & other) const
+{
+ if (image != other.image || position != other.position) {
+ return false;
+ }
+
+ if (!image) {
+ return true;
+ }
+
+ return *image == *(other.image);
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_POSITION_IMAGE_H
+#define DCPOMATIC_POSITION_IMAGE_H
+
+#include "position.h"
+#include <boost/shared_ptr.hpp>
+
+class Image;
+
+class PositionImage
+{
+public:
+ PositionImage () {}
+
+ PositionImage (boost::shared_ptr<Image> i, Position<int> p)
+ : image (i)
+ , position (p)
+ {}
+
+ boost::shared_ptr<Image> image;
+ Position<int> position;
+
+ bool same (PositionImage const & other) const;
+};
+
+#endif
*/
-#include <libdcp/types.h>
+#include <dcp/types.h>
#include "ratio.h"
#include "util.h"
#include <vector>
#include <boost/utility.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
class Ratio : public boost::noncopyable
{
--- /dev/null
+/*
+ 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.
+
+*/
+
+extern "C" {
+#include <libavutil/pixfmt.h>
+}
+#include <libcxml/cxml.h>
+#include <dcp/util.h>
+#include <dcp/raw_convert.h>
+#include "raw_image_proxy.h"
+#include "image.h"
+
+#include "i18n.h"
+
+using std::string;
+using boost::shared_ptr;
+
+RawImageProxy::RawImageProxy (shared_ptr<Image> image, shared_ptr<Log> log)
+ : ImageProxy (log)
+ , _image (image)
+{
+
+}
+
+RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
+ : ImageProxy (log)
+{
+ dcp::Size size (
+ xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
+ );
+
+ _image.reset (new Image (static_cast<AVPixelFormat> (xml->number_child<int> ("PixelFormat")), size, true));
+ _image->read_from_socket (socket);
+}
+
+shared_ptr<Image>
+RawImageProxy::image () const
+{
+ return _image;
+}
+
+void
+RawImageProxy::add_metadata (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text (N_("Raw"));
+ node->add_child("Width")->add_child_text (dcp::raw_convert<string> (_image->size().width));
+ node->add_child("Height")->add_child_text (dcp::raw_convert<string> (_image->size().height));
+ node->add_child("PixelFormat")->add_child_text (dcp::raw_convert<string> (_image->pixel_format ()));
+}
+
+void
+RawImageProxy::send_binary (shared_ptr<Socket> socket) const
+{
+ _image->write_to_socket (socket);
+}
--- /dev/null
+/*
+ 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 "image_proxy.h"
+
+class RawImageProxy : public ImageProxy
+{
+public:
+ RawImageProxy (boost::shared_ptr<Image>, boost::shared_ptr<Log> log);
+ RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
+
+ boost::shared_ptr<Image> image () const;
+ void add_metadata (xmlpp::Node *) const;
+ void send_binary (boost::shared_ptr<Socket>) const;
+
+private:
+ boost::shared_ptr<Image> _image;
+};
, height (0)
{}
+ Rect (Position<T> p, T w_, T h_)
+ : x (p.x)
+ , y (p.y)
+ , width (w_)
+ , height (h_)
+ {}
+
Rect (T x_, T y_, T w_, T h_)
: x (x_)
, y (y_)
T width;
T height;
- Position<T> position () const {
+ Position<T> position () const
+ {
return Position<T> (x, y);
}
- Rect<T> intersection (Rect<T> const & other) const {
+ Rect<T> intersection (Rect<T> const & other) const
+ {
T const tx = max (x, other.x);
T const ty = max (y, other.y);
);
}
- bool contains (Position<T> p) const {
+ void extend (Rect<T> const & other)
+ {
+ x = std::min (x, other.x);
+ y = std::min (y, other.y);
+ width = std::max (x + width, other.x + other.width) - x;
+ height = std::max (y + height, other.y + other.height) - y;
+ }
+
+ bool contains (Position<T> p) const
+ {
return (p.x >= x && p.x <= (x + width) && p.y >= y && p.y <= (y + height));
}
};
--- /dev/null
+/*
+ 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 <cairomm/cairomm.h>
+#include <pangomm.h>
+#include "render_subtitles.h"
+#include "types.h"
+#include "image.h"
+
+using std::list;
+using std::cout;
+using std::string;
+using std::min;
+using std::max;
+using std::pair;
+using boost::shared_ptr;
+using boost::optional;
+
+static int
+calculate_position (dcp::VAlign v_align, double v_position, int target_height, int offset)
+{
+ switch (v_align) {
+ case dcp::TOP:
+ return v_position * target_height - offset;
+ case dcp::CENTER:
+ return (0.5 + v_position) * target_height - offset;
+ case dcp::BOTTOM:
+ return (1.0 - v_position) * target_height - offset;
+ }
+
+ return 0;
+}
+
+PositionImage
+render_subtitles (list<dcp::SubtitleString> subtitles, dcp::Size target)
+{
+ if (subtitles.empty ()) {
+ return PositionImage ();
+ }
+
+ /* Estimate height that the subtitle image needs to be */
+ optional<int> top;
+ optional<int> bottom;
+ for (list<dcp::SubtitleString>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
+ int const b = calculate_position (i->v_align(), i->v_position(), target.height, 0);
+ int const t = b - i->size() * target.height / (11 * 72);
+
+ top = min (top.get_value_or (t), t);
+ bottom = max (bottom.get_value_or (b), b);
+ }
+
+ top = top.get() - 32;
+ bottom = bottom.get() + 32;
+
+ shared_ptr<Image> image (new Image (PIX_FMT_RGBA, dcp::Size (target.width, bottom.get() - top.get ()), false));
+ image->make_black ();
+
+ Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create (
+ image->data()[0],
+ Cairo::FORMAT_ARGB32,
+ image->size().width,
+ image->size().height,
+ Cairo::ImageSurface::format_stride_for_width (Cairo::FORMAT_ARGB32, image->size().width)
+ );
+
+ Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (surface);
+ Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (context);
+
+ layout->set_width (image->size().width * PANGO_SCALE);
+ layout->set_alignment (Pango::ALIGN_CENTER);
+
+ context->set_line_width (1);
+
+ for (list<dcp::SubtitleString>::const_iterator i = subtitles.begin(); i != subtitles.end(); ++i) {
+ string f = i->font ();
+ if (f.empty ()) {
+ f = "Arial";
+ }
+ Pango::FontDescription font (f);
+ font.set_absolute_size (i->size_in_pixels (target.height) * PANGO_SCALE);
+ if (i->italic ()) {
+ font.set_style (Pango::STYLE_ITALIC);
+ }
+ layout->set_font_description (font);
+ layout->set_text (i->text ());
+
+ /* Compute fade factor */
+ /* XXX */
+ float fade_factor = 1;
+#if 0
+ dcp::Time now (time * 1000 / (4 * TIME_HZ));
+ dcp::Time end_fade_up = i->in() + i->fade_up_time ();
+ dcp::Time start_fade_down = i->out() - i->fade_down_time ();
+ if (now < end_fade_up) {
+ fade_factor = (now - i->in()) / i->fade_up_time();
+ } else if (now > start_fade_down) {
+ fade_factor = 1.0 - ((now - start_fade_down) / i->fade_down_time ());
+ }
+#endif
+
+ layout->update_from_cairo_context (context);
+
+ /* Work out position */
+
+ int const x = 0;
+ int const y = calculate_position (i->v_align (), i->v_position (), target.height, (layout->get_baseline() / PANGO_SCALE) + top.get ());
+
+ if (i->effect() == dcp::SHADOW) {
+ /* Drop-shadow effect */
+ dcp::Color const ec = i->effect_color ();
+ context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor);
+ context->move_to (x + 4, y + 4);
+ layout->add_to_cairo_context (context);
+ context->fill ();
+ }
+
+ /* The actual subtitle */
+ context->move_to (x, y);
+ dcp::Color const c = i->color ();
+ context->set_source_rgba (float(c.r) / 255, float(c.g) / 255, float(c.b) / 255, fade_factor);
+ layout->add_to_cairo_context (context);
+ context->fill ();
+
+ if (i->effect() == dcp::BORDER) {
+ /* Border effect */
+ context->move_to (x, y);
+ dcp::Color ec = i->effect_color ();
+ context->set_source_rgba (float(ec.r) / 255, float(ec.g) / 255, float(ec.b) / 255, fade_factor);
+ layout->add_to_cairo_context (context);
+ context->stroke ();
+ }
+ }
+
+ return PositionImage (image, Position<int> (0, top.get ()));
+}
+
--- /dev/null
+/*
+ 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 <dcp/subtitle_string.h>
+#include <dcp/util.h>
+#include "position_image.h"
+
+PositionImage render_subtitles (std::list<dcp::SubtitleString>, dcp::Size);
swr_free (&_swr_context);
}
-pair<shared_ptr<const AudioBuffers>, AudioContent::Frame>
-Resampler::run (shared_ptr<const AudioBuffers> in, AudioContent::Frame frame)
+shared_ptr<const AudioBuffers>
+Resampler::run (shared_ptr<const AudioBuffers> in)
{
- AudioContent::Frame const resamp_time = swr_next_pts (_swr_context, frame * _out_rate) / _in_rate;
-
/* Compute the resampled frames count and add 32 for luck */
int const max_resampled_frames = ceil ((double) in->frames() * _out_rate / _in_rate) + 32;
shared_ptr<AudioBuffers> resampled (new AudioBuffers (_channels, max_resampled_frames));
}
resampled->set_frames (resampled_frames);
- return make_pair (resampled, resamp_time);
+ return resampled;
}
shared_ptr<const AudioBuffers>
Resampler (int, int, int);
~Resampler ();
- std::pair<boost::shared_ptr<const AudioBuffers>, AudioContent::Frame> run (boost::shared_ptr<const AudioBuffers>, AudioContent::Frame);
+ boost::shared_ptr<const AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
boost::shared_ptr<const AudioBuffers> flush ();
private:
_stream.width (w);
}
+ void fill (int f)
+ {
+ _stream.fill (f);
+ }
+
void precision (int p)
{
_stream.precision (p);
boost::filesystem::path dcp,
boost::posix_time::ptime from,
boost::posix_time::ptime to,
- libdcp::KDM::Formulation formulation
+ dcp::Formulation formulation
)
: Job (f)
, _screens (screens)
*/
#include <boost/filesystem.hpp>
-#include <libdcp/kdm.h>
+#include <dcp/types.h>
#include "job.h"
class Screen;
boost::filesystem::path,
boost::posix_time::ptime,
boost::posix_time::ptime,
- libdcp::KDM::Formulation
+ dcp::Formulation
);
std::string name () const;
boost::filesystem::path _dcp;
boost::posix_time::ptime _from;
boost::posix_time::ptime _to;
- libdcp::KDM::Formulation _formulation;
+ dcp::Formulation _formulation;
};
#include <boost/algorithm/string.hpp>
#include <boost/scoped_array.hpp>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "server.h"
#include "util.h"
#include "scaler.h"
#include "image.h"
-#include "dcp_video_frame.h"
+#include "dcp_video.h"
#include "config.h"
#include "cross.h"
-#include "player_video_frame.h"
+#include "player_video.h"
+#include "encoded_data.h"
#include "safe_stringstream.h"
#include "i18n.h"
using boost::bind;
using boost::scoped_array;
using boost::optional;
-using libdcp::Size;
-using libdcp::raw_convert;
+using dcp::Size;
+using dcp::raw_convert;
Server::Server (shared_ptr<Log> log, bool verbose)
- : _log (log)
+ : _terminate (false)
+ , _log (log)
, _verbose (verbose)
+ , _acceptor (_io_service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), Config::instance()->server_port_base()))
{
}
+Server::~Server ()
+{
+ {
+ boost::mutex::scoped_lock lm (_worker_mutex);
+ _terminate = true;
+ _empty_condition.notify_all ();
+ }
+
+ for (vector<boost::thread*>::iterator i = _worker_threads.begin(); i != _worker_threads.end(); ++i) {
+ (*i)->join ();
+ delete *i;
+ }
+
+ _io_service.stop ();
+
+ _broadcast.io_service.stop ();
+ _broadcast.thread->join ();
+}
+
/** @param after_read Filled in with gettimeofday() after reading the input from the network.
* @param after_encode Filled in with gettimeofday() after encoding the image.
*/
return -1;
}
- shared_ptr<PlayerVideoFrame> pvf (new PlayerVideoFrame (xml, socket, _log));
+ shared_ptr<PlayerVideo> pvf (new PlayerVideo (xml, socket, _log));
- DCPVideoFrame dcp_video_frame (pvf, xml, _log);
+ DCPVideo dcp_video_frame (pvf, xml, _log);
gettimeofday (&after_read, 0);
{
while (true) {
boost::mutex::scoped_lock lock (_worker_mutex);
- while (_queue.empty ()) {
+ while (_queue.empty () && !_terminate) {
_empty_condition.wait (lock);
}
+ if (_terminate) {
+ return;
+ }
+
shared_ptr<Socket> socket = _queue.front ();
_queue.pop_front ();
_broadcast.thread = new thread (bind (&Server::broadcast_thread, this));
- boost::asio::io_service io_service;
-
- boost::asio::ip::tcp::acceptor acceptor (
- io_service,
- boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), Config::instance()->server_port_base ())
- );
-
- while (true) {
- shared_ptr<Socket> socket (new Socket);
- acceptor.accept (socket->socket ());
-
- boost::mutex::scoped_lock lock (_worker_mutex);
-
- /* Wait until the queue has gone down a bit */
- while (int (_queue.size()) >= num_threads * 2) {
- _full_condition.wait (lock);
- }
-
- _queue.push_back (socket);
- _empty_condition.notify_all ();
- }
+ start_accept ();
+ _io_service.run ();
}
void
Server::broadcast_thread ()
try
{
- boost::asio::io_service io_service;
-
boost::asio::ip::address address = boost::asio::ip::address_v4::any ();
boost::asio::ip::udp::endpoint listen_endpoint (address, Config::instance()->server_port_base() + 1);
- _broadcast.socket = new boost::asio::ip::udp::socket (io_service);
+ _broadcast.socket = new boost::asio::ip::udp::socket (_broadcast.io_service);
_broadcast.socket->open (listen_endpoint.protocol ());
_broadcast.socket->bind (listen_endpoint);
boost::bind (&Server::broadcast_received, this)
);
- io_service.run ();
+ _broadcast.io_service.run ();
}
catch (...)
{
_broadcast.send_endpoint, boost::bind (&Server::broadcast_received, this)
);
}
+
+void
+Server::start_accept ()
+{
+ if (_terminate) {
+ return;
+ }
+
+ shared_ptr<Socket> socket (new Socket);
+ _acceptor.async_accept (socket->socket (), boost::bind (&Server::handle_accept, this, socket, boost::asio::placeholders::error));
+}
+
+void
+Server::handle_accept (shared_ptr<Socket> socket, boost::system::error_code const & error)
+{
+ if (error) {
+ return;
+ }
+
+ boost::mutex::scoped_lock lock (_worker_mutex);
+
+ /* Wait until the queue has gone down a bit */
+ while (_queue.size() >= _worker_threads.size() * 2 && !_terminate) {
+ _full_condition.wait (lock);
+ }
+
+ _queue.push_back (socket);
+ _empty_condition.notify_all ();
+
+ start_accept ();
+}
+
{
public:
Server (boost::shared_ptr<Log> log, bool verbose);
+ ~Server ();
void run (int num_threads);
int process (boost::shared_ptr<Socket> socket, struct timeval &, struct timeval &);
void broadcast_thread ();
void broadcast_received ();
+ void start_accept ();
+ void handle_accept (boost::shared_ptr<Socket>, boost::system::error_code const &);
+
+ bool _terminate;
std::vector<boost::thread *> _worker_threads;
std::list<boost::shared_ptr<Socket> > _queue;
boost::shared_ptr<Log> _log;
bool _verbose;
+ boost::asio::io_service _io_service;
+ boost::asio::ip::tcp::acceptor _acceptor;
+
struct Broadcast {
Broadcast ()
boost::asio::ip::udp::socket* socket;
char buffer[64];
boost::asio::ip::udp::endpoint send_endpoint;
+ boost::asio::io_service io_service;
} _broadcast;
};
*/
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "server_finder.h"
#include "exceptions.h"
#include "util.h"
using std::cout;
using boost::shared_ptr;
using boost::scoped_array;
-using libdcp::raw_convert;
+using dcp::raw_convert;
ServerFinder* ServerFinder::_instance = 0;
--- /dev/null
+/*
+ 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 <dcp/raw_convert.h>
+#include "single_stream_audio_content.h"
+#include "audio_examiner.h"
+#include "film.h"
+
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using dcp::raw_convert;
+
+SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr<const Film> f)
+ : Content (f)
+ , AudioContent (f)
+ , _audio_channels (0)
+ , _audio_length (0)
+ , _audio_frame_rate (0)
+{
+
+}
+
+SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr<const Film> f, boost::filesystem::path p)
+ : Content (f, p)
+ , AudioContent (f, p)
+ , _audio_channels (0)
+ , _audio_length (0)
+ , _audio_frame_rate (0)
+{
+
+}
+
+SingleStreamAudioContent::SingleStreamAudioContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
+ : Content (f, node)
+ , AudioContent (f, node)
+ , _audio_mapping (node->node_child ("AudioMapping"), version)
+{
+ _audio_channels = node->number_child<int> ("AudioChannels");
+ _audio_length = ContentTime (node->number_child<ContentTime::Type> ("AudioLength"));
+ _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
+}
+
+void
+SingleStreamAudioContent::set_audio_mapping (AudioMapping m)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _audio_mapping = m;
+ }
+
+ AudioContent::set_audio_mapping (m);
+}
+
+
+void
+SingleStreamAudioContent::as_xml (xmlpp::Node* node) const
+{
+ AudioContent::as_xml (node);
+ node->add_child("AudioChannels")->add_child_text (raw_convert<string> (audio_channels ()));
+ node->add_child("AudioLength")->add_child_text (raw_convert<string> (audio_length().get ()));
+ node->add_child("AudioFrameRate")->add_child_text (raw_convert<string> (audio_frame_rate ()));
+ _audio_mapping.as_xml (node->add_child("AudioMapping"));
+}
+
+void
+SingleStreamAudioContent::take_from_audio_examiner (shared_ptr<AudioExaminer> examiner)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _audio_channels = examiner->audio_channels ();
+ _audio_length = examiner->audio_length ();
+ _audio_frame_rate = examiner->audio_frame_rate ();
+ }
+
+ signal_changed (AudioContentProperty::AUDIO_CHANNELS);
+ signal_changed (AudioContentProperty::AUDIO_LENGTH);
+ signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
+
+ int const p = processed_audio_channels ();
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ /* XXX: do this in signal_changed...? */
+ _audio_mapping = AudioMapping (p);
+ _audio_mapping.make_default ();
+ }
+
+ signal_changed (AudioContentProperty::AUDIO_MAPPING);
+}
--- /dev/null
+/*
+ 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/lib/single_stream_audio_content.h
+ * @brief SingleStreamAudioContent class.
+ */
+
+#ifndef DCPOMATIC_SINGLE_STREAM_AUDIO_CONTENT_H
+#define DCPOMATIC_SINGLE_STREAM_AUDIO_CONTENT_H
+
+#include "audio_content.h"
+
+class AudioExaminer;
+
+/** @class SingleStreamAudioContent
+ * @brief A piece of AudioContent that has a single audio stream.
+ */
+class SingleStreamAudioContent : public AudioContent
+{
+public:
+ SingleStreamAudioContent (boost::shared_ptr<const Film>);
+ SingleStreamAudioContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+ SingleStreamAudioContent (boost::shared_ptr<const Film> f, cxml::ConstNodePtr node, int version);
+
+ void as_xml (xmlpp::Node* node) const;
+
+ int audio_channels () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_channels;
+ }
+
+ ContentTime audio_length () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_length;
+ }
+
+ int audio_frame_rate () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_frame_rate;
+ }
+
+ AudioMapping audio_mapping () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_mapping;
+ }
+
+ void set_audio_mapping (AudioMapping);
+
+ void take_from_audio_examiner (boost::shared_ptr<AudioExaminer>);
+
+protected:
+ int _audio_channels;
+ ContentTime _audio_length;
+ int _audio_frame_rate;
+ AudioMapping _audio_mapping;
+};
+
+#endif
*/
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "sndfile_content.h"
#include "sndfile_decoder.h"
#include "film.h"
using std::string;
using std::cout;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
SndfileContent::SndfileContent (shared_ptr<const Film> f, boost::filesystem::path p)
: Content (f, p)
- , AudioContent (f, p)
- , _audio_channels (0)
- , _audio_length (0)
- , _audio_frame_rate (0)
+ , SingleStreamAudioContent (f, p)
{
}
-SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+SndfileContent::SndfileContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
- , AudioContent (f, node)
- , _audio_mapping (node->node_child ("AudioMapping"), version)
+ , SingleStreamAudioContent (f, node, version)
{
- _audio_channels = node->number_child<int> ("AudioChannels");
- _audio_length = node->number_child<AudioContent::Frame> ("AudioLength");
- _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
+
}
+void
+SndfileContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("Sndfile");
+ Content::as_xml (node);
+ SingleStreamAudioContent::as_xml (node);
+}
+
+
string
SndfileContent::summary () const
{
s << String::compose (
_("%1 channels, %2kHz, %3 samples"),
audio_channels(),
- content_audio_frame_rate() / 1000.0,
- audio_length()
+ audio_frame_rate() / 1000.0,
+ audio_length().frames (audio_frame_rate ())
);
return s.str ();
{
job->set_progress_unknown ();
Content::examine (job);
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- SndfileDecoder dec (film, shared_from_this());
-
- {
- boost::mutex::scoped_lock lm (_mutex);
- _audio_channels = dec.audio_channels ();
- _audio_length = dec.audio_length ();
- _audio_frame_rate = dec.audio_frame_rate ();
- }
-
- signal_changed (AudioContentProperty::AUDIO_CHANNELS);
- signal_changed (AudioContentProperty::AUDIO_LENGTH);
- signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
-
- {
- boost::mutex::scoped_lock lm (_mutex);
- /* XXX: do this in signal_changed...? */
- _audio_mapping = AudioMapping (_audio_channels);
- _audio_mapping.make_default ();
- }
-
- signal_changed (AudioContentProperty::AUDIO_MAPPING);
-}
-
-void
-SndfileContent::as_xml (xmlpp::Node* node) const
-{
- node->add_child("Type")->add_child_text ("Sndfile");
- Content::as_xml (node);
- AudioContent::as_xml (node);
-
- node->add_child("AudioChannels")->add_child_text (raw_convert<string> (audio_channels ()));
- node->add_child("AudioLength")->add_child_text (raw_convert<string> (audio_length ()));
- node->add_child("AudioFrameRate")->add_child_text (raw_convert<string> (content_audio_frame_rate ()));
- _audio_mapping.as_xml (node->add_child("AudioMapping"));
+ shared_ptr<AudioExaminer> dec (new SndfileDecoder (shared_from_this()));
+ take_from_audio_examiner (dec);
}
-Time
+DCPTime
SndfileContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc = film->active_frame_rate_change (position ());
-
- OutputAudioFrame const len = divide_with_round (
- audio_length() * output_audio_frame_rate() * frc.source,
- content_audio_frame_rate() * film->video_frame_rate()
- );
-
- return film->audio_frames_to_time (len);
+ return DCPTime (audio_length(), film->active_frame_rate_change (position ()));
}
-void
-SndfileContent::set_audio_mapping (AudioMapping m)
-{
- {
- boost::mutex::scoped_lock lm (_mutex);
- _audio_mapping = m;
- }
-
- signal_changed (AudioContentProperty::AUDIO_MAPPING);
-}
extern "C" {
#include <libavutil/audioconvert.h>
}
-#include "audio_content.h"
+#include "single_stream_audio_content.h"
namespace cxml {
class Node;
}
-class SndfileContent : public AudioContent
+class SndfileContent : public SingleStreamAudioContent
{
public:
SndfileContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- SndfileContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
+ SndfileContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
boost::shared_ptr<SndfileContent> shared_from_this () {
return boost::dynamic_pointer_cast<SndfileContent> (Content::shared_from_this ());
}
+ DCPTime full_length () const;
+
void examine (boost::shared_ptr<Job>);
std::string summary () const;
std::string technical_summary () const;
std::string information () const;
void as_xml (xmlpp::Node *) const;
- Time full_length () const;
-
- /* AudioContent */
- int audio_channels () const {
- boost::mutex::scoped_lock lm (_mutex);
- return _audio_channels;
- }
-
- AudioContent::Frame audio_length () const {
- boost::mutex::scoped_lock lm (_mutex);
- return _audio_length;
- }
-
- int content_audio_frame_rate () const {
- boost::mutex::scoped_lock lm (_mutex);
- return _audio_frame_rate;
- }
-
- AudioMapping audio_mapping () const {
- boost::mutex::scoped_lock lm (_mutex);
- return _audio_mapping;
- }
-
- void set_audio_mapping (AudioMapping);
static bool valid_file (boost::filesystem::path);
-
-private:
- int _audio_channels;
- AudioContent::Frame _audio_length;
- int _audio_frame_rate;
- AudioMapping _audio_mapping;
};
#endif
#include <sndfile.h>
#include "sndfile_content.h"
#include "sndfile_decoder.h"
-#include "film.h"
#include "exceptions.h"
#include "audio_buffers.h"
using std::cout;
using boost::shared_ptr;
-SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c)
- : Decoder (f)
- , AudioDecoder (f, c)
+SndfileDecoder::SndfileDecoder (shared_ptr<const SndfileContent> c)
+ : AudioDecoder (c)
, _sndfile_content (c)
, _deinterleave_buffer (0)
{
delete[] _deinterleave_buffer;
}
-void
+bool
SndfileDecoder::pass ()
{
+ if (_remaining == 0) {
+ return true;
+ }
+
/* Do things in half second blocks as I think there may be limits
to what FFmpeg (and in particular the resampler) can cope with.
*/
- sf_count_t const block = _sndfile_content->content_audio_frame_rate() / 2;
+ sf_count_t const block = _sndfile_content->audio_frame_rate() / 2;
sf_count_t const this_time = min (block, _remaining);
int const channels = _sndfile_content->audio_channels ();
}
data->set_frames (this_time);
- audio (data, _done);
+ audio (data, ContentTime::from_frames (_done, audio_frame_rate ()));
_done += this_time;
_remaining -= this_time;
+
+ return _remaining == 0;
}
int
return _info.channels;
}
-AudioContent::Frame
+ContentTime
SndfileDecoder::audio_length () const
{
- return _info.frames;
+ return ContentTime::from_frames (_info.frames, audio_frame_rate ());
}
int
return _info.samplerate;
}
-bool
-SndfileDecoder::done () const
+void
+SndfileDecoder::seek (ContentTime t, bool accurate)
{
- return _audio_position >= _sndfile_content->audio_length ();
+ AudioDecoder::seek (t, accurate);
+
+ _done = t.frames (audio_frame_rate ());
+ _remaining = _info.frames - _done;
}
#include <sndfile.h>
#include "decoder.h"
#include "audio_decoder.h"
+#include "audio_examiner.h"
class SndfileContent;
-class SndfileDecoder : public AudioDecoder
+class SndfileDecoder : public AudioDecoder, public AudioExaminer
{
public:
- SndfileDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const SndfileContent>);
+ SndfileDecoder (boost::shared_ptr<const SndfileContent> c);
~SndfileDecoder ();
- void pass ();
- bool done () const;
+ void seek (ContentTime, bool);
int audio_channels () const;
- AudioContent::Frame audio_length () const;
+ ContentTime audio_length () const;
int audio_frame_rate () const;
private:
+ bool pass ();
+
boost::shared_ptr<const SndfileContent> _sndfile_content;
SNDFILE* _sndfile;
SF_INFO _info;
- AudioContent::Frame _done;
- AudioContent::Frame _remaining;
+ int64_t _done;
+ int64_t _remaining;
float* _deinterleave_buffer;
};
+++ /dev/null
-/*
- 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/sound_processor.cc
- * @brief A class to describe a sound processor.
- */
-
-#include <iostream>
-#include <cassert>
-#include "sound_processor.h"
-#include "dolby_cp750.h"
-
-using namespace std;
-
-vector<SoundProcessor const *> SoundProcessor::_sound_processors;
-
-/** @param i Our id.
- * @param n User-visible name.
- */
-SoundProcessor::SoundProcessor (string i, string n)
- : _id (i)
- , _name (n)
-{
-
-}
-
-/** @return All available sound processors */
-vector<SoundProcessor const *>
-SoundProcessor::all ()
-{
- return _sound_processors;
-}
-
-/** Set up the static _sound_processors vector; must be called before from_*
- * methods are used.
- */
-void
-SoundProcessor::setup_sound_processors ()
-{
- _sound_processors.push_back (new DolbyCP750);
-}
-
-/** @param id One of our ids.
- * @return Corresponding sound processor, or 0.
- */
-SoundProcessor const *
-SoundProcessor::from_id (string id)
-{
- vector<SoundProcessor const *>::iterator i = _sound_processors.begin ();
- while (i != _sound_processors.end() && (*i)->id() != id) {
- ++i;
- }
-
- if (i == _sound_processors.end ()) {
- return 0;
- }
-
- return *i;
-}
-
-/** @param s A sound processor from our static list.
- * @return Index of the sound processor with the list, or -1.
- */
-int
-SoundProcessor::as_index (SoundProcessor const * s)
-{
- vector<SoundProcessor*>::size_type i = 0;
- while (i < _sound_processors.size() && _sound_processors[i] != s) {
- ++i;
- }
-
- if (i == _sound_processors.size ()) {
- return -1;
- }
-
- return i;
-}
-
-/** @param i An index returned from as_index().
- * @return Corresponding sound processor.
- */
-SoundProcessor const *
-SoundProcessor::from_index (int i)
-{
- assert (i <= int(_sound_processors.size ()));
- return _sound_processors[i];
-}
+++ /dev/null
-/*
- 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/sound_processor.h
- * @brief A class to describe a sound processor.
- */
-
-#ifndef DCPOMATIC_SOUND_PROCESSOR_H
-#define DCPOMATIC_SOUND_PROCESSOR_H
-
-#include <string>
-#include <vector>
-#include <boost/utility.hpp>
-
-/** @class SoundProcessor
- * @brief Class to describe a sound processor.
- */
-class SoundProcessor : public boost::noncopyable
-{
-public:
- SoundProcessor (std::string i, std::string n);
-
- virtual float db_for_fader_change (float from, float to) const = 0;
-
- /** @return id for our use */
- std::string id () const {
- return _id;
- }
-
- /** @return user-visible name for this sound processor */
- std::string name () const {
- return _name;
- }
-
- static std::vector<SoundProcessor const *> all ();
- static void setup_sound_processors ();
- static SoundProcessor const * from_id (std::string id);
- static SoundProcessor const * from_index (int);
- static int as_index (SoundProcessor const *);
-
-private:
- /** id for our use */
- std::string _id;
- /** user-visible name for this sound processor */
- std::string _name;
-
- /** sll available sound processors */
- static std::vector<SoundProcessor const *> _sound_processors;
-};
-
-#endif
--- /dev/null
+/*
+ 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 "subrip.h"
+#include "cross.h"
+#include "exceptions.h"
+#include "subrip_content.h"
+#include <libsub/subrip_reader.h>
+#include <libsub/collect.h>
+
+#include "i18n.h"
+
+using std::vector;
+using boost::shared_ptr;
+
+SubRip::SubRip (shared_ptr<const SubRipContent> content)
+{
+ FILE* f = fopen_boost (content->path (0), "r");
+ if (!f) {
+ throw OpenFileError (content->path (0));
+ }
+
+ sub::SubripReader reader (f);
+ _subtitles = sub::collect<vector<sub::Subtitle> > (reader.subtitles ());
+}
+
+ContentTime
+SubRip::length () const
+{
+ if (_subtitles.empty ()) {
+ return ContentTime ();
+ }
+
+ return ContentTime::from_seconds (_subtitles.back().to.metric().get().all_as_seconds ());
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_SUBRIP_H
+#define DCPOMATIC_SUBRIP_H
+
+#include "subrip_subtitle.h"
+#include <libsub/subtitle.h>
+
+class SubRipContent;
+class subrip_time_test;
+class subrip_coordinate_test;
+class subrip_content_test;
+class subrip_parse_test;
+
+class SubRip
+{
+public:
+ SubRip (boost::shared_ptr<const SubRipContent>);
+
+ ContentTime length () const;
+
+protected:
+ std::vector<sub::Subtitle> _subtitles;
+};
+
+#endif
--- /dev/null
+/*
+ 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 "subrip_content.h"
+#include "util.h"
+#include "subrip.h"
+#include "film.h"
+#include <dcp/raw_convert.h>
+
+#include "i18n.h"
+
+using std::string;
+using std::cout;
+using dcp::raw_convert;
+using boost::shared_ptr;
+using boost::lexical_cast;
+
+SubRipContent::SubRipContent (shared_ptr<const Film> film, boost::filesystem::path path)
+ : Content (film, path)
+ , SubtitleContent (film, path)
+{
+
+}
+
+SubRipContent::SubRipContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
+ : Content (film, node)
+ , SubtitleContent (film, node, version)
+ , _length (node->number_child<DCPTime::Type> ("Length"))
+{
+
+}
+
+void
+SubRipContent::examine (boost::shared_ptr<Job> job)
+{
+ Content::examine (job);
+ SubRip s (shared_from_this ());
+
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+
+ DCPTime len (s.length (), film->active_frame_rate_change (position ()));
+
+ boost::mutex::scoped_lock lm (_mutex);
+ _length = len;
+}
+
+string
+SubRipContent::summary () const
+{
+ return path_summary() + " " + _("[subtitles]");
+}
+
+string
+SubRipContent::technical_summary () const
+{
+ return Content::technical_summary() + " - " + _("SubRip subtitles");
+}
+
+string
+SubRipContent::information () const
+{
+
+}
+
+void
+SubRipContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("SubRip");
+ Content::as_xml (node);
+ SubtitleContent::as_xml (node);
+ node->add_child("Length")->add_child_text (raw_convert<string> (_length.get ()));
+}
+
+DCPTime
+SubRipContent::full_length () const
+{
+ /* XXX: this assumes that the timing of the SubRip file is appropriate
+ for the DCP's frame rate.
+ */
+ return _length;
+}
--- /dev/null
+/*
+ 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 "subtitle_content.h"
+
+class SubRipContent : public SubtitleContent
+{
+public:
+ SubRipContent (boost::shared_ptr<const Film>, boost::filesystem::path);
+ SubRipContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
+
+ boost::shared_ptr<SubRipContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<SubRipContent> (Content::shared_from_this ());
+ }
+
+ /* Content */
+ void examine (boost::shared_ptr<Job>);
+ std::string summary () const;
+ std::string technical_summary () const;
+ std::string information () const;
+ void as_xml (xmlpp::Node *) const;
+ DCPTime full_length () const;
+
+ /* SubtitleContent */
+ bool has_subtitles () const {
+ return true;
+ }
+
+private:
+ DCPTime _length;
+};
--- /dev/null
+/*
+ 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 <dcp/subtitle_string.h>
+#include "subrip_decoder.h"
+#include "subrip_content.h"
+
+using std::list;
+using std::vector;
+using boost::shared_ptr;
+
+SubRipDecoder::SubRipDecoder (shared_ptr<const SubRipContent> content)
+ : SubtitleDecoder (content)
+ , SubRip (content)
+ , _next (0)
+{
+
+}
+
+void
+SubRipDecoder::seek (ContentTime time, bool accurate)
+{
+ SubtitleDecoder::seek (time, accurate);
+
+ _next = 0;
+ while (_next < _subtitles.size() && ContentTime::from_seconds (_subtitles[_next].from.metric().get().all_as_seconds ()) < time) {
+ ++_next;
+ }
+}
+
+bool
+SubRipDecoder::pass ()
+{
+ if (_next >= _subtitles.size ()) {
+ return true;
+ }
+
+ /* XXX: we are ignoring positioning specified in the file */
+
+ list<dcp::SubtitleString> out;
+ for (list<sub::Line>::const_iterator i = _subtitles[_next].lines.begin(); i != _subtitles[_next].lines.end(); ++i) {
+ for (list<sub::Block>::const_iterator j = i->blocks.begin(); j != i->blocks.end(); ++j) {
+ out.push_back (
+ dcp::SubtitleString (
+ "Arial",
+ j->italic,
+ dcp::Color (255, 255, 255),
+ /* .srt files don't specify size, so this is an arbitrary value */
+ 48,
+ dcp::Time (rint (_subtitles[_next].from.metric().get().all_as_milliseconds() / 4)),
+ dcp::Time (rint (_subtitles[_next].to.metric().get().all_as_milliseconds() / 4)),
+ i->vertical_position.line.get() * (1.5 / 22) + 0.8,
+ dcp::TOP,
+ j->text,
+ dcp::NONE,
+ dcp::Color (255, 255, 255),
+ 0,
+ 0
+ )
+ );
+ }
+ }
+
+ text_subtitle (out);
+ ++_next;
+ return false;
+}
+
+list<ContentTimePeriod>
+SubRipDecoder::subtitles_during (ContentTimePeriod p, bool starting) const
+{
+ /* XXX: inefficient */
+
+ list<ContentTimePeriod> d;
+
+ for (vector<sub::Subtitle>::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+
+ ContentTimePeriod t (
+ ContentTime::from_seconds (i->from.metric().get().all_as_seconds()),
+ ContentTime::from_seconds (i->to.metric().get().all_as_seconds())
+ );
+
+ if ((starting && p.contains (t.from)) || (!starting && p.overlaps (t))) {
+ d.push_back (t);
+ }
+ }
+
+ return d;
+}
--- /dev/null
+/*
+ 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 DCPOMATIC_SUBRIP_DECODER_H
+#define DCPOMATIC_SUBRIP_DECODER_H
+
+#include "subtitle_decoder.h"
+#include "subrip.h"
+
+class SubRipContent;
+
+class SubRipDecoder : public SubtitleDecoder, public SubRip
+{
+public:
+ SubRipDecoder (boost::shared_ptr<const SubRipContent>);
+
+protected:
+ void seek (ContentTime time, bool accurate);
+ bool pass ();
+
+private:
+ std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const;
+
+ size_t _next;
+};
+
+#endif
--- /dev/null
+/*
+ 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 DCPOMATIC_SUBRIP_SUBTITLE_H
+#define DCPOMATIC_SUBRIP_SUBTITLE_H
+
+#include <boost/optional.hpp>
+#include <dcp/types.h>
+#include "types.h"
+#include "dcpomatic_time.h"
+
+struct SubRipSubtitlePiece
+{
+ SubRipSubtitlePiece ()
+ : bold (false)
+ , italic (false)
+ , underline (false)
+ {}
+
+ std::string text;
+ bool bold;
+ bool italic;
+ bool underline;
+ dcp::Color color;
+};
+
+struct SubRipSubtitle
+{
+ ContentTimePeriod period;
+ boost::optional<int> x1;
+ boost::optional<int> x2;
+ boost::optional<int> y1;
+ boost::optional<int> y2;
+ std::list<SubRipSubtitlePiece> pieces;
+};
+
+#endif
+++ /dev/null
-/*
- 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 <boost/shared_ptr.hpp>
-#include <boost/weak_ptr.hpp>
-#include <boost/optional.hpp>
-#include <libdcp/util.h>
-#include "rect.h"
-#include "types.h"
-
-class Film;
-class Piece;
-class Image;
-
-class Subtitle
-{
-public:
-
- Subtitle (boost::shared_ptr<const Film>, libdcp::Size, boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
-
- void update (boost::shared_ptr<const Film>, libdcp::Size);
- void set_stop (Time t) {
- _stop = t;
- check_out_to ();
- }
-
- bool covers (Time t) const;
- bool ends_before (Time t) const {
- return _out_to < t;
- }
-
- boost::shared_ptr<Image> out_image () const {
- return _out_image;
- }
-
- Position<int> out_position () const {
- return _out_position;
- }
-
-private:
- void check_out_to ();
-
- boost::weak_ptr<Piece> _piece;
- boost::shared_ptr<Image> _in_image;
- dcpomatic::Rect<double> _in_rect;
- Time _in_from;
- Time _in_to;
-
- boost::shared_ptr<Image> _out_image;
- Position<int> _out_position;
- Time _out_from;
- Time _out_to;
-
- /** Time at which this subtitle should stop (overriding _out_to) */
- boost::optional<Time> _stop;
-};
*/
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "subtitle_content.h"
#include "util.h"
#include "exceptions.h"
+#include "safe_stringstream.h"
#include "i18n.h"
using std::string;
using std::vector;
+using std::cout;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
int const SubtitleContentProperty::SUBTITLE_X_OFFSET = 500;
int const SubtitleContentProperty::SUBTITLE_Y_OFFSET = 501;
int const SubtitleContentProperty::SUBTITLE_X_SCALE = 502;
int const SubtitleContentProperty::SUBTITLE_Y_SCALE = 503;
+int const SubtitleContentProperty::USE_SUBTITLES = 504;
+
+SubtitleContent::SubtitleContent (shared_ptr<const Film> f)
+ : Content (f)
+ , _use_subtitles (false)
+ , _subtitle_x_offset (0)
+ , _subtitle_y_offset (0)
+ , _subtitle_x_scale (1)
+ , _subtitle_y_scale (1)
+{
+
+}
SubtitleContent::SubtitleContent (shared_ptr<const Film> f, boost::filesystem::path p)
: Content (f, p)
+ , _use_subtitles (false)
, _subtitle_x_offset (0)
, _subtitle_y_offset (0)
, _subtitle_x_scale (1)
}
-SubtitleContent::SubtitleContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+SubtitleContent::SubtitleContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
+ , _use_subtitles (false)
, _subtitle_x_offset (0)
, _subtitle_y_offset (0)
, _subtitle_x_scale (1)
, _subtitle_y_scale (1)
{
+ if (version >= 32) {
+ _use_subtitles = node->bool_child ("UseSubtitles");
+ } else {
+ _use_subtitles = false;
+ }
+
if (version >= 7) {
_subtitle_x_offset = node->number_child<float> ("SubtitleXOffset");
_subtitle_y_offset = node->number_child<float> ("SubtitleYOffset");
for (size_t i = 0; i < c.size(); ++i) {
shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (c[i]);
+ if (sc->use_subtitles() != ref->use_subtitles()) {
+ throw JoinError (_("Content to be joined must have the same 'use subtitles' setting."));
+ }
+
if (sc->subtitle_x_offset() != ref->subtitle_x_offset()) {
throw JoinError (_("Content to be joined must have the same subtitle X offset."));
}
}
}
+ _use_subtitles = ref->use_subtitles ();
_subtitle_x_offset = ref->subtitle_x_offset ();
_subtitle_y_offset = ref->subtitle_y_offset ();
_subtitle_x_scale = ref->subtitle_x_scale ();
void
SubtitleContent::as_xml (xmlpp::Node* root) const
{
+ root->add_child("UseSubtitles")->add_child_text (raw_convert<string> (_use_subtitles));
root->add_child("SubtitleXOffset")->add_child_text (raw_convert<string> (_subtitle_x_offset));
root->add_child("SubtitleYOffset")->add_child_text (raw_convert<string> (_subtitle_y_offset));
root->add_child("SubtitleXScale")->add_child_text (raw_convert<string> (_subtitle_x_scale));
root->add_child("SubtitleYScale")->add_child_text (raw_convert<string> (_subtitle_y_scale));
}
+void
+SubtitleContent::set_use_subtitles (bool u)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _use_subtitles = u;
+ }
+ signal_changed (SubtitleContentProperty::USE_SUBTITLES);
+}
+
void
SubtitleContent::set_subtitle_x_offset (double o)
{
}
signal_changed (SubtitleContentProperty::SUBTITLE_Y_SCALE);
}
+
+string
+SubtitleContent::identifier () const
+{
+ SafeStringStream s;
+ s << Content::identifier()
+ << "_" << raw_convert<string> (subtitle_x_scale())
+ << "_" << raw_convert<string> (subtitle_y_scale())
+ << "_" << raw_convert<string> (subtitle_x_offset())
+ << "_" << raw_convert<string> (subtitle_y_offset());
+
+ return s.str ();
+}
static int const SUBTITLE_Y_OFFSET;
static int const SUBTITLE_X_SCALE;
static int const SUBTITLE_Y_SCALE;
+ static int const USE_SUBTITLES;
};
+/** @class SubtitleContent
+ * @brief Parent for content which has the potential to include subtitles.
+ *
+ * Although inheriting from this class indicates that the content could
+ * have subtitles, it may not. ::has_subtitles() will tell you.
+ */
class SubtitleContent : public virtual Content
{
public:
+ SubtitleContent (boost::shared_ptr<const Film>);
SubtitleContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- SubtitleContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int version);
+ SubtitleContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int version);
SubtitleContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
-
+
void as_xml (xmlpp::Node *) const;
+ std::string identifier () const;
+
+ virtual bool has_subtitles () const = 0;
+ void set_use_subtitles (bool);
void set_subtitle_x_offset (double);
void set_subtitle_y_offset (double);
void set_subtitle_x_scale (double);
void set_subtitle_y_scale (double);
+ bool use_subtitles () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _use_subtitles;
+ }
+
double subtitle_x_offset () const {
boost::mutex::scoped_lock lm (_mutex);
return _subtitle_x_offset;
boost::mutex::scoped_lock lm (_mutex);
return _subtitle_y_scale;
}
-
+
private:
- friend class ffmpeg_pts_offset_test;
+ friend struct ffmpeg_pts_offset_test;
+ bool _use_subtitles;
/** x offset for placing subtitles, as a proportion of the container width;
* +ve is further right, -ve is further left.
*/
/*
- 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>
#include "subtitle_decoder.h"
+#include "subtitle_content.h"
+using std::list;
+using std::cout;
using boost::shared_ptr;
+using boost::optional;
-SubtitleDecoder::SubtitleDecoder (shared_ptr<const Film> f)
- : Decoder (f)
+SubtitleDecoder::SubtitleDecoder (shared_ptr<const SubtitleContent> c)
+ : _subtitle_content (c)
{
}
-
-/** Called by subclasses when a subtitle is ready.
+/** Called by subclasses when an image subtitle is ready.
* Image may be 0 to say that there is no current subtitle.
*/
void
-SubtitleDecoder::subtitle (shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to)
+SubtitleDecoder::image_subtitle (ContentTimePeriod period, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
+{
+ _decoded_image_subtitles.push_back (ContentImageSubtitle (period, image, rect));
+}
+
+void
+SubtitleDecoder::text_subtitle (list<dcp::SubtitleString> s)
+{
+ _decoded_text_subtitles.push_back (ContentTextSubtitle (s));
+}
+
+template <class T>
+list<T>
+SubtitleDecoder::get (list<T> const & subs, ContentTimePeriod period, bool starting)
+{
+ /* Get the full periods of the subtitles that are showing or starting during the specified period */
+ list<ContentTimePeriod> sp = subtitles_during (period, starting);
+ if (sp.empty ()) {
+ /* Nothing in this period */
+ return list<T> ();
+ }
+
+ /* Seek if what we want is before what we have, or more than a reasonable amount after */
+ if (subs.empty() || sp.back().to < subs.front().period().from || sp.front().from > (subs.back().period().to + ContentTime::from_seconds (5))) {
+ seek (sp.front().from, true);
+ }
+
+ /* Now enough pass() calls will either:
+ * (a) give us what we want, or
+ * (b) hit the end of the decoder.
+ */
+ while (!pass() && (subs.empty() || (subs.back().period().to < sp.back().to))) {}
+
+ /* Now look for what we wanted in the data we have collected */
+ /* XXX: inefficient */
+
+ list<T> out;
+ for (typename list<T>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
+ if ((starting && period.contains (i->period().from)) || (!starting && period.overlaps (i->period ()))) {
+ out.push_back (*i);
+ }
+ }
+
+ return out;
+}
+
+list<ContentTextSubtitle>
+SubtitleDecoder::get_text_subtitles (ContentTimePeriod period, bool starting)
+{
+ return get<ContentTextSubtitle> (_decoded_text_subtitles, period, starting);
+}
+
+list<ContentImageSubtitle>
+SubtitleDecoder::get_image_subtitles (ContentTimePeriod period, bool starting)
+{
+ return get<ContentImageSubtitle> (_decoded_image_subtitles, period, starting);
+}
+
+void
+SubtitleDecoder::seek (ContentTime, bool)
{
- Subtitle (image, rect, from, to);
+ _decoded_text_subtitles.clear ();
+ _decoded_image_subtitles.clear ();
}
*/
-#include <boost/signals2.hpp>
+#ifndef DCPOMATIC_SUBTITLE_DECODER_H
+#define DCPOMATIC_SUBTITLE_DECODER_H
+
+#include <dcp/subtitle_string.h>
#include "decoder.h"
#include "rect.h"
#include "types.h"
+#include "content_subtitle.h"
class Film;
-class TimedSubtitle;
+class DCPTimedSubtitle;
class Image;
class SubtitleDecoder : public virtual Decoder
{
public:
- SubtitleDecoder (boost::shared_ptr<const Film>);
+ SubtitleDecoder (boost::shared_ptr<const SubtitleContent>);
- boost::signals2::signal<void (boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time)> Subtitle;
+ std::list<ContentImageSubtitle> get_image_subtitles (ContentTimePeriod period, bool starting);
+ std::list<ContentTextSubtitle> get_text_subtitles (ContentTimePeriod period, bool starting);
protected:
- void subtitle (boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time);
+ void seek (ContentTime, bool);
+
+ void image_subtitle (ContentTimePeriod period, boost::shared_ptr<Image>, dcpomatic::Rect<double>);
+ void text_subtitle (std::list<dcp::SubtitleString>);
+
+ std::list<ContentImageSubtitle> _decoded_image_subtitles;
+ std::list<ContentTextSubtitle> _decoded_text_subtitles;
+
+private:
+ template <class T>
+ std::list<T> get (std::list<T> const & subs, ContentTimePeriod period, bool starting);
+
+ virtual std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod, bool starting) const = 0;
+
+ boost::shared_ptr<const SubtitleContent> _subtitle_content;
};
+
+#endif
using std::string;
using std::fixed;
using std::setprecision;
+using std::cout;
using boost::shared_ptr;
/** @param s Film to use.
return s.str ();
}
+/** @return Approximate remaining time in seconds */
int
TranscodeJob::remaining_time () const
{
}
/* Compute approximate proposed length here, as it's only here that we need it */
- OutputVideoFrame const left = _film->time_to_video_frames (_film->length ()) - t->video_frames_out();
- return left / fps;
+ return (_film->length().frames (_film->video_frame_rate ()) - t->video_frames_out()) / fps;
}
#include "audio_decoder.h"
#include "player.h"
#include "job.h"
+#include "writer.h"
using std::string;
+using std::cout;
+using std::list;
using boost::shared_ptr;
using boost::weak_ptr;
using boost::dynamic_pointer_cast;
-static void
-video_proxy (weak_ptr<Encoder> encoder, shared_ptr<PlayerVideoFrame> pvf, bool same)
-{
- shared_ptr<Encoder> e = encoder.lock ();
- if (e) {
- e->process_video (pvf, same);
- }
-}
-
-static void
-audio_proxy (weak_ptr<Encoder> encoder, shared_ptr<const AudioBuffers> audio)
-{
- shared_ptr<Encoder> e = encoder.lock ();
- if (e) {
- e->process_audio (audio);
- }
-}
-
/** Construct a transcoder using a Decoder that we create and a supplied Encoder.
* @param f Film that we are transcoding.
* @param e Encoder to use.
*/
Transcoder::Transcoder (shared_ptr<const Film> f, shared_ptr<Job> j)
- : _player (f->make_player ())
- , _encoder (new Encoder (f, j))
+ : _film (f)
+ , _player (f->make_player ())
+ , _writer (new Writer (f, j))
+ , _encoder (new Encoder (f, j, _writer))
, _finishing (false)
{
- _player->Video.connect (bind (video_proxy, _encoder, _1, _2));
- _player->Audio.connect (bind (audio_proxy, _encoder, _1));
+
}
void
Transcoder::go ()
{
- _encoder->process_begin ();
- while (!_player->pass ()) {}
+ _encoder->begin ();
+
+ DCPTime const frame = DCPTime::from_frames (1, _film->video_frame_rate ());
+ DCPTime const length = _film->length ();
+ for (DCPTime t; t < length; t += frame) {
+ list<shared_ptr<PlayerVideo> > v = _player->get_video (t, true);
+ for (list<shared_ptr<PlayerVideo> >::const_iterator i = v.begin(); i != v.end(); ++i) {
+ _encoder->enqueue (*i);
+ }
+ _writer->write (_player->get_audio (t, frame, true));
+ if (!_film->burn_subtitles ()) {
+ _writer->write (_player->get_subtitles (t, frame, true));
+ }
+ }
_finishing = true;
- _encoder->process_end ();
+ _encoder->end ();
+ _writer->finish ();
+
+ _player->statistics().dump (_film->log ());
}
float
}
private:
+ boost::shared_ptr<const Film> _film;
boost::shared_ptr<Player> _player;
+ boost::shared_ptr<Writer> _writer;
boost::shared_ptr<Encoder> _encoder;
bool _finishing;
};
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "types.h"
using std::max;
using std::min;
using std::string;
using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
bool operator== (Crop const & a, Crop const & b)
{
#include <vector>
#include <stdint.h>
#include <boost/shared_ptr.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
+#include "dcpomatic_time.h"
+#include "position.h"
class Content;
class VideoContent;
*/
#define SERVER_LINK_VERSION 2
-typedef int64_t Time;
-#define TIME_MAX INT64_MAX
-#define TIME_HZ ((Time) 96000)
-typedef int64_t OutputAudioFrame;
-typedef int OutputVideoFrame;
typedef std::vector<boost::shared_ptr<Content> > ContentList;
typedef std::vector<boost::shared_ptr<VideoContent> > VideoContentList;
typedef std::vector<boost::shared_ptr<AudioContent> > AudioContentList;
typedef std::vector<boost::shared_ptr<SubtitleContent> > SubtitleContentList;
typedef std::vector<boost::shared_ptr<FFmpegContent> > FFmpegContentList;
-template<class T>
+typedef int64_t VideoFrame;
+typedef int64_t AudioFrame;
+
+/* XXX -> DCPAudio */
struct TimedAudioBuffers
{
TimedAudioBuffers ()
: time (0)
{}
- TimedAudioBuffers (boost::shared_ptr<AudioBuffers> a, T t)
+ TimedAudioBuffers (boost::shared_ptr<AudioBuffers> a, DCPTime t)
: audio (a)
, time (t)
{}
boost::shared_ptr<AudioBuffers> audio;
- T time;
+ DCPTime time;
};
enum VideoFrameType
/** Number of pixels to remove from the bottom */
int bottom;
- libdcp::Size apply (libdcp::Size s, int minimum = 4) const {
+ dcp::Size apply (dcp::Size s, int minimum = 4) const {
s.width -= left + right;
s.height -= top + bottom;
#include <boost/algorithm/string.hpp>
#include <curl/curl.h>
#include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "update.h"
#include "version.h"
#include "ui_signaller.h"
using std::cout;
using std::min;
using std::string;
-using libdcp::raw_convert;
+using dcp::raw_convert;
+/** Singleton instance */
UpdateChecker* UpdateChecker::_instance = 0;
static size_t
return reinterpret_cast<UpdateChecker*>(user)->write_callback (data, size, nmemb);
}
+/** Construct an UpdateChecker. This sets things up and starts a thread to
+ * do the work.
+ */
UpdateChecker::UpdateChecker ()
: _buffer (new char[BUFFER_SIZE])
, _offset (0)
delete[] _buffer;
}
+/** Start running the update check */
void
UpdateChecker::run ()
{
UpdateChecker::thread ()
{
while (true) {
+ /* Block until there is something to do */
boost::mutex::scoped_lock lock (_process_mutex);
while (_to_do == 0) {
_condition.wait (lock);
try {
_offset = 0;
+
+ /* Perform the request */
int r = curl_easy_perform (_curl);
if (r != CURLE_OK) {
set_state (FAILED);
return;
}
+
+ /* Parse the reply */
_buffer[_offset] = '\0';
string s (_buffer);
*/
+/** @file src/lib/update.h
+ * @brief UpdateChecker class.
+ */
+
#include <boost/signals2.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread.hpp>
#include <curl/curl.h>
+/** Class to check for the existance of an update for DCP-o-matic on a remote server */
class UpdateChecker
{
public:
void run ();
enum State {
- YES,
- FAILED,
- NO,
- NOT_RUN
+ YES, ///< there is an update
+ FAILED, ///< the check failed, so we don't know
+ NO, ///< there is no update
+ NOT_RUN ///< the check has not been run (yet)
};
+ /** @return state of the checker */
State state () {
boost::mutex::scoped_lock lm (_data_mutex);
return _state;
}
+ /** @return the version string of the latest stable version (if _state == YES or NO) */
std::string stable () {
boost::mutex::scoped_lock lm (_data_mutex);
return _stable;
}
+ /** @return the version string of the latest test version (if _state == YES or NO) */
std::string test () {
boost::mutex::scoped_lock lm (_data_mutex);
return _test;
}
- /** @return true if the list signal emission was the first */
+ /** @return true if the last signal emission was the first */
bool last_emit_was_first () const {
boost::mutex::scoped_lock lm (_data_mutex);
return _emits == 1;
--- /dev/null
+/*
+ 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 "upmixer_a.h"
+#include "audio_buffers.h"
+
+#include "i18n.h"
+
+using std::string;
+using boost::shared_ptr;
+
+UpmixerA::UpmixerA (int sampling_rate)
+ : _left (0.02, 1900.0 / sampling_rate, 4800.0 / sampling_rate)
+ , _right (0.02, 1900.0 / sampling_rate, 4800.0 / sampling_rate)
+ , _centre (0.02, 150.0 / sampling_rate, 1900.0 / sampling_rate)
+ , _lfe (0.02, 20.0 / sampling_rate, 150.0 / sampling_rate)
+ , _ls (0.02, 4800.0 / sampling_rate, 20000.0 / sampling_rate)
+ , _rs (0.02, 4800.0 / sampling_rate, 20000.0 / sampling_rate)
+{
+
+}
+
+string
+UpmixerA::name () const
+{
+ return _("Stereo to 5.1 up-mixer A");
+}
+
+
+string
+UpmixerA::id () const
+{
+ return N_("stereo-5.1-upmix-a");
+}
+
+ChannelCount
+UpmixerA::in_channels () const
+{
+ return ChannelCount (2);
+}
+
+int
+UpmixerA::out_channels (int) const
+{
+ return 6;
+}
+
+shared_ptr<AudioProcessor>
+UpmixerA::clone (int sampling_rate) const
+{
+ return shared_ptr<AudioProcessor> (new UpmixerA (sampling_rate));
+}
+
+shared_ptr<AudioBuffers>
+UpmixerA::run (shared_ptr<const AudioBuffers> in)
+{
+ /* Input L and R */
+ shared_ptr<AudioBuffers> in_L = in->channel (0);
+ shared_ptr<AudioBuffers> in_R = in->channel (1);
+
+ /* Mix of L and R */
+ shared_ptr<AudioBuffers> in_LR = in_L->clone ();
+ in_LR->accumulate_frames (in_R.get(), 0, 0, in_R->frames ());
+ in_LR->apply_gain (0.5);
+
+ /* Run filters */
+ shared_ptr<AudioBuffers> L = _left.run (in_L);
+ shared_ptr<AudioBuffers> R = _right.run (in_R);
+ shared_ptr<AudioBuffers> C = _centre.run (in_LR);
+ shared_ptr<AudioBuffers> Lfe = _lfe.run (in_LR);
+ shared_ptr<AudioBuffers> Ls = _ls.run (in_L);
+ shared_ptr<AudioBuffers> Rs = _rs.run (in_R);
+
+ shared_ptr<AudioBuffers> out (new AudioBuffers (6, in->frames ()));
+ out->copy_channel_from (L.get(), 0, 0);
+ out->copy_channel_from (R.get(), 0, 1);
+ out->copy_channel_from (C.get(), 0, 2);
+ out->copy_channel_from (Lfe.get(), 0, 3);
+ out->copy_channel_from (Ls.get(), 0, 4);
+ out->copy_channel_from (Rs.get(), 0, 5);
+ return out;
+}
+
+void
+UpmixerA::flush ()
+{
+ _left.flush ();
+ _right.flush ();
+ _centre.flush ();
+ _lfe.flush ();
+ _ls.flush ();
+ _rs.flush ();
+}
--- /dev/null
+/*
+ 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 "audio_processor.h"
+#include "audio_filter.h"
+
+class UpmixerA : public AudioProcessor
+{
+public:
+ UpmixerA (int sampling_rate);
+
+ std::string name () const;
+ std::string id () const;
+ ChannelCount in_channels () const;
+ int out_channels (int) const;
+ boost::shared_ptr<AudioProcessor> clone (int) const;
+ boost::shared_ptr<AudioBuffers> run (boost::shared_ptr<const AudioBuffers>);
+ void flush ();
+
+private:
+ BandPassAudioFilter _left;
+ BandPassAudioFilter _right;
+ BandPassAudioFilter _centre;
+ BandPassAudioFilter _lfe;
+ BandPassAudioFilter _ls;
+ BandPassAudioFilter _rs;
+};
#endif
#include <glib.h>
#include <openjpeg.h>
+#include <pangomm/init.h>
#ifdef DCPOMATIC_IMAGE_MAGICK
#include <magick/MagickCore.h>
#else
#include <magick/magick_config.h>
#endif
#include <magick/version.h>
-#include <libdcp/version.h>
-#include <libdcp/util.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/signer.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/version.h>
+#include <dcp/util.h>
+#include <dcp/signer.h>
+#include <dcp/raw_convert.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "scaler.h"
#include "dcp_content_type.h"
#include "filter.h"
-#include "sound_processor.h"
+#include "cinema_sound_processor.h"
#include "config.h"
#include "ratio.h"
#include "job.h"
#include "cross.h"
#include "video_content.h"
+#include "rect.h"
#include "md5_digester.h"
+#include "audio_processor.h"
#include "safe_stringstream.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
using boost::shared_ptr;
using boost::thread;
using boost::optional;
-using libdcp::Size;
-using libdcp::raw_convert;
+using dcp::Size;
+using dcp::raw_convert;
static boost::thread::id ui_thread;
static boost::filesystem::path backtrace_file;
return s.str ();
}
-/** Return a user-readable string summarising the versions of our dependencies */
-string
-dependency_version_summary ()
-{
- SafeStringStream s;
- s << N_("libopenjpeg ") << opj_version () << N_(", ")
- << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
- << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
- << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
- << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
- << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
- << MagickVersion << N_(", ")
- << N_("libssh ") << ssh_version (0) << N_(", ")
- << N_("libdcp ") << libdcp::version << N_(" git ") << libdcp::git_commit;
-
- return s.str ();
-}
-
double
seconds (struct timeval t)
{
set_terminate (terminate);
- libdcp::init ();
+ Pango::init ();
+ dcp::init ();
Ratio::setup_ratios ();
VideoContentScale::setup_scales ();
DCPContentType::setup_dcp_content_types ();
Scaler::setup_scalers ();
Filter::setup_filters ();
- SoundProcessor::setup_sound_processors ();
+ CinemaSoundProcessor::setup_cinema_sound_processors ();
+ AudioProcessor::setup_audio_processors ();
ui_thread = boost::this_thread::get_id ();
}
boost::filesystem::path
mo_path ()
{
- return "DCP-o-matic.app/Contents/Resources";
+ return "DCP-o-matic 2.app/Contents/Resources";
}
#endif
while (remaining > 0) {
int const t = min (remaining, buffer_size);
- fread (buffer, 1, t, f);
+ int const r = fread (buffer, 1, t, f);
+ if (r != t) {
+ throw ReadFileError (files[i], errno);
+ }
digester.add (buffer, t);
remaining -= t;
return a - (a % t);
}
+/** @param n A number.
+ * @param r Rounding `boundary' (must be a power of 2)
+ * @return n rounded to the nearest r
+ */
+int
+round_to (float n, int r)
+{
+ assert (r == 1 || r == 2 || r == 4);
+ return int (n + float(r) / 2) &~ (r - 1);
+}
+
/** Read a sequence of key / value pairs from a text stream;
* the keys are the first words on the line, and the values are
* the remainder of the line following the key. Lines beginning
assert (boost::this_thread::get_id() == ui_thread);
}
-/** @param v Content video frame.
- * @param audio_sample_rate Source audio sample rate.
- * @param frames_per_second Number of video frames per second.
- * @return Equivalent number of audio frames for `v'.
- */
-int64_t
-video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
-{
- return ((int64_t) v * audio_sample_rate / frames_per_second);
-}
-
string
audio_channel_name (int c)
{
return t;
}
-shared_ptr<const libdcp::Signer>
-make_signer ()
-{
- boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
-
- /* Remake the chain if any of it is missing */
-
- list<boost::filesystem::path> files;
- files.push_back ("ca.self-signed.pem");
- files.push_back ("intermediate.signed.pem");
- files.push_back ("leaf.signed.pem");
- files.push_back ("leaf.key");
-
- list<boost::filesystem::path>::const_iterator i = files.begin();
- while (i != files.end()) {
- boost::filesystem::path p (sd);
- p /= *i;
- if (!boost::filesystem::exists (p)) {
- boost::filesystem::remove_all (sd);
- boost::filesystem::create_directories (sd);
- libdcp::make_signer_chain (sd, openssl_path ());
- break;
- }
-
- ++i;
- }
-
- libdcp::CertificateChain chain;
-
- {
- boost::filesystem::path p (sd);
- p /= "ca.self-signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
- }
-
- {
- boost::filesystem::path p (sd);
- p /= "intermediate.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
- }
-
- {
- boost::filesystem::path p (sd);
- p /= "leaf.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
- }
-
- boost::filesystem::path signer_key (sd);
- signer_key /= "leaf.key";
-
- return shared_ptr<const libdcp::Signer> (new libdcp::Signer (chain, signer_key));
-}
-
map<string, string>
split_get_request (string url)
{
return r;
}
-libdcp::Size
-fit_ratio_within (float ratio, libdcp::Size full_frame)
+dcp::Size
+fit_ratio_within (float ratio, dcp::Size full_frame, int round)
{
if (ratio < full_frame.ratio ()) {
- return libdcp::Size (rint (full_frame.height * ratio), full_frame.height);
+ return dcp::Size (round_to (full_frame.height * ratio, round), full_frame.height);
}
- return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
+ return dcp::Size (full_frame.width, round_to (full_frame.width / ratio, round));
}
void *
}
}
+/** Return a user-readable string summarising the versions of our dependencies */
+string
+dependency_version_summary ()
+{
+ SafeStringStream s;
+ s << N_("libopenjpeg ") << opj_version () << N_(", ")
+ << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
+ << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
+ << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
+ << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
+ << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
+ << MagickVersion << N_(", ")
+ << N_("libssh ") << ssh_version (0) << N_(", ")
+ << N_("libdcp ") << dcp::version << N_(" git ") << dcp::git_commit;
+
+ return s.str ();
+}
+
+/** Construct a ScopedTemporary. A temporary filename is decided but the file is not opened
+ * until ::open() is called.
+ */
ScopedTemporary::ScopedTemporary ()
: _open (0)
{
_file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path ();
}
+/** Close and delete the temporary file */
ScopedTemporary::~ScopedTemporary ()
{
close ();
boost::filesystem::remove (_file, ec);
}
+/** @return temporary filename */
char const *
ScopedTemporary::c_str () const
{
return _file.string().c_str ();
}
+/** Open the temporary file.
+ * @return File's FILE pointer.
+ */
FILE*
ScopedTemporary::open (char const * params)
{
return _open;
}
+/** Close the file */
void
ScopedTemporary::close ()
{
_open = 0;
}
}
+
+ContentTimePeriod
+subtitle_period (AVSubtitle const & sub)
+{
+ ContentTime const packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE);
+
+ ContentTimePeriod period (
+ packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3),
+ packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3)
+ );
+
+ return period;
+}
#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
#define DCPOMATIC_HELLO "Boys, you gotta learn not to talk to nuns that way"
#define HISTORY_SIZE 10
-namespace libdcp {
- class Signer;
-}
-
class Job;
+struct AVSubtitle;
extern std::string seconds_to_hms (int);
extern std::string seconds_to_approximate_hms (int);
extern boost::filesystem::path mo_path ();
#endif
extern std::string tidy_for_filename (std::string);
-extern boost::shared_ptr<const libdcp::Signer> make_signer ();
-extern libdcp::Size fit_ratio_within (float ratio, libdcp::Size);
+extern dcp::Size fit_ratio_within (float ratio, dcp::Size, int);
extern std::string entities_to_text (std::string e);
extern std::map<std::string, std::string> split_get_request (std::string url);
extern int dcp_audio_frame_rate (int);
extern int stride_round_up (int, int const *, int);
+extern int round_to (float n, int r);
extern std::multimap<std::string, std::string> read_key_value (std::istream& s);
extern int get_required_int (std::multimap<std::string, std::string> const & kv, std::string k);
extern float get_required_float (std::multimap<std::string, std::string> const & kv, std::string k);
extern std::string get_optional_string (std::multimap<std::string, std::string> const & kv, std::string k);
extern void* wrapped_av_malloc (size_t);
extern int64_t divide_with_round (int64_t a, int64_t b);
+extern ContentTimePeriod subtitle_period (AVSubtitle const &);
/** @class Socket
* @brief A class to wrap a boost::asio::ip::tcp::socket with some things
extern int64_t video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second);
+/** @class ScopedTemporary
+ * @brief A temporary file which is deleted when the ScopedTemporary object goes out of scope.
+ */
class ScopedTemporary
{
public:
ScopedTemporary ();
~ScopedTemporary ();
+ /** @return temporary filename */
boost::filesystem::path file () const {
return _file;
}
-
+
char const * c_str () const;
FILE* open (char const *);
void close ();
#include <iomanip>
#include <libcxml/cxml.h>
-#include <libdcp/colour_matrix.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/raw_convert.h>
#include "video_content.h"
#include "video_examiner.h"
#include "compose.hpp"
#include "film.h"
#include "exceptions.h"
#include "frame_rate_change.h"
+#include "log.h"
#include "safe_stringstream.h"
#include "i18n.h"
+#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
int const VideoContentProperty::VIDEO_SIZE = 0;
int const VideoContentProperty::VIDEO_FRAME_RATE = 1;
int const VideoContentProperty::VIDEO_FRAME_TYPE = 2;
int const VideoContentProperty::VIDEO_CROP = 3;
int const VideoContentProperty::VIDEO_SCALE = 4;
int const VideoContentProperty::COLOUR_CONVERSION = 5;
+int const VideoContentProperty::VIDEO_FADE_IN = 6;
+int const VideoContentProperty::VIDEO_FADE_OUT = 7;
using std::string;
using std::setprecision;
using boost::shared_ptr;
using boost::optional;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
VideoContent::VideoContent (shared_ptr<const Film> f)
: Content (f)
, _video_length (0)
- , _original_video_frame_rate (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
, _scale (Config::instance()->default_scale ())
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len)
: Content (f, s)
, _video_length (len)
- , _original_video_frame_rate (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
, _scale (Config::instance()->default_scale ())
VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
: Content (f, p)
, _video_length (0)
- , _original_video_frame_rate (0)
, _video_frame_rate (0)
, _video_frame_type (VIDEO_FRAME_TYPE_2D)
, _scale (Config::instance()->default_scale ())
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
+VideoContent::VideoContent (shared_ptr<const Film> f, cxml::ConstNodePtr node, int version)
: Content (f, node)
{
- _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
_video_size.width = node->number_child<int> ("VideoWidth");
_video_size.height = node->number_child<int> ("VideoHeight");
_video_frame_rate = node->number_child<float> ("VideoFrameRate");
- _original_video_frame_rate = node->optional_number_child<float> ("OriginalVideoFrameRate").get_value_or (_video_frame_rate);
+
+ if (version < 32) {
+ /* DCP-o-matic 1.0 branch */
+ _video_length = ContentTime::from_frames (node->number_child<int64_t> ("VideoLength"), _video_frame_rate);
+ } else {
+ _video_length = ContentTime (node->number_child<ContentTime::Type> ("VideoLength"));
+ }
+
_video_frame_type = static_cast<VideoFrameType> (node->number_child<int> ("VideoFrameType"));
_crop.left = node->number_child<int> ("LeftCrop");
_crop.right = node->number_child<int> ("RightCrop");
}
_colour_conversion = ColourConversion (node->node_child ("ColourConversion"));
+ if (version >= 32) {
+ _fade_in = ContentTime (node->number_child<int64_t> ("FadeIn"));
+ _fade_out = ContentTime (node->number_child<int64_t> ("FadeOut"));
+ }
}
VideoContent::VideoContent (shared_ptr<const Film> f, vector<shared_ptr<Content> > c)
throw JoinError (_("Content to be joined must have the same colour conversion."));
}
+ if (vc->fade_in() != ref->fade_in() || vc->fade_out() != ref->fade_out()) {
+ throw JoinError (_("Content to be joined must have the same fades."));
+ }
+
_video_length += vc->video_length ();
}
_video_size = ref->video_size ();
- _original_video_frame_rate = ref->original_video_frame_rate ();
_video_frame_rate = ref->video_frame_rate ();
_video_frame_type = ref->video_frame_type ();
_crop = ref->crop ();
_scale = ref->scale ();
_colour_conversion = ref->colour_conversion ();
+ _fade_in = ref->fade_in ();
+ _fade_out = ref->fade_out ();
}
void
VideoContent::as_xml (xmlpp::Node* node) const
{
boost::mutex::scoped_lock lm (_mutex);
- node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length));
+ node->add_child("VideoLength")->add_child_text (raw_convert<string> (_video_length.get ()));
node->add_child("VideoWidth")->add_child_text (raw_convert<string> (_video_size.width));
node->add_child("VideoHeight")->add_child_text (raw_convert<string> (_video_size.height));
node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
- node->add_child("OriginalVideoFrameRate")->add_child_text (raw_convert<string> (_original_video_frame_rate));
node->add_child("VideoFrameType")->add_child_text (raw_convert<string> (static_cast<int> (_video_frame_type)));
_crop.as_xml (node);
_scale.as_xml (node->add_child("Scale"));
_colour_conversion.as_xml (node->add_child("ColourConversion"));
+ node->add_child("FadeIn")->add_child_text (raw_convert<string> (_fade_in.get ()));
+ node->add_child("FadeOut")->add_child_text (raw_convert<string> (_fade_out.get ()));
}
void
VideoContent::setup_default_colour_conversion ()
{
- _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
+ _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
}
void
VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
{
/* These examiner calls could call other content methods which take a lock on the mutex */
- libdcp::Size const vs = d->video_size ();
+ dcp::Size const vs = d->video_size ();
float const vfr = d->video_frame_rate ();
-
+ ContentTime vl = d->video_length ();
+
{
boost::mutex::scoped_lock lm (_mutex);
_video_size = vs;
_video_frame_rate = vfr;
- _original_video_frame_rate = vfr;
+ _video_length = vl;
}
+
+ shared_ptr<const Film> film = _film.lock ();
+ assert (film);
+ LOG_GENERAL ("Video length obtained from header as %1 frames", _video_length.frames (_video_frame_rate));
signal_changed (VideoContentProperty::VIDEO_SIZE);
signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
+ signal_changed (ContentProperty::LENGTH);
}
{
return String::compose (
"video: length %1, size %2x%3, rate %4",
- video_length_after_3d_combine(), video_size().width, video_size().height, video_frame_rate()
+ video_length_after_3d_combine().seconds(),
+ video_size().width,
+ video_size().height,
+ video_frame_rate()
);
}
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_3d_split () const
{
- libdcp::Size const s = video_size ();
+ dcp::Size const s = video_size ();
switch (video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
case VIDEO_FRAME_TYPE_3D_RIGHT:
return s;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- return libdcp::Size (s.width / 2, s.height);
+ return dcp::Size (s.width / 2, s.height);
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- return libdcp::Size (s.width, s.height / 2);
+ return dcp::Size (s.width, s.height / 2);
}
assert (false);
signal_changed (VideoContentProperty::COLOUR_CONVERSION);
}
+void
+VideoContent::set_fade_in (ContentTime t)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _fade_in = t;
+ }
+
+ signal_changed (VideoContentProperty::VIDEO_FADE_IN);
+}
+
+void
+VideoContent::set_fade_out (ContentTime t)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _fade_out = t;
+ }
+
+ signal_changed (VideoContentProperty::VIDEO_FADE_OUT);
+}
+
/** @return Video size after 3D split and crop */
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_crop () const
{
return crop().apply (video_size_after_3d_split ());
}
/** @param t A time offset from the start of this piece of content.
- * @return Corresponding frame index.
+ * @return Corresponding time with respect to the content.
*/
-VideoContent::Frame
-VideoContent::time_to_content_video_frames (Time t) const
+ContentTime
+VideoContent::dcp_time_to_content_time (DCPTime t) const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateChange frc (video_frame_rate(), film->video_frame_rate());
-
- /* Here we are converting from time (in the DCP) to a frame number in the content.
- Hence we need to use the DCP's frame rate and the double/skip correction, not
- the source's rate.
- */
- return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
+ return ContentTime (t, FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
void
signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
}
+optional<float>
+VideoContent::fade (VideoFrame f) const
+{
+ assert (f >= 0);
+
+ if (f < fade_in().frames (video_frame_rate ())) {
+ return float (f) / _fade_in.frames (video_frame_rate ());
+ }
+
+ VideoFrame fade_out_start = ContentTime (video_length() - fade_out()).frames (video_frame_rate ());
+ if (f >= fade_out_start) {
+ return 1 - float (f - fade_out_start) / fade_out().frames (video_frame_rate ());
+ }
+
+ return optional<float> ();
+}
static int const VIDEO_CROP;
static int const VIDEO_SCALE;
static int const COLOUR_CONVERSION;
+ static int const VIDEO_FADE_IN;
+ static int const VIDEO_FADE_OUT;
};
class VideoContent : public virtual Content
typedef int Frame;
VideoContent (boost::shared_ptr<const Film>);
- VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
+ VideoContent (boost::shared_ptr<const Film>, DCPTime, ContentTime);
VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
- VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
+ VideoContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int);
VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
void as_xml (xmlpp::Node *) const;
virtual std::string information () const;
virtual std::string identifier () const;
- VideoContent::Frame video_length () const {
+ ContentTime video_length () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_length;
}
- VideoContent::Frame video_length_after_3d_combine () const {
+ ContentTime video_length_after_3d_combine () const {
boost::mutex::scoped_lock lm (_mutex);
if (_video_frame_type == VIDEO_FRAME_TYPE_3D_ALTERNATE) {
- return _video_length / 2;
+ return ContentTime (_video_length.get() / 2);
}
return _video_length;
}
- libdcp::Size video_size () const {
+ dcp::Size video_size () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_size;
}
return _video_frame_rate;
}
- float original_video_frame_rate () const {
- boost::mutex::scoped_lock lm (_mutex);
- return _original_video_frame_rate;
- }
-
void set_video_frame_type (VideoFrameType);
void set_video_frame_rate (float);
void set_scale (VideoContentScale);
void set_colour_conversion (ColourConversion);
+
+ void set_fade_in (ContentTime);
+ void set_fade_out (ContentTime);
VideoFrameType video_frame_type () const {
boost::mutex::scoped_lock lm (_mutex);
return _colour_conversion;
}
- libdcp::Size video_size_after_3d_split () const;
- libdcp::Size video_size_after_crop () const;
+ ContentTime fade_in () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _fade_in;
+ }
+
+ ContentTime fade_out () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _fade_out;
+ }
+
+ dcp::Size video_size_after_3d_split () const;
+ dcp::Size video_size_after_crop () const;
+
+ ContentTime dcp_time_to_content_time (DCPTime) const;
- VideoContent::Frame time_to_content_video_frames (Time) const;
+ boost::optional<float> fade (VideoFrame) const;
void scale_and_crop_to_fit_width ();
void scale_and_crop_to_fit_height ();
protected:
void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
- VideoContent::Frame _video_length;
- float _original_video_frame_rate;
+ ContentTime _video_length;
float _video_frame_rate;
private:
- friend class ffmpeg_pts_offset_test;
- friend class best_dcp_frame_rate_test_single;
- friend class best_dcp_frame_rate_test_double;
- friend class audio_sampling_rate_test;
+ friend struct ffmpeg_pts_offset_test;
+ friend struct best_dcp_frame_rate_test_single;
+ friend struct best_dcp_frame_rate_test_double;
+ friend struct audio_sampling_rate_test;
void setup_default_colour_conversion ();
- libdcp::Size _video_size;
+ dcp::Size _video_size;
VideoFrameType _video_frame_type;
Crop _crop;
VideoContentScale _scale;
ColourConversion _colour_conversion;
+ ContentTime _fade_in;
+ ContentTime _fade_out;
};
#endif
/** @param display_container Size of the container that we are displaying this content in.
* @param film_container The size of the film's image.
*/
-libdcp::Size
-VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size display_container, libdcp::Size film_container) const
+dcp::Size
+VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container, int round) const
{
if (_ratio) {
- return fit_ratio_within (_ratio->ratio (), display_container);
+ return fit_ratio_within (_ratio->ratio (), display_container, round);
}
- libdcp::Size const ac = c->video_size_after_crop ();
+ dcp::Size const ac = c->video_size_after_crop ();
/* Force scale if the film_container is smaller than the content's image */
if (_scale || film_container.width < ac.width || film_container.height < ac.height) {
- return fit_ratio_within (ac.ratio (), display_container);
+ return fit_ratio_within (ac.ratio (), display_container, round);
}
/* Scale the image so that it will be in the right place in film_container, even if display_container is a
different size.
*/
- return libdcp::Size (
+ return dcp::Size (
c->video_size().width * float(display_container.width) / film_container.width,
c->video_size().height * float(display_container.height) / film_container.height
);
#include <vector>
#include <boost/shared_ptr.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
namespace cxml {
class Node;
VideoContentScale (bool);
VideoContentScale (boost::shared_ptr<cxml::Node>);
- libdcp::Size size (boost::shared_ptr<const VideoContent>, libdcp::Size, libdcp::Size) const;
+ dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size, dcp::Size, int round) const;
std::string id () const;
std::string name () const;
void as_xml (xmlpp::Node *) const;
#include "video_decoder.h"
#include "image.h"
+#include "image_proxy.h"
+#include "content_video.h"
#include "i18n.h"
using std::cout;
+using std::list;
+using std::max;
using boost::shared_ptr;
+using boost::optional;
-VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c)
- : Decoder (f)
+VideoDecoder::VideoDecoder (shared_ptr<const VideoContent> c)
+#ifdef DCPOMATIC_DEBUG
+ : test_gaps (0)
, _video_content (c)
- , _video_position (0)
+#else
+ : _video_content (c)
+#endif
+ , _same (false)
{
}
+list<ContentVideo>
+VideoDecoder::decoded_video (VideoFrame frame)
+{
+ list<ContentVideo> output;
+
+ for (list<ContentVideo>::const_iterator i = _decoded_video.begin(); i != _decoded_video.end(); ++i) {
+ if (i->frame == frame) {
+ output.push_back (*i);
+ }
+ }
+
+ return output;
+}
+
+/** Get all frames which exist in the content at a given frame index.
+ * @param frame Frame index.
+ * @param accurate true to try hard to return frames at the precise time that was requested, otherwise frames nearby may be returned.
+ * @return Frames; there may be none (if there is no video there), 1 for 2D or 2 for 3D.
+ */
+list<ContentVideo>
+VideoDecoder::get_video (VideoFrame frame, bool accurate)
+{
+ /* At this stage, if we have get_video()ed before, _decoded_video will contain the last frame that this
+ method returned (and possibly a few more). If the requested frame is not in _decoded_video and it is not the next
+ one after the end of _decoded_video we need to seek.
+ */
+
+ if (_decoded_video.empty() || frame < _decoded_video.front().frame || frame > (_decoded_video.back().frame + 1)) {
+ seek (ContentTime::from_frames (frame, _video_content->video_frame_rate()), accurate);
+ }
+
+ list<ContentVideo> dec;
+
+ /* Now enough pass() calls should either:
+ * (a) give us what we want, or
+ * (b) give us something after what we want, indicating that we will never get what we want, or
+ * (c) hit the end of the decoder.
+ */
+ if (accurate) {
+ /* We are being accurate, so we want the right frame.
+ * This could all be one statement but it's split up for clarity.
+ */
+ while (true) {
+ if (!decoded_video(frame).empty ()) {
+ /* We got what we want */
+ break;
+ }
+
+ if (pass ()) {
+ /* The decoder has nothing more for us */
+ break;
+ }
+
+ if (!_decoded_video.empty() && _decoded_video.front().frame > frame) {
+ /* We're never going to get the frame we want. Perhaps the caller is asking
+ * for a video frame before the content's video starts (if its audio
+ * begins before its video, for example).
+ */
+ break;
+ }
+ }
+
+ dec = decoded_video (frame);
+ } else {
+ /* Any frame will do: use the first one that comes out of pass() */
+ while (_decoded_video.empty() && !pass ()) {}
+ if (!_decoded_video.empty ()) {
+ dec.push_back (_decoded_video.front ());
+ }
+ }
+
+ /* Clean up _decoded_video; keep the frame we are returning, but nothing before that */
+ while (!_decoded_video.empty() && _decoded_video.front().frame < dec.front().frame) {
+ _decoded_video.pop_front ();
+ }
+
+ return dec;
+}
+
+
+/** Called by subclasses when they have a video frame ready */
void
-VideoDecoder::video (shared_ptr<const ImageProxy> image, bool same, VideoContent::Frame frame)
+VideoDecoder::video (shared_ptr<const ImageProxy> image, VideoFrame frame)
{
+ /* We may receive the same frame index twice for 3D, and we need to know
+ when that happens.
+ */
+ _same = (!_decoded_video.empty() && frame == _decoded_video.back().frame);
+
+ /* Fill in gaps */
+ /* XXX: 3D */
+
+ while (!_decoded_video.empty () && (_decoded_video.back().frame + 1) < frame) {
+#ifdef DCPOMATIC_DEBUG
+ test_gaps++;
+#endif
+ _decoded_video.push_back (
+ ContentVideo (
+ _decoded_video.back().image,
+ _decoded_video.back().eyes,
+ _decoded_video.back().part,
+ _decoded_video.back().frame + 1
+ )
+ );
+ }
+
switch (_video_content->video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
- Video (image, EYES_BOTH, PART_WHOLE, same, frame);
+ _decoded_video.push_back (ContentVideo (image, EYES_BOTH, PART_WHOLE, frame));
break;
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
- Video (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, same, frame / 2);
+ _decoded_video.push_back (ContentVideo (image, _same ? EYES_RIGHT : EYES_LEFT, PART_WHOLE, frame));
break;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- Video (image, EYES_LEFT, PART_LEFT_HALF, same, frame);
- Video (image, EYES_RIGHT, PART_RIGHT_HALF, same, frame);
+ _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_LEFT_HALF, frame));
+ _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_RIGHT_HALF, frame));
break;
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- Video (image, EYES_LEFT, PART_TOP_HALF, same, frame);
- Video (image, EYES_RIGHT, PART_BOTTOM_HALF, same, frame);
+ _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_TOP_HALF, frame));
+ _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_BOTTOM_HALF, frame));
break;
case VIDEO_FRAME_TYPE_3D_LEFT:
- Video (image, EYES_LEFT, PART_WHOLE, same, frame);
+ _decoded_video.push_back (ContentVideo (image, EYES_LEFT, PART_WHOLE, frame));
break;
case VIDEO_FRAME_TYPE_3D_RIGHT:
- Video (image, EYES_RIGHT, PART_WHOLE, same, frame);
+ _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, PART_WHOLE, frame));
break;
+ default:
+ assert (false);
}
-
- _video_position = frame + 1;
+}
+
+void
+VideoDecoder::seek (ContentTime, bool)
+{
+ _decoded_video.clear ();
}
/*
- 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/lib/video_decoder.h
+ * @brief VideoDecoder class.
+ */
+
#ifndef DCPOMATIC_VIDEO_DECODER_H
#define DCPOMATIC_VIDEO_DECODER_H
#include "decoder.h"
#include "video_content.h"
#include "util.h"
+#include "content_video.h"
class VideoContent;
class ImageProxy;
+/** @class VideoDecoder
+ * @brief Parent for classes which decode video.
+ */
class VideoDecoder : public virtual Decoder
{
public:
- VideoDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const VideoContent>);
-
- /** Seek so that the next pass() will yield (approximately) the requested frame.
- * Pass accurate = true to try harder to get close to the request.
- */
- virtual void seek (VideoContent::Frame frame, bool accurate) = 0;
-
- /** Emitted when a video frame is ready.
- * First parameter is the video image.
- * Second parameter is the eye(s) which should see this image.
- * Third parameter is the part of this image that should be used.
- * Fourth parameter is true if the image is the same as the last one that was emitted for this Eyes value.
- * Fourth parameter is the frame within our source.
- */
- boost::signals2::signal<void (boost::shared_ptr<const ImageProxy>, Eyes, Part, bool, VideoContent::Frame)> Video;
-
+ VideoDecoder (boost::shared_ptr<const VideoContent> c);
+
+ std::list<ContentVideo> get_video (VideoFrame frame, bool accurate);
+
+ boost::shared_ptr<const VideoContent> video_content () const {
+ return _video_content;
+ }
+
+#ifdef DCPOMATIC_DEBUG
+ int test_gaps;
+#endif
+
protected:
- void video (boost::shared_ptr<const ImageProxy>, bool, VideoContent::Frame);
+ void seek (ContentTime time, bool accurate);
+ void video (boost::shared_ptr<const ImageProxy>, VideoFrame frame);
+ std::list<ContentVideo> decoded_video (VideoFrame frame);
+
boost::shared_ptr<const VideoContent> _video_content;
- /** This is in frames without taking 3D into account (e.g. if we are doing 3D alternate,
- * this would equal 2 on the left-eye second frame (not 1)).
- */
- VideoContent::Frame _video_position;
+ std::list<ContentVideo> _decoded_video;
+ bool _same;
};
#endif
/*
- 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 <libdcp/types.h>
+/** @file src/lib/video_examiner.h
+ * @brief VideoExaminer class.
+ */
+
+#include <dcp/types.h>
#include "types.h"
#include "video_content.h"
+/** @class VideoExaminer
+ * @brief Parent for classes which examine video sources and obtain information about them.
+ */
class VideoExaminer
{
public:
virtual ~VideoExaminer () {}
virtual float video_frame_rate () const = 0;
- virtual libdcp::Size video_size () const = 0;
- virtual VideoContent::Frame video_length () const = 0;
+ virtual dcp::Size video_size () const = 0;
+ virtual ContentTime video_length () const = 0;
};
#include <fstream>
#include <cerrno>
-#include <libdcp/mono_picture_asset.h>
-#include <libdcp/stereo_picture_asset.h>
-#include <libdcp/sound_asset.h>
-#include <libdcp/reel.h>
-#include <libdcp/dcp.h>
-#include <libdcp/cpl.h>
+#include <dcp/mono_picture_mxf.h>
+#include <dcp/stereo_picture_mxf.h>
+#include <dcp/sound_mxf.h>
+#include <dcp/sound_mxf_writer.h>
+#include <dcp/reel.h>
+#include <dcp/reel_mono_picture_asset.h>
+#include <dcp/reel_stereo_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/reel_subtitle_asset.h>
+#include <dcp/dcp.h>
+#include <dcp/cpl.h>
+#include <dcp/signer.h>
#include "writer.h"
#include "compose.hpp"
#include "film.h"
#include "ratio.h"
#include "log.h"
-#include "dcp_video_frame.h"
+#include "dcp_video.h"
#include "dcp_content_type.h"
-#include "player.h"
#include "audio_mapping.h"
#include "config.h"
#include "job.h"
#include "cross.h"
+#include "audio_buffers.h"
#include "md5_digester.h"
+#include "encoded_data.h"
#include "version.h"
#include "i18n.h"
using std::cout;
using boost::shared_ptr;
using boost::weak_ptr;
+using boost::dynamic_pointer_cast;
int const Writer::_maximum_frames_in_memory = Config::instance()->num_local_encoding_threads() + 4;
, _last_written_eyes (EYES_RIGHT)
, _full_written (0)
, _fake_written (0)
- , _repeat_written (0)
, _pushed_to_disk (0)
{
/* Remove any old DCP */
*/
if (_film->three_d ()) {
- _picture_asset.reset (new libdcp::StereoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
+ _picture_mxf.reset (new dcp::StereoPictureMXF (dcp::Fraction (_film->video_frame_rate (), 1)));
} else {
- _picture_asset.reset (new libdcp::MonoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
+ _picture_mxf.reset (new dcp::MonoPictureMXF (dcp::Fraction (_film->video_frame_rate (), 1)));
}
- _picture_asset->set_edit_rate (_film->video_frame_rate ());
- _picture_asset->set_size (_film->frame_size ());
- _picture_asset->set_interop (_film->interop ());
+ _picture_mxf->set_size (_film->frame_size ());
if (_film->encrypted ()) {
- _picture_asset->set_key (_film->key ());
+ _picture_mxf->set_key (_film->key ());
}
- _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
+ _picture_mxf_writer = _picture_mxf->start_write (
+ _film->internal_video_mxf_dir() / _film->internal_video_mxf_filename(),
+ _film->interop() ? dcp::INTEROP : dcp::SMPTE,
+ _first_nonexistant_frame > 0
+ );
if (_film->audio_channels ()) {
- _sound_asset.reset (new libdcp::SoundAsset (_film->directory (), _film->audio_mxf_filename ()));
- _sound_asset->set_edit_rate (_film->video_frame_rate ());
- _sound_asset->set_channels (_film->audio_channels ());
- _sound_asset->set_sampling_rate (_film->audio_frame_rate ());
- _sound_asset->set_interop (_film->interop ());
+ _sound_mxf.reset (new dcp::SoundMXF (dcp::Fraction (_film->video_frame_rate(), 1), _film->audio_frame_rate (), _film->audio_channels ()));
if (_film->encrypted ()) {
- _sound_asset->set_key (_film->key ());
+ _sound_mxf->set_key (_film->key ());
}
-
- /* Write the sound asset into the film directory so that we leave the creation
+
+ /* Write the sound MXF into the film directory so that we leave the creation
of the DCP directory until the last minute.
*/
- _sound_asset_writer = _sound_asset->start_write ();
+ _sound_mxf_writer = _sound_mxf->start_write (_film->directory() / _film->audio_mxf_filename(), _film->interop() ? dcp::INTEROP : dcp::SMPTE);
+ }
+
+ /* Check that the signer is OK if we need one */
+ if (_film->is_signed() && !Config::instance()->signer()->valid ()) {
+ throw InvalidSignerError ();
}
_thread = new boost::thread (boost::bind (&Writer::thread, this));
}
FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
- libdcp::FrameInfo info (ifi);
+ dcp::FrameInfo info (ifi);
fclose (ifi);
QueueItem qi;
void
Writer::write (shared_ptr<const AudioBuffers> audio)
{
- if (_sound_asset) {
- _sound_asset_writer->write (audio->data(), audio->frames());
+ if (_sound_mxf_writer) {
+ _sound_mxf_writer->write (audio->data(), audio->frames());
}
}
qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
}
- libdcp::FrameInfo fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
+ dcp::FrameInfo fin = _picture_mxf_writer->write (qi.encoded->data(), qi.encoded->size());
qi.encoded->write_info (_film, qi.frame, qi.eyes, fin);
_last_written[qi.eyes] = qi.encoded;
++_full_written;
}
case QueueItem::FAKE:
LOG_GENERAL (N_("Writer FAKE-writes %1 to MXF"), qi.frame);
- _picture_asset_writer->fake_write (qi.size);
+ _picture_mxf_writer->fake_write (qi.size);
_last_written[qi.eyes].reset ();
++_fake_written;
break;
- case QueueItem::REPEAT:
- {
- LOG_GENERAL (N_("Writer REPEAT-writes %1 to MXF"), qi.frame);
- libdcp::FrameInfo fin = _picture_asset_writer->write (
- _last_written[qi.eyes]->data(),
- _last_written[qi.eyes]->size()
- );
-
- _last_written[qi.eyes]->write_info (_film, qi.frame, qi.eyes, fin);
- ++_repeat_written;
- break;
- }
}
lock.lock ();
_last_written_frame = qi.frame;
_last_written_eyes = qi.eyes;
- if (_film->length()) {
- shared_ptr<Job> job = _job.lock ();
- assert (job);
- int total = _film->time_to_video_frames (_film->length ());
- if (_film->three_d ()) {
- /* _full_written and so on are incremented for each eye, so we need to double the total
- frames to get the correct progress.
- */
- total *= 2;
- }
- job->set_progress (float (_full_written + _fake_written + _repeat_written) / total);
+ shared_ptr<Job> job = _job.lock ();
+ assert (job);
+ int64_t total = _film->length().frames (_film->video_frame_rate ());
+ if (_film->three_d ()) {
+ /* _full_written and so on are incremented for each eye, so we need to double the total
+ frames to get the correct progress.
+ */
+ total *= 2;
+ }
+ if (total) {
+ job->set_progress (float (_full_written + _fake_written) / total);
}
}
terminate_thread (true);
- _picture_asset_writer->finalize ();
- if (_sound_asset_writer) {
- _sound_asset_writer->finalize ();
+ _picture_mxf_writer->finalize ();
+ if (_sound_mxf_writer) {
+ _sound_mxf_writer->finalize ();
}
- int const frames = _last_written_frame + 1;
-
- _picture_asset->set_duration (frames);
-
/* Hard-link the video MXF into the DCP */
boost::filesystem::path video_from;
video_from /= _film->internal_video_mxf_dir();
}
}
- /* And update the asset */
-
- _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
- _picture_asset->set_file_name (_film->video_mxf_filename ());
+ _picture_mxf->set_file (video_to);
/* Move the audio MXF into the DCP */
- if (_sound_asset) {
+ if (_sound_mxf) {
boost::filesystem::path audio_to;
audio_to /= _film->dir (_film->dcp_name ());
audio_to /= _film->audio_mxf_filename ();
String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
);
}
-
- _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
- _sound_asset->set_duration (frames);
+
+ _sound_mxf->set_file (audio_to);
}
-
- libdcp::DCP dcp (_film->dir (_film->dcp_name()));
- shared_ptr<libdcp::CPL> cpl (
- new libdcp::CPL (
- _film->dir (_film->dcp_name()),
+ dcp::DCP dcp (_film->dir (_film->dcp_name()));
+
+ shared_ptr<dcp::CPL> cpl (
+ new dcp::CPL (
_film->dcp_name(),
- _film->dcp_content_type()->libdcp_kind (),
- frames,
- _film->video_frame_rate ()
+ _film->dcp_content_type()->libdcp_kind ()
)
);
- dcp.add_cpl (cpl);
+ dcp.add (cpl);
+
+ shared_ptr<dcp::Reel> reel (new dcp::Reel ());
+
+ shared_ptr<dcp::MonoPictureMXF> mono = dynamic_pointer_cast<dcp::MonoPictureMXF> (_picture_mxf);
+ if (mono) {
+ reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelMonoPictureAsset (mono, 0)));
+ dcp.add (mono);
+ }
+
+ shared_ptr<dcp::StereoPictureMXF> stereo = dynamic_pointer_cast<dcp::StereoPictureMXF> (_picture_mxf);
+ if (stereo) {
+ reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelStereoPictureAsset (stereo, 0)));
+ dcp.add (stereo);
+ }
+
+ if (_sound_mxf) {
+ reel->add (shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (_sound_mxf, 0)));
+ dcp.add (_sound_mxf);
+ }
- cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
- _picture_asset,
- _sound_asset,
- shared_ptr<libdcp::SubtitleAsset> ()
- )
- ));
+ if (_subtitle_content) {
+ _subtitle_content->write_xml (_film->dir (_film->dcp_name ()) / _film->subtitle_xml_filename ());
+ reel->add (shared_ptr<dcp::ReelSubtitleAsset> (
+ new dcp::ReelSubtitleAsset (
+ _subtitle_content,
+ dcp::Fraction (_film->video_frame_rate(), 1),
+ _picture_mxf->intrinsic_duration (),
+ 0
+ )
+ ));
+
+ dcp.add (_subtitle_content);
+ }
+
+ cpl->add (reel);
shared_ptr<Job> job = _job.lock ();
assert (job);
job->sub (_("Computing image digest"));
- _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
+ _picture_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
- if (_sound_asset) {
+ if (_sound_mxf) {
job->sub (_("Computing audio digest"));
- _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
+ _sound_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
}
- libdcp::XMLMetadata meta;
+ dcp::XMLMetadata meta;
meta.issuer = Config::instance()->dcp_issuer ();
meta.creator = String::compose ("DCP-o-matic %1 %2", dcpomatic_version, dcpomatic_git_commit);
meta.set_issue_date_now ();
- dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
-
- LOG_GENERAL (
- N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT; %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk
- );
-}
-/** Tell the writer that frame `f' should be a repeat of the frame before it */
-void
-Writer::repeat (int f, Eyes e)
-{
- boost::mutex::scoped_lock lock (_mutex);
-
- while (_queued_full_in_memory > _maximum_frames_in_memory) {
- /* The queue is too big; wait until that is sorted out */
- _full_condition.wait (lock);
- }
-
- QueueItem qi;
- qi.type = QueueItem::REPEAT;
- qi.frame = f;
- if (_film->three_d() && e == EYES_BOTH) {
- qi.eyes = EYES_LEFT;
- _queue.push_back (qi);
- qi.eyes = EYES_RIGHT;
- _queue.push_back (qi);
- } else {
- qi.eyes = e;
- _queue.push_back (qi);
+ shared_ptr<const dcp::Signer> signer;
+ if (_film->is_signed ()) {
+ signer = Config::instance()->signer ();
+ /* We did check earlier, but check again here to be on the safe side */
+ if (!signer->valid ()) {
+ throw InvalidSignerError ();
+ }
}
- /* Now there's something to do: wake anything wait()ing on _empty_condition */
- _empty_condition.notify_all ();
+ dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, signer);
+
+ LOG_GENERAL (
+ N_("Wrote %1 FULL, %2 FAKE, %3 pushed to disk"), _full_written, _fake_written, _pushed_to_disk
+ );
}
bool
return false;
}
- libdcp::FrameInfo info (ifi);
+ dcp::FrameInfo info (ifi);
fclose (ifi);
if (info.size == 0) {
LOG_GENERAL ("Existing frame %1 has no info file", f);
return (frame != 0 && frame < _first_nonexistant_frame);
}
+void
+Writer::write (PlayerSubtitles subs)
+{
+ if (subs.text.empty ()) {
+ return;
+ }
+
+ if (!_subtitle_content) {
+ _subtitle_content.reset (
+ new dcp::SubtitleContent (_film->name(), _film->isdcf_metadata().subtitle_language)
+ );
+ }
+
+ for (list<dcp::SubtitleString>::const_iterator i = subs.text.begin(); i != subs.text.end(); ++i) {
+ _subtitle_content->add (*i);
+ }
+}
+
bool
operator< (QueueItem const & a, QueueItem const & b)
{
*/
+/** @file src/lib/writer.h
+ * @brief Writer class.
+ */
+
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
+#include <dcp/subtitle_content.h>
#include "exceptions.h"
#include "types.h"
+#include "player_subtitles.h"
class Film;
class EncodedData;
class AudioBuffers;
class Job;
-namespace libdcp {
- class MonoPictureAsset;
- class MonoPictureAssetWriter;
- class StereoPictureAsset;
- class StereoPictureAssetWriter;
- class PictureAsset;
- class PictureAssetWriter;
- class SoundAsset;
- class SoundAssetWriter;
+namespace dcp {
+ class MonoPictureMXF;
+ class MonoPictureMXFWriter;
+ class StereoPictureMXF;
+ class StereoPictureMXFWriter;
+ class PictureMXF;
+ class PictureMXFWriter;
+ class SoundMXF;
+ class SoundMXFWriter;
}
struct QueueItem
state but we use the data that is already on disk.
*/
FAKE,
- /** this is a repeat of the last frame to be written */
- REPEAT
} type;
/** encoded data for FULL */
bool operator< (QueueItem const & a, QueueItem const & b);
bool operator== (QueueItem const & a, QueueItem const & b);
+/** @class Writer
+ * @brief Class to manage writing JPEG2000 and audio data to MXFs on disk.
+ *
+ * This class creates sound and picture MXFs, then takes EncodedData
+ * or AudioBuffers objects (containing image or sound data respectively)
+ * and writes them to the MXFs.
+ *
+ * ::write() for EncodedData can be called out of order, and the Writer
+ * will sort it out. write() for AudioBuffers must be called in order.
+ */
+
class Writer : public ExceptionStore, public boost::noncopyable
{
public:
void write (boost::shared_ptr<const EncodedData>, int, Eyes);
void fake_write (int, Eyes);
void write (boost::shared_ptr<const AudioBuffers>);
+ void write (PlayerSubtitles);
void repeat (int f, Eyes);
void finish ();
int _full_written;
/** number of FAKE written frames */
int _fake_written;
- /** number of REPEAT written frames */
- int _repeat_written;
/** number of frames pushed to disk and then recovered
due to the limit of frames to be held in memory.
*/
int _pushed_to_disk;
- boost::shared_ptr<libdcp::PictureAsset> _picture_asset;
- boost::shared_ptr<libdcp::PictureAssetWriter> _picture_asset_writer;
- boost::shared_ptr<libdcp::SoundAsset> _sound_asset;
- boost::shared_ptr<libdcp::SoundAssetWriter> _sound_asset_writer;
+ boost::shared_ptr<dcp::PictureMXF> _picture_mxf;
+ boost::shared_ptr<dcp::PictureMXFWriter> _picture_mxf_writer;
+ boost::shared_ptr<dcp::SoundMXF> _sound_mxf;
+ boost::shared_ptr<dcp::SoundMXFWriter> _sound_mxf_writer;
+ boost::shared_ptr<dcp::SubtitleContent> _subtitle_content;
};
audio_buffers.cc
audio_content.cc
audio_decoder.cc
+ audio_filter.cc
audio_mapping.cc
+ audio_processor.cc
cinema.cc
+ cinema_sound_processor.cc
colour_conversion.cc
config.cc
content.cc
content_factory.cc
+ content_subtitle.cc
cross.cc
+ dcp_content.cc
dcp_content_type.cc
- dcp_video_frame.cc
- decoder.cc
+ dcp_decoder.cc
+ dcp_examiner.cc
+ dcp_subtitle_content.cc
+ dcp_subtitle_decoder.cc
+ dcp_video.cc
+ dcpomatic_time.cc
dolby_cp750.cc
encoder.cc
+ encoded_data.cc
examine_content_job.cc
exceptions.cc
file_group.cc
filter_graph.cc
ffmpeg.cc
+ ffmpeg_audio_stream.cc
ffmpeg_content.cc
ffmpeg_decoder.cc
ffmpeg_examiner.cc
+ ffmpeg_stream.cc
+ ffmpeg_subtitle_stream.cc
film.cc
filter.cc
frame_rate_change.cc
image_examiner.cc
image_proxy.cc
isdcf_metadata.cc
+ j2k_image_proxy.cc
job.cc
job_manager.cc
kdm.cc
log.cc
+ magick_image_proxy.cc
md5_digester.cc
- piece.cc
+ mid_side_decoder.cc
player.cc
- player_video_frame.cc
+ player_video.cc
playlist.cc
+ position_image.cc
ratio.cc
+ raw_image_proxy.cc
+ render_subtitles.cc
resampler.cc
safe_stringstream.cc
scp_dcp_job.cc
send_kdm_email_job.cc
server.cc
server_finder.cc
+ single_stream_audio_content.cc
sndfile_content.cc
sndfile_decoder.cc
- sound_processor.cc
- subtitle.cc
+ subrip.cc
+ subrip_content.cc
+ subrip_decoder.cc
subtitle_content.cc
subtitle_decoder.cc
timer.cc
types.cc
ui_signaller.cc
update.cc
+ upmixer_a.cc
util.cc
video_content.cc
video_content_scale.cc
else:
obj = bld(features = 'cxx cxxshlib')
- obj.name = 'libdcpomatic'
+ obj.name = 'libdcpomatic2'
obj.export_includes = ['..']
obj.uselib = """
AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2
- SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XMLPP
- CURL ZIP QUICKMAIL XMLSEC
+ SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
+ CURL ZIP QUICKMAIL PANGOMM CAIROMM XMLSEC SUB
"""
if bld.env.TARGET_OSX:
if bld.env.BUILD_STATIC:
obj.uselib += ' XMLPP'
- obj.target = 'dcpomatic'
+ obj.target = 'dcpomatic2'
- i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic', bld)
+ i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic2', bld)
def pot(bld):
i18n.pot(os.path.join('src', 'lib'), sources, 'libdcpomatic')
#include <wx/stdpaths.h>
#include <wx/cmdline.h>
#include <wx/preferences.h>
-#include <libdcp/exceptions.h>
+#include <dcp/exceptions.h>
#include "wx/film_viewer.h"
#include "wx/film_editor.h"
#include "wx/job_manager_view.h"
#include "wx/servers_list_dialog.h"
#include "wx/hints_dialog.h"
#include "wx/update_dialog.h"
+#include "wx/content_panel.h"
#include "lib/film.h"
#include "lib/config.h"
#include "lib/util.h"
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-// #define DCPOMATIC_WINDOWS_CONSOLE 1
-
class FilmChangedDialog
{
public:
, _history_position (0)
, _history_separator (0)
{
-#if defined(DCPOMATIC_WINDOWS) && defined(DCPOMATIC_WINDOWS_CONSOLE)
- AllocConsole();
-
- HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
- int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT);
- FILE* hf_out = _fdopen(hCrt, "w");
- setvbuf(hf_out, NULL, _IONBF, 1);
- *stdout = *hf_out;
-
- HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
- hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT);
- FILE* hf_in = _fdopen(hCrt, "r");
- setvbuf(hf_in, NULL, _IONBF, 128);
- *stdin = *hf_in;
+#if defined(DCPOMATIC_WINDOWS)
+ if (Config::instance()->win32_console ()) {
+ AllocConsole();
+
+ HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
+ int hCrt = _open_osfhandle((intptr_t) handle_out, _O_TEXT);
+ FILE* hf_out = _fdopen(hCrt, "w");
+ setvbuf(hf_out, NULL, _IONBF, 1);
+ *stdout = *hf_out;
+
+ HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
+ hCrt = _open_osfhandle((intptr_t) handle_in, _O_TEXT);
+ FILE* hf_in = _fdopen(hCrt, "r");
+ setvbuf(hf_in, NULL, _IONBF, 128);
+ *stdin = *hf_in;
+
+ cout << "DCP-o-matic is starting." << "\n";
+ }
#endif
wxMenuBar* bar = new wxMenuBar;
Bind (wxEVT_CLOSE_WINDOW, boost::bind (&Frame::close, this, _1));
- wxAcceleratorEntry accel[1];
- accel[0].Set (wxACCEL_CTRL, static_cast<int>('A'), ID_add_file);
- Bind (wxEVT_MENU, boost::bind (&FilmEditor::content_add_file_clicked, _film_editor), ID_add_file);
- wxAcceleratorTable accel_table (1, accel);
- SetAcceleratorTable (accel_table);
-
/* Use a panel as the only child of the Frame so that we avoid
the dark-grey background on Windows.
*/
JobManager::instance()->ActiveJobsChanged.connect (boost::bind (&Frame::set_menu_sensitivity, this));
overall_panel->SetSizer (main_sizer);
+
+ wxAcceleratorEntry accel[1];
+ accel[0].Set (wxACCEL_CTRL, static_cast<int>('A'), ID_add_file);
+ Bind (wxEVT_MENU, boost::bind (&ContentPanel::add_file_clicked, _film_editor->content_panel()), ID_add_file);
+ wxAcceleratorTable accel_table (1, accel);
+ SetAcceleratorTable (accel_table);
}
void new_film (boost::filesystem::path path)
shared_ptr<Job> (new SendKDMEmailJob (_film, d->screens (), d->cpl (), d->from (), d->until (), d->formulation ()))
);
}
- } catch (libdcp::NotEncryptedError& e) {
+ } catch (dcp::NotEncryptedError& e) {
error_dialog (this, _("CPL's content is not encrypted."));
} catch (exception& e) {
error_dialog (this, e.what ());
void content_scale_to_fit_width ()
{
- VideoContentList vc = _film_editor->selected_video_content ();
+ VideoContentList vc = _film_editor->content_panel()->selected_video ();
for (VideoContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
(*i)->scale_and_crop_to_fit_width ();
}
void content_scale_to_fit_height ()
{
- VideoContentList vc = _film_editor->selected_video_content ();
+ VideoContentList vc = _film_editor->content_panel()->selected_video ();
for (VideoContentList::iterator i = vc.begin(); i != vc.end(); ++i) {
(*i)->scale_and_crop_to_fit_height ();
}
}
bool const dcp_creation = (i != jobs.end ()) && !(*i)->finished ();
bool const have_cpl = _film && !_film->cpls().empty ();
- bool const have_selected_video_content = !_film_editor->selected_video_content().empty();
+ bool const have_selected_video_content = !_film_editor->content_panel()->selected_video().empty();
for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
{ wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
};
+/** @class App
+ * @brief The magic App class for wxWidgets.
+ */
class App : public wxApp
{
bool OnInit ()
#include <iostream>
#include <iomanip>
#include <getopt.h>
-#include <libdcp/version.h>
+#include <dcp/version.h>
#include "lib/film.h"
#include "lib/filter.h"
#include "lib/transcode_job.h"
for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (*i);
if (ic) {
- ic->set_video_length (still_length * 24);
+ ic->set_video_length (ContentTime::from_seconds (still_length));
}
}
*/
#include <getopt.h>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
#include "lib/film.h"
#include "lib/cinema.h"
#include "lib/kdm.h"
cerr << "Syntax: " << program_name << " [OPTION] [<FILM>]\n"
" -h, --help show this help\n"
" -o, --output output file or directory\n"
- " -f, --valid-from valid from time (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
- " -t, --valid-to valid to time (e.g. \"2014-09-28 01:41:51\")\n"
+ " -f, --valid-from valid from time (in local time zone) (e.g. \"2013-09-28 01:41:51\") or \"now\"\n"
+ " -t, --valid-to valid to time (in local time zone) (e.g. \"2014-09-28 01:41:51\")\n"
" -d, --valid-duration valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")\n"
" --formulation modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]\n"
" -z, --zip ZIP each cinema's KDMs into its own file\n"
bool cinemas = false;
string duration_string;
bool verbose = false;
- libdcp::KDM::Formulation formulation = libdcp::KDM::MODIFIED_TRANSITIONAL_1;
+ dcp::Formulation formulation = dcp::MODIFIED_TRANSITIONAL_1;
program_name = argv[0];
break;
case 'C':
if (string (optarg) == "modified-transitional-1") {
- formulation = libdcp::KDM::MODIFIED_TRANSITIONAL_1;
+ formulation = dcp::MODIFIED_TRANSITIONAL_1;
} else if (string (optarg) == "dci-any") {
- formulation = libdcp::KDM::DCI_ANY;
+ formulation = dcp::DCI_ANY;
} else if (string (optarg) == "dci-specific") {
- formulation = libdcp::KDM::DCI_SPECIFIC;
+ formulation = dcp::DCI_SPECIFIC;
} else {
- error ("unrecognised KDM formulation " + formulation);
+ error ("unrecognised KDM formulation " + string (optarg));
}
}
}
error ("you must specify --output");
}
- shared_ptr<libdcp::Certificate> certificate (new libdcp::Certificate (boost::filesystem::path (certificate_file)));
- libdcp::KDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get(), formulation);
+ dcp::Certificate certificate (dcp::file_to_string (certificate_file));
+ dcp::EncryptedKDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get(), formulation);
kdm.as_xml (output);
if (verbose) {
cout << "Generated KDM " << output << " for certificate.\n";
try {
if (zip) {
- write_kdm_zip_files (film, (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation, output);
+ write_kdm_zip_files (
+ film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), formulation, output
+ );
+
if (verbose) {
cout << "Wrote ZIP files to " << output << "\n";
}
} else {
- write_kdm_files (film, (*i)->screens(), cpl, valid_from.get(), valid_to.get(), formulation, output);
+ write_kdm_files (
+ film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), formulation, output
+ );
+
if (verbose) {
cout << "Wrote KDM files to " << output << "\n";
}
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>
#include "lib/config.h"
-#include "lib/dcp_video_frame.h"
+#include "lib/dcp_video.h"
#include "lib/exceptions.h"
#include "lib/util.h"
#include "lib/config.h"
"X-Generator: Poedit 1.6.5\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/tools/dcpomatic.cc:306
+#: src/tools/dcpomatic.cc:309
msgid "%1 already exists as a file, so you cannot use it for a new film."
msgstr ""
"%1 existiert bereits als Datei, kann also nicht für einen neuen Film benutzt "
msgid "&Add Film..."
msgstr "&Projekt hinzufügen"
-#: src/tools/dcpomatic.cc:652
+#: src/tools/dcpomatic.cc:655
msgid "&Content"
msgstr "&Quelle..."
-#: src/tools/dcpomatic.cc:650
+#: src/tools/dcpomatic.cc:653
msgid "&Edit"
msgstr "&Bearbeiten"
-#: src/tools/dcpomatic.cc:614
+#: src/tools/dcpomatic.cc:617
msgid "&Exit"
msgstr "&Ende"
-#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:56
+#: src/tools/dcpomatic.cc:651 src/tools/dcpomatic_batch.cc:56
msgid "&File"
msgstr "&Datei"
-#: src/tools/dcpomatic.cc:655 src/tools/dcpomatic_batch.cc:57
+#: src/tools/dcpomatic.cc:658 src/tools/dcpomatic_batch.cc:57
msgid "&Help"
msgstr "&Hilfe"
-#: src/tools/dcpomatic.cc:653
+#: src/tools/dcpomatic.cc:656
msgid "&Jobs"
msgstr "&Aufgaben"
-#: src/tools/dcpomatic.cc:631
+#: src/tools/dcpomatic.cc:634
#, fuzzy
msgid "&Make DCP\tCtrl-M"
msgstr "&DCP erstellen\tCtrl-M"
-#: src/tools/dcpomatic.cc:601
+#: src/tools/dcpomatic.cc:604
#, fuzzy
msgid "&Open...\tCtrl-O"
msgstr "&Öffnen...\tCtrl-O"
-#: src/tools/dcpomatic.cc:620 src/tools/dcpomatic.cc:623
+#: src/tools/dcpomatic.cc:623 src/tools/dcpomatic.cc:626
#, fuzzy
msgid "&Preferences...\tCtrl-P"
msgstr "&Einstellungen...\tCtrl-P"
-#: src/tools/dcpomatic.cc:605
+#: src/tools/dcpomatic.cc:608
msgid "&Properties..."
msgstr "&Eigenschaften..."
-#: src/tools/dcpomatic.cc:616 src/tools/dcpomatic_batch.cc:51
+#: src/tools/dcpomatic.cc:619 src/tools/dcpomatic_batch.cc:51
msgid "&Quit"
msgstr "&Beenden"
-#: src/tools/dcpomatic.cc:603
+#: src/tools/dcpomatic.cc:606
msgid "&Save\tCtrl-S"
msgstr ""
-#: src/tools/dcpomatic.cc:633
+#: src/tools/dcpomatic.cc:636
msgid "&Send DCP to TMS"
msgstr "&DCP an TMS senden"
-#: src/tools/dcpomatic.cc:654
+#: src/tools/dcpomatic.cc:657
msgid "&Tools"
msgstr "&Werkzeuge"
-#: src/tools/dcpomatic.cc:645 src/tools/dcpomatic_batch.cc:54
+#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:54
msgid "About"
msgstr "Ãœber"
-#: src/tools/dcpomatic.cc:643
+#: src/tools/dcpomatic.cc:646
msgid "About DCP-o-matic"
msgstr "Ãœber DCP-o-matic"
msgid "Add Film..."
msgstr "Projekt hinzufügen..."
-#: src/tools/dcpomatic.cc:821
+#: src/tools/dcpomatic.cc:827
#, fuzzy, c-format
msgid ""
"An exception occurred (%s). Please report this problem to the DCP-o-matic "
"Ein unbekannter Fehler ist aufgetreten. Bitte melden Sie dieses Problem an "
"den Autor von DCP-o-matic (carl@dcpomatic.com)!"
-#: src/tools/dcpomatic.cc:823 src/tools/dcpomatic.cc:832
+#: src/tools/dcpomatic.cc:829 src/tools/dcpomatic.cc:838
msgid ""
"An unknown exception occurred. Please report this problem to the DCP-o-"
"matic author (carl@dcpomatic.com)."
"Ein unbekannter Fehler ist aufgetreten. Bitte melden Sie dieses Problem an "
"den Autor von DCP-o-matic (carl@dcpomatic.com)!"
-#: src/tools/dcpomatic.cc:418
+#: src/tools/dcpomatic.cc:421
msgid "An unknown exeception occurred."
msgstr "Ein unbekannter Fehler ist aufgetreten."
-#: src/tools/dcpomatic.cc:414
+#: src/tools/dcpomatic.cc:417
msgid "CPL's content is not encrypted."
msgstr "Medien der CPL sind nicht verschlüsselt worden."
-#: src/tools/dcpomatic.cc:639
+#: src/tools/dcpomatic.cc:642
msgid "Check for updates"
msgstr "Auf Updates überprüfen..."
-#: src/tools/dcpomatic.cc:760 src/tools/dcpomatic_batch.cc:238
+#: src/tools/dcpomatic.cc:766 src/tools/dcpomatic_batch.cc:238
msgid "Could not load film %1 (%2)"
msgstr "Film %1 (%2) konnte nicht geladen werden"
-#: src/tools/dcpomatic.cc:257 src/tools/dcpomatic_batch.cc:175
+#: src/tools/dcpomatic.cc:260 src/tools/dcpomatic_batch.cc:175
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Der Film konnte nicht bei %s (%s) geöffnet werden"
-#: src/tools/dcpomatic.cc:464
+#: src/tools/dcpomatic.cc:467
msgid "Could not show DCP (could not run konqueror)"
msgstr ""
"DCP kann nicht angezeigt werden (Konqueror konnte nicht gestartet werden)"
-#: src/tools/dcpomatic.cc:457
+#: src/tools/dcpomatic.cc:460
msgid "Could not show DCP (could not run nautilus)"
msgstr "DCP kann nicht angezeigt werden (Nautilus konnte nicht geladen werden)"
-#: src/tools/dcpomatic.cc:277 src/tools/dcpomatic.cc:715
-#: src/tools/dcpomatic.cc:751
+#: src/tools/dcpomatic.cc:280 src/tools/dcpomatic.cc:721
+#: src/tools/dcpomatic.cc:757
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic Batch Converter"
msgstr "DCP-o-matic Batch Converter"
-#: src/tools/dcpomatic.cc:638
+#: src/tools/dcpomatic.cc:641
msgid "Encoding servers..."
msgstr "Encoding Server..."
-#: src/tools/dcpomatic.cc:85
+#: src/tools/dcpomatic.cc:84
msgid "Film changed"
msgstr "Projekt gewechselt"
-#: src/tools/dcpomatic.cc:637
+#: src/tools/dcpomatic.cc:640
msgid "Hints...\tCtrl-H"
msgstr "Tipps...\tCtrl-H"
-#: src/tools/dcpomatic.cc:632
+#: src/tools/dcpomatic.cc:635
msgid "Make &KDMs...\tCtrl-K"
msgstr "&KDM erstellen...\tCtrl-K"
-#: src/tools/dcpomatic.cc:600
+#: src/tools/dcpomatic.cc:603
msgid "New...\tCtrl-N"
msgstr "Neu...\tCtrl-N"
-#: src/tools/dcpomatic.cc:634
+#: src/tools/dcpomatic.cc:637
msgid "S&how DCP"
msgstr "Z&eige DCP"
-#: src/tools/dcpomatic.cc:84
+#: src/tools/dcpomatic.cc:83
#, c-format
msgid "Save changes to film \"%s\" before closing?"
msgstr "Änderungen des Projekts \"%s\" vor dem Schließen speichern ?"
-#: src/tools/dcpomatic.cc:628
+#: src/tools/dcpomatic.cc:631
msgid "Scale to fit &height"
msgstr "...skalieren auf &Höhe DCI-Container"
-#: src/tools/dcpomatic.cc:627
+#: src/tools/dcpomatic.cc:630
msgid "Scale to fit &width"
msgstr "...skalieren auf &Breite DCI-Container"
-#: src/tools/dcpomatic.cc:322 src/tools/dcpomatic_batch.cc:152
+#: src/tools/dcpomatic.cc:325 src/tools/dcpomatic_batch.cc:152
msgid "Select film to open"
msgstr "Zu öffnendes Projekt auswählen"
-#: src/tools/dcpomatic.cc:385
+#: src/tools/dcpomatic.cc:388
#, c-format
msgid ""
"The DCP for this film will take up about %.1f Gb, and the disk that you are "
"Das DCP für diesen Film wird etwa %.1f Gbyte groß. Auf dem ausgewählten "
"Laufwerk sind aber nur %.1f Gbyte frei. Möchten Sie trotzdem weitermachen ?"
-#: src/tools/dcpomatic.cc:870
+#: src/tools/dcpomatic.cc:876
msgid "The DCP-o-matic download server could not be contacted."
msgstr "Der DCP-o-matic Download Server ist nicht erreichbar."
-#: src/tools/dcpomatic.cc:296
+#: src/tools/dcpomatic.cc:299
msgid ""
"The directory %1 already exists and is not empty. Are you sure you want to "
"use it?"
"Der Ordner %1 existiert bereits und ist nicht leer. Wollen Sie ihn trotzdem "
"benutzen ?"
-#: src/tools/dcpomatic.cc:865
+#: src/tools/dcpomatic.cc:871
msgid "There are no new versions of DCP-o-matic available."
msgstr "Es ist keine neue Version von DCP-o-matic verfügbar."
-#: src/tools/dcpomatic.cc:509 src/tools/dcpomatic_batch.cc:111
+#: src/tools/dcpomatic.cc:512 src/tools/dcpomatic_batch.cc:111
msgid "There are unfinished jobs; are you sure you want to quit?"
msgstr ""
"Manche Aufgaben sind nicht erledigt - sind Sie sicher, dass Sie Beenden "
"wollen ?"
-#: src/tools/dcpomatic.cc:243
+#: src/tools/dcpomatic.cc:246
msgid ""
"This film was created with an old version of DVD-o-matic and may not load "
"correctly in this version. Please check the film's settings carefully."
"worden und wird in dieser Programmversion möglicherweise nicht korrekt "
"umgesetzt. Bitte prüfen Sie alle Projekteinstellungen sorgfältig!"
-#: src/tools/dcpomatic.cc:510 src/tools/dcpomatic_batch.cc:112
+#: src/tools/dcpomatic.cc:513 src/tools/dcpomatic_batch.cc:112
msgid "Unfinished jobs"
msgstr "Unerledigte Aufgaben"
-#: src/tools/dcpomatic.cc:331 src/tools/dcpomatic_batch.cc:161
+#: src/tools/dcpomatic.cc:334 src/tools/dcpomatic_batch.cc:161
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.4\n"
-#: src/tools/dcpomatic.cc:306
+#: src/tools/dcpomatic.cc:309
msgid "%1 already exists as a file, so you cannot use it for a new film."
msgstr "%1 ya existe como fichero, no puedes usarlo para una nueva pelÃcula."
msgid "&Add Film..."
msgstr "&Añadir pelÃcula..."
-#: src/tools/dcpomatic.cc:652
+#: src/tools/dcpomatic.cc:655
msgid "&Content"
msgstr ""
-#: src/tools/dcpomatic.cc:650
+#: src/tools/dcpomatic.cc:653
msgid "&Edit"
msgstr "&Editar"
-#: src/tools/dcpomatic.cc:614
+#: src/tools/dcpomatic.cc:617
msgid "&Exit"
msgstr "&Salir"
-#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:56
+#: src/tools/dcpomatic.cc:651 src/tools/dcpomatic_batch.cc:56
msgid "&File"
msgstr "&Archivo"
-#: src/tools/dcpomatic.cc:655 src/tools/dcpomatic_batch.cc:57
+#: src/tools/dcpomatic.cc:658 src/tools/dcpomatic_batch.cc:57
msgid "&Help"
msgstr "&Ayuda"
-#: src/tools/dcpomatic.cc:653
+#: src/tools/dcpomatic.cc:656
msgid "&Jobs"
msgstr "&Tareas"
-#: src/tools/dcpomatic.cc:631
+#: src/tools/dcpomatic.cc:634
#, fuzzy
msgid "&Make DCP\tCtrl-M"
msgstr "&Crear DCP\tCtrl-M"
-#: src/tools/dcpomatic.cc:601
+#: src/tools/dcpomatic.cc:604
#, fuzzy
msgid "&Open...\tCtrl-O"
msgstr "&Abrir...\tCtrl-O"
-#: src/tools/dcpomatic.cc:620 src/tools/dcpomatic.cc:623
+#: src/tools/dcpomatic.cc:623 src/tools/dcpomatic.cc:626
#, fuzzy
msgid "&Preferences...\tCtrl-P"
msgstr "&Preferencias...\tCtrl-P"
-#: src/tools/dcpomatic.cc:605
+#: src/tools/dcpomatic.cc:608
msgid "&Properties..."
msgstr "&Propiedades..."
-#: src/tools/dcpomatic.cc:616 src/tools/dcpomatic_batch.cc:51
+#: src/tools/dcpomatic.cc:619 src/tools/dcpomatic_batch.cc:51
msgid "&Quit"
msgstr "&Salir"
-#: src/tools/dcpomatic.cc:603
+#: src/tools/dcpomatic.cc:606
msgid "&Save\tCtrl-S"
msgstr ""
-#: src/tools/dcpomatic.cc:633
+#: src/tools/dcpomatic.cc:636
msgid "&Send DCP to TMS"
msgstr "&Enviar DCP al TMS"
-#: src/tools/dcpomatic.cc:654
+#: src/tools/dcpomatic.cc:657
msgid "&Tools"
msgstr "&Herramientas"
-#: src/tools/dcpomatic.cc:645 src/tools/dcpomatic_batch.cc:54
+#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:54
msgid "About"
msgstr "Acerca de"
-#: src/tools/dcpomatic.cc:643
+#: src/tools/dcpomatic.cc:646
msgid "About DCP-o-matic"
msgstr "Acerca de DVD-o-matic"
msgid "Add Film..."
msgstr "Añadir pelÃcula..."
-#: src/tools/dcpomatic.cc:821
+#: src/tools/dcpomatic.cc:827
#, c-format
msgid ""
"An exception occurred (%s). Please report this problem to the DCP-o-matic "
"author (carl@dcpomatic.com)."
msgstr ""
-#: src/tools/dcpomatic.cc:823 src/tools/dcpomatic.cc:832
+#: src/tools/dcpomatic.cc:829 src/tools/dcpomatic.cc:838
msgid ""
"An unknown exception occurred. Please report this problem to the DCP-o-"
"matic author (carl@dcpomatic.com)."
msgstr ""
-#: src/tools/dcpomatic.cc:418
+#: src/tools/dcpomatic.cc:421
msgid "An unknown exeception occurred."
msgstr "Ha ocurrido un error desconocido."
-#: src/tools/dcpomatic.cc:414
+#: src/tools/dcpomatic.cc:417
msgid "CPL's content is not encrypted."
msgstr ""
-#: src/tools/dcpomatic.cc:639
+#: src/tools/dcpomatic.cc:642
msgid "Check for updates"
msgstr "Buscar actualizaciones"
-#: src/tools/dcpomatic.cc:760 src/tools/dcpomatic_batch.cc:238
+#: src/tools/dcpomatic.cc:766 src/tools/dcpomatic_batch.cc:238
msgid "Could not load film %1 (%2)"
msgstr "No se pudo cargar la pelÃcula %s (%s)"
-#: src/tools/dcpomatic.cc:257 src/tools/dcpomatic_batch.cc:175
+#: src/tools/dcpomatic.cc:260 src/tools/dcpomatic_batch.cc:175
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "No se pudo cargar la pelÃcula en %s (%s)"
-#: src/tools/dcpomatic.cc:464
+#: src/tools/dcpomatic.cc:467
msgid "Could not show DCP (could not run konqueror)"
msgstr "No se pudo mostrar el DCP (no se pudo ejecutar konqueror)"
-#: src/tools/dcpomatic.cc:457
+#: src/tools/dcpomatic.cc:460
msgid "Could not show DCP (could not run nautilus)"
msgstr "No se pudo mostrar el DCP (no se pudo ejecutar nautilos)"
-#: src/tools/dcpomatic.cc:277 src/tools/dcpomatic.cc:715
-#: src/tools/dcpomatic.cc:751
+#: src/tools/dcpomatic.cc:280 src/tools/dcpomatic.cc:721
+#: src/tools/dcpomatic.cc:757
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic Batch Converter"
msgstr "Convertidor por lotes DCP-o-matic"
-#: src/tools/dcpomatic.cc:638
+#: src/tools/dcpomatic.cc:641
msgid "Encoding servers..."
msgstr "Servidores de codificación..."
-#: src/tools/dcpomatic.cc:85
+#: src/tools/dcpomatic.cc:84
msgid "Film changed"
msgstr "PelÃcula cambiada"
-#: src/tools/dcpomatic.cc:637
+#: src/tools/dcpomatic.cc:640
#, fuzzy
msgid "Hints...\tCtrl-H"
msgstr "Pistas...\tCtrl-H"
-#: src/tools/dcpomatic.cc:632
+#: src/tools/dcpomatic.cc:635
#, fuzzy
msgid "Make &KDMs...\tCtrl-K"
msgstr "Crear &KDMs...\tCtrl-K"
-#: src/tools/dcpomatic.cc:600
+#: src/tools/dcpomatic.cc:603
#, fuzzy
msgid "New...\tCtrl-N"
msgstr "Nuevo...\tCtrl-N"
-#: src/tools/dcpomatic.cc:634
+#: src/tools/dcpomatic.cc:637
msgid "S&how DCP"
msgstr "&Mostrar DCP"
-#: src/tools/dcpomatic.cc:84
+#: src/tools/dcpomatic.cc:83
#, c-format
msgid "Save changes to film \"%s\" before closing?"
msgstr "Guardar cambios de la pelÃcula \"%s\" antes de cerrar?"
-#: src/tools/dcpomatic.cc:628
+#: src/tools/dcpomatic.cc:631
msgid "Scale to fit &height"
msgstr ""
-#: src/tools/dcpomatic.cc:627
+#: src/tools/dcpomatic.cc:630
msgid "Scale to fit &width"
msgstr ""
-#: src/tools/dcpomatic.cc:322 src/tools/dcpomatic_batch.cc:152
+#: src/tools/dcpomatic.cc:325 src/tools/dcpomatic_batch.cc:152
msgid "Select film to open"
msgstr "Selecciona la pelÃcula a abrir"
-#: src/tools/dcpomatic.cc:385
+#: src/tools/dcpomatic.cc:388
#, c-format
msgid ""
"The DCP for this film will take up about %.1f Gb, and the disk that you are "
"seleccionado solo tiene %.1f Gb disponibles. Quieres continuar de todas "
"formas?"
-#: src/tools/dcpomatic.cc:870
+#: src/tools/dcpomatic.cc:876
msgid "The DCP-o-matic download server could not be contacted."
msgstr "Imposible conectar con el servidor de descarga de DCP-o-matic."
-#: src/tools/dcpomatic.cc:296
+#: src/tools/dcpomatic.cc:299
msgid ""
"The directory %1 already exists and is not empty. Are you sure you want to "
"use it?"
msgstr ""
"El directorio %1 ya existe y no está vacÃo. ¿Estás seguro de querer usarlo?"
-#: src/tools/dcpomatic.cc:865
+#: src/tools/dcpomatic.cc:871
msgid "There are no new versions of DCP-o-matic available."
msgstr "No hay disponibles nuevas versiones de DCP-o-matic."
-#: src/tools/dcpomatic.cc:509 src/tools/dcpomatic_batch.cc:111
+#: src/tools/dcpomatic.cc:512 src/tools/dcpomatic_batch.cc:111
msgid "There are unfinished jobs; are you sure you want to quit?"
msgstr "Hay trabajos sin finalizar; ¿estás seguro de querer cerrar?"
-#: src/tools/dcpomatic.cc:243
+#: src/tools/dcpomatic.cc:246
msgid ""
"This film was created with an old version of DVD-o-matic and may not load "
"correctly in this version. Please check the film's settings carefully."
"cargue correctamente en esta versión. Por favor revisa cuidadosamente las "
"opciones."
-#: src/tools/dcpomatic.cc:510 src/tools/dcpomatic_batch.cc:112
+#: src/tools/dcpomatic.cc:513 src/tools/dcpomatic_batch.cc:112
msgid "Unfinished jobs"
msgstr "Trabajos sin finalizar"
-#: src/tools/dcpomatic.cc:331 src/tools/dcpomatic_batch.cc:161
+#: src/tools/dcpomatic.cc:334 src/tools/dcpomatic_batch.cc:161
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.6\n"
-#: src/tools/dcpomatic.cc:306
+#: src/tools/dcpomatic.cc:309
msgid "%1 already exists as a file, so you cannot use it for a new film."
msgstr ""
"Le fichier %1 existe déjà , vous ne pouvez l'utiliser pour un nouveau projet."
msgid "&Add Film..."
msgstr "&Ajouter Film..."
-#: src/tools/dcpomatic.cc:652
+#: src/tools/dcpomatic.cc:655
msgid "&Content"
msgstr "&Contenu"
-#: src/tools/dcpomatic.cc:650
+#: src/tools/dcpomatic.cc:653
msgid "&Edit"
msgstr "&Edition"
-#: src/tools/dcpomatic.cc:614
+#: src/tools/dcpomatic.cc:617
msgid "&Exit"
msgstr "&Quitter"
-#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:56
+#: src/tools/dcpomatic.cc:651 src/tools/dcpomatic_batch.cc:56
msgid "&File"
msgstr "&Fichier"
-#: src/tools/dcpomatic.cc:655 src/tools/dcpomatic_batch.cc:57
+#: src/tools/dcpomatic.cc:658 src/tools/dcpomatic_batch.cc:57
msgid "&Help"
msgstr "&Aide"
-#: src/tools/dcpomatic.cc:653
+#: src/tools/dcpomatic.cc:656
msgid "&Jobs"
msgstr "&Travaux"
-#: src/tools/dcpomatic.cc:631
+#: src/tools/dcpomatic.cc:634
#, fuzzy
msgid "&Make DCP\tCtrl-M"
msgstr "&Créer le DCP\tCtrl-M"
-#: src/tools/dcpomatic.cc:601
+#: src/tools/dcpomatic.cc:604
#, fuzzy
msgid "&Open...\tCtrl-O"
msgstr "&Ouvrir...\tCtrl-O"
-#: src/tools/dcpomatic.cc:620 src/tools/dcpomatic.cc:623
+#: src/tools/dcpomatic.cc:623 src/tools/dcpomatic.cc:626
#, fuzzy
msgid "&Preferences...\tCtrl-P"
msgstr "&Préférences...\tCtrl-P"
-#: src/tools/dcpomatic.cc:605
+#: src/tools/dcpomatic.cc:608
msgid "&Properties..."
msgstr "&Propriétés..."
-#: src/tools/dcpomatic.cc:616 src/tools/dcpomatic_batch.cc:51
+#: src/tools/dcpomatic.cc:619 src/tools/dcpomatic_batch.cc:51
msgid "&Quit"
msgstr "&Quitter"
-#: src/tools/dcpomatic.cc:603
+#: src/tools/dcpomatic.cc:606
msgid "&Save\tCtrl-S"
msgstr ""
-#: src/tools/dcpomatic.cc:633
+#: src/tools/dcpomatic.cc:636
msgid "&Send DCP to TMS"
msgstr "&Envoyer le DCP au TMS"
-#: src/tools/dcpomatic.cc:654
+#: src/tools/dcpomatic.cc:657
msgid "&Tools"
msgstr "&Outils"
-#: src/tools/dcpomatic.cc:645 src/tools/dcpomatic_batch.cc:54
+#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:54
msgid "About"
msgstr "A propos"
-#: src/tools/dcpomatic.cc:643
+#: src/tools/dcpomatic.cc:646
msgid "About DCP-o-matic"
msgstr "À propos de DCP-o-matic"
msgid "Add Film..."
msgstr "Ajouter Film..."
-#: src/tools/dcpomatic.cc:821
+#: src/tools/dcpomatic.cc:827
#, fuzzy, c-format
msgid ""
"An exception occurred (%s). Please report this problem to the DCP-o-matic "
"Erreur indéterminée. Merci de rapporter le problème à l'auteur de DCP-o-"
"matic (carl@dcpomatic.com)."
-#: src/tools/dcpomatic.cc:823 src/tools/dcpomatic.cc:832
+#: src/tools/dcpomatic.cc:829 src/tools/dcpomatic.cc:838
msgid ""
"An unknown exception occurred. Please report this problem to the DCP-o-"
"matic author (carl@dcpomatic.com)."
"Erreur indéterminée. Merci de rapporter le problème à l'auteur de DCP-o-"
"matic (carl@dcpomatic.com)."
-#: src/tools/dcpomatic.cc:418
+#: src/tools/dcpomatic.cc:421
msgid "An unknown exeception occurred."
msgstr "Exception inconnue"
-#: src/tools/dcpomatic.cc:414
+#: src/tools/dcpomatic.cc:417
msgid "CPL's content is not encrypted."
msgstr "Le contenu du CPL n'est pas crypté."
-#: src/tools/dcpomatic.cc:639
+#: src/tools/dcpomatic.cc:642
msgid "Check for updates"
msgstr "Recherche mises à jour"
-#: src/tools/dcpomatic.cc:760 src/tools/dcpomatic_batch.cc:238
+#: src/tools/dcpomatic.cc:766 src/tools/dcpomatic_batch.cc:238
msgid "Could not load film %1 (%2)"
msgstr "Impossible de charger le film %1 (%2)"
-#: src/tools/dcpomatic.cc:257 src/tools/dcpomatic_batch.cc:175
+#: src/tools/dcpomatic.cc:260 src/tools/dcpomatic_batch.cc:175
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Impossible d'ouvrir le film à %s (%s)"
-#: src/tools/dcpomatic.cc:464
+#: src/tools/dcpomatic.cc:467
msgid "Could not show DCP (could not run konqueror)"
msgstr "Ouverture du DCP impossible (konqueror est introuvable)"
-#: src/tools/dcpomatic.cc:457
+#: src/tools/dcpomatic.cc:460
msgid "Could not show DCP (could not run nautilus)"
msgstr "Ouverture du DCP impossible (nautilus est introuvable)"
-#: src/tools/dcpomatic.cc:277 src/tools/dcpomatic.cc:715
-#: src/tools/dcpomatic.cc:751
+#: src/tools/dcpomatic.cc:280 src/tools/dcpomatic.cc:721
+#: src/tools/dcpomatic.cc:757
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic Batch Converter"
msgstr "DCP-o-matic - Convertisseur par lots"
-#: src/tools/dcpomatic.cc:638
+#: src/tools/dcpomatic.cc:641
msgid "Encoding servers..."
msgstr "Serveurs d'encodage"
-#: src/tools/dcpomatic.cc:85
+#: src/tools/dcpomatic.cc:84
msgid "Film changed"
msgstr "Film changé"
-#: src/tools/dcpomatic.cc:637
+#: src/tools/dcpomatic.cc:640
#, fuzzy
msgid "Hints...\tCtrl-H"
msgstr "Conseils...\tCtrl-H"
-#: src/tools/dcpomatic.cc:632
+#: src/tools/dcpomatic.cc:635
msgid "Make &KDMs...\tCtrl-K"
msgstr "Générer &KDMs...\tCtrl-K"
-#: src/tools/dcpomatic.cc:600
+#: src/tools/dcpomatic.cc:603
msgid "New...\tCtrl-N"
msgstr "Nouveau...\tCtrl-N"
-#: src/tools/dcpomatic.cc:634
+#: src/tools/dcpomatic.cc:637
msgid "S&how DCP"
msgstr "Voir le DCP"
-#: src/tools/dcpomatic.cc:84
+#: src/tools/dcpomatic.cc:83
#, c-format
msgid "Save changes to film \"%s\" before closing?"
msgstr "Enregistrer les changements du film \"%s\" avant de fermer ?"
-#: src/tools/dcpomatic.cc:628
+#: src/tools/dcpomatic.cc:631
msgid "Scale to fit &height"
msgstr "Adapter pour remplir la &hauteur"
-#: src/tools/dcpomatic.cc:627
+#: src/tools/dcpomatic.cc:630
msgid "Scale to fit &width"
msgstr "Adapter pour remplir la largeur"
-#: src/tools/dcpomatic.cc:322 src/tools/dcpomatic_batch.cc:152
+#: src/tools/dcpomatic.cc:325 src/tools/dcpomatic_batch.cc:152
msgid "Select film to open"
msgstr "Sélectionner le film à ouvrir"
-#: src/tools/dcpomatic.cc:385
+#: src/tools/dcpomatic.cc:388
#, c-format
msgid ""
"The DCP for this film will take up about %.1f Gb, and the disk that you are "
"Le DCP de ce film pèsera environ %.1f Go. Le disque que vous utilisez n'a "
"que %.1f Go disponible(s). Souhaitez-vous continuer?"
-#: src/tools/dcpomatic.cc:870
+#: src/tools/dcpomatic.cc:876
msgid "The DCP-o-matic download server could not be contacted."
msgstr "Le serveur de téléchargement de DCP-o-matic ne peut être contacté."
-#: src/tools/dcpomatic.cc:296
+#: src/tools/dcpomatic.cc:299
msgid ""
"The directory %1 already exists and is not empty. Are you sure you want to "
"use it?"
msgstr ""
"Le dossier %1 existe et n'est pas vide. Etes-vous sûr de vouloir l'utiliser ?"
-#: src/tools/dcpomatic.cc:865
+#: src/tools/dcpomatic.cc:871
msgid "There are no new versions of DCP-o-matic available."
msgstr "Aucune mise à jour disponible pour DCP-o-matic."
-#: src/tools/dcpomatic.cc:509 src/tools/dcpomatic_batch.cc:111
+#: src/tools/dcpomatic.cc:512 src/tools/dcpomatic_batch.cc:111
msgid "There are unfinished jobs; are you sure you want to quit?"
msgstr "Il y a des tâches inachevées ; voulez-vous vraiment quitter ?"
-#: src/tools/dcpomatic.cc:243
+#: src/tools/dcpomatic.cc:246
msgid ""
"This film was created with an old version of DVD-o-matic and may not load "
"correctly in this version. Please check the film's settings carefully."
"être ouvert correctement dans cette version. Veuillez vérifier les "
"paramètres de réglages très attentivement."
-#: src/tools/dcpomatic.cc:510 src/tools/dcpomatic_batch.cc:112
+#: src/tools/dcpomatic.cc:513 src/tools/dcpomatic_batch.cc:112
msgid "Unfinished jobs"
msgstr "Travaux incomplets"
-#: src/tools/dcpomatic.cc:331 src/tools/dcpomatic_batch.cc:161
+#: src/tools/dcpomatic.cc:334 src/tools/dcpomatic_batch.cc:161
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.3\n"
-#: src/tools/dcpomatic.cc:306
+#: src/tools/dcpomatic.cc:309
msgid "%1 already exists as a file, so you cannot use it for a new film."
msgstr "%1 esiste già il file, non è possibile usarlo per un nuovo film"
msgid "&Add Film..."
msgstr ""
-#: src/tools/dcpomatic.cc:652
+#: src/tools/dcpomatic.cc:655
msgid "&Content"
msgstr ""
-#: src/tools/dcpomatic.cc:650
+#: src/tools/dcpomatic.cc:653
msgid "&Edit"
msgstr "&Modifica"
-#: src/tools/dcpomatic.cc:614
+#: src/tools/dcpomatic.cc:617
msgid "&Exit"
msgstr "&Esci"
-#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:56
+#: src/tools/dcpomatic.cc:651 src/tools/dcpomatic_batch.cc:56
msgid "&File"
msgstr "&File"
-#: src/tools/dcpomatic.cc:655 src/tools/dcpomatic_batch.cc:57
+#: src/tools/dcpomatic.cc:658 src/tools/dcpomatic_batch.cc:57
msgid "&Help"
msgstr "&Aiuto"
-#: src/tools/dcpomatic.cc:653
+#: src/tools/dcpomatic.cc:656
msgid "&Jobs"
msgstr "&Lavori"
-#: src/tools/dcpomatic.cc:631
+#: src/tools/dcpomatic.cc:634
msgid "&Make DCP\tCtrl-M"
msgstr "&Crea DCP\tCtrl-M"
-#: src/tools/dcpomatic.cc:601
+#: src/tools/dcpomatic.cc:604
msgid "&Open...\tCtrl-O"
msgstr "&Apri...\tCtrl-O"
-#: src/tools/dcpomatic.cc:620 src/tools/dcpomatic.cc:623
+#: src/tools/dcpomatic.cc:623 src/tools/dcpomatic.cc:626
msgid "&Preferences...\tCtrl-P"
msgstr "&Preferenze...\tCtrl-P"
-#: src/tools/dcpomatic.cc:605
+#: src/tools/dcpomatic.cc:608
msgid "&Properties..."
msgstr "&Proprieta'..."
-#: src/tools/dcpomatic.cc:616 src/tools/dcpomatic_batch.cc:51
+#: src/tools/dcpomatic.cc:619 src/tools/dcpomatic_batch.cc:51
msgid "&Quit"
msgstr "&Esci"
-#: src/tools/dcpomatic.cc:603
+#: src/tools/dcpomatic.cc:606
msgid "&Save\tCtrl-S"
msgstr ""
-#: src/tools/dcpomatic.cc:633
+#: src/tools/dcpomatic.cc:636
msgid "&Send DCP to TMS"
msgstr "&Invia DCP a TMS"
-#: src/tools/dcpomatic.cc:654
+#: src/tools/dcpomatic.cc:657
msgid "&Tools"
msgstr "&Strumenti"
-#: src/tools/dcpomatic.cc:645 src/tools/dcpomatic_batch.cc:54
+#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:54
msgid "About"
msgstr "Informazioni"
-#: src/tools/dcpomatic.cc:643
+#: src/tools/dcpomatic.cc:646
msgid "About DCP-o-matic"
msgstr "Su DVD-o-matic"
msgid "Add Film..."
msgstr ""
-#: src/tools/dcpomatic.cc:821
+#: src/tools/dcpomatic.cc:827
#, c-format
msgid ""
"An exception occurred (%s). Please report this problem to the DCP-o-matic "
"author (carl@dcpomatic.com)."
msgstr ""
-#: src/tools/dcpomatic.cc:823 src/tools/dcpomatic.cc:832
+#: src/tools/dcpomatic.cc:829 src/tools/dcpomatic.cc:838
msgid ""
"An unknown exception occurred. Please report this problem to the DCP-o-"
"matic author (carl@dcpomatic.com)."
msgstr ""
-#: src/tools/dcpomatic.cc:418
+#: src/tools/dcpomatic.cc:421
msgid "An unknown exeception occurred."
msgstr ""
-#: src/tools/dcpomatic.cc:414
+#: src/tools/dcpomatic.cc:417
msgid "CPL's content is not encrypted."
msgstr ""
-#: src/tools/dcpomatic.cc:639
+#: src/tools/dcpomatic.cc:642
msgid "Check for updates"
msgstr "Controlla aggiornamenti"
-#: src/tools/dcpomatic.cc:760 src/tools/dcpomatic_batch.cc:238
+#: src/tools/dcpomatic.cc:766 src/tools/dcpomatic_batch.cc:238
msgid "Could not load film %1 (%2)"
msgstr "Non posso caricare il film %s (%s)"
-#: src/tools/dcpomatic.cc:257 src/tools/dcpomatic_batch.cc:175
+#: src/tools/dcpomatic.cc:260 src/tools/dcpomatic_batch.cc:175
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Non posso aprire il film in %s (%s)"
-#: src/tools/dcpomatic.cc:464
+#: src/tools/dcpomatic.cc:467
msgid "Could not show DCP (could not run konqueror)"
msgstr ""
-#: src/tools/dcpomatic.cc:457
+#: src/tools/dcpomatic.cc:460
msgid "Could not show DCP (could not run nautilus)"
msgstr ""
-#: src/tools/dcpomatic.cc:277 src/tools/dcpomatic.cc:715
-#: src/tools/dcpomatic.cc:751
+#: src/tools/dcpomatic.cc:280 src/tools/dcpomatic.cc:721
+#: src/tools/dcpomatic.cc:757
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic Batch Converter"
msgstr ""
-#: src/tools/dcpomatic.cc:638
+#: src/tools/dcpomatic.cc:641
msgid "Encoding servers..."
msgstr ""
-#: src/tools/dcpomatic.cc:85
+#: src/tools/dcpomatic.cc:84
msgid "Film changed"
msgstr "Film modificato"
-#: src/tools/dcpomatic.cc:637
+#: src/tools/dcpomatic.cc:640
msgid "Hints...\tCtrl-H"
msgstr "Suggerimenti...\tCtrl-H"
-#: src/tools/dcpomatic.cc:632
+#: src/tools/dcpomatic.cc:635
msgid "Make &KDMs...\tCtrl-K"
msgstr ""
-#: src/tools/dcpomatic.cc:600
+#: src/tools/dcpomatic.cc:603
msgid "New...\tCtrl-N"
msgstr "Nuovo...\tCtrl-N"
-#: src/tools/dcpomatic.cc:634
+#: src/tools/dcpomatic.cc:637
msgid "S&how DCP"
msgstr "&Mostra DCP"
-#: src/tools/dcpomatic.cc:84
+#: src/tools/dcpomatic.cc:83
#, c-format
msgid "Save changes to film \"%s\" before closing?"
msgstr "Salvare i cambiamenti del film \"%s\" prima di chiudere?"
-#: src/tools/dcpomatic.cc:628
+#: src/tools/dcpomatic.cc:631
msgid "Scale to fit &height"
msgstr ""
-#: src/tools/dcpomatic.cc:627
+#: src/tools/dcpomatic.cc:630
msgid "Scale to fit &width"
msgstr ""
-#: src/tools/dcpomatic.cc:322 src/tools/dcpomatic_batch.cc:152
+#: src/tools/dcpomatic.cc:325 src/tools/dcpomatic_batch.cc:152
msgid "Select film to open"
msgstr "Seleziona il film da aprire"
-#: src/tools/dcpomatic.cc:385
+#: src/tools/dcpomatic.cc:388
#, c-format
msgid ""
"The DCP for this film will take up about %.1f Gb, and the disk that you are "
"Il DCP di questo film occupa %.1f Gb, ma il disco che stai usando dispone di "
"%.1f Gb liberi. Vuoi continuare ugualmente?"
-#: src/tools/dcpomatic.cc:870
+#: src/tools/dcpomatic.cc:876
msgid "The DCP-o-matic download server could not be contacted."
msgstr "Il download server di DCP-o-matic non può essere contattato."
-#: src/tools/dcpomatic.cc:296
+#: src/tools/dcpomatic.cc:299
msgid ""
"The directory %1 already exists and is not empty. Are you sure you want to "
"use it?"
msgstr "La cartella %1 esiste già e non è vuota. Sei sicuro di volerla usare?"
-#: src/tools/dcpomatic.cc:865
+#: src/tools/dcpomatic.cc:871
msgid "There are no new versions of DCP-o-matic available."
msgstr "Non ci sono nuove versioni di DCP-o-matic disponibili."
-#: src/tools/dcpomatic.cc:509 src/tools/dcpomatic_batch.cc:111
+#: src/tools/dcpomatic.cc:512 src/tools/dcpomatic_batch.cc:111
msgid "There are unfinished jobs; are you sure you want to quit?"
msgstr "C'è un processo in corso: sei sicuro di voler uscire?"
-#: src/tools/dcpomatic.cc:243
+#: src/tools/dcpomatic.cc:246
msgid ""
"This film was created with an old version of DVD-o-matic and may not load "
"correctly in this version. Please check the film's settings carefully."
msgstr ""
-#: src/tools/dcpomatic.cc:510 src/tools/dcpomatic_batch.cc:112
+#: src/tools/dcpomatic.cc:513 src/tools/dcpomatic_batch.cc:112
msgid "Unfinished jobs"
msgstr "Processo in corso"
-#: src/tools/dcpomatic.cc:331 src/tools/dcpomatic_batch.cc:161
+#: src/tools/dcpomatic.cc:334 src/tools/dcpomatic_batch.cc:161
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
"X-Generator: Poedit 1.6.9\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/tools/dcpomatic.cc:306
+#: src/tools/dcpomatic.cc:309
msgid "%1 already exists as a file, so you cannot use it for a new film."
msgstr ""
"%1 Dit bestand bestaat al, hierdoor kunt u het niet gebruiken voor een "
msgid "&Add Film..."
msgstr "Open een DCP map"
-#: src/tools/dcpomatic.cc:652
+#: src/tools/dcpomatic.cc:655
msgid "&Content"
msgstr "&Content"
-#: src/tools/dcpomatic.cc:650
+#: src/tools/dcpomatic.cc:653
msgid "&Edit"
msgstr "&Edit"
-#: src/tools/dcpomatic.cc:614
+#: src/tools/dcpomatic.cc:617
msgid "&Exit"
msgstr "&Afsluiten"
-#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:56
+#: src/tools/dcpomatic.cc:651 src/tools/dcpomatic_batch.cc:56
msgid "&File"
msgstr "&Bestand"
-#: src/tools/dcpomatic.cc:655 src/tools/dcpomatic_batch.cc:57
+#: src/tools/dcpomatic.cc:658 src/tools/dcpomatic_batch.cc:57
msgid "&Help"
msgstr "&Help"
-#: src/tools/dcpomatic.cc:653
+#: src/tools/dcpomatic.cc:656
msgid "&Jobs"
msgstr "&Projecten"
msgid "&Make DCP\tCtrl-M"
msgstr "&Maak een DCP\tCtrl-M"
-#: src/tools/dcpomatic.cc:601
+#: src/tools/dcpomatic.cc:604
msgid "&Open...\tCtrl-O"
msgstr "&Openen...\tCtrl-O"
-#: src/tools/dcpomatic.cc:620 src/tools/dcpomatic.cc:623
+#: src/tools/dcpomatic.cc:623 src/tools/dcpomatic.cc:626
msgid "&Preferences...\tCtrl-P"
msgstr "&Voorkeuren...\tCtrl-P"
-#: src/tools/dcpomatic.cc:605
+#: src/tools/dcpomatic.cc:608
msgid "&Properties..."
msgstr "&Instellingen..."
-#: src/tools/dcpomatic.cc:616 src/tools/dcpomatic_batch.cc:51
+#: src/tools/dcpomatic.cc:619 src/tools/dcpomatic_batch.cc:51
msgid "&Quit"
msgstr "&Afsluiten"
-#: src/tools/dcpomatic.cc:603
+#: src/tools/dcpomatic.cc:606
msgid "&Save\tCtrl-S"
msgstr ""
-#: src/tools/dcpomatic.cc:633
+#: src/tools/dcpomatic.cc:636
msgid "&Send DCP to TMS"
msgstr "&Verstuur DCP naar TMS"
-#: src/tools/dcpomatic.cc:654
+#: src/tools/dcpomatic.cc:657
msgid "&Tools"
msgstr "&Gereedschappen"
-#: src/tools/dcpomatic.cc:645 src/tools/dcpomatic_batch.cc:54
+#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:54
msgid "About"
msgstr "Over.."
-#: src/tools/dcpomatic.cc:643
+#: src/tools/dcpomatic.cc:646
msgid "About DCP-o-matic"
msgstr "Over DCP-o-matic"
msgid "Add Film..."
msgstr "Voeg Film Toe"
-#: src/tools/dcpomatic.cc:821
+#: src/tools/dcpomatic.cc:827
#, fuzzy, c-format
msgid ""
"An exception occurred (%s). Please report this problem to the DCP-o-matic "
"Een ongekende fout is opgetreden. AUB meld deze aan de maker van DCP-o-matic "
"(carl@dcpomatic.com)."
-#: src/tools/dcpomatic.cc:823 src/tools/dcpomatic.cc:832
+#: src/tools/dcpomatic.cc:829 src/tools/dcpomatic.cc:838
msgid ""
"An unknown exception occurred. Please report this problem to the DCP-o-"
"matic author (carl@dcpomatic.com)."
"Een ongekende fout is opgetreden. AUB meld deze aan de maker van DCP-o-matic "
"(carl@dcpomatic.com)."
-#: src/tools/dcpomatic.cc:418
+#: src/tools/dcpomatic.cc:421
msgid "An unknown exeception occurred."
msgstr "Er is een onbekende fout opgetreden."
-#: src/tools/dcpomatic.cc:414
+#: src/tools/dcpomatic.cc:417
msgid "CPL's content is not encrypted."
msgstr "De inhoud van de CPL is niet geëncrypteerd."
-#: src/tools/dcpomatic.cc:639
+#: src/tools/dcpomatic.cc:642
msgid "Check for updates"
msgstr "Controleer op updates"
-#: src/tools/dcpomatic.cc:760 src/tools/dcpomatic_batch.cc:238
+#: src/tools/dcpomatic.cc:766 src/tools/dcpomatic_batch.cc:238
msgid "Could not load film %1 (%2)"
msgstr "Kan film niet openen %1 (%2)"
-#: src/tools/dcpomatic.cc:257 src/tools/dcpomatic_batch.cc:175
+#: src/tools/dcpomatic.cc:260 src/tools/dcpomatic_batch.cc:175
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Kan film niet openen in %s (%s)"
-#: src/tools/dcpomatic.cc:464
+#: src/tools/dcpomatic.cc:467
msgid "Could not show DCP (could not run konqueror)"
msgstr "Kan DCP niet vertonen (Kan Konqueror niet starten)"
-#: src/tools/dcpomatic.cc:457
+#: src/tools/dcpomatic.cc:460
msgid "Could not show DCP (could not run nautilus)"
msgstr "Kan DCP niet vertonen (Kan Nautilus niet starten)"
-#: src/tools/dcpomatic.cc:277 src/tools/dcpomatic.cc:715
-#: src/tools/dcpomatic.cc:751
+#: src/tools/dcpomatic.cc:280 src/tools/dcpomatic.cc:721
+#: src/tools/dcpomatic.cc:757
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic Batch Converter"
msgstr "DCP-o-matic Bulk Omzetter"
-#: src/tools/dcpomatic.cc:638
+#: src/tools/dcpomatic.cc:641
msgid "Encoding servers..."
msgstr "Render servers..."
-#: src/tools/dcpomatic.cc:85
+#: src/tools/dcpomatic.cc:84
msgid "Film changed"
msgstr "Film is veranderd"
-#: src/tools/dcpomatic.cc:637
+#: src/tools/dcpomatic.cc:640
msgid "Hints...\tCtrl-H"
msgstr "Tips...\tCtrl-H"
-#: src/tools/dcpomatic.cc:632
+#: src/tools/dcpomatic.cc:635
msgid "Make &KDMs...\tCtrl-K"
msgstr "Maak &KDMs...\tCtrl-K"
-#: src/tools/dcpomatic.cc:600
+#: src/tools/dcpomatic.cc:603
msgid "New...\tCtrl-N"
msgstr "Nieuw...\tCtrl-N"
-#: src/tools/dcpomatic.cc:634
+#: src/tools/dcpomatic.cc:637
msgid "S&how DCP"
msgstr "S&hoe DCP"
-#: src/tools/dcpomatic.cc:84
+#: src/tools/dcpomatic.cc:83
#, c-format
msgid "Save changes to film \"%s\" before closing?"
msgstr "Bewaar veranderingen naar film \"%s\" voor afsluiten?"
-#: src/tools/dcpomatic.cc:628
+#: src/tools/dcpomatic.cc:631
msgid "Scale to fit &height"
msgstr "Scaal naar &height"
-#: src/tools/dcpomatic.cc:627
+#: src/tools/dcpomatic.cc:630
msgid "Scale to fit &width"
msgstr "Schaal naar &width"
-#: src/tools/dcpomatic.cc:322 src/tools/dcpomatic_batch.cc:152
+#: src/tools/dcpomatic.cc:325 src/tools/dcpomatic_batch.cc:152
msgid "Select film to open"
msgstr "Kies een film om te openen"
-#: src/tools/dcpomatic.cc:385
+#: src/tools/dcpomatic.cc:388
#, c-format
msgid ""
"The DCP for this film will take up about %.1f Gb, and the disk that you are "
"De DCP voor deze film neemt ongeveer %.1f Gb in beslag, er is echter maar "
"%.1f Gb beschikbaar. Wilt u toch doorgaan?"
-#: src/tools/dcpomatic.cc:870
+#: src/tools/dcpomatic.cc:876
msgid "The DCP-o-matic download server could not be contacted."
msgstr "De verbinding met de DCP-o-matic download server is niet beschikbaar."
-#: src/tools/dcpomatic.cc:296
+#: src/tools/dcpomatic.cc:299
msgid ""
"The directory %1 already exists and is not empty. Are you sure you want to "
"use it?"
msgstr "De map %1 bestaat al en is niet leeg. Wilt u deze toch gebruiken?"
-#: src/tools/dcpomatic.cc:865
+#: src/tools/dcpomatic.cc:871
msgid "There are no new versions of DCP-o-matic available."
msgstr "Er is geen nieuwere versie van DCP-o-matic beschikbaar."
-#: src/tools/dcpomatic.cc:509 src/tools/dcpomatic_batch.cc:111
+#: src/tools/dcpomatic.cc:512 src/tools/dcpomatic_batch.cc:111
msgid "There are unfinished jobs; are you sure you want to quit?"
msgstr ""
"Er zijn nog niet afgeronde projecten, weet u zeker dat u wilt afsluiten?"
-#: src/tools/dcpomatic.cc:243
+#: src/tools/dcpomatic.cc:246
msgid ""
"This film was created with an old version of DVD-o-matic and may not load "
"correctly in this version. Please check the film's settings carefully."
"Deze film is gemaakt met een oude versie van DCP-o-matic en opent mogelijk "
"niet goed in de huidige versie. Controleer alle instellingen zorgvuldig."
-#: src/tools/dcpomatic.cc:510 src/tools/dcpomatic_batch.cc:112
+#: src/tools/dcpomatic.cc:513 src/tools/dcpomatic_batch.cc:112
msgid "Unfinished jobs"
msgstr "Niet afgemaakte projecten"
-#: src/tools/dcpomatic.cc:331 src/tools/dcpomatic_batch.cc:161
+#: src/tools/dcpomatic.cc:334 src/tools/dcpomatic_batch.cc:161
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
msgstr ""
"Project-Id-Version: DCP-o-matic\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-01-19 08:59+0100\n"
"Last-Translator: Adam Klotblixt <adam.klotblixt@gmail.com>\n"
"Language-Team: \n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.3\n"
-#: src/tools/dcpomatic.cc:306
+#: src/tools/dcpomatic.cc:309
msgid "%1 already exists as a file, so you cannot use it for a new film."
msgstr "%1 finns redan som fil, så du kan inte använda den för en ny film."
msgid "&Add Film..."
msgstr ""
-#: src/tools/dcpomatic.cc:652
+#: src/tools/dcpomatic.cc:655
msgid "&Content"
msgstr ""
-#: src/tools/dcpomatic.cc:650
+#: src/tools/dcpomatic.cc:653
msgid "&Edit"
msgstr "&Redigera"
-#: src/tools/dcpomatic.cc:614
+#: src/tools/dcpomatic.cc:617
msgid "&Exit"
msgstr "&Avsluta"
-#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:56
+#: src/tools/dcpomatic.cc:651 src/tools/dcpomatic_batch.cc:56
msgid "&File"
msgstr "&Fil"
-#: src/tools/dcpomatic.cc:655 src/tools/dcpomatic_batch.cc:57
+#: src/tools/dcpomatic.cc:658 src/tools/dcpomatic_batch.cc:57
msgid "&Help"
msgstr "&Hjälp"
-#: src/tools/dcpomatic.cc:653
+#: src/tools/dcpomatic.cc:656
msgid "&Jobs"
msgstr "&Jobb"
-#: src/tools/dcpomatic.cc:631
+#: src/tools/dcpomatic.cc:634
#, fuzzy
msgid "&Make DCP\tCtrl-M"
msgstr "&Skapa DCP\tCtrl-M"
-#: src/tools/dcpomatic.cc:601
+#: src/tools/dcpomatic.cc:604
#, fuzzy
msgid "&Open...\tCtrl-O"
msgstr "&Öppna...\tCtrl-O"
-#: src/tools/dcpomatic.cc:620 src/tools/dcpomatic.cc:623
+#: src/tools/dcpomatic.cc:623 src/tools/dcpomatic.cc:626
#, fuzzy
msgid "&Preferences...\tCtrl-P"
msgstr "&Inställningar...\tCtrl-P"
-#: src/tools/dcpomatic.cc:605
+#: src/tools/dcpomatic.cc:608
msgid "&Properties..."
msgstr "&Egenskaper"
-#: src/tools/dcpomatic.cc:616 src/tools/dcpomatic_batch.cc:51
+#: src/tools/dcpomatic.cc:619 src/tools/dcpomatic_batch.cc:51
msgid "&Quit"
msgstr "&Avsluta"
-#: src/tools/dcpomatic.cc:603
+#: src/tools/dcpomatic.cc:606
msgid "&Save\tCtrl-S"
msgstr ""
-#: src/tools/dcpomatic.cc:633
+#: src/tools/dcpomatic.cc:636
msgid "&Send DCP to TMS"
msgstr "&Skicka DCP till TMS"
-#: src/tools/dcpomatic.cc:654
+#: src/tools/dcpomatic.cc:657
msgid "&Tools"
msgstr "&Verktyg"
-#: src/tools/dcpomatic.cc:645 src/tools/dcpomatic_batch.cc:54
+#: src/tools/dcpomatic.cc:648 src/tools/dcpomatic_batch.cc:54
msgid "About"
msgstr "Om"
-#: src/tools/dcpomatic.cc:643
+#: src/tools/dcpomatic.cc:646
msgid "About DCP-o-matic"
msgstr "Om DCP-o-matic"
msgid "Add Film..."
msgstr ""
-#: src/tools/dcpomatic.cc:821
+#: src/tools/dcpomatic.cc:827
#, c-format
msgid ""
"An exception occurred (%s). Please report this problem to the DCP-o-matic "
"author (carl@dcpomatic.com)."
msgstr ""
-#: src/tools/dcpomatic.cc:823 src/tools/dcpomatic.cc:832
+#: src/tools/dcpomatic.cc:829 src/tools/dcpomatic.cc:838
msgid ""
"An unknown exception occurred. Please report this problem to the DCP-o-"
"matic author (carl@dcpomatic.com)."
msgstr ""
-#: src/tools/dcpomatic.cc:418
+#: src/tools/dcpomatic.cc:421
msgid "An unknown exeception occurred."
msgstr ""
-#: src/tools/dcpomatic.cc:414
+#: src/tools/dcpomatic.cc:417
msgid "CPL's content is not encrypted."
msgstr ""
-#: src/tools/dcpomatic.cc:639
+#: src/tools/dcpomatic.cc:642
msgid "Check for updates"
msgstr "Leta efter uppdateringar"
-#: src/tools/dcpomatic.cc:760 src/tools/dcpomatic_batch.cc:238
+#: src/tools/dcpomatic.cc:766 src/tools/dcpomatic_batch.cc:238
msgid "Could not load film %1 (%2)"
msgstr "Kunde inte öppna filmen %1 (%2)"
-#: src/tools/dcpomatic.cc:257 src/tools/dcpomatic_batch.cc:175
+#: src/tools/dcpomatic.cc:260 src/tools/dcpomatic_batch.cc:175
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Kunde inte öppna filmen vid %s (%s)"
-#: src/tools/dcpomatic.cc:464
+#: src/tools/dcpomatic.cc:467
msgid "Could not show DCP (could not run konqueror)"
msgstr "Kunde inte visa DCP (kunde inte köra konqueror)"
-#: src/tools/dcpomatic.cc:457
+#: src/tools/dcpomatic.cc:460
msgid "Could not show DCP (could not run nautilus)"
msgstr "Kunde inte visa DCP (kunde inte köra nautilus)"
-#: src/tools/dcpomatic.cc:277 src/tools/dcpomatic.cc:715
-#: src/tools/dcpomatic.cc:751
+#: src/tools/dcpomatic.cc:280 src/tools/dcpomatic.cc:721
+#: src/tools/dcpomatic.cc:757
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic Batch Converter"
msgstr ""
-#: src/tools/dcpomatic.cc:638
+#: src/tools/dcpomatic.cc:641
msgid "Encoding servers..."
msgstr "Kodningsservrar..."
-#: src/tools/dcpomatic.cc:85
+#: src/tools/dcpomatic.cc:84
msgid "Film changed"
msgstr "Film ändrad"
-#: src/tools/dcpomatic.cc:637
+#: src/tools/dcpomatic.cc:640
msgid "Hints...\tCtrl-H"
msgstr "RÃ¥d...\tCtrl-H"
-#: src/tools/dcpomatic.cc:632
+#: src/tools/dcpomatic.cc:635
msgid "Make &KDMs...\tCtrl-K"
msgstr "Skapa &KDM:er...\tCtrl-K"
-#: src/tools/dcpomatic.cc:600
+#: src/tools/dcpomatic.cc:603
msgid "New...\tCtrl-N"
msgstr "Ny...\tCtrl-N"
-#: src/tools/dcpomatic.cc:634
+#: src/tools/dcpomatic.cc:637
msgid "S&how DCP"
msgstr "&Visa DCP"
-#: src/tools/dcpomatic.cc:84
+#: src/tools/dcpomatic.cc:83
#, c-format
msgid "Save changes to film \"%s\" before closing?"
msgstr "Spara ändringarna till filmen \"%s\" före avslut?"
-#: src/tools/dcpomatic.cc:628
+#: src/tools/dcpomatic.cc:631
msgid "Scale to fit &height"
msgstr ""
-#: src/tools/dcpomatic.cc:627
+#: src/tools/dcpomatic.cc:630
msgid "Scale to fit &width"
msgstr ""
-#: src/tools/dcpomatic.cc:322 src/tools/dcpomatic_batch.cc:152
+#: src/tools/dcpomatic.cc:325 src/tools/dcpomatic_batch.cc:152
msgid "Select film to open"
msgstr "Välj film att öppna"
-#: src/tools/dcpomatic.cc:385
+#: src/tools/dcpomatic.cc:388
#, c-format
msgid ""
"The DCP for this film will take up about %.1f Gb, and the disk that you are "
"DCP:n för denna film kommer att uppta ungefär %.1f Gb, och disken du "
"använder har bara %.1f Gb ledigt. Vill du fortsätta ändå?"
-#: src/tools/dcpomatic.cc:870
+#: src/tools/dcpomatic.cc:876
msgid "The DCP-o-matic download server could not be contacted."
msgstr "DCP-o-matics nedladdningsserver kunde inte kontaktas."
-#: src/tools/dcpomatic.cc:296
+#: src/tools/dcpomatic.cc:299
msgid ""
"The directory %1 already exists and is not empty. Are you sure you want to "
"use it?"
"Foldern %1 finns redan och är inte tom. Är du säker på att du vill använda "
"den?"
-#: src/tools/dcpomatic.cc:865
+#: src/tools/dcpomatic.cc:871
msgid "There are no new versions of DCP-o-matic available."
msgstr "Det finns inga nya versioner av DCP-o-matic tillgängligt."
-#: src/tools/dcpomatic.cc:509 src/tools/dcpomatic_batch.cc:111
+#: src/tools/dcpomatic.cc:512 src/tools/dcpomatic_batch.cc:111
msgid "There are unfinished jobs; are you sure you want to quit?"
msgstr "Det finns oasvlutade jobb; är du säker på att du vill avsluta?"
-#: src/tools/dcpomatic.cc:243
+#: src/tools/dcpomatic.cc:246
msgid ""
"This film was created with an old version of DVD-o-matic and may not load "
"correctly in this version. Please check the film's settings carefully."
msgstr ""
-#: src/tools/dcpomatic.cc:510 src/tools/dcpomatic_batch.cc:112
+#: src/tools/dcpomatic.cc:513 src/tools/dcpomatic_batch.cc:112
msgid "Unfinished jobs"
msgstr "Oavslutade jobb"
-#: src/tools/dcpomatic.cc:331 src/tools/dcpomatic_batch.cc:161
+#: src/tools/dcpomatic.cc:334 src/tools/dcpomatic_batch.cc:161
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
#include "lib/util.h"
#include "lib/scaler.h"
#include "lib/server.h"
-#include "lib/dcp_video_frame.h"
+#include "lib/dcp_video.h"
#include "lib/decoder.h"
#include "lib/exceptions.h"
#include "lib/scaler.h"
#include "lib/log.h"
#include "lib/video_decoder.h"
#include "lib/player.h"
-#include "lib/player_video_frame.h"
+#include "lib/player_video.h"
+#include "lib/encoded_data.h"
using std::cout;
using std::cerr;
static shared_ptr<Film> film;
static ServerDescription* server;
static shared_ptr<FileLog> log_ (new FileLog ("servomatictest.log"));
-static int frame = 0;
+static int frame_count = 0;
void
-process_video (shared_ptr<PlayerVideoFrame> pvf)
+process_video (shared_ptr<PlayerVideo> pvf)
{
- shared_ptr<DCPVideoFrame> local (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
- shared_ptr<DCPVideoFrame> remote (new DCPVideoFrame (pvf, frame, film->video_frame_rate(), 250000000, RESOLUTION_2K, log_));
+ shared_ptr<DCPVideo> local (new DCPVideo (pvf, frame_count, film->video_frame_rate(), 250000000, RESOLUTION_2K, true, log_));
+ shared_ptr<DCPVideo> remote (new DCPVideo (pvf, frame_count, film->video_frame_rate(), 250000000, RESOLUTION_2K, true, log_));
- cout << "Frame " << frame << ": ";
+ cout << "Frame " << frame_count << ": ";
cout.flush ();
- ++frame;
+ ++frame_count;
shared_ptr<EncodedData> local_encoded = local->encode_locally ();
shared_ptr<EncodedData> remote_encoded;
film->read_metadata ();
shared_ptr<Player> player = film->make_player ();
- player->disable_audio ();
- player->Video.connect (boost::bind (process_video, _1));
- bool done = false;
- while (!done) {
- done = player->pass ();
+ DCPTime const frame = DCPTime::from_frames (1, film->video_frame_rate ());
+ for (DCPTime t; t < film->length(); t += frame) {
+ process_video (player->get_video(t, true).front ());
}
} catch (std::exception& e) {
cerr << "Error: " << e.what() << "\n";
def build(bld):
for t in ['dcpomatic_cli', 'dcpomatic_server_cli', 'server_test', 'dcpomatic_kdm', 'dcpomatic_create']:
obj = bld(features = 'cxx cxxprogram')
- obj.uselib = 'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC QUICKMAIL'
+ obj.uselib = 'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC WXWIDGETS QUICKMAIL SUB'
obj.includes = ['..']
- obj.use = ['libdcpomatic']
+ obj.use = ['libdcpomatic2']
obj.source = '%s.cc' % t
- obj.target = t
+ obj.target = t.replace('dcpomatic', 'dcpomatic2')
if t == 'server_test':
obj.install_path = None
if not bld.env.DISABLE_GUI:
for t in ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server']:
obj = bld(features = 'cxx cxxprogram')
- obj.uselib = 'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS QUICKMAIL'
+ obj.uselib = 'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS QUICKMAIL SUB'
if bld.env.BUILD_STATIC:
obj.uselib += ' GTK'
obj.includes = ['..']
- obj.use = ['libdcpomatic', 'libdcpomatic-wx']
+ obj.use = ['libdcpomatic2', 'libdcpomatic2-wx']
obj.source = '%s.cc' % t
if bld.env.TARGET_WINDOWS:
obj.source += ' ../../platform/windows/dcpomatic.rc'
- obj.target = t
+ obj.target = t.replace('dcpomatic', 'dcpomatic2')
- i18n.po_to_mo(os.path.join('src', 'tools'), 'dcpomatic', bld)
+ i18n.po_to_mo(os.path.join('src', 'tools'), 'dcpomatic2', bld)
def pot(bld):
i18n.pot(os.path.join('src', 'tools'), 'dcpomatic.cc dcpomatic_batch.cc', 'dcpomatic')
/*
- 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/wx/about_dialog.cc
+ * @brief The "about DCP-o-matic" dialogue box.
+ */
+
#include <wx/notebook.h>
#include <wx/hyperlink.h>
#include "lib/version.h"
SetSizerAndFit (overall_sizer);
}
+/** Add a section of credits.
+ * @param name Name of section.
+ * @param credits List of names.
+ */
void
AboutDialog::add_section (wxString name, wxArrayString credits)
{
/*
- 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/wx/about_dialog.h
+ * @brief The "about DCP-o-matic" dialogue box.
+ */
+
#include <wx/wx.h>
class wxNotebook;
+/** @class AboutDialog
+ * @brief The "about DCP-o-matic" dialogue box.
+ */
class AboutDialog : public wxDialog
{
public:
private:
void add_section (wxString, wxArrayString);
- wxNotebook* _notebook;
+ wxNotebook* _notebook; ///< notebook used to keep each list of names for the credits
};
/*
- 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/wx/audio_mapping_view.cc
+ * @brief AudioMappingView class and helpers.
+ */
+
#include <wx/wx.h>
#include <wx/renderer.h>
#include <wx/grid.h>
-#include <libdcp/types.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/types.h>
+#include <dcp/raw_convert.h>
#include "lib/audio_mapping.h"
#include "lib/util.h"
#include "audio_mapping_view.h"
#include "wx_util.h"
#include "audio_gain_dialog.h"
+#include <boost/lexical_cast.hpp>
using std::cout;
using std::list;
}
};
+/** @class ValueRenderer
+ * @brief wxGridCellRenderer for a gain value.
+ */
class ValueRenderer : public wxGridCellRenderer
{
public:
return;
}
- libdcp::Channel d = static_cast<libdcp::Channel> (ev.GetCol() - 1);
+ dcp::Channel d = static_cast<dcp::Channel> (ev.GetCol() - 1);
if (_map.get (ev.GetRow(), d) > 0) {
_map.set (ev.GetRow(), d, 0);
void
AudioMappingView::off ()
{
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), 0);
+ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), 0);
map_changed ();
}
void
AudioMappingView::full ()
{
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), 1);
+ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), 1);
map_changed ();
}
void
AudioMappingView::minus6dB ()
{
- _map.set (_menu_row, static_cast<libdcp::Channel> (_menu_column - 1), pow (10, -6.0 / 20));
+ _map.set (_menu_row, static_cast<dcp::Channel> (_menu_column - 1), pow (10, -6.0 / 20));
map_changed ();
}
void
AudioMappingView::edit ()
{
- libdcp::Channel d = static_cast<libdcp::Channel> (_menu_column - 1);
+ dcp::Channel d = static_cast<dcp::Channel> (_menu_column - 1);
AudioGainDialog* dialog = new AudioGainDialog (this, _menu_row, _menu_column - 1, _map.get (_menu_row, d));
if (dialog->ShowModal () == wxID_OK) {
_grid->SetCellValue (i, 0, wxString::Format (wxT("%d"), i + 1));
for (int j = 1; j < _grid->GetNumberCols(); ++j) {
- _grid->SetCellValue (i, j, std_to_wx (libdcp::raw_convert<string> (_map.get (i, static_cast<libdcp::Channel> (j - 1)))));
+ _grid->SetCellValue (i, j, std_to_wx (dcp::raw_convert<string> (_map.get (i, static_cast<dcp::Channel> (j - 1)))));
}
}
if (row != _last_tooltip_row || column != _last_tooltip_column) {
wxString s;
- float const gain = _map.get (row, static_cast<libdcp::Channel> (column - 1));
+ float const gain = _map.get (row, static_cast<dcp::Channel> (column - 1));
if (gain == 0) {
s = wxString::Format (_("No audio will be passed from content channel %d to DCP channel %d."), row + 1, column);
} else if (gain == 1) {
/*
- 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/wx/audio_mapping_view.h
+ * @brief AudioMappingView class
+ *
+ * This class displays the mapping of one set of audio channels to another,
+ * with gain values on each node of the map.
+ */
+
#include <boost/signals2.hpp>
#include <wx/wx.h>
#include <wx/grid.h>
/*
- 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 <boost/lexical_cast.hpp>
#include <wx/spinctrl.h>
#include "lib/config.h"
-#include "lib/sound_processor.h"
#include "lib/ffmpeg_content.h"
+#include "lib/ffmpeg_audio_stream.h"
+#include "lib/audio_processor.h"
+#include "lib/cinema_sound_processor.h"
#include "audio_dialog.h"
#include "audio_panel.h"
#include "audio_mapping_view.h"
#include "wx_util.h"
#include "gain_calculator_dialog.h"
-#include "film_editor.h"
+#include "content_panel.h"
using std::vector;
using std::cout;
using std::string;
+using std::list;
using boost::dynamic_pointer_cast;
using boost::lexical_cast;
using boost::shared_ptr;
-AudioPanel::AudioPanel (FilmEditor* e)
- : FilmEditorPanel (e, _("Audio"))
+AudioPanel::AudioPanel (ContentPanel* p)
+ : ContentSubPanel (p, _("Audio"))
, _audio_dialog (0)
{
wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
add_label_to_grid_bag_sizer (grid, this, _("Stream"), true, wxGBPosition (r, 0));
_stream = new wxChoice (this, wxID_ANY);
- grid->Add (_stream, wxGBPosition (r, 1));
- _stream_description = add_label_to_grid_bag_sizer (grid, this, "", false, wxGBPosition (r, 3));
+ grid->Add (_stream, wxGBPosition (r, 1), wxGBSpan (1, 3), wxEXPAND);
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, this, _("Process with"), true, wxGBPosition (r, 0));
+ _processor = new wxChoice (this, wxID_ANY);
+ setup_processors ();
+ grid->Add (_processor, wxGBPosition (r, 1), wxGBSpan (1, 3), wxEXPAND);
++r;
_mapping = new AudioMappingView (this);
_gain->wrapped()->SetIncrement (0.5);
_delay->wrapped()->SetRange (-1000, 1000);
- _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::stream_changed, this));
- _show->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::show_clicked, this));
- _gain_calculate_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::gain_calculate_button_clicked, this));
+ _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::stream_changed, this));
+ _show->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::show_clicked, this));
+ _gain_calculate_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&AudioPanel::gain_calculate_button_clicked, this));
+ _processor->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&AudioPanel::processor_changed, this));
_mapping->Changed.connect (boost::bind (&AudioPanel::mapping_changed, this, _1));
}
{
switch (property) {
case Film::AUDIO_CHANNELS:
- _mapping->set_channels (_editor->film()->audio_channels ());
+ _mapping->set_channels (_parent->film()->audio_channels ());
_sizer->Layout ();
break;
case Film::VIDEO_FRAME_RATE:
void
AudioPanel::film_content_changed (int property)
{
- AudioContentList ac = _editor->selected_audio_content ();
+ AudioContentList ac = _parent->selected_audio ();
shared_ptr<AudioContent> acs;
shared_ptr<FFmpegContent> fcs;
if (ac.size() == 1) {
} else if (property == AudioContentProperty::AUDIO_FRAME_RATE) {
setup_description ();
} else if (property == FFmpegContentProperty::AUDIO_STREAM) {
- setup_stream_description ();
_mapping->set (acs ? acs->audio_mapping () : AudioMapping ());
_sizer->Layout ();
} else if (property == FFmpegContentProperty::AUDIO_STREAMS) {
if (fcs->audio_stream()) {
checked_set (_stream, fcs->audio_stream()->identifier ());
- setup_stream_description ();
}
}
+ } else if (property == AudioContentProperty::AUDIO_PROCESSOR) {
+ if (acs) {
+ checked_set (_processor, acs->audio_processor() ? acs->audio_processor()->id() : N_("none"));
+ } else {
+ checked_set (_processor, N_("none"));
+ }
}
}
}
_gain->wrapped()->SetValue (
- Config::instance()->sound_processor()->db_for_fader_change (
+ Config::instance()->cinema_sound_processor()->db_for_fader_change (
d->wanted_fader (),
d->actual_fader ()
)
_audio_dialog = 0;
}
- AudioContentList ac = _editor->selected_audio_content ();
+ AudioContentList ac = _parent->selected_audio ();
if (ac.size() != 1) {
return;
}
void
AudioPanel::stream_changed ()
{
- FFmpegContentList fc = _editor->selected_ffmpeg_content ();
+ FFmpegContentList fc = _parent->selected_ffmpeg ();
if (fc.size() != 1) {
return;
}
if (i != a.end ()) {
fcs->set_audio_stream (*i);
}
+}
- setup_stream_description ();
+void
+AudioPanel::processor_changed ()
+{
+ string const s = string_client_data (_processor->GetClientObject (_processor->GetSelection ()));
+ AudioProcessor const * p = 0;
+ if (s != wx_to_std (N_("none"))) {
+ p = AudioProcessor::from_id (s);
+ }
+
+ AudioContentList c = _parent->selected_audio ();
+ for (AudioContentList::const_iterator i = c.begin(); i != c.end(); ++i) {
+ (*i)->set_audio_processor (p);
+ }
}
void
AudioPanel::setup_description ()
{
- AudioContentList ac = _editor->selected_audio_content ();
+ AudioContentList ac = _parent->selected_audio ();
if (ac.size () != 1) {
_description->SetLabel ("");
return;
}
shared_ptr<AudioContent> acs = ac.front ();
- if (acs->content_audio_frame_rate() != acs->output_audio_frame_rate ()) {
+ if (acs->audio_frame_rate() != acs->resampled_audio_frame_rate ()) {
_description->SetLabel (wxString::Format (
_("Audio will be resampled from %.3fkHz to %.3fkHz."),
- acs->content_audio_frame_rate() / 1000.0,
- acs->output_audio_frame_rate() / 1000.0
+ acs->audio_frame_rate() / 1000.0,
+ acs->resampled_audio_frame_rate() / 1000.0
));
} else {
_description->SetLabel (_("Audio will not be resampled."));
}
}
-void
-AudioPanel::setup_stream_description ()
-{
- FFmpegContentList fc = _editor->selected_ffmpeg_content ();
- if (fc.size() != 1) {
- _stream_description->SetLabel ("");
- return;
- }
-
- shared_ptr<FFmpegContent> fcs = fc.front ();
-
- if (!fcs->audio_stream ()) {
- _stream_description->SetLabel (wxT (""));
- } else {
- wxString s;
- if (fcs->audio_channels() == 1) {
- s << _("1 channel");
- } else {
- s << fcs->audio_channels() << wxT (" ") << _("channels");
- }
- s << wxT (", ") << fcs->content_audio_frame_rate() << _("Hz");
- _stream_description->SetLabel (s);
- }
-}
-
void
AudioPanel::mapping_changed (AudioMapping m)
{
- AudioContentList c = _editor->selected_audio_content ();
+ AudioContentList c = _parent->selected_audio ();
if (c.size() == 1) {
c.front()->set_audio_mapping (m);
}
void
AudioPanel::content_selection_changed ()
{
- AudioContentList sel = _editor->selected_audio_content ();
+ AudioContentList sel = _parent->selected_audio ();
if (_audio_dialog && sel.size() == 1) {
_audio_dialog->set_content (sel.front ());
_gain->set_content (sel);
_delay->set_content (sel);
+ _gain_calculate_button->Enable (sel.size() == 1);
_show->Enable (sel.size() == 1);
_stream->Enable (sel.size() == 1);
+ _processor->Enable (!sel.empty());
_mapping->Enable (sel.size() == 1);
+ setup_processors ();
+
film_content_changed (AudioContentProperty::AUDIO_MAPPING);
+ film_content_changed (AudioContentProperty::AUDIO_PROCESSOR);
film_content_changed (AudioContentProperty::AUDIO_FRAME_RATE);
film_content_changed (FFmpegContentProperty::AUDIO_STREAM);
film_content_changed (FFmpegContentProperty::AUDIO_STREAMS);
}
+
+void
+AudioPanel::setup_processors ()
+{
+ AudioContentList sel = _parent->selected_audio ();
+
+ _processor->Clear ();
+ list<AudioProcessor const *> ap = AudioProcessor::all ();
+ _processor->Append (_("None"), new wxStringClientData (N_("none")));
+ for (list<AudioProcessor const *>::const_iterator i = ap.begin(); i != ap.end(); ++i) {
+
+ AudioContentList::const_iterator j = sel.begin();
+ while (j != sel.end() && (*i)->in_channels().includes ((*j)->audio_channels ())) {
+ ++j;
+ }
+
+ if (j == sel.end ()) {
+ _processor->Append (std_to_wx ((*i)->name ()), new wxStringClientData (std_to_wx ((*i)->id ())));
+ }
+ }
+}
*/
#include "lib/audio_mapping.h"
-#include "film_editor_panel.h"
+#include "content_sub_panel.h"
#include "content_widget.h"
class wxSpinCtrlDouble;
class AudioMappingView;
class AudioDialog;
-class AudioPanel : public FilmEditorPanel
+class AudioPanel : public ContentSubPanel
{
public:
- AudioPanel (FilmEditor *);
+ AudioPanel (ContentPanel *);
void film_changed (Film::Property);
void film_content_changed (int);
void show_clicked ();
void stream_changed ();
void mapping_changed (AudioMapping);
+ void processor_changed ();
+ void setup_processors ();
void setup_description ();
- void setup_stream_description ();
ContentSpinCtrlDouble<AudioContent>* _gain;
wxButton* _gain_calculate_button;
wxButton* _show;
ContentSpinCtrl<AudioContent>* _delay;
wxChoice* _stream;
+ wxChoice* _processor;
wxStaticText* _stream_description;
AudioMappingView* _mapping;
wxStaticText* _description;
#include <wx/preferences.h>
#include <wx/filepicker.h>
#include <wx/spinctrl.h>
-#include <libdcp/colour_matrix.h>
+#include <dcp/colour_matrix.h>
+#include <dcp/exceptions.h>
+#include <dcp/signer.h>
#include "lib/config.h"
#include "lib/ratio.h"
#include "lib/scaler.h"
#include "lib/filter.h"
#include "lib/dcp_content_type.h"
#include "lib/colour_conversion.h"
+#include "lib/log.h"
+#include "lib/util.h"
+#include "lib/cross.h"
+#include "lib/exceptions.h"
#include "config_dialog.h"
#include "wx_util.h"
#include "editable_list.h"
#include "isdcf_metadata_dialog.h"
#include "preset_colour_conversion_dialog.h"
#include "server_dialog.h"
+#include "make_signer_chain_dialog.h"
using std::vector;
using std::string;
_num_local_encoding_threads = new wxSpinCtrl (panel);
table->Add (_num_local_encoding_threads, 1);
-
_check_for_updates = new wxCheckBox (panel, wxID_ANY, _("Check for updates on startup"));
table->Add (_check_for_updates, 1, wxEXPAND | wxALL);
table->AddSpacer (0);
}
};
+class KeysPage : public wxPreferencesPage, public Page
+{
+public:
+ KeysPage (wxSize panel_size, int border)
+ : Page (panel_size, border)
+ {}
+
+ wxString GetName () const
+ {
+ return _("Keys");
+ }
+
+#ifdef DCPOMATIC_OSX
+ wxBitmap GetLargeIcon () const
+ {
+ return wxBitmap ("keys", wxBITMAP_TYPE_PNG_RESOURCE);
+ }
+#endif
+
+ wxWindow* CreateWindow (wxWindow* parent)
+ {
+ _panel = new wxPanel (parent, wxID_ANY, wxDefaultPosition, _panel_size);
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ _panel->SetSizer (overall_sizer);
+
+ wxStaticText* m = new wxStaticText (_panel, wxID_ANY, _("Certificate chain for signing DCPs and KDMs:"));
+ overall_sizer->Add (m, 0, wxALL, _border);
+
+ wxBoxSizer* certificates_sizer = new wxBoxSizer (wxHORIZONTAL);
+ overall_sizer->Add (certificates_sizer, 0, wxLEFT | wxRIGHT, _border);
+
+ _certificates = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (400, 200), wxLC_REPORT | wxLC_SINGLE_SEL);
+
+ {
+ wxListItem ip;
+ ip.SetId (0);
+ ip.SetText (_("Type"));
+ ip.SetWidth (100);
+ _certificates->InsertColumn (0, ip);
+ }
+
+ {
+ wxListItem ip;
+ ip.SetId (1);
+ ip.SetText (_("Thumbprint"));
+ ip.SetWidth (300);
+
+ wxFont font = ip.GetFont ();
+ font.SetFamily (wxFONTFAMILY_TELETYPE);
+ ip.SetFont (font);
+
+ _certificates->InsertColumn (1, ip);
+ }
+
+ certificates_sizer->Add (_certificates, 1, wxEXPAND);
+
+ {
+ wxSizer* s = new wxBoxSizer (wxVERTICAL);
+ _add_certificate = new wxButton (_panel, wxID_ANY, _("Add..."));
+ s->Add (_add_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+ _remove_certificate = new wxButton (_panel, wxID_ANY, _("Remove"));
+ s->Add (_remove_certificate, 0, wxTOP | wxBOTTOM, DCPOMATIC_BUTTON_STACK_GAP);
+ certificates_sizer->Add (s, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
+ }
+
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ table->AddGrowableCol (1, 1);
+ overall_sizer->Add (table, 1, wxALL | wxEXPAND, _border);
+
+ _remake_certificates = new wxButton (_panel, wxID_ANY, _("Re-make certificates..."));
+ table->Add (_remake_certificates, 0);
+ table->AddSpacer (0);
+
+ add_label_to_sizer (table, _panel, _("Private key for leaf certificate"), true);
+ {
+ wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _signer_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
+ wxFont font = _signer_private_key->GetFont ();
+ font.SetFamily (wxFONTFAMILY_TELETYPE);
+ _signer_private_key->SetFont (font);
+ s->Add (_signer_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
+ _load_signer_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
+ s->Add (_load_signer_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
+ table->Add (s, 0);
+ }
+
+ add_label_to_sizer (table, _panel, _("Certificate for decrypting DCPs"), true);
+ {
+ wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _decryption_certificate = new wxStaticText (_panel, wxID_ANY, wxT (""));
+ wxFont font = _decryption_certificate->GetFont ();
+ font.SetFamily (wxFONTFAMILY_TELETYPE);
+ _decryption_certificate->SetFont (font);
+ s->Add (_decryption_certificate, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
+ _load_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Load..."));
+ s->Add (_load_decryption_certificate, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
+ table->Add (s, 0);
+ }
+
+ add_label_to_sizer (table, _panel, _("Private key for decrypting DCPs"), true);
+ {
+ wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _decryption_private_key = new wxStaticText (_panel, wxID_ANY, wxT (""));
+ wxFont font = _decryption_private_key->GetFont ();
+ font.SetFamily (wxFONTFAMILY_TELETYPE);
+ _decryption_private_key->SetFont (font);
+ s->Add (_decryption_private_key, 1, wxLEFT | wxRIGHT | wxALIGN_CENTER_VERTICAL, DCPOMATIC_SIZER_X_GAP);
+ _load_decryption_private_key = new wxButton (_panel, wxID_ANY, _("Load..."));
+ s->Add (_load_decryption_private_key, 0, wxLEFT, DCPOMATIC_SIZER_X_GAP);
+ table->Add (s, 0);
+ }
+
+ _export_decryption_certificate = new wxButton (_panel, wxID_ANY, _("Export DCP decryption certificate..."));
+ table->Add (_export_decryption_certificate);
+ table->AddSpacer (0);
+
+ _add_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::add_certificate, this));
+ _remove_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remove_certificate, this));
+ _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KeysPage::update_sensitivity, this));
+ _certificates->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KeysPage::update_sensitivity, this));
+ _remake_certificates->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::remake_certificates, this));
+ _load_signer_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_signer_private_key, this));
+ _load_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_certificate, this));
+ _load_decryption_private_key->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::load_decryption_private_key, this));
+ _export_decryption_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KeysPage::export_decryption_certificate, this));
+
+ _signer.reset (new dcp::Signer (*Config::instance()->signer().get ()));
+
+ update_certificate_list ();
+ update_signer_private_key ();
+ update_decryption_certificate ();
+ update_decryption_private_key ();
+ update_sensitivity ();
+
+ return _panel;
+ }
+
+private:
+ void add_certificate ()
+ {
+ wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
+
+ if (d->ShowModal() == wxID_OK) {
+ try {
+ dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
+ _signer->certificates().add (c);
+ Config::instance()->set_signer (_signer);
+ update_certificate_list ();
+ } catch (dcp::MiscError& e) {
+ error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
+ }
+ }
+
+ d->Destroy ();
+
+ update_sensitivity ();
+ }
+
+ void remove_certificate ()
+ {
+ int i = _certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (i == -1) {
+ return;
+ }
+
+ _certificates->DeleteItem (i);
+ _signer->certificates().remove (i);
+ Config::instance()->set_signer (_signer);
+
+ update_sensitivity ();
+ }
+
+ void update_certificate_list ()
+ {
+ _certificates->DeleteAllItems ();
+ dcp::CertificateChain::List certs = _signer->certificates().root_to_leaf ();
+ size_t n = 0;
+ for (dcp::CertificateChain::List::const_iterator i = certs.begin(); i != certs.end(); ++i) {
+ wxListItem item;
+ item.SetId (n);
+ _certificates->InsertItem (item);
+ _certificates->SetItem (n, 1, std_to_wx (i->thumbprint ()));
+
+ if (n == 0) {
+ _certificates->SetItem (n, 0, _("Root"));
+ } else if (n == (certs.size() - 1)) {
+ _certificates->SetItem (n, 0, _("Leaf"));
+ } else {
+ _certificates->SetItem (n, 0, _("Intermediate"));
+ }
+
+ ++n;
+ }
+ }
+
+ void remake_certificates ()
+ {
+ MakeSignerChainDialog* d = new MakeSignerChainDialog (_panel);
+ if (d->ShowModal () == wxID_OK) {
+ _signer.reset (
+ new dcp::Signer (
+ openssl_path (),
+ d->organisation (),
+ d->organisational_unit (),
+ d->root_common_name (),
+ d->intermediate_common_name (),
+ d->leaf_common_name ()
+ )
+ );
+
+ Config::instance()->set_signer (_signer);
+ update_certificate_list ();
+ update_signer_private_key ();
+ }
+
+ d->Destroy ();
+ }
+
+ void update_sensitivity ()
+ {
+ _remove_certificate->Enable (_certificates->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1);
+ }
+
+ void update_signer_private_key ()
+ {
+ _signer_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (_signer->key ())));
+ }
+
+ void load_signer_private_key ()
+ {
+ wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
+
+ if (d->ShowModal() == wxID_OK) {
+ try {
+ boost::filesystem::path p (wx_to_std (d->GetPath ()));
+ if (boost::filesystem::file_size (p) > 1024) {
+ error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), std_to_wx (p.string ())));
+ return;
+ }
+
+ _signer->set_key (dcp::file_to_string (p));
+ Config::instance()->set_signer (_signer);
+ update_signer_private_key ();
+ } catch (dcp::MiscError& e) {
+ error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
+ }
+ }
+
+ d->Destroy ();
+
+ update_sensitivity ();
+
+ }
+
+ void load_decryption_certificate ()
+ {
+ wxFileDialog* d = new wxFileDialog (_panel, _("Select Certificate File"));
+
+ if (d->ShowModal() == wxID_OK) {
+ try {
+ dcp::Certificate c (dcp::file_to_string (wx_to_std (d->GetPath ())));
+ Config::instance()->set_decryption_certificate (c);
+ update_decryption_certificate ();
+ } catch (dcp::MiscError& e) {
+ error_dialog (_panel, wxString::Format (_("Could not read certificate file (%s)"), e.what ()));
+ }
+ }
+
+ d->Destroy ();
+ }
+
+ void update_decryption_certificate ()
+ {
+ _decryption_certificate->SetLabel (std_to_wx (Config::instance()->decryption_certificate().thumbprint ()));
+ }
+
+ void load_decryption_private_key ()
+ {
+ wxFileDialog* d = new wxFileDialog (_panel, _("Select Key File"));
+
+ if (d->ShowModal() == wxID_OK) {
+ try {
+ boost::filesystem::path p (wx_to_std (d->GetPath ()));
+ Config::instance()->set_decryption_private_key (dcp::file_to_string (p));
+ update_decryption_private_key ();
+ } catch (dcp::MiscError& e) {
+ error_dialog (_panel, wxString::Format (_("Could not read key file (%s)"), e.what ()));
+ }
+ }
+
+ d->Destroy ();
+ }
+
+ void update_decryption_private_key ()
+ {
+ _decryption_private_key->SetLabel (std_to_wx (dcp::private_key_fingerprint (Config::instance()->decryption_private_key())));
+ }
+
+ void export_decryption_certificate ()
+ {
+ wxFileDialog* d = new wxFileDialog (
+ _panel, _("Select Certificate File"), wxEmptyString, wxEmptyString, wxT ("PEM files (*.pem)|*.pem"),
+ wxFD_SAVE | wxFD_OVERWRITE_PROMPT
+ );
+
+ if (d->ShowModal () == wxID_OK) {
+ FILE* f = fopen_boost (wx_to_std (d->GetPath ()), "w");
+ if (!f) {
+ throw OpenFileError (wx_to_std (d->GetPath ()));
+ }
+
+ string const s = Config::instance()->decryption_certificate().certificate (true);
+ fwrite (s.c_str(), 1, s.length(), f);
+ fclose (f);
+ }
+ d->Destroy ();
+ }
+
+ wxPanel* _panel;
+ wxListCtrl* _certificates;
+ wxButton* _add_certificate;
+ wxButton* _remove_certificate;
+ wxButton* _remake_certificates;
+ wxStaticText* _signer_private_key;
+ wxButton* _load_signer_private_key;
+ wxStaticText* _decryption_certificate;
+ wxButton* _load_decryption_certificate;
+ wxStaticText* _decryption_private_key;
+ wxButton* _load_decryption_private_key;
+ wxButton* _export_decryption_certificate;
+ shared_ptr<dcp::Signer> _signer;
+};
+
class TMSPage : public wxPreferencesPage, public Page
{
public:
wxButton* _reset_kdm_email;
};
+/** @class AdvancedPage
+ * @brief Advanced page of the preferences dialog.
+ */
class AdvancedPage : public wxStockPreferencesPage, public Page
{
public:
table->Add (t, 0, wxALL, 6);
}
+#ifdef DCPOMATIC_WINDOWS
+ _win32_console = new wxCheckBox (panel, wxID_ANY, _("Open console window"));
+ table->Add (_win32_console, 1, wxEXPAND | wxALL);
+ table->AddSpacer (0);
+#endif
+
Config* config = Config::instance ();
_maximum_j2k_bandwidth->SetRange (1, 500);
_log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
_log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
_log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
+#ifdef DCPOMATIC_WINDOWS
+ _win32_console->SetValue (config->win32_console());
+ _win32_console->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::win32_console_changed, this));
+#endif
return panel;
}
}
Config::instance()->set_log_types (types);
}
+
+#ifdef DCPOMATIC_WINDOWS
+ void win32_console_changed ()
+ {
+ Config::instance()->set_win32_console (_win32_console->GetValue ());
+ }
+#endif
wxSpinCtrl* _maximum_j2k_bandwidth;
wxCheckBox* _allow_any_dcp_frame_rate;
wxCheckBox* _log_warning;
wxCheckBox* _log_error;
wxCheckBox* _log_timing;
+#ifdef DCPOMATIC_WINDOWS
+ wxCheckBox* _win32_console;
+#endif
};
wxPreferencesEditor*
e->AddPage (new DefaultsPage (ps, border));
e->AddPage (new EncodingServersPage (ps, border));
e->AddPage (new ColourConversionsPage (ps, border));
+ e->AddPage (new KeysPage (ps, border));
e->AddPage (new TMSPage (ps, border));
e->AddPage (new KDMEmailPage (ps, border));
e->AddPage (new AdvancedPage (ps, border));
#include "lib/examine_content_job.h"
#include "lib/job_manager.h"
#include "lib/exceptions.h"
+#include "lib/dcp_content.h"
#include "content_menu.h"
#include "repeat_dialog.h"
#include "wx_util.h"
ID_repeat = 1,
ID_join,
ID_find_missing,
+ ID_re_examine,
+ ID_kdm,
ID_remove
};
_repeat = _menu->Append (ID_repeat, _("Repeat..."));
_join = _menu->Append (ID_join, _("Join"));
_find_missing = _menu->Append (ID_find_missing, _("Find missing..."));
+ _re_examine = _menu->Append (ID_re_examine, _("Re-examine..."));
+ _kdm = _menu->Append (ID_kdm, _("Add KDM..."));
_menu->AppendSeparator ();
_remove = _menu->Append (ID_remove, _("Remove"));
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::repeat, this), ID_repeat);
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::join, this), ID_join);
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::find_missing, this), ID_find_missing);
+ _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::re_examine, this), ID_re_examine);
+ _parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::kdm, this), ID_kdm);
_parent->Bind (wxEVT_COMMAND_MENU_SELECTED, boost::bind (&ContentMenu::remove, this), ID_remove);
}
_join->Enable (n > 1);
_find_missing->Enable (_content.size() == 1 && !_content.front()->paths_valid ());
+ _re_examine->Enable (!_content.empty ());
+
+ if (_content.size() == 1) {
+ shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
+ _kdm->Enable (dcp && dcp->encrypted ());
+ } else {
+ _kdm->Enable (false);
+ }
+
_remove->Enable (!_content.empty ());
_parent->PopupMenu (_menu, p);
}
JobManager::instance()->add (j);
}
+void
+ContentMenu::re_examine ()
+{
+ shared_ptr<Film> film = _film.lock ();
+ if (!film) {
+ return;
+ }
+
+ for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
+ film->examine_content (*i);
+ }
+}
+
void
ContentMenu::maybe_found_missing (weak_ptr<Job> j, weak_ptr<Content> oc, weak_ptr<Content> nc)
{
old_content->set_path (new_content->path (0));
}
+
+void
+ContentMenu::kdm ()
+{
+ assert (!_content.empty ());
+ shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (_content.front ());
+ assert (dcp);
+
+ wxFileDialog* d = new wxFileDialog (_parent, _("Select KDM"));
+
+ if (d->ShowModal() == wxID_OK) {
+ dcp->add_kdm (dcp::EncryptedKDM (dcp::file_to_string (wx_to_std (d->GetPath ()))));
+ shared_ptr<Film> film = _film.lock ();
+ assert (film);
+ film->examine_content (dcp);
+ }
+
+ d->Destroy ();
+}
class ContentMenu
{
public:
- ContentMenu (wxWindow *);
+ ContentMenu (wxWindow* p);
~ContentMenu ();
-
+
void popup (boost::weak_ptr<Film>, ContentList, wxPoint);
private:
void repeat ();
void join ();
void find_missing ();
+ void re_examine ();
+ void kdm ();
void remove ();
void maybe_found_missing (boost::weak_ptr<Job>, boost::weak_ptr<Content>, boost::weak_ptr<Content>);
wxMenuItem* _repeat;
wxMenuItem* _join;
wxMenuItem* _find_missing;
+ wxMenuItem* _re_examine;
+ wxMenuItem* _kdm;
wxMenuItem* _remove;
};
--- /dev/null
+/*
+ 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 <wx/wx.h>
+#include <wx/notebook.h>
+#include <wx/listctrl.h>
+#include "lib/audio_content.h"
+#include "lib/subtitle_content.h"
+#include "lib/video_content.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/content_factory.h"
+#include "lib/image_content.h"
+#include "lib/dcp_content.h"
+#include "lib/playlist.h"
+#include "content_panel.h"
+#include "wx_util.h"
+#include "video_panel.h"
+#include "audio_panel.h"
+#include "subtitle_panel.h"
+#include "timing_panel.h"
+#include "timeline_dialog.h"
+
+using std::list;
+using std::string;
+using std::cout;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::dynamic_pointer_cast;
+
+ContentPanel::ContentPanel (wxNotebook* n, boost::shared_ptr<Film> f)
+ : _timeline_dialog (0)
+ , _film (f)
+ , _generally_sensitive (true)
+{
+ _panel = new wxPanel (n);
+ _sizer = new wxBoxSizer (wxVERTICAL);
+ _panel->SetSizer (_sizer);
+
+ _menu = new ContentMenu (_panel);
+
+ {
+ wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+
+ _content = new wxListCtrl (_panel, wxID_ANY, wxDefaultPosition, wxSize (320, 160), wxLC_REPORT | wxLC_NO_HEADER);
+ s->Add (_content, 1, wxEXPAND | wxTOP | wxBOTTOM, 6);
+
+ _content->InsertColumn (0, wxT(""));
+ _content->SetColumnWidth (0, 512);
+
+ wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
+ _add_file = new wxButton (_panel, wxID_ANY, _("Add file(s)..."));
+ b->Add (_add_file, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+ _add_folder = new wxButton (_panel, wxID_ANY, _("Add folder..."));
+ b->Add (_add_folder, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+ _remove = new wxButton (_panel, wxID_ANY, _("Remove"));
+ b->Add (_remove, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+ _earlier = new wxButton (_panel, wxID_ANY, _("Up"));
+ b->Add (_earlier, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+ _later = new wxButton (_panel, wxID_ANY, _("Down"));
+ b->Add (_later, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+ _timeline = new wxButton (_panel, wxID_ANY, _("Timeline..."));
+ b->Add (_timeline, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+
+ s->Add (b, 0, wxALL, 4);
+
+ _sizer->Add (s, 0, wxEXPAND | wxALL, 6);
+ }
+
+ _sequence_video = new wxCheckBox (_panel, wxID_ANY, _("Keep video in sequence"));
+ _sizer->Add (_sequence_video);
+
+ _notebook = new wxNotebook (_panel, wxID_ANY);
+ _sizer->Add (_notebook, 1, wxEXPAND | wxTOP, 6);
+
+ _video_panel = new VideoPanel (this);
+ _panels.push_back (_video_panel);
+ _audio_panel = new AudioPanel (this);
+ _panels.push_back (_audio_panel);
+ _subtitle_panel = new SubtitlePanel (this);
+ _panels.push_back (_subtitle_panel);
+ _timing_panel = new TimingPanel (this);
+ _panels.push_back (_timing_panel);
+
+ _content->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&ContentPanel::selection_changed, this));
+ _content->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&ContentPanel::selection_changed, this));
+ _content->Bind (wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, boost::bind (&ContentPanel::right_click, this, _1));
+ _content->Bind (wxEVT_DROP_FILES, boost::bind (&ContentPanel::files_dropped, this, _1));
+ _add_file->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentPanel::add_file_clicked, this));
+ _add_folder->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentPanel::add_folder_clicked, this));
+ _remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentPanel::remove_clicked, this));
+ _earlier->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentPanel::earlier_clicked, this));
+ _later->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentPanel::later_clicked, this));
+ _timeline->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ContentPanel::timeline_clicked, this));
+ _sequence_video->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&ContentPanel::sequence_video_changed, this));
+}
+
+ContentList
+ContentPanel::selected ()
+{
+ ContentList sel;
+ long int s = -1;
+ while (true) {
+ s = _content->GetNextItem (s, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (s == -1) {
+ break;
+ }
+
+ if (s < int (_film->content().size ())) {
+ sel.push_back (_film->content()[s]);
+ }
+ }
+
+ return sel;
+}
+
+VideoContentList
+ContentPanel::selected_video ()
+{
+ ContentList c = selected ();
+ VideoContentList vc;
+
+ for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ shared_ptr<VideoContent> t = dynamic_pointer_cast<VideoContent> (*i);
+ if (t) {
+ vc.push_back (t);
+ }
+ }
+
+ return vc;
+}
+
+AudioContentList
+ContentPanel::selected_audio ()
+{
+ ContentList c = selected ();
+ AudioContentList ac;
+
+ for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ shared_ptr<AudioContent> t = dynamic_pointer_cast<AudioContent> (*i);
+ if (t) {
+ ac.push_back (t);
+ }
+ }
+
+ return ac;
+}
+
+SubtitleContentList
+ContentPanel::selected_subtitle ()
+{
+ ContentList c = selected ();
+ SubtitleContentList sc;
+
+ for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ shared_ptr<SubtitleContent> t = dynamic_pointer_cast<SubtitleContent> (*i);
+ if (t) {
+ sc.push_back (t);
+ }
+ }
+
+ return sc;
+}
+
+FFmpegContentList
+ContentPanel::selected_ffmpeg ()
+{
+ ContentList c = selected ();
+ FFmpegContentList sc;
+
+ for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ shared_ptr<FFmpegContent> t = dynamic_pointer_cast<FFmpegContent> (*i);
+ if (t) {
+ sc.push_back (t);
+ }
+ }
+
+ return sc;
+}
+
+void
+ContentPanel::sequence_video_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_sequence_video (_sequence_video->GetValue ());
+}
+
+void
+ContentPanel::film_changed (Film::Property p)
+{
+ switch (p) {
+ case Film::CONTENT:
+ setup ();
+ break;
+ case Film::SEQUENCE_VIDEO:
+ checked_set (_sequence_video, _film->sequence_video ());
+ break;
+ default:
+ break;
+ }
+
+ for (list<ContentSubPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
+ (*i)->film_changed (p);
+ }
+}
+
+void
+ContentPanel::selection_changed ()
+{
+ setup_sensitivity ();
+
+ for (list<ContentSubPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
+ (*i)->content_selection_changed ();
+ }
+}
+
+void
+ContentPanel::add_file_clicked ()
+{
+ /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
+ non-Latin filenames or paths.
+ */
+ wxFileDialog* d = new wxFileDialog (_panel, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE | wxFD_CHANGE_DIR);
+ int const r = d->ShowModal ();
+
+ if (r != wxID_OK) {
+ d->Destroy ();
+ return;
+ }
+
+ wxArrayString paths;
+ d->GetPaths (paths);
+
+ /* XXX: check for lots of files here and do something */
+
+ for (unsigned int i = 0; i < paths.GetCount(); ++i) {
+ _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
+ }
+
+ d->Destroy ();
+}
+
+void
+ContentPanel::add_folder_clicked ()
+{
+ wxDirDialog* d = new wxDirDialog (_panel, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
+ int const r = d->ShowModal ();
+ d->Destroy ();
+
+ if (r != wxID_OK) {
+ return;
+ }
+
+ shared_ptr<Content> content;
+
+ try {
+ content.reset (new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
+ } catch (...) {
+ try {
+ content.reset (new DCPContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
+ } catch (...) {
+ error_dialog (_panel, _("Could not find any images nor a DCP in that folder"));
+ return;
+ }
+ }
+
+ if (content) {
+ _film->examine_and_add_content (content);
+ }
+}
+
+void
+ContentPanel::remove_clicked ()
+{
+ ContentList c = selected ();
+ if (c.size() == 1) {
+ _film->remove_content (c.front ());
+ }
+
+ selection_changed ();
+}
+
+void
+ContentPanel::timeline_clicked ()
+{
+ if (_timeline_dialog) {
+ _timeline_dialog->Destroy ();
+ _timeline_dialog = 0;
+ }
+
+ _timeline_dialog = new TimelineDialog (this, _film);
+ _timeline_dialog->Show ();
+}
+
+void
+ContentPanel::right_click (wxListEvent& ev)
+{
+ _menu->popup (_film, selected (), ev.GetPoint ());
+}
+
+/** Set up broad sensitivity based on the type of content that is selected */
+void
+ContentPanel::setup_sensitivity ()
+{
+ _add_file->Enable (_generally_sensitive);
+ _add_folder->Enable (_generally_sensitive);
+
+ ContentList selection = selected ();
+ VideoContentList video_selection = selected_video ();
+ AudioContentList audio_selection = selected_audio ();
+
+ _remove->Enable (selection.size() == 1 && _generally_sensitive);
+ _earlier->Enable (selection.size() == 1 && _generally_sensitive);
+ _later->Enable (selection.size() == 1 && _generally_sensitive);
+ _timeline->Enable (!_film->content().empty() && _generally_sensitive);
+
+ _video_panel->Enable (video_selection.size() > 0 && _generally_sensitive);
+ _audio_panel->Enable (audio_selection.size() > 0 && _generally_sensitive);
+ _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<SubtitleContent> (selection.front()) && _generally_sensitive);
+ _timing_panel->Enable (selection.size() == 1 && _generally_sensitive);
+}
+
+void
+ContentPanel::set_film (shared_ptr<Film> f)
+{
+ _film = f;
+
+ film_changed (Film::CONTENT);
+ selection_changed ();
+}
+
+void
+ContentPanel::set_general_sensitivity (bool s)
+{
+ _generally_sensitive = s;
+
+ _content->Enable (s);
+ _add_file->Enable (s);
+ _add_folder->Enable (s);
+ _remove->Enable (s);
+ _earlier->Enable (s);
+ _later->Enable (s);
+ _timeline->Enable (s);
+ _sequence_video->Enable (s);
+
+ /* Set the panels in the content notebook */
+ for (list<ContentSubPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
+ (*i)->Enable (s);
+ }
+}
+
+void
+ContentPanel::earlier_clicked ()
+{
+ ContentList sel = selected ();
+ if (sel.size() == 1) {
+ _film->move_content_earlier (sel.front ());
+ selection_changed ();
+ }
+}
+
+void
+ContentPanel::later_clicked ()
+{
+ ContentList sel = selected ();
+ if (sel.size() == 1) {
+ _film->move_content_later (sel.front ());
+ selection_changed ();
+ }
+}
+
+void
+ContentPanel::set_selection (weak_ptr<Content> wc)
+{
+ ContentList content = _film->content ();
+ for (size_t i = 0; i < content.size(); ++i) {
+ if (content[i] == wc.lock ()) {
+ _content->SetItemState (i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ } else {
+ _content->SetItemState (i, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
+ }
+ }
+}
+
+void
+ContentPanel::film_content_changed (int property)
+{
+ if (property == ContentProperty::PATH || property == ContentProperty::POSITION || property == DCPContentProperty::CAN_BE_PLAYED) {
+ setup ();
+ }
+
+ for (list<ContentSubPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
+ (*i)->film_content_changed (property);
+ }
+}
+
+void
+ContentPanel::setup ()
+{
+ string selected_summary;
+ int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (s != -1) {
+ selected_summary = wx_to_std (_content->GetItemText (s));
+ }
+
+ _content->DeleteAllItems ();
+
+ ContentList content = _film->content ();
+ sort (content.begin(), content.end(), ContentSorter ());
+
+ for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ int const t = _content->GetItemCount ();
+ bool const valid = (*i)->paths_valid ();
+ shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (*i);
+ bool const needs_kdm = dcp && !dcp->can_be_played ();
+
+ string s = (*i)->summary ();
+
+ if (!valid) {
+ s = _("MISSING: ") + s;
+ }
+
+ if (needs_kdm) {
+ s = _("NEEDS KDM: ") + s;
+ }
+
+ _content->InsertItem (t, std_to_wx (s));
+
+ if ((*i)->summary() == selected_summary) {
+ _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ }
+
+ if (!valid || needs_kdm) {
+ _content->SetItemTextColour (t, *wxRED);
+ }
+ }
+
+ if (selected_summary.empty () && !content.empty ()) {
+ /* Select the item of content if none was selected before */
+ _content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ }
+}
+
+void
+ContentPanel::files_dropped (wxDropFilesEvent& event)
+{
+ if (!_film) {
+ return;
+ }
+
+ wxString* paths = event.GetFiles ();
+ for (int i = 0; i < event.GetNumberOfFiles(); i++) {
+ _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
+ }
+}
--- /dev/null
+/*
+ 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 <list>
+#include <boost/shared_ptr.hpp>
+#include "lib/types.h"
+#include "lib/film.h"
+#include "content_menu.h"
+
+class wxNotebook;
+class wxPanel;
+class wxSizer;
+class wxListCtrl;
+class wxListEvent;
+class TimelineDialog;
+class FilmEditor;
+class ContentSubPanel;
+class Film;
+
+class ContentPanel
+{
+public:
+ ContentPanel (wxNotebook *, boost::shared_ptr<Film>);
+
+ boost::shared_ptr<Film> film () const {
+ return _film;
+ }
+
+ void set_film (boost::shared_ptr<Film> f);
+ void set_general_sensitivity (bool s);
+ void set_selection (boost::weak_ptr<Content>);
+
+ void film_changed (Film::Property p);
+ void film_content_changed (int p);
+
+ wxPanel* panel () const {
+ return _panel;
+ }
+
+ wxNotebook* notebook () const {
+ return _notebook;
+ }
+
+ ContentList selected ();
+ VideoContentList selected_video ();
+ AudioContentList selected_audio ();
+ SubtitleContentList selected_subtitle ();
+ FFmpegContentList selected_ffmpeg ();
+
+ void add_file_clicked ();
+
+private:
+ void sequence_video_changed ();
+ void selection_changed ();
+ void add_folder_clicked ();
+ void remove_clicked ();
+ void earlier_clicked ();
+ void later_clicked ();
+ void right_click (wxListEvent &);
+ void files_dropped (wxDropFilesEvent &);
+ void timeline_clicked ();
+
+ void setup ();
+ void setup_sensitivity ();
+
+ wxPanel* _panel;
+ wxSizer* _sizer;
+ wxNotebook* _notebook;
+ wxListCtrl* _content;
+ wxButton* _add_file;
+ wxButton* _add_folder;
+ wxButton* _remove;
+ wxButton* _earlier;
+ wxButton* _later;
+ wxButton* _timeline;
+ wxCheckBox* _sequence_video;
+ ContentSubPanel* _video_panel;
+ ContentSubPanel* _audio_panel;
+ ContentSubPanel* _subtitle_panel;
+ ContentSubPanel* _timing_panel;
+ std::list<ContentSubPanel *> _panels;
+ ContentMenu* _menu;
+ TimelineDialog* _timeline_dialog;
+
+ boost::shared_ptr<Film> _film;
+ bool _generally_sensitive;
+};
--- /dev/null
+/*
+ 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 <wx/notebook.h>
+#include "content_sub_panel.h"
+#include "content_panel.h"
+
+using boost::shared_ptr;
+
+ContentSubPanel::ContentSubPanel (ContentPanel* p, wxString name)
+ : wxPanel (p->notebook(), wxID_ANY)
+ , _parent (p)
+ , _sizer (new wxBoxSizer (wxVERTICAL))
+{
+ p->notebook()->AddPage (this, name, false);
+ SetSizer (_sizer);
+}
+
--- /dev/null
+/*
+ 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 DCPOMATIC_CONTENT_SUB_PANEL_H
+#define DCPOMATIC_CONTENT_SUB_PANEL_H
+
+#include <boost/shared_ptr.hpp>
+#include <wx/wx.h>
+#include "lib/film.h"
+
+class ContentPanel;
+class Content;
+
+class ContentSubPanel : public wxPanel
+{
+public:
+ ContentSubPanel (ContentPanel *, wxString);
+
+ virtual void film_changed (Film::Property) {}
+ /** Called when a given property of one of the selected Contents changes */
+ virtual void film_content_changed (int) = 0;
+ /** Called when the list of selected Contents changes */
+ virtual void content_selection_changed () = 0;
+
+protected:
+ ContentPanel* _parent;
+ wxSizer* _sizer;
+};
+
+#endif
/*
- 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/wx/content_widget.h
+ * @brief ContentWidget class.
+ */
+
#ifndef DCPOMATIC_MULTIPLE_WIDGET_H
#define DCPOMATIC_MULTIPLE_WIDGET_H
#include <vector>
#include <wx/wx.h>
#include <wx/gbsizer.h>
+#include <wx/spinctrl.h>
#include <boost/function.hpp>
#include "wx_util.h"
-/** A widget which represents some Content state and which can be used
+/** @class ContentWidget
+ * @brief A widget which represents some Content state and which can be used
* when multiple pieces of content are selected.
*
* @param S Type containing the content being represented (e.g. VideoContent)
}
/** Add this widget to a wxGridBagSizer */
- void add (wxGridBagSizer* sizer, wxGBPosition position)
+ void add (wxGridBagSizer* sizer, wxGBPosition position, wxGBSpan span = wxDefaultSpan)
{
_sizer = sizer;
_position = position;
- _sizer->Add (_wrapped, _position);
+ _span = span;
+ _sizer->Add (_wrapped, _position, _span);
}
/** Update the view from the model */
_sizer->Detach (_button);
_button->Hide ();
- _sizer->Add (_wrapped, _position);
+ _sizer->Add (_wrapped, _position, _span);
_wrapped->Show ();
_sizer->Layout ();
}
_wrapped->Hide ();
_sizer->Detach (_wrapped);
_button->Show ();
- _sizer->Add (_button, _position);
+ _sizer->Add (_button, _position, _span);
_sizer->Layout ();
}
T* _wrapped;
wxGridBagSizer* _sizer;
wxGBPosition _position;
+ wxGBSpan _span;
wxButton* _button;
List _content;
int _property;
--- /dev/null
+/*
+ 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 "dcp_panel.h"
+#include "wx_util.h"
+#include "isdcf_metadata_dialog.h"
+#include "lib/ratio.h"
+#include "lib/scaler.h"
+#include "lib/config.h"
+#include "lib/dcp_content_type.h"
+#include "lib/util.h"
+#include "lib/film.h"
+#include "lib/ffmpeg_content.h"
+#include <wx/wx.h>
+#include <wx/notebook.h>
+#include <wx/gbsizer.h>
+#include <wx/spinctrl.h>
+#include <boost/lexical_cast.hpp>
+
+using std::cout;
+using std::list;
+using std::string;
+using std::vector;
+using boost::lexical_cast;
+using boost::shared_ptr;
+
+DCPPanel::DCPPanel (wxNotebook* n, boost::shared_ptr<Film> f)
+ : _film (f)
+ , _generally_sensitive (true)
+{
+ _panel = new wxPanel (n);
+ _sizer = new wxBoxSizer (wxVERTICAL);
+ _panel->SetSizer (_sizer);
+
+ wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ _sizer->Add (grid, 0, wxEXPAND | wxALL, 8);
+
+ int r = 0;
+
+ add_label_to_grid_bag_sizer (grid, _panel, _("Name"), true, wxGBPosition (r, 0));
+ _name = new wxTextCtrl (_panel, wxID_ANY);
+ grid->Add (_name, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND | wxLEFT | wxRIGHT);
+ ++r;
+
+ int flags = wxALIGN_CENTER_VERTICAL;
+#ifdef __WXOSX__
+ flags |= wxALIGN_RIGHT;
+#endif
+
+ _use_isdcf_name = new wxCheckBox (_panel, wxID_ANY, _("Use ISDCF name"));
+ grid->Add (_use_isdcf_name, wxGBPosition (r, 0), wxDefaultSpan, flags);
+ _edit_isdcf_button = new wxButton (_panel, wxID_ANY, _("Details..."));
+ grid->Add (_edit_isdcf_button, wxGBPosition (r, 1));
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, _panel, _("DCP Name"), true, wxGBPosition (r, 0));
+ _dcp_name = new wxStaticText (_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
+ grid->Add (_dcp_name, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL | wxEXPAND);
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, _panel, _("Content Type"), true, wxGBPosition (r, 0));
+ _dcp_content_type = new wxChoice (_panel, wxID_ANY);
+ grid->Add (_dcp_content_type, wxGBPosition (r, 1));
+ ++r;
+
+ _notebook = new wxNotebook (_panel, wxID_ANY);
+ _sizer->Add (_notebook, 1, wxEXPAND | wxTOP, 6);
+
+ _notebook->AddPage (make_video_panel (), _("Video"), false);
+ _notebook->AddPage (make_audio_panel (), _("Audio"), false);
+
+ _signed = new wxCheckBox (_panel, wxID_ANY, _("Signed"));
+ grid->Add (_signed, wxGBPosition (r, 0), wxGBSpan (1, 2));
+ ++r;
+
+ _encrypted = new wxCheckBox (_panel, wxID_ANY, _("Encrypted"));
+ grid->Add (_encrypted, wxGBPosition (r, 0), wxGBSpan (1, 2));
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, _panel, _("Standard"), true, wxGBPosition (r, 0));
+ _standard = new wxChoice (_panel, wxID_ANY);
+ grid->Add (_standard, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ ++r;
+
+ _name->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&DCPPanel::name_changed, this));
+ _use_isdcf_name->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&DCPPanel::use_isdcf_name_toggled, this));
+ _edit_isdcf_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DCPPanel::edit_isdcf_button_clicked, this));
+ _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DCPPanel::dcp_content_type_changed, this));
+ _signed->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&DCPPanel::signed_toggled, this));
+ _encrypted->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&DCPPanel::encrypted_toggled, this));
+ _standard->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DCPPanel::standard_changed, this));
+
+ vector<DCPContentType const *> const ct = DCPContentType::all ();
+ for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
+ _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
+ }
+
+ _standard->Append (_("SMPTE"));
+ _standard->Append (_("Interop"));
+
+ Config::instance()->Changed.connect (boost::bind (&DCPPanel::config_changed, this));
+}
+
+void
+DCPPanel::name_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_name (string (_name->GetValue().mb_str()));
+}
+
+void
+DCPPanel::j2k_bandwidth_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
+}
+
+void
+DCPPanel::signed_toggled ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_signed (_signed->GetValue ());
+}
+
+void
+DCPPanel::burn_subtitles_toggled ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_burn_subtitles (_burn_subtitles->GetValue ());
+}
+
+void
+DCPPanel::encrypted_toggled ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_encrypted (_encrypted->GetValue ());
+}
+
+/** Called when the frame rate choice widget has been changed */
+void
+DCPPanel::frame_rate_choice_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_video_frame_rate (
+ boost::lexical_cast<int> (
+ wx_to_std (_frame_rate_choice->GetString (_frame_rate_choice->GetSelection ()))
+ )
+ );
+}
+
+/** Called when the frame rate spin widget has been changed */
+void
+DCPPanel::frame_rate_spin_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_video_frame_rate (_frame_rate_spin->GetValue ());
+}
+
+void
+DCPPanel::audio_channels_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_audio_channels (_audio_channels->GetValue ());
+}
+
+void
+DCPPanel::resolution_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_resolution (_resolution->GetSelection() == 0 ? RESOLUTION_2K : RESOLUTION_4K);
+}
+
+void
+DCPPanel::standard_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_interop (_standard->GetSelection() == 1);
+}
+
+void
+DCPPanel::film_changed (int p)
+{
+ switch (p) {
+ case Film::NONE:
+ break;
+ case Film::CONTAINER:
+ setup_container ();
+ break;
+ case Film::NAME:
+ checked_set (_name, _film->name());
+ setup_dcp_name ();
+ break;
+ case Film::DCP_CONTENT_TYPE:
+ checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
+ setup_dcp_name ();
+ break;
+ case Film::SCALER:
+ checked_set (_scaler, Scaler::as_index (_film->scaler ()));
+ break;
+ case Film::BURN_SUBTITLES:
+ checked_set (_burn_subtitles, _film->burn_subtitles ());
+ break;
+ case Film::SIGNED:
+ checked_set (_signed, _film->is_signed ());
+ break;
+ case Film::ENCRYPTED:
+ checked_set (_encrypted, _film->encrypted ());
+ if (_film->encrypted ()) {
+ _film->set_signed (true);
+ _signed->Enable (false);
+ } else {
+ _signed->Enable (_generally_sensitive);
+ }
+ break;
+ case Film::RESOLUTION:
+ checked_set (_resolution, _film->resolution() == RESOLUTION_2K ? 0 : 1);
+ setup_dcp_name ();
+ break;
+ case Film::J2K_BANDWIDTH:
+ checked_set (_j2k_bandwidth, _film->j2k_bandwidth() / 1000000);
+ break;
+ case Film::USE_ISDCF_NAME:
+ {
+ checked_set (_use_isdcf_name, _film->use_isdcf_name ());
+ setup_dcp_name ();
+ bool const i = _film->use_isdcf_name ();
+ if (!i) {
+ _film->set_name (_film->isdcf_name (true));
+ }
+ _edit_isdcf_button->Enable (i);
+ break;
+ }
+ case Film::ISDCF_METADATA:
+ setup_dcp_name ();
+ break;
+ case Film::VIDEO_FRAME_RATE:
+ {
+ bool done = false;
+ for (unsigned int i = 0; i < _frame_rate_choice->GetCount(); ++i) {
+ if (wx_to_std (_frame_rate_choice->GetString(i)) == boost::lexical_cast<string> (_film->video_frame_rate())) {
+ checked_set (_frame_rate_choice, i);
+ done = true;
+ break;
+ }
+ }
+
+ if (!done) {
+ checked_set (_frame_rate_choice, -1);
+ }
+
+ _frame_rate_spin->SetValue (_film->video_frame_rate ());
+
+ _best_frame_rate->Enable (_film->best_video_frame_rate () != _film->video_frame_rate ());
+ break;
+ }
+ case Film::AUDIO_CHANNELS:
+ checked_set (_audio_channels, _film->audio_channels ());
+ setup_dcp_name ();
+ break;
+ case Film::THREE_D:
+ checked_set (_three_d, _film->three_d ());
+ setup_dcp_name ();
+ break;
+ case Film::INTEROP:
+ checked_set (_standard, _film->interop() ? 1 : 0);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+DCPPanel::film_content_changed (int property)
+{
+ if (property == FFmpegContentProperty::AUDIO_STREAM ||
+ property == SubtitleContentProperty::USE_SUBTITLES ||
+ property == VideoContentProperty::VIDEO_SCALE) {
+ setup_dcp_name ();
+ }
+}
+
+
+void
+DCPPanel::setup_container ()
+{
+ int n = 0;
+ vector<Ratio const *> ratios = Ratio::all ();
+ vector<Ratio const *>::iterator i = ratios.begin ();
+ while (i != ratios.end() && *i != _film->container ()) {
+ ++i;
+ ++n;
+ }
+
+ if (i == ratios.end()) {
+ checked_set (_container, -1);
+ } else {
+ checked_set (_container, n);
+ }
+
+ setup_dcp_name ();
+}
+
+/** Called when the container widget has been changed */
+void
+DCPPanel::container_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ int const n = _container->GetSelection ();
+ if (n >= 0) {
+ vector<Ratio const *> ratios = Ratio::all ();
+ assert (n < int (ratios.size()));
+ _film->set_container (ratios[n]);
+ }
+}
+
+/** Called when the DCP content type widget has been changed */
+void
+DCPPanel::dcp_content_type_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ int const n = _dcp_content_type->GetSelection ();
+ if (n != wxNOT_FOUND) {
+ _film->set_dcp_content_type (DCPContentType::from_index (n));
+ }
+}
+
+void
+DCPPanel::set_film (shared_ptr<Film> film)
+{
+ _film = film;
+
+ film_changed (Film::NAME);
+ film_changed (Film::USE_ISDCF_NAME);
+ film_changed (Film::CONTENT);
+ film_changed (Film::DCP_CONTENT_TYPE);
+ film_changed (Film::CONTAINER);
+ film_changed (Film::RESOLUTION);
+ film_changed (Film::SCALER);
+ film_changed (Film::SIGNED);
+ film_changed (Film::BURN_SUBTITLES);
+ film_changed (Film::ENCRYPTED);
+ film_changed (Film::J2K_BANDWIDTH);
+ film_changed (Film::ISDCF_METADATA);
+ film_changed (Film::VIDEO_FRAME_RATE);
+ film_changed (Film::AUDIO_CHANNELS);
+ film_changed (Film::SEQUENCE_VIDEO);
+ film_changed (Film::THREE_D);
+ film_changed (Film::INTEROP);
+}
+
+void
+DCPPanel::set_general_sensitivity (bool s)
+{
+ _name->Enable (s);
+ _use_isdcf_name->Enable (s);
+ _edit_isdcf_button->Enable (s);
+ _dcp_content_type->Enable (s);
+
+ bool si = s;
+ if (_film && _film->encrypted ()) {
+ si = false;
+ }
+ _burn_subtitles->Enable (s);
+ _signed->Enable (si);
+
+ _encrypted->Enable (s);
+ _frame_rate_choice->Enable (s);
+ _frame_rate_spin->Enable (s);
+ _audio_channels->Enable (s);
+ _j2k_bandwidth->Enable (s);
+ _container->Enable (s);
+ _best_frame_rate->Enable (s && _film && _film->best_video_frame_rate () != _film->video_frame_rate ());
+ _resolution->Enable (s);
+ _scaler->Enable (s);
+ _three_d->Enable (s);
+ _standard->Enable (s);
+}
+
+/** Called when the scaler widget has been changed */
+void
+DCPPanel::scaler_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ int const n = _scaler->GetSelection ();
+ if (n >= 0) {
+ _film->set_scaler (Scaler::from_index (n));
+ }
+}
+
+void
+DCPPanel::use_isdcf_name_toggled ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_use_isdcf_name (_use_isdcf_name->GetValue ());
+}
+
+void
+DCPPanel::edit_isdcf_button_clicked ()
+{
+ if (!_film) {
+ return;
+ }
+
+ ISDCFMetadataDialog* d = new ISDCFMetadataDialog (_panel, _film->isdcf_metadata ());
+ d->ShowModal ();
+ _film->set_isdcf_metadata (d->isdcf_metadata ());
+ d->Destroy ();
+}
+
+void
+DCPPanel::setup_dcp_name ()
+{
+ string s = _film->dcp_name (true);
+ if (s.length() > 28) {
+ _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("..."));
+ _dcp_name->SetToolTip (std_to_wx (s));
+ } else {
+ _dcp_name->SetLabel (std_to_wx (s));
+ }
+}
+
+void
+DCPPanel::best_frame_rate_clicked ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_video_frame_rate (_film->best_video_frame_rate ());
+}
+
+void
+DCPPanel::three_d_changed ()
+{
+ if (!_film) {
+ return;
+ }
+
+ _film->set_three_d (_three_d->GetValue ());
+}
+
+void
+DCPPanel::config_changed ()
+{
+ _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
+ setup_frame_rate_widget ();
+}
+
+void
+DCPPanel::setup_frame_rate_widget ()
+{
+ if (Config::instance()->allow_any_dcp_frame_rate ()) {
+ _frame_rate_choice->Hide ();
+ _frame_rate_spin->Show ();
+ } else {
+ _frame_rate_choice->Show ();
+ _frame_rate_spin->Hide ();
+ }
+
+ _frame_rate_sizer->Layout ();
+}
+
+wxPanel *
+DCPPanel::make_video_panel ()
+{
+ wxPanel* panel = new wxPanel (_notebook);
+ wxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+ wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ sizer->Add (grid, 0, wxALL, 8);
+ panel->SetSizer (sizer);
+
+ int r = 0;
+
+ add_label_to_grid_bag_sizer (grid, panel, _("Container"), true, wxGBPosition (r, 0));
+ _container = new wxChoice (panel, wxID_ANY);
+ grid->Add (_container, wxGBPosition (r, 1));
+ ++r;
+
+ {
+ add_label_to_grid_bag_sizer (grid, panel, _("Frame Rate"), true, wxGBPosition (r, 0));
+ _frame_rate_sizer = new wxBoxSizer (wxHORIZONTAL);
+ _frame_rate_choice = new wxChoice (panel, wxID_ANY);
+ _frame_rate_sizer->Add (_frame_rate_choice, 1, wxALIGN_CENTER_VERTICAL);
+ _frame_rate_spin = new wxSpinCtrl (panel, wxID_ANY);
+ _frame_rate_sizer->Add (_frame_rate_spin, 1, wxALIGN_CENTER_VERTICAL);
+ setup_frame_rate_widget ();
+ _best_frame_rate = new wxButton (panel, wxID_ANY, _("Use best"));
+ _frame_rate_sizer->Add (_best_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
+ grid->Add (_frame_rate_sizer, wxGBPosition (r, 1));
+ }
+ ++r;
+
+ _burn_subtitles = new wxCheckBox (panel, wxID_ANY, _("Burn subtitles into image"));
+ grid->Add (_burn_subtitles, wxGBPosition (r, 0), wxGBSpan (1, 2));
+ ++r;
+
+ _three_d = new wxCheckBox (panel, wxID_ANY, _("3D"));
+ grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, panel, _("Resolution"), true, wxGBPosition (r, 0));
+ _resolution = new wxChoice (panel, wxID_ANY);
+ grid->Add (_resolution, wxGBPosition (r, 1));
+ ++r;
+
+ {
+ add_label_to_grid_bag_sizer (grid, panel, _("JPEG2000 bandwidth"), true, wxGBPosition (r, 0));
+ wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _j2k_bandwidth = new wxSpinCtrl (panel, wxID_ANY);
+ s->Add (_j2k_bandwidth, 1);
+ add_label_to_sizer (s, panel, _("Mbit/s"), false);
+ grid->Add (s, wxGBPosition (r, 1));
+ }
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, panel, _("Scaler"), true, wxGBPosition (r, 0));
+ _scaler = new wxChoice (panel, wxID_ANY);
+ grid->Add (_scaler, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ ++r;
+
+ _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DCPPanel::container_changed, this));
+ _scaler->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DCPPanel::scaler_changed, this));
+ _frame_rate_choice->Bind(wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DCPPanel::frame_rate_choice_changed, this));
+ _frame_rate_spin->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DCPPanel::frame_rate_spin_changed, this));
+ _best_frame_rate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&DCPPanel::best_frame_rate_clicked, this));
+ _burn_subtitles->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&DCPPanel::burn_subtitles_toggled, this));
+ _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DCPPanel::j2k_bandwidth_changed, this));
+ _resolution->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&DCPPanel::resolution_changed, this));
+ _three_d->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&DCPPanel::three_d_changed, this));
+
+ vector<Scaler const *> const sc = Scaler::all ();
+ for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
+ _scaler->Append (std_to_wx ((*i)->name()));
+ }
+
+ vector<Ratio const *> const ratio = Ratio::all ();
+ for (vector<Ratio const *>::const_iterator i = ratio.begin(); i != ratio.end(); ++i) {
+ _container->Append (std_to_wx ((*i)->nickname ()));
+ }
+
+ list<int> const dfr = Config::instance()->allowed_dcp_frame_rates ();
+ for (list<int>::const_iterator i = dfr.begin(); i != dfr.end(); ++i) {
+ _frame_rate_choice->Append (std_to_wx (boost::lexical_cast<string> (*i)));
+ }
+
+ _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
+ _frame_rate_spin->SetRange (1, 480);
+
+ _resolution->Append (_("2K"));
+ _resolution->Append (_("4K"));
+
+ return panel;
+}
+
+wxPanel *
+DCPPanel::make_audio_panel ()
+{
+ wxPanel* panel = new wxPanel (_notebook);
+ wxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+ wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ sizer->Add (grid, 0, wxALL, 8);
+ panel->SetSizer (sizer);
+
+ int r = 0;
+ add_label_to_grid_bag_sizer (grid, panel, _("Channels"), true, wxGBPosition (r, 0));
+ _audio_channels = new wxSpinCtrl (panel, wxID_ANY);
+ grid->Add (_audio_channels, wxGBPosition (r, 1));
+ ++r;
+
+ _audio_channels->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&DCPPanel::audio_channels_changed, this));
+
+ _audio_channels->SetRange (0, MAX_DCP_AUDIO_CHANNELS);
+
+ return panel;
+}
--- /dev/null
+/*
+ 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>
+
+class wxNotebook;
+class wxPanel;
+class wxBoxSizer;
+class wxTextCtrl;
+class wxStaticText;
+class wxCheckBox;
+class wxChoice;
+class wxButton;
+class wxSpinCtrl;
+class wxSizer;
+
+class Film;
+
+class DCPPanel
+{
+public:
+ DCPPanel (wxNotebook *, boost::shared_ptr<Film>);
+
+ void set_film (boost::shared_ptr<Film>);
+ void set_general_sensitivity (bool);
+
+ void film_changed (int);
+ void film_content_changed (int);
+
+ wxPanel* panel () const {
+ return _panel;
+ }
+
+private:
+ void name_changed ();
+ void use_isdcf_name_toggled ();
+ void edit_isdcf_button_clicked ();
+ void container_changed ();
+ void dcp_content_type_changed ();
+ void scaler_changed ();
+ void j2k_bandwidth_changed ();
+ void frame_rate_choice_changed ();
+ void frame_rate_spin_changed ();
+ void best_frame_rate_clicked ();
+ void content_timeline_clicked ();
+ void audio_channels_changed ();
+ void resolution_changed ();
+ void three_d_changed ();
+ void standard_changed ();
+ void signed_toggled ();
+ void burn_subtitles_toggled ();
+ void encrypted_toggled ();
+
+ void setup_frame_rate_widget ();
+ void setup_container ();
+ void setup_dcp_name ();
+
+ wxPanel* make_general_panel ();
+ wxPanel* make_video_panel ();
+ wxPanel* make_audio_panel ();
+
+ void config_changed ();
+
+ wxPanel* _panel;
+ wxNotebook* _notebook;
+ wxBoxSizer* _sizer;
+
+ wxTextCtrl* _name;
+ wxStaticText* _dcp_name;
+ wxCheckBox* _use_isdcf_name;
+ wxChoice* _container;
+ wxButton* _edit_isdcf_button;
+ wxChoice* _scaler;
+ wxSpinCtrl* _j2k_bandwidth;
+ wxChoice* _dcp_content_type;
+ wxChoice* _frame_rate_choice;
+ wxSpinCtrl* _frame_rate_spin;
+ wxSizer* _frame_rate_sizer;
+ wxSpinCtrl* _audio_channels;
+ wxButton* _best_frame_rate;
+ wxCheckBox* _three_d;
+ wxChoice* _resolution;
+ wxChoice* _standard;
+ wxCheckBox* _signed;
+ wxCheckBox* _burn_subtitles;
+ wxCheckBox* _encrypted;
+
+ boost::shared_ptr<Film> _film;
+ bool _generally_sensitive;
+};
wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
table->AddGrowableCol (0, 1);
- s->Add (table, 1, wxALL | wxEXPAND, 8);
+ s->Add (table, 1, wxEXPAND);
_list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (columns.size() * 200, height), wxLC_REPORT | wxLC_SINGLE_SEL);
#include "lib/ffmpeg_content.h"
#include "lib/sndfile_content.h"
#include "lib/dcp_content_type.h"
-#include "lib/sound_processor.h"
#include "lib/scaler.h"
#include "lib/playlist.h"
#include "lib/content.h"
#include "lib/content_factory.h"
+#include "lib/dcp_content.h"
#include "lib/safe_stringstream.h"
#include "timecode.h"
#include "wx_util.h"
#include "film_editor.h"
-#include "isdcf_metadata_dialog.h"
#include "timeline_dialog.h"
#include "timing_panel.h"
#include "subtitle_panel.h"
#include "audio_panel.h"
#include "video_panel.h"
+#include "content_panel.h"
+#include "dcp_panel.h"
using std::string;
using std::cout;
/** @param f Film to edit */
FilmEditor::FilmEditor (wxWindow* parent)
: wxPanel (parent)
- , _menu (this)
- , _generally_sensitive (true)
- , _timeline_dialog (0)
{
wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
_main_notebook = new wxNotebook (this, wxID_ANY);
s->Add (_main_notebook, 1);
- make_content_panel ();
- _main_notebook->AddPage (_content_panel, _("Content"), true);
- make_dcp_panel ();
- _main_notebook->AddPage (_dcp_panel, _("DCP"), false);
+ _content_panel = new ContentPanel (_main_notebook, _film);
+ _main_notebook->AddPage (_content_panel->panel (), _("Content"), true);
+ _dcp_panel = new DCPPanel (_main_notebook, _film);
+ _main_notebook->AddPage (_dcp_panel->panel (), _("DCP"), false);
- connect_to_widgets ();
-
JobManager::instance()->ActiveJobsChanged.connect (
bind (&FilmEditor::active_jobs_changed, this, _1)
);
- Config::instance()->Changed.connect (boost::bind (&FilmEditor::config_changed, this));
-
set_film (shared_ptr<Film> ());
- SetSizerAndFit (s);
-}
-
-void
-FilmEditor::make_dcp_panel ()
-{
- _dcp_panel = new wxPanel (_main_notebook);
- _dcp_sizer = new wxBoxSizer (wxVERTICAL);
- _dcp_panel->SetSizer (_dcp_sizer);
-
- wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
- _dcp_sizer->Add (grid, 0, wxEXPAND | wxALL, 8);
-
- int r = 0;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Name"), true, wxGBPosition (r, 0));
- _name = new wxTextCtrl (_dcp_panel, wxID_ANY);
- grid->Add (_name, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND | wxLEFT | wxRIGHT);
- ++r;
-
- int flags = wxALIGN_CENTER_VERTICAL;
-#ifdef __WXOSX__
- flags |= wxALIGN_RIGHT;
-#endif
-
- _use_isdcf_name = new wxCheckBox (_dcp_panel, wxID_ANY, _("Use ISDCF name"));
- grid->Add (_use_isdcf_name, wxGBPosition (r, 0), wxDefaultSpan, flags);
- _edit_isdcf_button = new wxButton (_dcp_panel, wxID_ANY, _("Details..."));
- grid->Add (_edit_isdcf_button, wxGBPosition (r, 1), wxDefaultSpan);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("DCP Name"), true, wxGBPosition (r, 0));
- _dcp_name = new wxStaticText (_dcp_panel, wxID_ANY, wxT (""));
- grid->Add (_dcp_name, wxGBPosition(r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Container"), true, wxGBPosition (r, 0));
- _container = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_container, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Content Type"), true, wxGBPosition (r, 0));
- _dcp_content_type = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_dcp_content_type, wxGBPosition (r, 1));
- ++r;
-
- {
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Frame Rate"), true, wxGBPosition (r, 0));
- _frame_rate_sizer = new wxBoxSizer (wxHORIZONTAL);
- _frame_rate_choice = new wxChoice (_dcp_panel, wxID_ANY);
- _frame_rate_sizer->Add (_frame_rate_choice, 1, wxALIGN_CENTER_VERTICAL);
- _frame_rate_spin = new wxSpinCtrl (_dcp_panel, wxID_ANY);
- _frame_rate_sizer->Add (_frame_rate_spin, 1, wxALIGN_CENTER_VERTICAL);
- setup_frame_rate_widget ();
- _best_frame_rate = new wxButton (_dcp_panel, wxID_ANY, _("Use best"));
- _frame_rate_sizer->Add (_best_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND);
- grid->Add (_frame_rate_sizer, wxGBPosition (r, 1));
- }
- ++r;
-
- _signed = new wxCheckBox (_dcp_panel, wxID_ANY, _("Signed"));
- grid->Add (_signed, wxGBPosition (r, 0), wxGBSpan (1, 2));
- ++r;
- _encrypted = new wxCheckBox (_dcp_panel, wxID_ANY, _("Encrypted"));
- grid->Add (_encrypted, wxGBPosition (r, 0), wxGBSpan (1, 2));
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Audio channels"), true, wxGBPosition (r, 0));
- _audio_channels = new wxSpinCtrl (_dcp_panel, wxID_ANY);
- grid->Add (_audio_channels, wxGBPosition (r, 1));
- ++r;
-
- _three_d = new wxCheckBox (_dcp_panel, wxID_ANY, _("3D"));
- grid->Add (_three_d, wxGBPosition (r, 0), wxGBSpan (1, 2));
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Resolution"), true, wxGBPosition (r, 0));
- _resolution = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_resolution, wxGBPosition (r, 1));
- ++r;
-
- {
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("JPEG2000 bandwidth"), true, wxGBPosition (r, 0));
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _j2k_bandwidth = new wxSpinCtrl (_dcp_panel, wxID_ANY);
- s->Add (_j2k_bandwidth, 1);
- add_label_to_sizer (s, _dcp_panel, _("Mbit/s"), false);
- grid->Add (s, wxGBPosition (r, 1));
- }
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Standard"), true, wxGBPosition (r, 0));
- _standard = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_standard, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- ++r;
-
- add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Scaler"), true, wxGBPosition (r, 0));
- _scaler = new wxChoice (_dcp_panel, wxID_ANY);
- grid->Add (_scaler, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- ++r;
-
- vector<Scaler const *> const sc = Scaler::all ();
- for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) {
- _scaler->Append (std_to_wx ((*i)->name()));
- }
-
- vector<Ratio const *> const ratio = Ratio::all ();
- for (vector<Ratio const *>::const_iterator i = ratio.begin(); i != ratio.end(); ++i) {
- _container->Append (std_to_wx ((*i)->nickname ()));
- }
-
- vector<DCPContentType const *> const ct = DCPContentType::all ();
- for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) {
- _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ()));
- }
-
- list<int> const dfr = Config::instance()->allowed_dcp_frame_rates ();
- for (list<int>::const_iterator i = dfr.begin(); i != dfr.end(); ++i) {
- _frame_rate_choice->Append (std_to_wx (boost::lexical_cast<string> (*i)));
- }
-
- _audio_channels->SetRange (0, MAX_DCP_AUDIO_CHANNELS);
- _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
- _frame_rate_spin->SetRange (1, 480);
-
- _resolution->Append (_("2K"));
- _resolution->Append (_("4K"));
-
- _standard->Append (_("SMPTE"));
- _standard->Append (_("Interop"));
-}
-
-void
-FilmEditor::connect_to_widgets ()
-{
- _name->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&FilmEditor::name_changed, this));
- _use_isdcf_name->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::use_isdcf_name_toggled, this));
- _edit_isdcf_button->Bind(wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::edit_isdcf_button_clicked, this));
- _container->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::container_changed, this));
- _content->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&FilmEditor::content_selection_changed, this));
- _content->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&FilmEditor::content_selection_changed, this));
- _content->Bind (wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, boost::bind (&FilmEditor::content_right_click, this, _1));
- _content->Bind (wxEVT_DROP_FILES, boost::bind (&FilmEditor::content_files_dropped, this, _1));
- _content_add_file->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_file_clicked, this));
- _content_add_folder->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_add_folder_clicked, this));
- _content_remove->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_remove_clicked, this));
- _content_earlier->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_earlier_clicked, this));
- _content_later->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_later_clicked, this));
- _content_timeline->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::content_timeline_clicked, this));
- _scaler->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::scaler_changed, this));
- _dcp_content_type->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::dcp_content_type_changed, this));
- _frame_rate_choice->Bind(wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::frame_rate_choice_changed, this));
- _frame_rate_spin->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&FilmEditor::frame_rate_spin_changed, this));
- _best_frame_rate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&FilmEditor::best_frame_rate_clicked, this));
- _signed->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::signed_toggled, this));
- _encrypted->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::encrypted_toggled, this));
- _audio_channels->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&FilmEditor::audio_channels_changed, this));
- _j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&FilmEditor::j2k_bandwidth_changed, this));
- _resolution->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::resolution_changed, this));
- _sequence_video->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::sequence_video_changed, this));
- _three_d->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmEditor::three_d_changed, this));
- _standard->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&FilmEditor::standard_changed, this));
-}
-
-void
-FilmEditor::make_content_panel ()
-{
- _content_panel = new wxPanel (_main_notebook);
- _content_sizer = new wxBoxSizer (wxVERTICAL);
- _content_panel->SetSizer (_content_sizer);
-
- {
- wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
- _content = new wxListCtrl (_content_panel, wxID_ANY, wxDefaultPosition, wxSize (320, 160), wxLC_REPORT | wxLC_NO_HEADER);
- s->Add (_content, 1, wxEXPAND | wxTOP | wxBOTTOM, 6);
-
- _content->InsertColumn (0, wxT(""));
- _content->SetColumnWidth (0, 512);
-
- wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
- _content_add_file = new wxButton (_content_panel, wxID_ANY, _("Add file(s)..."));
- b->Add (_content_add_file, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
- b->Add (_content_add_folder, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
- b->Add (_content_remove, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_earlier = new wxButton (_content_panel, wxID_ANY, _("Up"));
- b->Add (_content_earlier, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_later = new wxButton (_content_panel, wxID_ANY, _("Down"));
- b->Add (_content_later, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
- _content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
- b->Add (_content_timeline, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
-
- s->Add (b, 0, wxALL, 4);
-
- _content_sizer->Add (s, 0, wxEXPAND | wxALL, 6);
- }
-
- _sequence_video = new wxCheckBox (_content_panel, wxID_ANY, _("Keep video in sequence"));
- _content_sizer->Add (_sequence_video);
-
- _content_notebook = new wxNotebook (_content_panel, wxID_ANY);
- _content_sizer->Add (_content_notebook, 1, wxEXPAND | wxTOP, 6);
-
- _video_panel = new VideoPanel (this);
- _panels.push_back (_video_panel);
- _audio_panel = new AudioPanel (this);
- _panels.push_back (_audio_panel);
- _subtitle_panel = new SubtitlePanel (this);
- _panels.push_back (_subtitle_panel);
- _timing_panel = new TimingPanel (this);
- _panels.push_back (_timing_panel);
-
- _content->DragAcceptFiles (true);
-}
-
-/** Called when the name widget has been changed */
-void
-FilmEditor::name_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_name (string (_name->GetValue().mb_str()));
-}
-
-void
-FilmEditor::j2k_bandwidth_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_j2k_bandwidth (_j2k_bandwidth->GetValue() * 1000000);
-}
-
-void
-FilmEditor::signed_toggled ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_signed (_signed->GetValue ());
-}
-
-void
-FilmEditor::encrypted_toggled ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_encrypted (_encrypted->GetValue ());
-}
-
-/** Called when the frame rate choice widget has been changed */
-void
-FilmEditor::frame_rate_choice_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_video_frame_rate (
- boost::lexical_cast<int> (
- wx_to_std (_frame_rate_choice->GetString (_frame_rate_choice->GetSelection ()))
- )
- );
-}
-
-/** Called when the frame rate spin widget has been changed */
-void
-FilmEditor::frame_rate_spin_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_video_frame_rate (_frame_rate_spin->GetValue ());
-}
-
-void
-FilmEditor::audio_channels_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_audio_channels (_audio_channels->GetValue ());
-}
-
-void
-FilmEditor::resolution_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_resolution (_resolution->GetSelection() == 0 ? RESOLUTION_2K : RESOLUTION_4K);
+ SetSizerAndFit (s);
}
-void
-FilmEditor::standard_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_interop (_standard->GetSelection() == 1);
-}
/** Called when the metadata stored in the Film object has changed;
* so that we can update the GUI.
return;
}
- SafeStringStream s;
-
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->film_changed (p);
- }
-
- switch (p) {
- case Film::NONE:
- break;
- case Film::CONTENT:
- setup_content ();
- break;
- case Film::CONTAINER:
- setup_container ();
- break;
- case Film::NAME:
- checked_set (_name, _film->name());
- setup_dcp_name ();
- break;
- case Film::WITH_SUBTITLES:
- setup_dcp_name ();
- break;
- case Film::DCP_CONTENT_TYPE:
- checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
- setup_dcp_name ();
- break;
- case Film::SCALER:
- checked_set (_scaler, Scaler::as_index (_film->scaler ()));
- break;
- case Film::SIGNED:
- checked_set (_signed, _film->is_signed ());
- break;
- case Film::ENCRYPTED:
- checked_set (_encrypted, _film->encrypted ());
- if (_film->encrypted ()) {
- _film->set_signed (true);
- _signed->Enable (false);
- } else {
- _signed->Enable (_generally_sensitive);
- }
- break;
- case Film::RESOLUTION:
- checked_set (_resolution, _film->resolution() == RESOLUTION_2K ? 0 : 1);
- setup_dcp_name ();
- break;
- case Film::J2K_BANDWIDTH:
- checked_set (_j2k_bandwidth, _film->j2k_bandwidth() / 1000000);
- break;
- case Film::USE_ISDCF_NAME:
- checked_set (_use_isdcf_name, _film->use_isdcf_name ());
- setup_dcp_name ();
- use_isdcf_name_changed ();
- break;
- case Film::ISDCF_METADATA:
- setup_dcp_name ();
- break;
- case Film::VIDEO_FRAME_RATE:
- {
- bool done = false;
- for (unsigned int i = 0; i < _frame_rate_choice->GetCount(); ++i) {
- if (wx_to_std (_frame_rate_choice->GetString(i)) == boost::lexical_cast<string> (_film->video_frame_rate())) {
- checked_set (_frame_rate_choice, i);
- done = true;
- break;
- }
- }
-
- if (!done) {
- checked_set (_frame_rate_choice, -1);
- }
-
- _frame_rate_spin->SetValue (_film->video_frame_rate ());
-
- _best_frame_rate->Enable (_film->best_video_frame_rate () != _film->video_frame_rate ());
- break;
- }
- case Film::AUDIO_CHANNELS:
- checked_set (_audio_channels, _film->audio_channels ());
- setup_dcp_name ();
- break;
- case Film::SEQUENCE_VIDEO:
- checked_set (_sequence_video, _film->sequence_video ());
- break;
- case Film::THREE_D:
- checked_set (_three_d, _film->three_d ());
- setup_dcp_name ();
- break;
- case Film::INTEROP:
- checked_set (_standard, _film->interop() ? 1 : 0);
- break;
- }
+ _content_panel->film_changed (p);
+ _dcp_panel->film_changed (p);
}
void
return;
}
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->film_content_changed (property);
- }
-
- if (property == FFmpegContentProperty::AUDIO_STREAM) {
- setup_dcp_name ();
- } else if (property == ContentProperty::PATH) {
- setup_content ();
- } else if (property == ContentProperty::POSITION) {
- setup_content ();
- } else if (property == VideoContentProperty::VIDEO_SCALE) {
- setup_dcp_name ();
- }
-}
-
-void
-FilmEditor::setup_container ()
-{
- int n = 0;
- vector<Ratio const *> ratios = Ratio::all ();
- vector<Ratio const *>::iterator i = ratios.begin ();
- while (i != ratios.end() && *i != _film->container ()) {
- ++i;
- ++n;
- }
-
- if (i == ratios.end()) {
- checked_set (_container, -1);
- } else {
- checked_set (_container, n);
- }
-
- setup_dcp_name ();
-}
-
-/** Called when the container widget has been changed */
-void
-FilmEditor::container_changed ()
-{
- if (!_film) {
- return;
- }
-
- int const n = _container->GetSelection ();
- if (n >= 0) {
- vector<Ratio const *> ratios = Ratio::all ();
- assert (n < int (ratios.size()));
- _film->set_container (ratios[n]);
- }
-}
-
-/** Called when the DCP content type widget has been changed */
-void
-FilmEditor::dcp_content_type_changed ()
-{
- if (!_film) {
- return;
- }
-
- int const n = _dcp_content_type->GetSelection ();
- if (n != wxNOT_FOUND) {
- _film->set_dcp_content_type (DCPContentType::from_index (n));
- }
+ _content_panel->film_content_changed (property);
+ _dcp_panel->film_content_changed (property);
}
/** Sets the Film that we are editing */
_film = f;
+ _content_panel->set_film (_film);
+ _dcp_panel->set_film (_film);
+
if (_film) {
_film->Changed.connect (bind (&FilmEditor::film_changed, this, _1));
_film->ContentChanged.connect (bind (&FilmEditor::film_content_changed, this, _2));
FileChanged ("");
}
- film_changed (Film::NAME);
- film_changed (Film::USE_ISDCF_NAME);
- film_changed (Film::CONTENT);
- film_changed (Film::DCP_CONTENT_TYPE);
- film_changed (Film::CONTAINER);
- film_changed (Film::RESOLUTION);
- film_changed (Film::SCALER);
- film_changed (Film::WITH_SUBTITLES);
- film_changed (Film::SIGNED);
- film_changed (Film::ENCRYPTED);
- film_changed (Film::J2K_BANDWIDTH);
- film_changed (Film::ISDCF_METADATA);
- film_changed (Film::VIDEO_FRAME_RATE);
- film_changed (Film::AUDIO_CHANNELS);
- film_changed (Film::SEQUENCE_VIDEO);
- film_changed (Film::THREE_D);
- film_changed (Film::INTEROP);
-
if (!_film->content().empty ()) {
- set_selection (_film->content().front ());
+ _content_panel->set_selection (_film->content().front ());
}
-
- content_selection_changed ();
}
void
FilmEditor::set_general_sensitivity (bool s)
{
- _generally_sensitive = s;
-
- /* Stuff in the Content / DCP tabs */
- _name->Enable (s);
- _use_isdcf_name->Enable (s);
- _edit_isdcf_button->Enable (s);
- _content->Enable (s);
- _content_add_file->Enable (s);
- _content_add_folder->Enable (s);
- _content_remove->Enable (s);
- _content_earlier->Enable (s);
- _content_later->Enable (s);
- _content_timeline->Enable (s);
- _dcp_content_type->Enable (s);
-
- bool si = s;
- if (_film && _film->encrypted ()) {
- si = false;
- }
- _signed->Enable (si);
-
- _encrypted->Enable (s);
- _frame_rate_choice->Enable (s);
- _frame_rate_spin->Enable (s);
- _audio_channels->Enable (s);
- _j2k_bandwidth->Enable (s);
- _container->Enable (s);
- _best_frame_rate->Enable (s && _film && _film->best_video_frame_rate () != _film->video_frame_rate ());
- _sequence_video->Enable (s);
- _resolution->Enable (s);
- _scaler->Enable (s);
- _three_d->Enable (s);
- _standard->Enable (s);
-
- /* Set the panels in the content notebook */
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->Enable (s);
- }
-}
-
-/** Called when the scaler widget has been changed */
-void
-FilmEditor::scaler_changed ()
-{
- if (!_film) {
- return;
- }
-
- int const n = _scaler->GetSelection ();
- if (n >= 0) {
- _film->set_scaler (Scaler::from_index (n));
- }
-}
-
-void
-FilmEditor::use_isdcf_name_toggled ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_use_isdcf_name (_use_isdcf_name->GetValue ());
-}
-
-void
-FilmEditor::use_isdcf_name_changed ()
-{
- bool const i = _film->use_isdcf_name ();
-
- if (!i) {
- _film->set_name (_film->isdcf_name (true));
- }
-
- _edit_isdcf_button->Enable (i);
-}
-
-void
-FilmEditor::edit_isdcf_button_clicked ()
-{
- if (!_film) {
- return;
- }
-
- ISDCFMetadataDialog* d = new ISDCFMetadataDialog (this, _film->isdcf_metadata ());
- d->ShowModal ();
- _film->set_isdcf_metadata (d->isdcf_metadata ());
- d->Destroy ();
+ _content_panel->set_general_sensitivity (s);
+ _dcp_panel->set_general_sensitivity (s);
}
void
{
set_general_sensitivity (!a);
}
-
-void
-FilmEditor::setup_dcp_name ()
-{
- string s = _film->dcp_name (true);
- if (s.length() > 28) {
- _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("..."));
- _dcp_name->SetToolTip (std_to_wx (s));
- } else {
- _dcp_name->SetLabel (std_to_wx (s));
- }
-}
-
-void
-FilmEditor::best_frame_rate_clicked ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_video_frame_rate (_film->best_video_frame_rate ());
-}
-
-void
-FilmEditor::setup_content ()
-{
- string selected_summary;
- int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- if (s != -1) {
- selected_summary = wx_to_std (_content->GetItemText (s));
- }
-
- _content->DeleteAllItems ();
-
- ContentList content = _film->content ();
- sort (content.begin(), content.end(), ContentSorter ());
-
- for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
- int const t = _content->GetItemCount ();
- bool const valid = (*i)->paths_valid ();
-
- string s = (*i)->summary ();
- if (!valid) {
- s = _("MISSING: ") + s;
- }
-
- _content->InsertItem (t, std_to_wx (s));
-
- if ((*i)->summary() == selected_summary) {
- _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- }
-
- if (!valid) {
- _content->SetItemTextColour (t, *wxRED);
- }
- }
-
- if (selected_summary.empty () && !content.empty ()) {
- /* Select the item of content if none was selected before */
- _content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- }
-}
-
-void
-FilmEditor::content_add_file_clicked ()
-{
- /* The wxFD_CHANGE_DIR here prevents a `could not set working directory' error 123 on Windows when using
- non-Latin filenames or paths.
- */
- wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE | wxFD_CHANGE_DIR);
- int const r = d->ShowModal ();
-
- if (r != wxID_OK) {
- d->Destroy ();
- return;
- }
-
- wxArrayString paths;
- d->GetPaths (paths);
-
- /* XXX: check for lots of files here and do something */
-
- for (unsigned int i = 0; i < paths.GetCount(); ++i) {
- _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
- }
-
- d->Destroy ();
-}
-
-void
-FilmEditor::content_add_folder_clicked ()
-{
- wxDirDialog* d = new wxDirDialog (this, _("Choose a folder"), wxT (""), wxDD_DIR_MUST_EXIST);
- int const r = d->ShowModal ();
- d->Destroy ();
-
- if (r != wxID_OK) {
- return;
- }
-
- shared_ptr<ImageContent> ic;
-
- try {
- ic.reset (new ImageContent (_film, boost::filesystem::path (wx_to_std (d->GetPath ()))));
- } catch (FileError& e) {
- error_dialog (this, std_to_wx (e.what ()));
- return;
- }
-
- _film->examine_and_add_content (ic);
-}
-
-void
-FilmEditor::content_remove_clicked ()
-{
- ContentList c = selected_content ();
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- _film->remove_content (*i);
- }
-
- content_selection_changed ();
-}
-
-void
-FilmEditor::content_selection_changed ()
-{
- setup_content_sensitivity ();
-
- for (list<FilmEditorPanel*>::iterator i = _panels.begin(); i != _panels.end(); ++i) {
- (*i)->content_selection_changed ();
- }
-}
-
-/** Set up broad sensitivity based on the type of content that is selected */
-void
-FilmEditor::setup_content_sensitivity ()
-{
- _content_add_file->Enable (_generally_sensitive);
- _content_add_folder->Enable (_generally_sensitive);
-
- ContentList selection = selected_content ();
- VideoContentList video_selection = selected_video_content ();
- AudioContentList audio_selection = selected_audio_content ();
-
- _content_remove->Enable (!selection.empty() && _generally_sensitive);
- _content_earlier->Enable (selection.size() == 1 && _generally_sensitive);
- _content_later->Enable (selection.size() == 1 && _generally_sensitive);
- _content_timeline->Enable (!_film->content().empty() && _generally_sensitive);
-
- _video_panel->Enable (!video_selection.empty() && _generally_sensitive);
- _audio_panel->Enable (!audio_selection.empty() && _generally_sensitive);
- _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<FFmpegContent> (selection.front()) && _generally_sensitive);
- _timing_panel->Enable (!selection.empty() && _generally_sensitive);
-}
-
-ContentList
-FilmEditor::selected_content ()
-{
- ContentList sel;
-
- if (!_film) {
- return sel;
- }
-
- /* The list was populated using a sorted content list, so we must sort it here too
- so that we can look up by index and get the right thing.
- */
- ContentList content = _film->content ();
- sort (content.begin(), content.end(), ContentSorter ());
-
- long int s = -1;
- while (true) {
- s = _content->GetNextItem (s, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
- if (s == -1) {
- break;
- }
-
- if (s < int (_film->content().size ())) {
- sel.push_back (content[s]);
- }
- }
-
- return sel;
-}
-
-VideoContentList
-FilmEditor::selected_video_content ()
-{
- ContentList c = selected_content ();
- VideoContentList vc;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<VideoContent> t = dynamic_pointer_cast<VideoContent> (*i);
- if (t) {
- vc.push_back (t);
- }
- }
-
- return vc;
-}
-
-AudioContentList
-FilmEditor::selected_audio_content ()
-{
- ContentList c = selected_content ();
- AudioContentList ac;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<AudioContent> t = dynamic_pointer_cast<AudioContent> (*i);
- if (t) {
- ac.push_back (t);
- }
- }
-
- return ac;
-}
-
-SubtitleContentList
-FilmEditor::selected_subtitle_content ()
-{
- ContentList c = selected_content ();
- SubtitleContentList sc;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<SubtitleContent> t = dynamic_pointer_cast<SubtitleContent> (*i);
- if (t) {
- sc.push_back (t);
- }
- }
-
- return sc;
-}
-
-FFmpegContentList
-FilmEditor::selected_ffmpeg_content ()
-{
- ContentList c = selected_content ();
- FFmpegContentList sc;
-
- for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- shared_ptr<FFmpegContent> t = dynamic_pointer_cast<FFmpegContent> (*i);
- if (t) {
- sc.push_back (t);
- }
- }
-
- return sc;
-}
-
-void
-FilmEditor::content_timeline_clicked ()
-{
- if (_timeline_dialog) {
- _timeline_dialog->Destroy ();
- _timeline_dialog = 0;
- }
-
- _timeline_dialog = new TimelineDialog (this, _film);
- _timeline_dialog->Show ();
-}
-
-void
-FilmEditor::set_selection (weak_ptr<Content> wc)
-{
- ContentList content = _film->content ();
- for (size_t i = 0; i < content.size(); ++i) {
- if (content[i] == wc.lock ()) {
- _content->SetItemState (i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- } else {
- _content->SetItemState (i, 0, wxLIST_STATE_SELECTED);
- }
- }
-}
-
-void
-FilmEditor::sequence_video_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_sequence_video (_sequence_video->GetValue ());
-}
-
-void
-FilmEditor::content_right_click (wxListEvent& ev)
-{
- _menu.popup (_film, selected_content (), ev.GetPoint ());
-}
-
-void
-FilmEditor::three_d_changed ()
-{
- if (!_film) {
- return;
- }
-
- _film->set_three_d (_three_d->GetValue ());
-}
-
-void
-FilmEditor::content_earlier_clicked ()
-{
- ContentList sel = selected_content ();
- if (sel.size() == 1) {
- _film->move_content_earlier (sel.front ());
- content_selection_changed ();
- }
-}
-
-void
-FilmEditor::content_later_clicked ()
-{
- ContentList sel = selected_content ();
- if (sel.size() == 1) {
- _film->move_content_later (sel.front ());
- content_selection_changed ();
- }
-}
-
-void
-FilmEditor::config_changed ()
-{
- _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
- setup_frame_rate_widget ();
-}
-
-void
-FilmEditor::setup_frame_rate_widget ()
-{
- if (Config::instance()->allow_any_dcp_frame_rate ()) {
- _frame_rate_choice->Hide ();
- _frame_rate_spin->Show ();
- } else {
- _frame_rate_choice->Show ();
- _frame_rate_spin->Hide ();
- }
-
- _frame_rate_sizer->Layout ();
-}
-
-void
-FilmEditor::content_files_dropped (wxDropFilesEvent& event)
-{
- if (!_film) {
- return;
- }
-
- wxString* paths = event.GetFiles ();
- for (int i = 0; i < event.GetNumberOfFiles(); i++) {
- _film->examine_and_add_content (content_factory (_film, wx_to_std (paths[i])));
- }
-}
*/
#include <wx/wx.h>
-#include <wx/spinctrl.h>
-#include <wx/filepicker.h>
-#include <wx/collpane.h>
#include <boost/signals2.hpp>
#include "lib/film.h"
-#include "content_menu.h"
+class wxSpinCtrl;
class wxNotebook;
-class wxListCtrl;
-class wxListEvent;
-class wxGridBagSizer;
class Film;
-class TimelineDialog;
class Ratio;
-class Timecode;
-class FilmEditorPanel;
-class SubtitleContent;
+class ContentPanel;
+class DCPPanel;
/** @class FilmEditor
* @brief A wx widget to edit a film's metadata, and perform various functions.
FilmEditor (wxWindow *);
void set_film (boost::shared_ptr<Film>);
- void set_selection (boost::weak_ptr<Content>);
boost::signals2::signal<void (boost::filesystem::path)> FileChanged;
/* Stuff for panels */
-
- wxNotebook* content_notebook () const {
- return _content_notebook;
- }
+ ContentPanel* content_panel () const {
+ return _content_panel;
+ }
+
boost::shared_ptr<Film> film () const {
return _film;
}
- ContentList selected_content ();
- VideoContentList selected_video_content ();
- AudioContentList selected_audio_content ();
- SubtitleContentList selected_subtitle_content ();
- FFmpegContentList selected_ffmpeg_content ();
-
- void content_add_file_clicked ();
-
-private:
- void make_dcp_panel ();
- void make_content_panel ();
- void connect_to_widgets ();
-
- /* Handle changes to the view */
- void name_changed ();
- void use_isdcf_name_toggled ();
- void edit_isdcf_button_clicked ();
- void content_selection_changed ();
- void content_add_folder_clicked ();
- void content_remove_clicked ();
- void content_earlier_clicked ();
- void content_later_clicked ();
- void content_files_dropped (wxDropFilesEvent& event);
- void container_changed ();
- void dcp_content_type_changed ();
- void scaler_changed ();
- void j2k_bandwidth_changed ();
- void frame_rate_choice_changed ();
- void frame_rate_spin_changed ();
- void best_frame_rate_clicked ();
- void content_timeline_clicked ();
- void audio_channels_changed ();
- void resolution_changed ();
- void sequence_video_changed ();
- void content_right_click (wxListEvent &);
- void three_d_changed ();
- void standard_changed ();
- void signed_toggled ();
- void encrypted_toggled ();
-
/* Handle changes to the model */
void film_changed (Film::Property);
void film_content_changed (int);
- void use_isdcf_name_changed ();
void set_general_sensitivity (bool);
- void setup_dcp_name ();
- void setup_content ();
- void setup_container ();
- void setup_content_sensitivity ();
- void setup_frame_rate_widget ();
-
void active_jobs_changed (bool);
- void config_changed ();
-
- FilmEditorPanel* _video_panel;
- FilmEditorPanel* _audio_panel;
- FilmEditorPanel* _subtitle_panel;
- FilmEditorPanel* _timing_panel;
- std::list<FilmEditorPanel *> _panels;
wxNotebook* _main_notebook;
- wxNotebook* _content_notebook;
- wxPanel* _dcp_panel;
- wxSizer* _dcp_sizer;
- wxPanel* _content_panel;
- wxSizer* _content_sizer;
+ ContentPanel* _content_panel;
+ DCPPanel* _dcp_panel;
/** The film we are editing */
boost::shared_ptr<Film> _film;
- wxTextCtrl* _name;
- wxStaticText* _dcp_name;
- wxCheckBox* _use_isdcf_name;
- wxChoice* _container;
- wxListCtrl* _content;
- wxButton* _content_add_file;
- wxButton* _content_add_folder;
- wxButton* _content_remove;
- wxButton* _content_earlier;
- wxButton* _content_later;
- wxButton* _content_timeline;
- wxCheckBox* _sequence_video;
- wxButton* _edit_isdcf_button;
- wxChoice* _scaler;
- wxSpinCtrl* _j2k_bandwidth;
- wxChoice* _dcp_content_type;
- wxChoice* _frame_rate_choice;
- wxSpinCtrl* _frame_rate_spin;
- wxSizer* _frame_rate_sizer;
- wxSpinCtrl* _audio_channels;
- wxButton* _best_frame_rate;
- wxCheckBox* _three_d;
- wxChoice* _resolution;
- wxChoice* _standard;
- wxCheckBox* _signed;
- wxCheckBox* _encrypted;
-
- ContentMenu _menu;
-
- std::vector<Ratio const *> _ratios;
-
- bool _generally_sensitive;
- TimelineDialog* _timeline_dialog;
};
+++ /dev/null
-/*
- 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 <wx/notebook.h>
-#include "film_editor_panel.h"
-#include "film_editor.h"
-
-using boost::shared_ptr;
-
-FilmEditorPanel::FilmEditorPanel (FilmEditor* e, wxString name)
- : wxPanel (e->content_notebook (), wxID_ANY)
- , _editor (e)
- , _sizer (new wxBoxSizer (wxVERTICAL))
-{
- e->content_notebook()->AddPage (this, name, false);
- SetSizer (_sizer);
-}
-
+++ /dev/null
-/*
- 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 DCPOMATIC_FILM_EDITOR_PANEL_H
-#define DCPOMATIC_FILM_EDITOR_PANEL_H
-
-#include <boost/shared_ptr.hpp>
-#include <wx/wx.h>
-#include "lib/film.h"
-
-class FilmEditor;
-class Content;
-
-class FilmEditorPanel : public wxPanel
-{
-public:
- FilmEditorPanel (FilmEditor *, wxString);
-
- virtual void film_changed (Film::Property) {}
- /** Called when a given property of one of the selected Contents changes */
- virtual void film_content_changed (int) = 0;
- /** Called when the list of selected Contents changes */
- virtual void content_selection_changed () = 0;
-
-protected:
- FilmEditor* _editor;
- wxSizer* _sizer;
-};
-
-#endif
#include <iostream>
#include <iomanip>
#include <wx/tglbtn.h>
+#include <dcp/exceptions.h>
#include "lib/film.h"
#include "lib/ratio.h"
#include "lib/util.h"
#include "lib/examine_content_job.h"
#include "lib/filter.h"
#include "lib/player.h"
-#include "lib/player_video_frame.h"
+#include "lib/player_video.h"
#include "lib/video_content.h"
#include "lib/video_decoder.h"
+#include "lib/timer.h"
#include "film_viewer.h"
#include "wx_util.h"
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
using boost::weak_ptr;
-using libdcp::Size;
+using dcp::Size;
FilmViewer::FilmViewer (wxWindow* p)
: wxPanel (p)
, _panel (new wxPanel (this))
+ , _outline_content (new wxCheckBox (this, wxID_ANY, _("Outline content")))
, _slider (new wxSlider (this, wxID_ANY, 0, 0, 4096))
, _back_button (new wxButton (this, wxID_ANY, wxT("<")))
, _forward_button (new wxButton (this, wxID_ANY, wxT(">")))
, _frame_number (new wxStaticText (this, wxID_ANY, wxT("")))
, _timecode (new wxStaticText (this, wxID_ANY, wxT("")))
, _play_button (new wxToggleButton (this, wxID_ANY, _("Play")))
- , _got_frame (false)
+ , _last_get_accurate (true)
{
#ifndef __WXOSX__
_panel->SetDoubleBuffered (true);
_v_sizer->Add (_panel, 1, wxEXPAND);
+ _v_sizer->Add (_outline_content, 0, wxALL, DCPOMATIC_SIZER_GAP);
+
wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL);
wxBoxSizer* time_sizer = new wxBoxSizer (wxVERTICAL);
_panel->Bind (wxEVT_PAINT, boost::bind (&FilmViewer::paint_panel, this));
_panel->Bind (wxEVT_SIZE, boost::bind (&FilmViewer::panel_sized, this, _1));
+ _outline_content->Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&FilmViewer::refresh_panel, this));
_slider->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind (&FilmViewer::slider_moved, this));
_slider->Bind (wxEVT_SCROLL_PAGEUP, boost::bind (&FilmViewer::slider_moved, this));
_slider->Bind (wxEVT_SCROLL_PAGEDOWN, boost::bind (&FilmViewer::slider_moved, this));
JobManager::instance()->ActiveJobsChanged.connect (
bind (&FilmViewer::active_jobs_changed, this, _1)
);
+
+ setup_sensitivity ();
}
void
_frame.reset ();
_slider->SetValue (0);
- set_position_text (0);
+ set_position_text ();
if (!_film) {
return;
return;
}
- _player->disable_audio ();
- _player->Video.connect (boost::bind (&FilmViewer::process_video, this, _1, _3));
- _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
+ _film_connection = _film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
+
+ _player->set_approximate_size ();
+ _player_connection = _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
calculate_sizes ();
- fetch_next_frame ();
+ get (_position, _last_get_accurate);
+
+ setup_sensitivity ();
+}
+
+void
+FilmViewer::refresh_panel ()
+{
+ _panel->Refresh ();
+ _panel->Update ();
}
void
-FilmViewer::fetch_current_frame_again ()
+FilmViewer::get (DCPTime p, bool accurate)
{
if (!_player) {
return;
}
- /* We could do this with a seek and a fetch_next_frame, but this is
- a shortcut to make it quicker.
- */
-
- _got_frame = false;
- if (!_player->repeat_last_video ()) {
- fetch_next_frame ();
+ list<shared_ptr<PlayerVideo> > pvf = _player->get_video (p, accurate);
+ if (!pvf.empty ()) {
+ try {
+ _frame = pvf.front()->image (PIX_FMT_RGB24, true);
+ _frame = _frame->scale (_frame->size(), Scaler::from_id ("fastbilinear"), PIX_FMT_RGB24, false);
+ _position = pvf.front()->time ();
+ _inter_position = pvf.front()->inter_position ();
+ _inter_size = pvf.front()->inter_size ();
+ } catch (dcp::DCPReadError& e) {
+ /* This can happen on the following sequence of events:
+ * - load encrypted DCP
+ * - add KDM
+ * - DCP is examined again, which sets its "playable" flag to 1
+ * - as a side effect of the exam, the viewer is updated using the old pieces
+ * - the DCPDecoder in the old piece gives us an encrypted frame
+ * - then, the pieces are re-made (but too late).
+ *
+ * I hope there's a better way to handle this ...
+ */
+ _frame.reset ();
+ _position = p;
+ }
+ } else {
+ _frame.reset ();
+ _position = p;
}
-
- _panel->Refresh ();
- _panel->Update ();
+
+ set_position_text ();
+ refresh_panel ();
+
+ _last_get_accurate = accurate;
}
void
FilmViewer::timer ()
{
- if (!_player) {
- return;
- }
-
- fetch_next_frame ();
+ get (_position + DCPTime::from_frames (1, _film->video_frame_rate ()), true);
- Time const len = _film->length ();
+ DCPTime const len = _film->length ();
- if (len) {
- int const new_slider_position = 4096 * _player->video_position() / len;
+ if (len.get ()) {
+ int const new_slider_position = 4096 * _position.get() / len.get();
if (new_slider_position != _slider->GetValue()) {
_slider->SetValue (new_slider_position);
}
dc.SetPen (p);
dc.SetBrush (b);
dc.DrawRectangle (0, _out_size.height, _panel_size.width, _panel_size.height - _out_size.height);
- }
-}
+ }
+ if (_outline_content->GetValue ()) {
+ wxPen p (wxColour (255, 0, 0), 2);
+ dc.SetPen (p);
+ dc.SetBrush (*wxTRANSPARENT_BRUSH);
+ dc.DrawRectangle (_inter_position.x, _inter_position.y, _inter_size.width, _inter_size.height);
+ }
+}
void
FilmViewer::slider_moved ()
{
- if (_film && _player) {
- Time t = _slider->GetValue() * _film->length() / 4096;
- /* Ensure that we hit the end of the film at the end of the slider */
- if (t >= _film->length ()) {
- t = _film->length() - _film->video_frames_to_time (1);
- }
- _player->seek (t, false);
- fetch_next_frame ();
+ if (!_film) {
+ return;
+ }
+
+ DCPTime t (_slider->GetValue() * _film->length().get() / 4096);
+ /* Ensure that we hit the end of the film at the end of the slider */
+ if (t >= _film->length ()) {
+ t = _film->length() - DCPTime::from_frames (1, _film->video_frame_rate ());
}
+ get (t, false);
}
void
{
_panel_size.width = ev.GetSize().GetWidth();
_panel_size.height = ev.GetSize().GetHeight();
+
calculate_sizes ();
- fetch_current_frame_again ();
+ get (_position, _last_get_accurate);
}
void
if (panel_ratio < film_ratio) {
/* panel is less widscreen than the film; clamp width */
_out_size.width = _panel_size.width;
- _out_size.height = _out_size.width / film_ratio;
+ _out_size.height = rint (_out_size.width / film_ratio);
} else {
/* panel is more widescreen than the film; clamp height */
_out_size.height = _panel_size.height;
- _out_size.width = _out_size.height * film_ratio;
+ _out_size.width = rint (_out_size.height * film_ratio);
}
/* Catch silly values */
_out_size.width = max (64, _out_size.width);
_out_size.height = max (64, _out_size.height);
+ /* The player will round its image size down to the next lowest 4 pixels
+ to speed up its scale, so do similar here to avoid black borders
+ around things. This is a bit of a hack.
+ */
+ _out_size.width &= ~3;
+ _out_size.height &= ~3;
+
_player->set_video_container_size (_out_size);
}
}
void
-FilmViewer::process_video (shared_ptr<PlayerVideoFrame> pvf, Time t)
-{
- if (pvf->eyes() == EYES_RIGHT) {
- return;
- }
-
- _frame = pvf->image (PIX_FMT_RGB24);
- _got_frame = true;
-
- set_position_text (t);
-}
-
-void
-FilmViewer::set_position_text (Time t)
+FilmViewer::set_position_text ()
{
if (!_film) {
_frame_number->SetLabel ("0");
double const fps = _film->video_frame_rate ();
/* Count frame number from 1 ... not sure if this is the best idea */
- _frame_number->SetLabel (wxString::Format (wxT("%d"), int (rint (t * fps / TIME_HZ)) + 1));
+ _frame_number->SetLabel (wxString::Format (wxT("%d"), int (rint (_position.seconds() * fps)) + 1));
- double w = static_cast<double>(t) / TIME_HZ;
+ double w = _position.seconds ();
int const h = (w / 3600);
w -= h * 3600;
int const m = (w / 60);
_timecode->SetLabel (wxString::Format (wxT("%02d:%02d:%02d.%02d"), h, m, s, f));
}
-/** Ask the player to emit its next frame, then update our display */
-void
-FilmViewer::fetch_next_frame ()
-{
- /* Clear our frame in case we don't get a new one */
- _frame.reset ();
-
- if (!_player) {
- return;
- }
-
- _got_frame = false;
-
- try {
- while (!_got_frame && !_player->pass ()) {}
- } catch (DecodeError& e) {
- _play_button->SetValue (false);
- check_play_state ();
- error_dialog (this, wxString::Format (_("Could not decode video for view (%s)"), std_to_wx(e.what()).data()));
- } catch (OpenFileError& e) {
- /* There was a problem opening a content file; we'll let this slide as it
- probably means a missing content file, which we're already taking care of.
- */
- }
-
- _panel->Refresh ();
- _panel->Update ();
-}
-
void
FilmViewer::active_jobs_changed (bool a)
{
void
FilmViewer::back_clicked ()
{
- if (!_player) {
- return;
+ DCPTime p = _position - DCPTime::from_frames (1, _film->video_frame_rate ());
+ if (p < DCPTime ()) {
+ p = DCPTime ();
}
- /* Player::video_position is the time after the last frame that we received.
- We want to see the one before it, so we need to go back 2.
- */
-
- Time p = _player->video_position() - _film->video_frames_to_time (2);
- if (p < 0) {
- p = 0;
- }
-
- _player->seek (p, true);
- fetch_next_frame ();
+ get (p, true);
}
void
FilmViewer::forward_clicked ()
{
- if (!_player) {
- return;
- }
-
- fetch_next_frame ();
+ get (_position + DCPTime::from_frames (1, _film->video_frame_rate ()), true);
}
void
}
calculate_sizes ();
- fetch_current_frame_again ();
+ get (_position, _last_get_accurate);
+}
+
+void
+FilmViewer::setup_sensitivity ()
+{
+ bool const c = _film && !_film->content().empty ();
+
+ _slider->Enable (c);
+ _back_button->Enable (c);
+ _forward_button->Enable (c);
+ _play_button->Enable (c);
+ _outline_content->Enable (c);
+ _frame_number->Enable (c);
+ _timecode->Enable (c);
+}
+
+void
+FilmViewer::film_changed (Film::Property p)
+{
+ if (p == Film::CONTENT) {
+ setup_sensitivity ();
+ }
}
/*
- 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
class FFmpegPlayer;
class Image;
class RGBPlusAlphaImage;
-class PlayerVideoFrame;
+class PlayerVideo;
/** @class FilmViewer
* @brief A wx widget to view a preview of a Film.
- *
- * The film takes the following path through the viewer:
- *
- * 1. fetch_next_frame() asks our _player to decode some data. If it does, process_video()
- * will be called.
- *
- * 2. process_video() takes the image from the player (_frame).
- *
- * 3. fetch_next_frame() calls _panel->Refresh() and _panel->Update() which results in
- * paint_panel() being called; this creates frame_bitmap from _frame and blits it to the display.
- *
- * fetch_current_frame_again() asks the player to re-emit its current frame on the next pass(), and then
- * starts from step #1.
*/
class FilmViewer : public wxPanel
{
void slider_moved ();
void play_clicked ();
void timer ();
- void process_video (boost::shared_ptr<PlayerVideoFrame>, Time);
void calculate_sizes ();
void check_play_state ();
- void fetch_current_frame_again ();
- void fetch_next_frame ();
void active_jobs_changed (bool);
void back_clicked ();
void forward_clicked ();
void player_changed (bool);
- void set_position_text (Time);
+ void set_position_text ();
+ void get (DCPTime, bool);
+ void refresh_panel ();
+ void setup_sensitivity ();
+ void film_changed (Film::Property);
boost::shared_ptr<Film> _film;
boost::shared_ptr<Player> _player;
wxSizer* _v_sizer;
wxPanel* _panel;
+ wxCheckBox* _outline_content;
wxSlider* _slider;
wxButton* _back_button;
wxButton* _forward_button;
wxTimer _timer;
boost::shared_ptr<const Image> _frame;
- bool _got_frame;
+ DCPTime _position;
+ Position<int> _inter_position;
+ dcp::Size _inter_size;
/** Size of our output (including padding if we have any) */
- libdcp::Size _out_size;
+ dcp::Size _out_size;
/** Size of the panel that we have available */
- libdcp::Size _panel_size;
+ dcp::Size _panel_size;
+ /** true if the last call to ::get() was specified to be accurate;
+ * this is used so that when re-fetching the current frame we
+ * can get the same one that we got last time.
+ */
+ bool _last_get_accurate;
+
+ boost::signals2::scoped_connection _film_connection;
+ boost::signals2::scoped_connection _player_connection;
};
add_label_to_sizer (table, this, _("KDM type"), true);
_type = new wxChoice (this, wxID_ANY);
- _type->Append ("Modified Transitional 1", ((void *) libdcp::KDM::MODIFIED_TRANSITIONAL_1));
+ _type->Append ("Modified Transitional 1", ((void *) dcp::MODIFIED_TRANSITIONAL_1));
if (!film->interop ()) {
- _type->Append ("DCI Any", ((void *) libdcp::KDM::DCI_ANY));
- _type->Append ("DCI Specific", ((void *) libdcp::KDM::DCI_SPECIFIC));
+ _type->Append ("DCI Any", ((void *) dcp::DCI_ANY));
+ _type->Append ("DCI Specific", ((void *) dcp::DCI_SPECIFIC));
}
table->Add (_type, 1, wxEXPAND);
_type->SetSelection (0);
return _write_to->GetValue ();
}
-libdcp::KDM::Formulation
+dcp::Formulation
KDMDialog::formulation () const
{
- return (libdcp::KDM::Formulation) reinterpret_cast<intptr_t> (_type->GetClientData (_type->GetSelection()));
+ return (dcp::Formulation) reinterpret_cast<intptr_t> (_type->GetClientData (_type->GetSelection()));
}
void
boost::filesystem::path cpl () const;
boost::filesystem::path directory () const;
bool write_to () const;
- libdcp::KDM::Formulation formulation () const;
+ dcp::Formulation formulation () const;
private:
void add_cinema (boost::shared_ptr<Cinema>);
--- /dev/null
+/*
+ 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 "make_signer_chain_dialog.h"
+
+MakeSignerChainDialog::MakeSignerChainDialog (wxWindow* parent)
+ : TableDialog (parent, _("Make certificate chain"), 2, true)
+{
+ add (_("Organisation"), true);
+ add (_organisation = new wxTextCtrl (this, wxID_ANY));
+ add (_("Organisational unit"), true);
+ add (_organisational_unit = new wxTextCtrl (this, wxID_ANY));
+ add (_("Root common name"), true);
+ add (_root_common_name = new wxTextCtrl (this, wxID_ANY));
+ add (_("Intermediate common name"), true);
+ add (_intermediate_common_name = new wxTextCtrl (this, wxID_ANY));
+ add (_("Leaf common name"), true);
+ add (_leaf_common_name = new wxTextCtrl (this, wxID_ANY));
+}
--- /dev/null
+/*
+ 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 "table_dialog.h"
+#include "wx_util.h"
+
+class MakeSignerChainDialog : public TableDialog
+{
+public:
+ MakeSignerChainDialog (wxWindow* parent);
+
+ std::string organisation () const {
+ return wx_to_std (_organisation->GetValue ());
+ }
+
+ std::string organisational_unit () const {
+ return wx_to_std (_organisational_unit->GetValue ());
+ }
+
+ std::string root_common_name () const {
+ return wx_to_std (_root_common_name->GetValue ());
+ }
+
+ std::string intermediate_common_name () const {
+ return wx_to_std (_intermediate_common_name->GetValue ());
+ }
+
+ std::string leaf_common_name () const {
+ return wx_to_std (_leaf_common_name->GetValue ());
+ }
+
+
+private:
+ wxTextCtrl* _organisation;
+ wxTextCtrl* _organisational_unit;
+ wxTextCtrl* _root_common_name;
+ wxTextCtrl* _intermediate_common_name;
+ wxTextCtrl* _leaf_common_name;
+};
+
"X-Generator: Poedit 1.6.5\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/wx/subtitle_panel.cc:48 src/wx/subtitle_panel.cc:57
-#: src/wx/subtitle_panel.cc:66 src/wx/subtitle_panel.cc:75
+#: src/wx/subtitle_panel.cc:56 src/wx/subtitle_panel.cc:65
+#: src/wx/subtitle_panel.cc:74 src/wx/subtitle_panel.cc:83
msgid "%"
msgstr "%"
-#: src/wx/about_dialog.cc:78
+#: src/wx/about_dialog.cc:82
msgid ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/wx/config_dialog.cc:664
+#: src/wx/config_dialog.cc:1003
msgid "(password will be stored on disk in plaintext)"
msgstr "(Passwort wird im Klartext gespeichert!)"
-#: src/wx/config_dialog.cc:104
+#: src/wx/config_dialog.cc:111
msgid "(restart DCP-o-matic to see language changes)"
msgstr "(Programm zum Ändern der Sprache neu starten)"
-#: src/wx/audio_mapping_view.cc:134
+#: src/wx/audio_mapping_view.cc:142
msgid "-6dB"
msgstr "-6dB"
msgid "1 / "
msgstr "1/"
-#: src/wx/audio_panel.cc:238
-msgid "1 channel"
-msgstr "1 Kanal"
-
-#: src/wx/video_panel.cc:197
+#: src/wx/video_panel.cc:193
msgid "2D"
msgstr "2D"
msgid "2D version of content available in 3D"
msgstr "2D Version eines Films, der auch in 3D verfügbar ist"
-#: src/wx/film_editor.cc:224
+#: src/wx/dcp_panel.cc:607
msgid "2K"
msgstr "2K"
-#: src/wx/film_editor.cc:171
+#: src/wx/dcp_panel.cc:555
msgid "3D"
msgstr "3D"
-#: src/wx/video_panel.cc:200
+#: src/wx/video_panel.cc:196
msgid "3D alternate"
msgstr "3D L/R sequentiell"
-#: src/wx/video_panel.cc:201
+#: src/wx/video_panel.cc:197
msgid "3D left only"
msgstr "3D nur links"
-#: src/wx/video_panel.cc:198
+#: src/wx/video_panel.cc:194
msgid "3D left/right"
msgstr "3D Links/Rechts"
-#: src/wx/video_panel.cc:202
+#: src/wx/video_panel.cc:198
msgid "3D right only"
msgstr "3D nur rechts"
-#: src/wx/video_panel.cc:199
+#: src/wx/video_panel.cc:195
msgid "3D top/bottom"
msgstr "3D Oben/Unten"
-#: src/wx/film_editor.cc:225
+#: src/wx/dcp_panel.cc:608
msgid "4K"
msgstr "4K"
msgid "A new version of DCP-o-matic is available."
msgstr "Es ist eine neue Version von DCP-o-matic verfügbar."
-#: src/wx/about_dialog.cc:30
+#: src/wx/about_dialog.cc:34
msgid "About DCP-o-matic"
msgstr "Ãœber DCP-o-matic"
msgid "Add Cinema..."
msgstr "Kino hinzufügen..."
+#: src/wx/content_menu.cc:57
+#, fuzzy
+msgid "Add KDM..."
+msgstr "Kino hinzufügen..."
+
#: src/wx/kdm_dialog.cc:86
msgid "Add Screen..."
msgstr "Saal hinzufügen..."
-#: src/wx/film_editor.cc:280
+#: src/wx/content_panel.cc:67
msgid "Add file(s)..."
msgstr "Datei(en) hinzufügen..."
-#: src/wx/film_editor.cc:282
+#: src/wx/content_panel.cc:69
msgid "Add folder..."
msgstr "Ordner hinzufügen..."
-#: src/wx/editable_list.h:62
+#: src/wx/config_dialog.cc:597 src/wx/editable_list.h:62
msgid "Add..."
msgstr "Hinzufügen..."
"tab."
msgstr ""
-#: src/wx/config_dialog.cc:799
+#: src/wx/config_dialog.cc:1141
msgid "Allow any DCP frame rate"
msgstr "Auch Nicht-Standard-Bildraten erlauben (Vorsicht!)"
-#: src/wx/about_dialog.cc:111
+#: src/wx/about_dialog.cc:115
msgid "Artwork by"
msgstr "Grafik von"
-#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:40
+#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:43 src/wx/dcp_panel.cc:86
msgid "Audio"
msgstr "Ton"
msgid "Audio Language (e.g. EN)"
msgstr "Ton Sprache (z.B. DE)"
-#: src/wx/film_editor.cc:166
-msgid "Audio channels"
-msgstr "Ton Kanäle"
-
-#: src/wx/audio_mapping_view.cc:350
+#: src/wx/audio_mapping_view.cc:358
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d unaltered."
msgstr ""
"Der Ton von Kanal %d wird ohne Veränderung an den DCP Kanal %d weitergegeben."
-#: src/wx/audio_mapping_view.cc:353
+#: src/wx/audio_mapping_view.cc:361
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d with gain "
"Der Ton von Kanal %d wird wird an den DCP Kanal %d mit %.1fdB Pegel "
"weitergegeben."
-#: src/wx/config_dialog.cc:683
+#: src/wx/config_dialog.cc:1022
msgid "BCC address"
msgstr "BCC: Adresse"
msgid "Browse..."
msgstr "Durchsuchen..."
-#: src/wx/audio_mapping_view.cc:317
+#: src/wx/audio_mapping_view.cc:325
msgid "BsL"
msgstr "BsL"
-#: src/wx/audio_mapping_view.cc:321
+#: src/wx/audio_mapping_view.cc:329
msgid "BsR"
msgstr "BsR"
+#: src/wx/dcp_panel.cc:551
+msgid "Burn subtitles into image"
+msgstr ""
+
#: src/wx/gain_calculator_dialog.cc:32
msgid "But I have to use fader"
msgstr "Aber ich nutze gegenwärtig Faderstellung"
-#: src/wx/audio_mapping_view.cc:285
+#: src/wx/audio_mapping_view.cc:293
msgid "C"
msgstr "C"
-#: src/wx/config_dialog.cc:679
+#: src/wx/config_dialog.cc:1018
msgid "CC address"
msgstr "CC: Adresse"
msgid "CPL annotation text"
msgstr "CPL annotation text"
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
msgid "Calculate..."
msgstr "Berechne..."
msgid "Certificate"
msgstr "Zertifikat"
+#: src/wx/config_dialog.cc:564
+msgid "Certificate chain for signing DCPs and KDMs:"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:191
#: src/wx/doremi_certificate_dialog.cc:103
msgid "Certificate downloaded"
msgstr "Zertifikat heruntergeladen"
+#: src/wx/config_dialog.cc:625
+msgid "Certificate for decrypting DCPs"
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:65
msgid "Chain"
msgstr "Kinokette"
msgid "Channel gain"
msgstr "Kanal Verstärkung (+/-)"
-#: src/wx/audio_dialog.cc:44
+#: src/wx/audio_dialog.cc:44 src/wx/dcp_panel.cc:623
msgid "Channels"
msgstr "Kanäle"
-#: src/wx/config_dialog.cc:120
+#: src/wx/config_dialog.cc:126
msgid "Check for testing updates as well as stable ones"
msgstr "Zeige bei Updateprüfung auch Test-Versionen an"
-#: src/wx/config_dialog.cc:116
+#: src/wx/config_dialog.cc:122
msgid "Check for updates on startup"
msgstr "Beim Start auf Updates überprüfen."
-#: src/wx/content_menu.cc:182
+#: src/wx/content_menu.cc:198
msgid "Choose a file"
msgstr "Datei auswählen"
-#: src/wx/film_editor.cc:810
+#: src/wx/content_panel.cc:241
msgid "Choose a file or files"
msgstr "Eine oder mehrere Dateien auswählen"
-#: src/wx/content_menu.cc:175 src/wx/film_editor.cc:833
+#: src/wx/content_menu.cc:191 src/wx/content_panel.cc:264
msgid "Choose a folder"
msgstr "Ordner wählen"
msgid "Cinema"
msgstr "Kino"
-#: src/wx/config_dialog.cc:498
+#: src/wx/config_dialog.cc:504
msgid "Colour Conversions"
msgstr "Farbumwandlungen"
#: src/wx/content_colour_conversion_dialog.cc:34
-#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:162
+#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:167
msgid "Colour conversion"
msgstr "Farbumwandlung"
-#: src/wx/config_dialog.cc:819
+#: src/wx/config_dialog.cc:1161
msgid "Config|Timing"
msgstr "Timing"
-#: src/wx/film_editor.cc:134
+#: src/wx/dcp_panel.cc:532
msgid "Container"
msgstr "Container"
-#: src/wx/audio_mapping_view.cc:270 src/wx/film_editor.cc:85
+#: src/wx/audio_mapping_view.cc:278 src/wx/film_editor.cc:83
msgid "Content"
msgstr "Inhalt"
-#: src/wx/film_editor.cc:139
+#: src/wx/dcp_panel.cc:77
msgid "Content Type"
msgstr "Inhalt Typ"
-#: src/wx/video_panel.cc:332
+#: src/wx/video_panel.cc:353
#, c-format
msgid "Content frame rate %.4f\n"
msgstr "Inhalt Bildrate %4f\n"
msgid "Content version"
msgstr "Inhalt Version"
-#: src/wx/video_panel.cc:292
+#: src/wx/video_panel.cc:313
#, c-format
msgid "Content video is %dx%d (%.2f:1)\n"
msgstr "Inhalt Video ist %dx%d (%.2f:1)\n"
msgid "Could not analyse audio."
msgstr "Ton konnte nicht analysiert werden"
-#: src/wx/film_viewer.cc:346
-#, c-format
-msgid "Could not decode video for view (%s)"
-msgstr "Bild konnte nicht zur Vorschau dekodiert werden (%s)"
+#: src/wx/content_panel.cc:280
+msgid "Could not find any images nor a DCP in that folder"
+msgstr ""
#: src/wx/job_wrapper.cc:39
#, c-format
msgid "Could not make DCP: %s"
msgstr "DCP konnte nicht erstellt werden: %s"
-#: src/wx/screen_dialog.cc:95
+#: src/wx/config_dialog.cc:688 src/wx/config_dialog.cc:783
+#: src/wx/config_dialog.cc:803 src/wx/screen_dialog.cc:95
#, c-format
msgid "Could not read certificate file (%s)"
msgstr "Konnte die Zertifikatsdatei (%s) nicht lesen."
+#: src/wx/config_dialog.cc:775 src/wx/config_dialog.cc:825
+#, fuzzy, c-format
+msgid "Could not read key file (%s)"
+msgstr "Konnte die Zertifikatsdatei (%s) nicht lesen."
+
#: src/wx/dolby_certificate_dialog.cc:39
msgid "Country"
msgstr "Land"
msgid "Create in folder"
msgstr "In Ordner erstellen"
-#: src/wx/video_panel.cc:304
+#: src/wx/video_panel.cc:325
#, c-format
msgid "Cropped to %dx%d (%.2f:1)\n"
msgstr "Beschnitten zu %dx%d(%.2f:1)\n"
-#: src/wx/video_panel.cc:244
+#: src/wx/video_panel.cc:243
msgid "Custom"
msgstr "Eigene"
-#: src/wx/film_editor.cc:87
+#: src/wx/film_editor.cc:85
msgid "DCP"
msgstr "DCP"
-#: src/wx/film_editor.cc:129
+#: src/wx/dcp_panel.cc:72
msgid "DCP Name"
msgstr "DCP Name"
msgid "DCP directory"
msgstr "DCP Verzeichnis"
-#: src/wx/about_dialog.cc:45 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
+#: src/wx/about_dialog.cc:49 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic audio - %s"
msgstr "DCP-o-matic Ton - %s"
-#: src/wx/config_dialog.cc:270
+#: src/wx/config_dialog.cc:276
msgid "Default ISDCF name details"
msgstr "Standard ISDCF Name Details"
-#: src/wx/config_dialog.cc:287
+#: src/wx/config_dialog.cc:293
msgid "Default JPEG2000 bandwidth"
msgstr "Standard JPEG2000 Datenrate"
-#: src/wx/config_dialog.cc:296
+#: src/wx/config_dialog.cc:302
msgid "Default audio delay"
msgstr "Standard Ton Verzögerung (+/-)"
-#: src/wx/config_dialog.cc:278
+#: src/wx/config_dialog.cc:284
msgid "Default container"
msgstr "Standard Container"
-#: src/wx/config_dialog.cc:282
+#: src/wx/config_dialog.cc:288
msgid "Default content type"
msgstr "Standard Inhalt Typ"
-#: src/wx/config_dialog.cc:262
+#: src/wx/config_dialog.cc:268
msgid "Default directory for new films"
msgstr "Standard Ordner für neue Projekte"
-#: src/wx/config_dialog.cc:254
+#: src/wx/config_dialog.cc:260
msgid "Default duration of still images"
msgstr "Standard Länge für Standbilder"
-#: src/wx/config_dialog.cc:304
+#: src/wx/config_dialog.cc:310
msgid "Default issuer"
msgstr "Standard 'issuer' (DCI)"
-#: src/wx/config_dialog.cc:274
+#: src/wx/config_dialog.cc:280
msgid "Default scale to"
msgstr ""
-#: src/wx/config_dialog.cc:235
+#: src/wx/config_dialog.cc:241
msgid "Defaults"
msgstr "Vorgaben"
-#: src/wx/audio_panel.cc:67
+#: src/wx/audio_panel.cc:70
msgid "Delay"
msgstr "Verzögerung (+/-)"
-#: src/wx/film_editor.cc:125 src/wx/job_manager_view.cc:79
+#: src/wx/dcp_panel.cc:68 src/wx/job_manager_view.cc:79
msgid "Details..."
msgstr "Details..."
msgid "Doremi serial numbers must have 6 digits"
msgstr "Doremi Seriennummern müssen aus 6 Zahlen bestehen!"
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
msgid "Down"
msgstr "Nach unten"
msgid "Edit Screen..."
msgstr "Saal bearbeiten..."
-#: src/wx/audio_mapping_view.cc:135 src/wx/config_dialog.cc:271
-#: src/wx/video_panel.cc:155 src/wx/video_panel.cc:172
+#: src/wx/audio_mapping_view.cc:143 src/wx/config_dialog.cc:277
+#: src/wx/video_panel.cc:163 src/wx/video_panel.cc:170
#: src/wx/editable_list.h:66
msgid "Edit..."
msgstr "Bearbeiten..."
msgid "Encoding Servers"
msgstr "Encoding Server"
-#: src/wx/film_editor.cc:162
+#: src/wx/dcp_panel.cc:92
msgid "Encrypted"
msgstr "Verschlüsselt (->für KDM Erstellung)"
-#: src/wx/config_dialog.cc:817
+#: src/wx/subtitle_view.cc:47
+msgid "End"
+msgstr ""
+
+#: src/wx/config_dialog.cc:1159
msgid "Errors"
msgstr "Fehler"
+#: src/wx/config_dialog.cc:651
+msgid "Export DCP decryption certificate..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:50
msgid "Facility (e.g. DLA)"
msgstr "Hersteller (z.B. DXL)"
+#: src/wx/video_panel.cc:133
+msgid "Fade in"
+msgstr ""
+
+#: src/wx/video_panel.cc:138
+msgid "Fade out"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:76
#: src/wx/dolby_certificate_dialog.cc:100
#: src/wx/dolby_certificate_dialog.cc:123
msgid "Film name"
msgstr "Projekt Name"
-#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:146
+#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:160
msgid "Filters"
msgstr "Filter"
-#: src/wx/content_menu.cc:52
+#: src/wx/content_menu.cc:55
msgid "Find missing..."
msgstr "Suche fehlende..."
-#: src/wx/film_editor.cc:145
+#: src/wx/dcp_panel.cc:538
msgid "Frame Rate"
msgstr "Bild Rate"
msgid "Frames already encoded"
msgstr "Bilder bereits bearbeitet"
-#: src/wx/about_dialog.cc:61
+#: src/wx/about_dialog.cc:65
msgid "Free, open-source DCP generation from almost anything."
msgstr ""
"Kostenlose Open-Source-Software zur DCP-Erstellung aus nahezu allen "
msgid "From"
msgstr "Von"
-#: src/wx/config_dialog.cc:675
+#: src/wx/config_dialog.cc:1014
msgid "From address"
msgstr "Absenderadresse"
-#: src/wx/audio_mapping_view.cc:133
+#: src/wx/audio_mapping_view.cc:141
msgid "Full"
msgstr "Voll"
msgid "Full length"
msgstr "Gesamtlänge"
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
msgid "Gain"
msgstr "Verstärkung (+/-)"
msgid "Gain for content channel %d in DCP channel %d"
msgstr "Abschwächung des Kanals %d im DCP Kanal %d"
-#: src/wx/properties_dialog.cc:52
+#: src/wx/properties_dialog.cc:51
msgid "Gb"
msgstr "Gb"
-#: src/wx/config_dialog.cc:813
+#: src/wx/config_dialog.cc:1155
msgid "General"
msgstr "Allgemein"
-#: src/wx/audio_mapping_view.cc:301
+#: src/wx/audio_mapping_view.cc:309
msgid "HI"
msgstr "HI"
msgid "Host name or IP address"
msgstr "Host Name oder IP-Adresse"
-#: src/wx/audio_panel.cc:242
-msgid "Hz"
-msgstr "Hz"
-
#: src/wx/gain_calculator_dialog.cc:29
msgid "I want to play this back at fader"
msgstr "Ich möchte bei dieser Faderstellung spielen"
-#: src/wx/config_dialog.cc:560
+#: src/wx/config_dialog.cc:899
msgid "IP address"
msgstr "IP Adresse"
-#: src/wx/config_dialog.cc:456
+#: src/wx/config_dialog.cc:462
msgid "IP address / host name"
msgstr "IP Adresse / Host Name"
msgid "Input gamma"
msgstr "Eingangs Gamma"
-#: src/wx/film_editor.cc:228
+#: src/wx/config_dialog.cc:727
+msgid "Intermediate"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:31
+msgid "Intermediate common name"
+msgstr ""
+
+#: src/wx/dcp_panel.cc:115
msgid "Interop"
msgstr "Interop"
-#: src/wx/film_editor.cc:181
+#: src/wx/dcp_panel.cc:565
msgid "JPEG2000 bandwidth"
msgstr "JPEG2000 Datenrate"
-#: src/wx/content_menu.cc:51
+#: src/wx/content_menu.cc:54
msgid "Join"
msgstr "Verbinden"
-#: src/wx/config_dialog.cc:627
+#: src/wx/config_dialog.cc:966
msgid "KDM Email"
msgstr "KDM Email"
msgid "KDM|Timing"
msgstr "Zeitfenster"
-#: src/wx/film_editor.cc:298
+#: src/wx/content_panel.cc:85
msgid "Keep video in sequence"
msgstr "Lücken in Zeitleiste automatisch schließen"
-#: src/wx/audio_mapping_view.cc:277
+#: src/wx/config_dialog.cc:548
+msgid "Keys"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:285
msgid "L"
msgstr "L"
-#: src/wx/audio_mapping_view.cc:309
+#: src/wx/audio_mapping_view.cc:317
msgid "Lc"
msgstr "Lc"
-#: src/wx/video_panel.cc:88
+#: src/wx/config_dialog.cc:725
+msgid "Leaf"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:33
+msgid "Leaf common name"
+msgstr ""
+
+#: src/wx/video_panel.cc:89
msgid "Left crop"
msgstr "Links beschneiden"
-#: src/wx/audio_mapping_view.cc:289
+#: src/wx/audio_mapping_view.cc:297
msgid "Lfe"
msgstr "LFE"
msgid "Load from file..."
msgstr "Lade aus Datei..."
-#: src/wx/config_dialog.cc:807
+#: src/wx/config_dialog.cc:620 src/wx/config_dialog.cc:633
+#: src/wx/config_dialog.cc:646
+#, fuzzy
+msgid "Load..."
+msgstr "Hinzufügen..."
+
+#: src/wx/config_dialog.cc:1149
msgid "Log"
msgstr "Log"
-#: src/wx/config_dialog.cc:804
+#: src/wx/config_dialog.cc:1146
msgid "Log:"
msgstr ""
-#: src/wx/audio_mapping_view.cc:293
+#: src/wx/audio_mapping_view.cc:301
msgid "Ls"
msgstr "SL"
-#: src/wx/film_editor.cc:784
+#: src/wx/content_panel.cc:438
msgid "MISSING: "
msgstr "FEHLT:"
-#: src/wx/config_dialog.cc:660
+#: src/wx/config_dialog.cc:999
msgid "Mail password"
msgstr "Mail Passwort"
-#: src/wx/config_dialog.cc:656
+#: src/wx/config_dialog.cc:995
msgid "Mail user name"
msgstr "Mail/SMTP Server Benutzername/Login"
msgid "Make KDMs"
msgstr "KDMs erstellen"
+#: src/wx/make_signer_chain_dialog.cc:23
+#, fuzzy
+msgid "Make certificate chain"
+msgstr "Zertifikat Datei auswählen"
+
#: src/wx/isdcf_metadata_dialog.cc:71
msgid "Mastered luminance (e.g. 4fl)"
msgstr "Zielhelligkeit (z.B. '4fL' für 3D)"
msgid "Matrix"
msgstr "Matrix"
-#: src/wx/config_dialog.cc:791
+#: src/wx/config_dialog.cc:1133
msgid "Maximum JPEG2000 bandwidth"
msgstr "Maximale JPEG2000 Datenrate (Vorsicht!)"
-#: src/wx/config_dialog.cc:291 src/wx/config_dialog.cc:795
-#: src/wx/film_editor.cc:185
+#: src/wx/config_dialog.cc:297 src/wx/config_dialog.cc:1137
+#: src/wx/dcp_panel.cc:569
msgid "Mbit/s"
msgstr "Mbit/s"
-#: src/wx/video_panel.cc:280
+#: src/wx/video_panel.cc:301
msgid "Multiple content selected"
msgstr "Mehrere Inhalte ausgewählt"
msgid "My Documents"
msgstr "Meine Dokumente"
-#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:512
-#: src/wx/film_editor.cc:113 src/wx/preset_colour_conversion_dialog.cc:38
+#: src/wx/content_panel.cc:442
+msgid "NEEDS KDM: "
+msgstr ""
+
+#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:518
+#: src/wx/dcp_panel.cc:56 src/wx/preset_colour_conversion_dialog.cc:38
#: src/wx/screen_dialog.cc:38
msgid "Name"
msgstr "Name"
msgid "New versions of DCP-o-matic are available."
msgstr "Eine neue Version von DCP-o-matic ist verfügbar."
-#: src/wx/audio_mapping_view.cc:348
+#: src/wx/audio_mapping_view.cc:356
#, c-format
msgid "No audio will be passed from content channel %d to DCP channel %d."
msgstr "Der Ton von Kanal %d wird nicht an das DCP Kanal %d weitergegeben."
-#: src/wx/video_panel.cc:153 src/wx/video_panel.cc:249
+#: src/wx/audio_panel.cc:290 src/wx/video_panel.cc:161
+#: src/wx/video_panel.cc:248
msgid "None"
msgstr "Kein"
-#: src/wx/audio_mapping_view.cc:132
+#: src/wx/audio_mapping_view.cc:140
msgid "Off"
msgstr "Aus"
+#: src/wx/config_dialog.cc:1167
+msgid "Open console window"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:25
+msgid "Organisation"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:27
+msgid "Organisational unit"
+msgstr ""
+
#: src/wx/screen_dialog.cc:65
msgid "Other"
msgstr "Andere"
-#: src/wx/config_dialog.cc:652
+#: src/wx/config_dialog.cc:991
msgid "Outgoing mail server"
msgstr "Ausgehender/SMTP Mail Server"
+#: src/wx/film_viewer.cc:61
+#, fuzzy
+msgid "Outline content"
+msgstr "Mehrere Inhalte ausgewählt"
+
#: src/wx/kdm_dialog.cc:156
msgid "Output"
msgstr "Ausgabe"
msgid "Package Type (e.g. OV)"
msgstr "DCP Paket Typ (z.B. OV)"
-#: src/wx/video_panel.cc:325
+#: src/wx/video_panel.cc:346
#, c-format
msgid "Padded with black to %dx%d (%.2f:1)\n"
msgstr "Mit Schwarz gefüllt auf %dx%d (%.2f:1)\n"
-#: src/wx/config_dialog.cc:572
+#: src/wx/config_dialog.cc:911
msgid "Password"
msgstr "Passwort"
msgid "Peak"
msgstr "Spitze"
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
msgid "Play"
msgstr "Abspielen"
msgid "Pre-release"
msgstr "Vorabversion"
-#: src/wx/audio_mapping_view.cc:281
+#: src/wx/config_dialog.cc:638
+msgid "Private key for decrypting DCPs"
+msgstr ""
+
+#: src/wx/config_dialog.cc:612
+msgid "Private key for leaf certificate"
+msgstr ""
+
+#: src/wx/audio_panel.cc:89
+msgid "Process with"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:289
msgid "R"
msgstr "R"
msgid "Rating (e.g. 15)"
msgstr "Freigabe (z.B. FSK12)"
-#: src/wx/audio_mapping_view.cc:313
+#: src/wx/audio_mapping_view.cc:321
msgid "Rc"
msgstr "Rc"
+#: src/wx/content_menu.cc:56
+msgid "Re-examine..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:608
+#, fuzzy
+msgid "Re-make certificates..."
+msgstr "Lade Zertifikat"
+
#: src/wx/isdcf_metadata_dialog.cc:62
msgid "Red band"
msgstr "Red band (USA, für explizite Inhalte)"
-#: src/wx/content_menu.cc:54 src/wx/film_editor.cc:284
-#: src/wx/editable_list.h:68
+#: src/wx/config_dialog.cc:599 src/wx/content_menu.cc:59
+#: src/wx/content_panel.cc:71 src/wx/editable_list.h:68
msgid "Remove"
msgstr "Entfernen"
msgid "Repeat Content"
msgstr "Inhalt wiederholen"
-#: src/wx/content_menu.cc:50
+#: src/wx/content_menu.cc:53
msgid "Repeat..."
msgstr "Wiederhole..."
-#: src/wx/config_dialog.cc:690
+#: src/wx/config_dialog.cc:1029
msgid "Reset to default text"
msgstr "Auf Standardtext zurücksetzen"
-#: src/wx/film_editor.cc:175
+#: src/wx/dcp_panel.cc:559
msgid "Resolution"
msgstr "Auflösung"
msgid "Resume"
msgstr "Weiter"
-#: src/wx/audio_mapping_view.cc:356
+#: src/wx/audio_mapping_view.cc:364
msgid "Right click to change gain."
msgstr "Rechtsklick für Pegeländerung"
msgid "Right crop"
msgstr "Rechts beschneiden"
-#: src/wx/audio_mapping_view.cc:297
+#: src/wx/config_dialog.cc:723
+msgid "Root"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:29
+msgid "Root common name"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:305
msgid "Rs"
msgstr "SR"
-#: src/wx/film_editor.cc:227
+#: src/wx/dcp_panel.cc:114
msgid "SMPTE"
msgstr "SMPTE"
-#: src/wx/video_panel.cc:132
+#: src/wx/video_panel.cc:143
msgid "Scale to"
msgstr "Skaliere auf"
-#: src/wx/video_panel.cc:316
+#: src/wx/video_panel.cc:337
#, c-format
msgid "Scaled to %dx%d (%.2f:1)\n"
msgstr "Skaliert auf %dx%d (%.2f:1)\n"
-#: src/wx/film_editor.cc:195
+#: src/wx/dcp_panel.cc:574
msgid "Scaler"
msgstr "Skalierverfahren"
msgid "Select CPL XML file"
msgstr "CPL XML Datei auswählen"
-#: src/wx/screen_dialog.cc:102
+#: src/wx/config_dialog.cc:679 src/wx/config_dialog.cc:795
+#: src/wx/config_dialog.cc:840 src/wx/screen_dialog.cc:102
msgid "Select Certificate File"
msgstr "Zertifikat Datei auswählen"
+#: src/wx/content_menu.cc:266
+msgid "Select KDM"
+msgstr ""
+
+#: src/wx/config_dialog.cc:769 src/wx/config_dialog.cc:817
+#, fuzzy
+msgid "Select Key File"
+msgstr "Zertifikat Datei auswählen"
+
#: src/wx/kdm_dialog.cc:185
msgid "Send by email"
msgstr "Per Email senden"
msgid "Server serial number"
msgstr "Server Seriennummer"
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
msgid "Servers"
msgstr "Encoding Server"
msgid "Set"
msgstr "Setzen"
-#: src/wx/config_dialog.cc:92
+#: src/wx/config_dialog.cc:99
msgid "Set language"
msgstr "Sprache setzen"
-#: src/wx/audio_panel.cc:48
+#: src/wx/audio_panel.cc:51
msgid "Show Audio..."
msgstr "Ton anzeigen..."
-#: src/wx/film_editor.cc:158
+#: src/wx/dcp_panel.cc:88
msgid "Signed"
msgstr "Signiert"
msgid "Smoothing"
msgstr "Glätten"
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
msgid "Snap"
msgstr "Auf Objekte einrasten"
msgid "Stable version "
msgstr "Stabile Version"
-#: src/wx/film_editor.cc:190
+#: src/wx/dcp_panel.cc:96
msgid "Standard"
msgstr "Standard"
-#: src/wx/audio_panel.cc:81 src/wx/subtitle_panel.cc:79
+#: src/wx/subtitle_view.cc:39
+msgid "Start"
+msgstr ""
+
+#: src/wx/audio_panel.cc:84 src/wx/subtitle_panel.cc:87
msgid "Stream"
msgstr "Spur"
msgid "Studio (e.g. TCF)"
msgstr "Studio (z.B. TCF)"
-#: src/wx/config_dialog.cc:671
+#: src/wx/config_dialog.cc:1010
msgid "Subject"
msgstr ""
+#: src/wx/subtitle_view.cc:55
+#, fuzzy
+msgid "Subtitle"
+msgstr "Untertitel"
+
#: src/wx/isdcf_metadata_dialog.cc:38
msgid "Subtitle Language (e.g. FR)"
msgstr "Untertitel Sprache (z.B. EN)"
-#: src/wx/subtitle_panel.cc:34
+#: src/wx/subtitle_panel.cc:41 src/wx/subtitle_view.cc:32
msgid "Subtitles"
msgstr "Untertitel"
-#: src/wx/about_dialog.cc:165
+#: src/wx/about_dialog.cc:168
msgid "Supported by"
msgstr "Unterstützt durch"
-#: src/wx/config_dialog.cc:542
+#: src/wx/config_dialog.cc:881
msgid "TMS"
msgstr "TMS"
-#: src/wx/config_dialog.cc:564
+#: src/wx/config_dialog.cc:903
msgid "Target path"
msgstr "Zielpfad"
msgid "Tested by"
msgstr "Getestet von"
-#: src/wx/content_menu.cc:223
+#: src/wx/content_menu.cc:252
msgid ""
"The content file(s) you specified are not the same as those that are "
"missing. Either try again with the correct content file or remove the "
msgid "There are no hints: everything looks good!"
msgstr "Keine Warnungen: Alles sieht gut aus!"
-#: src/wx/film_viewer.cc:134
+#: src/wx/film_viewer.cc:142
msgid "There is not enough free memory to do that."
msgstr "Für diese Operation ist nicht genug freier Speicher verfügbar."
msgid "Threads"
msgstr "Threads"
-#: src/wx/config_dialog.cc:111
+#: src/wx/config_dialog.cc:118
msgid "Threads to use for encoding on this host"
msgstr "Auf diesem Rechner zu benutzende CPU-Threads"
+#: src/wx/config_dialog.cc:583
+msgid "Thumbprint"
+msgstr ""
+
#: src/wx/audio_plot.cc:165
msgid "Time"
msgstr "Zeit"
-#: src/wx/timeline_dialog.cc:32
+#: src/wx/timeline_dialog.cc:33
msgid "Timeline"
msgstr "Zeitleiste"
-#: src/wx/film_editor.cc:290
+#: src/wx/content_panel.cc:77
msgid "Timeline..."
msgstr "Zeitleiste..."
msgid "Timing|Timing"
msgstr "Trimmen"
-#: src/wx/video_panel.cc:110
+#: src/wx/video_panel.cc:111
msgid "Top crop"
msgstr "Oben beschneiden"
-#: src/wx/about_dialog.cc:107
+#: src/wx/about_dialog.cc:111
msgid "Translated by"
msgstr "Ãœbersetzt von"
msgid "Trim from start"
msgstr "Schnitt vom Anfang"
-#: src/wx/audio_dialog.cc:55 src/wx/video_panel.cc:75
+#: src/wx/audio_dialog.cc:55 src/wx/config_dialog.cc:575
+#: src/wx/video_panel.cc:76
msgid "Type"
msgstr "Typ"
msgid "Until"
msgstr "Bis"
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
msgid "Up"
msgstr "Nach oben"
msgid "Update"
msgstr "Update"
-#: src/wx/film_editor.cc:123
+#: src/wx/dcp_panel.cc:66
msgid "Use ISDCF name"
msgstr "ISDCF Name benutzen"
-#: src/wx/config_dialog.cc:452
+#: src/wx/config_dialog.cc:458
msgid "Use all servers"
msgstr "Alle verfügbaren Server im Subnetz benutzen"
-#: src/wx/film_editor.cc:152
+#: src/wx/dcp_panel.cc:545
msgid "Use best"
msgstr "Beste benutzen"
msgid "Use preset"
msgstr "Preset benutzen"
-#: src/wx/config_dialog.cc:568
+#: src/wx/subtitle_panel.cc:47
+#, fuzzy
+msgid "Use subtitles"
+msgstr "Untertitel"
+
+#: src/wx/config_dialog.cc:907
msgid "User name"
msgstr "Benutzer Name"
-#: src/wx/audio_mapping_view.cc:305
+#: src/wx/audio_mapping_view.cc:313
msgid "VI"
msgstr "VI"
-#: src/wx/video_panel.cc:68
+#: src/wx/dcp_panel.cc:85 src/wx/video_panel.cc:69
msgid "Video"
msgstr "Bild"
msgid "Video frame rate"
msgstr "Bildwiederholrate"
-#: src/wx/config_dialog.cc:815
+#: src/wx/subtitle_panel.cc:91
+msgid "View..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:1157
msgid "Warnings"
msgstr "Warnungen"
-#: src/wx/subtitle_panel.cc:39
-msgid "With Subtitles"
-msgstr "Mit Untertitelung"
-
#: src/wx/kdm_dialog.cc:172
msgid "Write to"
msgstr "Speichern nach"
-#: src/wx/about_dialog.cc:91
+#: src/wx/about_dialog.cc:95
msgid "Written by"
msgstr "Geschrieben von"
-#: src/wx/subtitle_panel.cc:44
+#: src/wx/subtitle_panel.cc:52
msgid "X Offset"
msgstr "Horizontale Verschiebung"
-#: src/wx/subtitle_panel.cc:62
+#: src/wx/subtitle_panel.cc:70
#, fuzzy
msgid "X Scale"
msgstr "Größe"
-#: src/wx/subtitle_panel.cc:53
+#: src/wx/subtitle_panel.cc:61
msgid "Y Offset"
msgstr "Vertikale Verschiebung"
-#: src/wx/subtitle_panel.cc:71
+#: src/wx/subtitle_panel.cc:79
#, fuzzy
msgid "Y Scale"
msgstr "Größe"
"Ihr DCP hat weniger als 6 Audiokanäle. Das kann auf manchen Projektoren zu "
"Problemen führen."
-#: src/wx/timeline.cc:220
+#: src/wx/timeline.cc:229
msgid "audio"
msgstr "Ton"
-#: src/wx/audio_panel.cc:240
-msgid "channels"
-msgstr "Kanäle"
-
#: src/wx/properties_dialog.cc:46
msgid "counting..."
msgstr "zähle..."
-#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:62
+#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:65
msgid "dB"
msgstr "dB"
#. / TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
-#: src/wx/audio_panel.cc:78 src/wx/config_dialog.cc:300
+#: src/wx/audio_panel.cc:81 src/wx/config_dialog.cc:306
msgid "ms"
msgstr "ms"
-#: src/wx/config_dialog.cc:258
+#: src/wx/config_dialog.cc:264
msgid "s"
msgstr "s"
-#: src/wx/timeline.cc:243
+#: src/wx/timeline.cc:258
msgid "still"
msgstr "Standbild"
+#: src/wx/timeline.cc:289
+#, fuzzy
+msgid "subtitles"
+msgstr "Untertitel"
+
#: src/wx/repeat_dialog.cc:28
msgid "times"
msgstr "mal"
-#: src/wx/timeline.cc:241
+#: src/wx/timeline.cc:260
msgid "video"
msgstr "Bild"
+#~ msgid "1 channel"
+#~ msgstr "1 Kanal"
+
+#~ msgid "Audio channels"
+#~ msgstr "Ton Kanäle"
+
+#~ msgid "Could not decode video for view (%s)"
+#~ msgstr "Bild konnte nicht zur Vorschau dekodiert werden (%s)"
+
+#~ msgid "Hz"
+#~ msgstr "Hz"
+
+#~ msgid "With Subtitles"
+#~ msgstr "Mit Untertitelung"
+
+#~ msgid "channels"
+#~ msgstr "Kanäle"
+
#~ msgid "Default creator"
#~ msgstr "Standard 'creator' (DCI)"
msgstr ""
"Project-Id-Version: libdcpomatic-wx\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-10-16 16:38+0100\n"
+"POT-Creation-Date: 2014-10-15 09:37+0100\n"
"PO-Revision-Date: 2014-04-20 12:06-0500\n"
"Last-Translator: Manuel AC <manuel.acevedo@civantos.>\n"
"Language-Team: Manuel AC <manuel.acevedo@civantos.com>\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.4\n"
-#: src/wx/subtitle_panel.cc:48 src/wx/subtitle_panel.cc:57
-#: src/wx/subtitle_panel.cc:66 src/wx/subtitle_panel.cc:75
+#: src/wx/subtitle_panel.cc:56 src/wx/subtitle_panel.cc:65
+#: src/wx/subtitle_panel.cc:74 src/wx/subtitle_panel.cc:83
msgid "%"
msgstr "%"
-#: src/wx/about_dialog.cc:78
+#: src/wx/about_dialog.cc:82
msgid ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/wx/config_dialog.cc:664
+#: src/wx/config_dialog.cc:1003
msgid "(password will be stored on disk in plaintext)"
msgstr "(claves guardadas como texto plano en el disco)"
-#: src/wx/config_dialog.cc:104
+#: src/wx/config_dialog.cc:111
msgid "(restart DCP-o-matic to see language changes)"
msgstr "(reinicia DCP-o-matic para ver el cambio de idioma)"
-#: src/wx/audio_mapping_view.cc:134
+#: src/wx/audio_mapping_view.cc:142
msgid "-6dB"
msgstr ""
msgid "1 / "
msgstr "1 / "
-#: src/wx/audio_panel.cc:238
-msgid "1 channel"
-msgstr "1 canal"
-
-#: src/wx/video_panel.cc:197
+#: src/wx/video_panel.cc:193
msgid "2D"
msgstr "2D"
msgid "2D version of content available in 3D"
msgstr "Nuevas versiones disponibles de DCP-o-matic."
-#: src/wx/film_editor.cc:224
+#: src/wx/dcp_panel.cc:607
msgid "2K"
msgstr "2K"
-#: src/wx/film_editor.cc:171
+#: src/wx/dcp_panel.cc:555
msgid "3D"
msgstr "3D"
-#: src/wx/video_panel.cc:200
+#: src/wx/video_panel.cc:196
msgid "3D alternate"
msgstr "3D alterno"
-#: src/wx/video_panel.cc:201
+#: src/wx/video_panel.cc:197
msgid "3D left only"
msgstr ""
-#: src/wx/video_panel.cc:198
+#: src/wx/video_panel.cc:194
msgid "3D left/right"
msgstr "3D izquierda/derecha"
-#: src/wx/video_panel.cc:202
+#: src/wx/video_panel.cc:198
#, fuzzy
msgid "3D right only"
msgstr "3D izquierda/derecha"
-#: src/wx/video_panel.cc:199
+#: src/wx/video_panel.cc:195
msgid "3D top/bottom"
msgstr "3D arriba/abajo"
-#: src/wx/film_editor.cc:225
+#: src/wx/dcp_panel.cc:608
msgid "4K"
msgstr "4K"
msgid "A new version of DCP-o-matic is available."
msgstr "Una nueva versión de DCP-o-matic está disponible."
-#: src/wx/about_dialog.cc:30
+#: src/wx/about_dialog.cc:34
msgid "About DCP-o-matic"
msgstr "Acerca de DCP-o-matic"
msgid "Add Cinema..."
msgstr "Añadir cine..."
+#: src/wx/content_menu.cc:57
+#, fuzzy
+msgid "Add KDM..."
+msgstr "Añadir cine..."
+
#: src/wx/kdm_dialog.cc:86
msgid "Add Screen..."
msgstr "Añadir pantalla..."
-#: src/wx/film_editor.cc:280
+#: src/wx/content_panel.cc:67
msgid "Add file(s)..."
msgstr "Añadir fichero(s)..."
-#: src/wx/film_editor.cc:282
+#: src/wx/content_panel.cc:69
msgid "Add folder..."
msgstr "Añadir carpeta..."
-#: src/wx/editable_list.h:62
+#: src/wx/config_dialog.cc:597 src/wx/editable_list.h:62
msgid "Add..."
msgstr "Añadir..."
"tab."
msgstr ""
-#: src/wx/config_dialog.cc:799
+#: src/wx/config_dialog.cc:1141
#, fuzzy
msgid "Allow any DCP frame rate"
msgstr "Velocidad de imagen"
-#: src/wx/about_dialog.cc:111
+#: src/wx/about_dialog.cc:115
msgid "Artwork by"
msgstr "Grafismo de"
-#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:40
+#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:43 src/wx/dcp_panel.cc:86
msgid "Audio"
msgstr "Audio"
msgid "Audio Language (e.g. EN)"
msgstr "Idioma del audio (ej. ES)"
-#: src/wx/film_editor.cc:166
-msgid "Audio channels"
-msgstr "Canales de audio"
-
-#: src/wx/audio_mapping_view.cc:350
+#: src/wx/audio_mapping_view.cc:358
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d unaltered."
msgstr "El audio del canal %d pasará al canal %d del DCP sin modificar."
-#: src/wx/audio_mapping_view.cc:353
+#: src/wx/audio_mapping_view.cc:361
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d with gain "
"%.1fdB."
msgstr "El audio del canal %d pasará al canal %d del DCP con ganancia %.1fdB."
-#: src/wx/config_dialog.cc:683
+#: src/wx/config_dialog.cc:1022
#, fuzzy
msgid "BCC address"
msgstr "Dirección IP"
msgid "Browse..."
msgstr "Explorar..."
-#: src/wx/audio_mapping_view.cc:317
+#: src/wx/audio_mapping_view.cc:325
msgid "BsL"
msgstr "BsL"
-#: src/wx/audio_mapping_view.cc:321
+#: src/wx/audio_mapping_view.cc:329
msgid "BsR"
msgstr "BsR"
+#: src/wx/dcp_panel.cc:551
+msgid "Burn subtitles into image"
+msgstr ""
+
#: src/wx/gain_calculator_dialog.cc:32
msgid "But I have to use fader"
msgstr "pero tengo que usar el fader a"
-#: src/wx/audio_mapping_view.cc:285
+#: src/wx/audio_mapping_view.cc:293
msgid "C"
msgstr "C"
-#: src/wx/config_dialog.cc:679
+#: src/wx/config_dialog.cc:1018
#, fuzzy
msgid "CC address"
msgstr "Dirección IP"
msgid "CPL annotation text"
msgstr ""
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
msgid "Calculate..."
msgstr "Calcular..."
msgid "Certificate"
msgstr "Certificado"
+#: src/wx/config_dialog.cc:564
+msgid "Certificate chain for signing DCPs and KDMs:"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:191
#: src/wx/doremi_certificate_dialog.cc:103
msgid "Certificate downloaded"
msgstr "Certificado descargado"
+#: src/wx/config_dialog.cc:625
+msgid "Certificate for decrypting DCPs"
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:65
msgid "Chain"
msgstr ""
msgid "Channel gain"
msgstr "Ganancia del canal"
-#: src/wx/audio_dialog.cc:44
+#: src/wx/audio_dialog.cc:44 src/wx/dcp_panel.cc:623
msgid "Channels"
msgstr "Canales"
-#: src/wx/config_dialog.cc:120
+#: src/wx/config_dialog.cc:126
msgid "Check for testing updates as well as stable ones"
msgstr "Buscar actualizaciones de prueba y estables"
-#: src/wx/config_dialog.cc:116
+#: src/wx/config_dialog.cc:122
msgid "Check for updates on startup"
msgstr "Buscar actualizaciones al iniciar"
-#: src/wx/content_menu.cc:182
+#: src/wx/content_menu.cc:198
msgid "Choose a file"
msgstr "Elige un fichero"
-#: src/wx/film_editor.cc:810
+#: src/wx/content_panel.cc:241
msgid "Choose a file or files"
msgstr "Elegir un fichero o ficheros"
-#: src/wx/content_menu.cc:175 src/wx/film_editor.cc:833
+#: src/wx/content_menu.cc:191 src/wx/content_panel.cc:264
msgid "Choose a folder"
msgstr "Elige una carpeta"
msgid "Cinema"
msgstr "Cine"
-#: src/wx/config_dialog.cc:498
+#: src/wx/config_dialog.cc:504
msgid "Colour Conversions"
msgstr "Conversiones de color"
#: src/wx/content_colour_conversion_dialog.cc:34
-#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:162
+#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:167
msgid "Colour conversion"
msgstr "Conversión de color"
-#: src/wx/config_dialog.cc:819
+#: src/wx/config_dialog.cc:1161
#, fuzzy
msgid "Config|Timing"
msgstr "Tiempo"
-#: src/wx/film_editor.cc:134
+#: src/wx/dcp_panel.cc:532
msgid "Container"
msgstr "Continente"
-#: src/wx/audio_mapping_view.cc:270 src/wx/film_editor.cc:85
+#: src/wx/audio_mapping_view.cc:278 src/wx/film_editor.cc:83
msgid "Content"
msgstr "Contenido"
-#: src/wx/film_editor.cc:139
+#: src/wx/dcp_panel.cc:77
msgid "Content Type"
msgstr "Tipo de contenido"
-#: src/wx/video_panel.cc:332
+#: src/wx/video_panel.cc:353
#, c-format
msgid "Content frame rate %.4f\n"
msgstr "Velocidad del contenido %.4f\n"
msgid "Content version"
msgstr "Versión del contenido"
-#: src/wx/video_panel.cc:292
+#: src/wx/video_panel.cc:313
#, c-format
msgid "Content video is %dx%d (%.2f:1)\n"
msgstr "El video es %dx%d (%.2f:1)\n"
msgid "Could not analyse audio."
msgstr "No se pudo analizar el audio."
-#: src/wx/film_viewer.cc:346
-#, c-format
-msgid "Could not decode video for view (%s)"
-msgstr "No se pudo decodificar el vÃdeo para mostrarlo (%s)"
+#: src/wx/content_panel.cc:280
+msgid "Could not find any images nor a DCP in that folder"
+msgstr ""
#: src/wx/job_wrapper.cc:39
#, c-format
msgid "Could not make DCP: %s"
msgstr "No se pudo crear el DCP: %s"
-#: src/wx/screen_dialog.cc:95
+#: src/wx/config_dialog.cc:688 src/wx/config_dialog.cc:783
+#: src/wx/config_dialog.cc:803 src/wx/screen_dialog.cc:95
#, fuzzy, c-format
msgid "Could not read certificate file (%s)"
msgstr "No se pudo abrir el fichero (%s)"
+#: src/wx/config_dialog.cc:775 src/wx/config_dialog.cc:825
+#, fuzzy, c-format
+msgid "Could not read key file (%s)"
+msgstr "No se pudo abrir el fichero (%s)"
+
#: src/wx/dolby_certificate_dialog.cc:39
msgid "Country"
msgstr "PaÃs"
msgid "Create in folder"
msgstr "Crear en carpeta"
-#: src/wx/video_panel.cc:304
+#: src/wx/video_panel.cc:325
#, c-format
msgid "Cropped to %dx%d (%.2f:1)\n"
msgstr "Recortado a %dx%d (%.2f:1)\n"
-#: src/wx/video_panel.cc:244
+#: src/wx/video_panel.cc:243
msgid "Custom"
msgstr "Personalizado"
-#: src/wx/film_editor.cc:87
+#: src/wx/film_editor.cc:85
msgid "DCP"
msgstr "DCP"
-#: src/wx/film_editor.cc:129
+#: src/wx/dcp_panel.cc:72
msgid "DCP Name"
msgstr "Nombre DCP"
msgid "DCP directory"
msgstr ""
-#: src/wx/about_dialog.cc:45 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
+#: src/wx/about_dialog.cc:49 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic audio - %s"
msgstr "Audio DCP-o-matic - %s"
-#: src/wx/config_dialog.cc:270
+#: src/wx/config_dialog.cc:276
#, fuzzy
msgid "Default ISDCF name details"
msgstr "Detalles por defecto del nombre DCI"
-#: src/wx/config_dialog.cc:287
+#: src/wx/config_dialog.cc:293
msgid "Default JPEG2000 bandwidth"
msgstr "Ancho de banda JPEG2000 por defecto"
-#: src/wx/config_dialog.cc:296
+#: src/wx/config_dialog.cc:302
msgid "Default audio delay"
msgstr "Retardo de audio por defecto"
-#: src/wx/config_dialog.cc:278
+#: src/wx/config_dialog.cc:284
msgid "Default container"
msgstr "Contenedor por defecto"
-#: src/wx/config_dialog.cc:282
+#: src/wx/config_dialog.cc:288
msgid "Default content type"
msgstr "Tipo de contenido por defecto"
-#: src/wx/config_dialog.cc:262
+#: src/wx/config_dialog.cc:268
msgid "Default directory for new films"
msgstr "Carpeta por defecto para nuevas pelÃculas"
-#: src/wx/config_dialog.cc:254
+#: src/wx/config_dialog.cc:260
msgid "Default duration of still images"
msgstr "Duración por defecto de las imágenes fijas"
-#: src/wx/config_dialog.cc:304
+#: src/wx/config_dialog.cc:310
msgid "Default issuer"
msgstr "Emisor por defecto"
-#: src/wx/config_dialog.cc:274
+#: src/wx/config_dialog.cc:280
msgid "Default scale to"
msgstr ""
-#: src/wx/config_dialog.cc:235
+#: src/wx/config_dialog.cc:241
msgid "Defaults"
msgstr "Por defecto"
-#: src/wx/audio_panel.cc:67
+#: src/wx/audio_panel.cc:70
#, fuzzy
msgid "Delay"
msgstr "Retardo del audio"
-#: src/wx/film_editor.cc:125 src/wx/job_manager_view.cc:79
+#: src/wx/dcp_panel.cc:68 src/wx/job_manager_view.cc:79
msgid "Details..."
msgstr "Detalles..."
msgid "Doremi serial numbers must have 6 digits"
msgstr "Los números de serie de Doremi deben tener 6 cifras"
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
msgid "Down"
msgstr "Bajar"
msgid "Edit Screen..."
msgstr "Editar pantalla..."
-#: src/wx/audio_mapping_view.cc:135 src/wx/config_dialog.cc:271
-#: src/wx/video_panel.cc:155 src/wx/video_panel.cc:172
+#: src/wx/audio_mapping_view.cc:143 src/wx/config_dialog.cc:277
+#: src/wx/video_panel.cc:163 src/wx/video_panel.cc:170
#: src/wx/editable_list.h:66
msgid "Edit..."
msgstr "Editar..."
msgid "Encoding Servers"
msgstr "Servidores de codificación"
-#: src/wx/film_editor.cc:162
+#: src/wx/dcp_panel.cc:92
msgid "Encrypted"
msgstr "Encriptado"
-#: src/wx/config_dialog.cc:817
+#: src/wx/subtitle_view.cc:47
+msgid "End"
+msgstr "Fin"
+
+#: src/wx/config_dialog.cc:1159
msgid "Errors"
msgstr ""
+#: src/wx/config_dialog.cc:651
+msgid "Export DCP decryption certificate..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:50
msgid "Facility (e.g. DLA)"
msgstr "CompañÃa (ej. DLA)"
+#: src/wx/video_panel.cc:133
+msgid "Fade in"
+msgstr ""
+
+#: src/wx/video_panel.cc:138
+msgid "Fade out"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:76
#: src/wx/dolby_certificate_dialog.cc:100
#: src/wx/dolby_certificate_dialog.cc:123
msgid "Film name"
msgstr "Nombre de la pelÃcula"
-#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:146
+#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:160
msgid "Filters"
msgstr "Filtros"
-#: src/wx/content_menu.cc:52
+#: src/wx/content_menu.cc:55
msgid "Find missing..."
msgstr "Buscar ausentes..."
-#: src/wx/film_editor.cc:145
+#: src/wx/dcp_panel.cc:538
msgid "Frame Rate"
msgstr "Velocidad"
msgid "Frames already encoded"
msgstr "Fotogramas ya codificados"
-#: src/wx/about_dialog.cc:61
+#: src/wx/about_dialog.cc:65
msgid "Free, open-source DCP generation from almost anything."
msgstr ""
"Generación de DCP a partir de casi cualquier cosa, libre y de código abierto."
msgid "From"
msgstr "De"
-#: src/wx/config_dialog.cc:675
+#: src/wx/config_dialog.cc:1014
#, fuzzy
msgid "From address"
msgstr "Dirección IP"
-#: src/wx/audio_mapping_view.cc:133
+#: src/wx/audio_mapping_view.cc:141
msgid "Full"
msgstr "Completo"
msgid "Full length"
msgstr "Duración completa"
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
msgid "Gain"
msgstr ""
msgid "Gain for content channel %d in DCP channel %d"
msgstr "Ganancia del canal %d en el canal %d del DCP"
-#: src/wx/properties_dialog.cc:52
+#: src/wx/properties_dialog.cc:51
msgid "Gb"
msgstr "Gb"
-#: src/wx/config_dialog.cc:813
+#: src/wx/config_dialog.cc:1155
msgid "General"
msgstr ""
-#: src/wx/audio_mapping_view.cc:301
+#: src/wx/audio_mapping_view.cc:309
msgid "HI"
msgstr "HI"
msgid "Host name or IP address"
msgstr "Nombre o dirección IP"
-#: src/wx/audio_panel.cc:242
-msgid "Hz"
-msgstr "Hz"
-
#: src/wx/gain_calculator_dialog.cc:29
msgid "I want to play this back at fader"
msgstr "Quiero reproducir con el fader a"
-#: src/wx/config_dialog.cc:560
+#: src/wx/config_dialog.cc:899
msgid "IP address"
msgstr "Dirección IP"
-#: src/wx/config_dialog.cc:456
+#: src/wx/config_dialog.cc:462
msgid "IP address / host name"
msgstr "Dirección IP / nombre"
msgid "Input gamma"
msgstr "Gamma de entrada"
-#: src/wx/film_editor.cc:228
+#: src/wx/config_dialog.cc:727
+msgid "Intermediate"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:31
+msgid "Intermediate common name"
+msgstr ""
+
+#: src/wx/dcp_panel.cc:115
msgid "Interop"
msgstr "Interop"
-#: src/wx/film_editor.cc:181
+#: src/wx/dcp_panel.cc:565
msgid "JPEG2000 bandwidth"
msgstr "Ancho de banda JPEG2000"
-#: src/wx/content_menu.cc:51
+#: src/wx/content_menu.cc:54
msgid "Join"
msgstr "Unir"
-#: src/wx/config_dialog.cc:627
+#: src/wx/config_dialog.cc:966
msgid "KDM Email"
msgstr "Email KDM"
msgid "KDM|Timing"
msgstr "Tiempo"
-#: src/wx/film_editor.cc:298
+#: src/wx/content_panel.cc:85
msgid "Keep video in sequence"
msgstr "Mantener el video secuencia"
-#: src/wx/audio_mapping_view.cc:277
+#: src/wx/config_dialog.cc:548
+msgid "Keys"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:285
msgid "L"
msgstr "L"
-#: src/wx/audio_mapping_view.cc:309
+#: src/wx/audio_mapping_view.cc:317
msgid "Lc"
msgstr "Lc"
-#: src/wx/video_panel.cc:88
+#: src/wx/config_dialog.cc:725
+msgid "Leaf"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:33
+msgid "Leaf common name"
+msgstr ""
+
+#: src/wx/video_panel.cc:89
msgid "Left crop"
msgstr "Recorte izquierda"
-#: src/wx/audio_mapping_view.cc:289
+#: src/wx/audio_mapping_view.cc:297
msgid "Lfe"
msgstr "Lfe"
msgid "Load from file..."
msgstr "Cargar de fichero..."
-#: src/wx/config_dialog.cc:807
+#: src/wx/config_dialog.cc:620 src/wx/config_dialog.cc:633
+#: src/wx/config_dialog.cc:646
+#, fuzzy
+msgid "Load..."
+msgstr "Añadir..."
+
+#: src/wx/config_dialog.cc:1149
msgid "Log"
msgstr ""
-#: src/wx/config_dialog.cc:804
+#: src/wx/config_dialog.cc:1146
msgid "Log:"
msgstr ""
-#: src/wx/audio_mapping_view.cc:293
+#: src/wx/audio_mapping_view.cc:301
msgid "Ls"
msgstr "Ls"
-#: src/wx/film_editor.cc:784
+#: src/wx/content_panel.cc:438
msgid "MISSING: "
msgstr "FALTA:"
-#: src/wx/config_dialog.cc:660
+#: src/wx/config_dialog.cc:999
msgid "Mail password"
msgstr "Clave del correo"
-#: src/wx/config_dialog.cc:656
+#: src/wx/config_dialog.cc:995
msgid "Mail user name"
msgstr "Usuario del correo"
msgid "Make KDMs"
msgstr "Crear KDMs"
+#: src/wx/make_signer_chain_dialog.cc:23
+#, fuzzy
+msgid "Make certificate chain"
+msgstr "Seleccionar fichero de certificado"
+
#: src/wx/isdcf_metadata_dialog.cc:71
msgid "Mastered luminance (e.g. 4fl)"
msgstr ""
msgid "Matrix"
msgstr "Matriz"
-#: src/wx/config_dialog.cc:791
+#: src/wx/config_dialog.cc:1133
msgid "Maximum JPEG2000 bandwidth"
msgstr "Ancho de banda JPEG2000 máximo"
-#: src/wx/config_dialog.cc:291 src/wx/config_dialog.cc:795
-#: src/wx/film_editor.cc:185
+#: src/wx/config_dialog.cc:297 src/wx/config_dialog.cc:1137
+#: src/wx/dcp_panel.cc:569
msgid "Mbit/s"
msgstr "Mbit/s"
-#: src/wx/video_panel.cc:280
+#: src/wx/video_panel.cc:301
msgid "Multiple content selected"
msgstr "Varios contenidos seleccionados"
msgid "My Documents"
msgstr "Mis documentos"
-#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:512
-#: src/wx/film_editor.cc:113 src/wx/preset_colour_conversion_dialog.cc:38
+#: src/wx/content_panel.cc:442
+msgid "NEEDS KDM: "
+msgstr ""
+
+#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:518
+#: src/wx/dcp_panel.cc:56 src/wx/preset_colour_conversion_dialog.cc:38
#: src/wx/screen_dialog.cc:38
msgid "Name"
msgstr "Nombre"
msgid "New versions of DCP-o-matic are available."
msgstr "Nuevas versiones disponibles de DCP-o-matic."
-#: src/wx/audio_mapping_view.cc:348
+#: src/wx/audio_mapping_view.cc:356
#, c-format
msgid "No audio will be passed from content channel %d to DCP channel %d."
msgstr "No pasará audio del canal de origen %d al canal %d del DCP."
-#: src/wx/video_panel.cc:153 src/wx/video_panel.cc:249
+#: src/wx/audio_panel.cc:290 src/wx/video_panel.cc:161
+#: src/wx/video_panel.cc:248
msgid "None"
msgstr "Ninguno"
-#: src/wx/audio_mapping_view.cc:132
+#: src/wx/audio_mapping_view.cc:140
msgid "Off"
msgstr "Off"
+#: src/wx/config_dialog.cc:1167
+msgid "Open console window"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:25
+#, fuzzy
+msgid "Organisation"
+msgstr "Duración"
+
+#: src/wx/make_signer_chain_dialog.cc:27
+msgid "Organisational unit"
+msgstr ""
+
#: src/wx/screen_dialog.cc:65
msgid "Other"
msgstr "Otros"
-#: src/wx/config_dialog.cc:652
+#: src/wx/config_dialog.cc:991
msgid "Outgoing mail server"
msgstr "Servidor de salida de correo"
+#: src/wx/film_viewer.cc:61
+#, fuzzy
+msgid "Outline content"
+msgstr "Varios contenidos seleccionados"
+
#: src/wx/kdm_dialog.cc:156
#, fuzzy
msgid "Output"
msgid "Package Type (e.g. OV)"
msgstr "Tipo de paquete (ej. OV)"
-#: src/wx/video_panel.cc:325
+#: src/wx/video_panel.cc:346
#, c-format
msgid "Padded with black to %dx%d (%.2f:1)\n"
msgstr "Completado con negro hasta %dx%d (%.2f:1)\n"
-#: src/wx/config_dialog.cc:572
+#: src/wx/config_dialog.cc:911
msgid "Password"
msgstr "Clave"
msgid "Peak"
msgstr "Pico"
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
msgid "Play"
msgstr "Reproducir"
msgid "Pre-release"
msgstr ""
-#: src/wx/audio_mapping_view.cc:281
+#: src/wx/config_dialog.cc:638
+msgid "Private key for decrypting DCPs"
+msgstr ""
+
+#: src/wx/config_dialog.cc:612
+msgid "Private key for leaf certificate"
+msgstr ""
+
+#: src/wx/audio_panel.cc:89
+msgid "Process with"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:289
msgid "R"
msgstr "R"
msgid "Rating (e.g. 15)"
msgstr "Clasificación (ej. 16)"
-#: src/wx/audio_mapping_view.cc:313
+#: src/wx/audio_mapping_view.cc:321
msgid "Rc"
msgstr "Rc"
+#: src/wx/content_menu.cc:56
+msgid "Re-examine..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:608
+#, fuzzy
+msgid "Re-make certificates..."
+msgstr "Descargar certificado"
+
#: src/wx/isdcf_metadata_dialog.cc:62
msgid "Red band"
msgstr ""
-#: src/wx/content_menu.cc:54 src/wx/film_editor.cc:284
-#: src/wx/editable_list.h:68
+#: src/wx/config_dialog.cc:599 src/wx/content_menu.cc:59
+#: src/wx/content_panel.cc:71 src/wx/editable_list.h:68
msgid "Remove"
msgstr "Quitar"
msgid "Repeat Content"
msgstr "Repetir contenido"
-#: src/wx/content_menu.cc:50
+#: src/wx/content_menu.cc:53
msgid "Repeat..."
msgstr "Repetir..."
-#: src/wx/config_dialog.cc:690
+#: src/wx/config_dialog.cc:1029
msgid "Reset to default text"
msgstr ""
-#: src/wx/film_editor.cc:175
+#: src/wx/dcp_panel.cc:559
msgid "Resolution"
msgstr "Resolución"
msgid "Resume"
msgstr "Continuar"
-#: src/wx/audio_mapping_view.cc:356
+#: src/wx/audio_mapping_view.cc:364
msgid "Right click to change gain."
msgstr "Click derecho para cambiar la ganancia."
msgid "Right crop"
msgstr "Recorte derecha"
-#: src/wx/audio_mapping_view.cc:297
+#: src/wx/config_dialog.cc:723
+msgid "Root"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:29
+msgid "Root common name"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:305
msgid "Rs"
msgstr "Rs"
-#: src/wx/film_editor.cc:227
+#: src/wx/dcp_panel.cc:114
msgid "SMPTE"
msgstr "SMPTE"
-#: src/wx/video_panel.cc:132
+#: src/wx/video_panel.cc:143
msgid "Scale to"
msgstr "Redimensionar a"
-#: src/wx/video_panel.cc:316
+#: src/wx/video_panel.cc:337
#, c-format
msgid "Scaled to %dx%d (%.2f:1)\n"
msgstr "Redimensionado a %dx%d (%.2f:1)\n"
-#: src/wx/film_editor.cc:195
+#: src/wx/dcp_panel.cc:574
msgid "Scaler"
msgstr "Escalador"
msgid "Select CPL XML file"
msgstr "Seleccionar fichero de audio"
-#: src/wx/screen_dialog.cc:102
+#: src/wx/config_dialog.cc:679 src/wx/config_dialog.cc:795
+#: src/wx/config_dialog.cc:840 src/wx/screen_dialog.cc:102
msgid "Select Certificate File"
msgstr "Seleccionar fichero de certificado"
+#: src/wx/content_menu.cc:266
+msgid "Select KDM"
+msgstr ""
+
+#: src/wx/config_dialog.cc:769 src/wx/config_dialog.cc:817
+#, fuzzy
+msgid "Select Key File"
+msgstr "Seleccionar fichero de certificado"
+
#: src/wx/kdm_dialog.cc:185
msgid "Send by email"
msgstr "Enviar por email"
msgid "Server serial number"
msgstr "Número de serie del servidor"
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
msgid "Servers"
msgstr "Servidores"
msgid "Set"
msgstr "Seleccionar"
-#: src/wx/config_dialog.cc:92
+#: src/wx/config_dialog.cc:99
msgid "Set language"
msgstr "Seleccionar idioma"
-#: src/wx/audio_panel.cc:48
+#: src/wx/audio_panel.cc:51
msgid "Show Audio..."
msgstr "Mostrar audio..."
-#: src/wx/film_editor.cc:158
+#: src/wx/dcp_panel.cc:88
msgid "Signed"
msgstr "Firmado"
msgid "Smoothing"
msgstr "Suavizado"
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
msgid "Snap"
msgstr "Acoplar"
msgid "Stable version "
msgstr "Versión estable"
-#: src/wx/film_editor.cc:190
+#: src/wx/dcp_panel.cc:96
msgid "Standard"
msgstr "Estandard"
-#: src/wx/audio_panel.cc:81 src/wx/subtitle_panel.cc:79
+#: src/wx/subtitle_view.cc:39
+#, fuzzy
+msgid "Start"
+msgstr "Inicio"
+
+#: src/wx/audio_panel.cc:84 src/wx/subtitle_panel.cc:87
#, fuzzy
msgid "Stream"
msgstr "Flujo"
msgid "Studio (e.g. TCF)"
msgstr "Estudio (ej. TCF)"
-#: src/wx/config_dialog.cc:671
+#: src/wx/config_dialog.cc:1010
msgid "Subject"
msgstr ""
+#: src/wx/subtitle_view.cc:55
+#, fuzzy
+msgid "Subtitle"
+msgstr "SubtÃtulos"
+
#: src/wx/isdcf_metadata_dialog.cc:38
msgid "Subtitle Language (e.g. FR)"
msgstr "Idioma del subtÃtulo (ej. EN)"
-#: src/wx/subtitle_panel.cc:34
+#: src/wx/subtitle_panel.cc:41 src/wx/subtitle_view.cc:32
msgid "Subtitles"
msgstr "SubtÃtulos"
msgid "Supported by"
msgstr "Soportado por"
-#: src/wx/config_dialog.cc:542
+#: src/wx/config_dialog.cc:881
msgid "TMS"
msgstr "TMS"
-#: src/wx/config_dialog.cc:564
+#: src/wx/config_dialog.cc:903
msgid "Target path"
msgstr "Ruta"
msgid "Tested by"
msgstr "Comprobado por"
-#: src/wx/content_menu.cc:223
+#: src/wx/content_menu.cc:252
msgid ""
"The content file(s) you specified are not the same as those that are "
"missing. Either try again with the correct content file or remove the "
msgid "There are no hints: everything looks good!"
msgstr "No hay recomendaciones: ¡todo parece bien!"
-#: src/wx/film_viewer.cc:134
+#: src/wx/film_viewer.cc:142
msgid "There is not enough free memory to do that."
msgstr "No hay suficiente memoria para hacer eso."
msgid "Threads"
msgstr "Hilos"
-#: src/wx/config_dialog.cc:111
+#: src/wx/config_dialog.cc:118
msgid "Threads to use for encoding on this host"
msgstr "Hilos a utilizar para la codificación en esta máquina"
+#: src/wx/config_dialog.cc:583
+msgid "Thumbprint"
+msgstr ""
+
#: src/wx/audio_plot.cc:165
msgid "Time"
msgstr "Tiempo"
-#: src/wx/timeline_dialog.cc:32
+#: src/wx/timeline_dialog.cc:33
msgid "Timeline"
msgstr "LÃnea de tiempo"
-#: src/wx/film_editor.cc:290
+#: src/wx/content_panel.cc:77
msgid "Timeline..."
msgstr "Linea de tiempo..."
msgid "Timing|Timing"
msgstr "Tiempo"
-#: src/wx/video_panel.cc:110
+#: src/wx/video_panel.cc:111
msgid "Top crop"
msgstr "Recortar arriba"
-#: src/wx/about_dialog.cc:107
+#: src/wx/about_dialog.cc:111
msgid "Translated by"
msgstr "Traducido por"
msgid "Trim from start"
msgstr "Recortar del inicio"
-#: src/wx/audio_dialog.cc:55 src/wx/video_panel.cc:75
+#: src/wx/audio_dialog.cc:55 src/wx/config_dialog.cc:575
+#: src/wx/video_panel.cc:76
msgid "Type"
msgstr "Tipo"
msgid "Until"
msgstr "Hasta"
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
msgid "Up"
msgstr "Subir"
msgid "Update"
msgstr "Actualizar"
-#: src/wx/film_editor.cc:123
+#: src/wx/dcp_panel.cc:66
#, fuzzy
msgid "Use ISDCF name"
msgstr "Usar el nombre DCI"
-#: src/wx/config_dialog.cc:452
+#: src/wx/config_dialog.cc:458
msgid "Use all servers"
msgstr "Usar todos los servidores"
-#: src/wx/film_editor.cc:152
+#: src/wx/dcp_panel.cc:545
msgid "Use best"
msgstr "Usar la mejor"
msgid "Use preset"
msgstr "Usar por defecto"
-#: src/wx/config_dialog.cc:568
+#: src/wx/subtitle_panel.cc:47
+#, fuzzy
+msgid "Use subtitles"
+msgstr "SubtÃtulos"
+
+#: src/wx/config_dialog.cc:907
msgid "User name"
msgstr "Nombre de usuario"
-#: src/wx/audio_mapping_view.cc:305
+#: src/wx/audio_mapping_view.cc:313
msgid "VI"
msgstr "VI"
-#: src/wx/video_panel.cc:68
+#: src/wx/dcp_panel.cc:85 src/wx/video_panel.cc:69
msgid "Video"
msgstr "VÃdeo"
msgid "Video frame rate"
msgstr "Velocidad de imagen"
-#: src/wx/config_dialog.cc:815
-msgid "Warnings"
+#: src/wx/subtitle_panel.cc:91
+msgid "View..."
msgstr ""
-#: src/wx/subtitle_panel.cc:39
-msgid "With Subtitles"
-msgstr "Con subtÃtulos"
+#: src/wx/config_dialog.cc:1157
+msgid "Warnings"
+msgstr ""
#: src/wx/kdm_dialog.cc:172
msgid "Write to"
msgstr "Escribe a"
-#: src/wx/about_dialog.cc:91
+#: src/wx/about_dialog.cc:95
msgid "Written by"
msgstr "Escrito por"
-#: src/wx/subtitle_panel.cc:44
+#: src/wx/subtitle_panel.cc:52
#, fuzzy
msgid "X Offset"
msgstr "Desplazamiento en X"
-#: src/wx/subtitle_panel.cc:62
+#: src/wx/subtitle_panel.cc:70
#, fuzzy
msgid "X Scale"
msgstr "Escalador"
-#: src/wx/subtitle_panel.cc:53
+#: src/wx/subtitle_panel.cc:61
#, fuzzy
msgid "Y Offset"
msgstr "Desplazamiento en Y"
-#: src/wx/subtitle_panel.cc:71
+#: src/wx/subtitle_panel.cc:79
#, fuzzy
msgid "Y Scale"
msgstr "Escalador"
"Tu DCP tiene menos de 6 canales de audio. Esto puede causar problemas con "
"algunos proyectores."
-#: src/wx/timeline.cc:220
+#: src/wx/timeline.cc:229
msgid "audio"
msgstr "audio"
-#: src/wx/audio_panel.cc:240
-msgid "channels"
-msgstr "canales"
-
#: src/wx/properties_dialog.cc:46
msgid "counting..."
msgstr "contando..."
-#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:62
+#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:65
msgid "dB"
msgstr "dB"
#. / TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
-#: src/wx/audio_panel.cc:78 src/wx/config_dialog.cc:300
+#: src/wx/audio_panel.cc:81 src/wx/config_dialog.cc:306
msgid "ms"
msgstr "ms"
-#: src/wx/config_dialog.cc:258
+#: src/wx/config_dialog.cc:264
msgid "s"
msgstr "s"
-#: src/wx/timeline.cc:243
+#: src/wx/timeline.cc:258
msgid "still"
msgstr "fijo"
+#: src/wx/timeline.cc:289
+#, fuzzy
+msgid "subtitles"
+msgstr "SubtÃtulos"
+
#: src/wx/repeat_dialog.cc:28
msgid "times"
msgstr "veces"
-#: src/wx/timeline.cc:241
+#: src/wx/timeline.cc:260
msgid "video"
msgstr "vÃdeo"
+#~ msgid "1 channel"
+#~ msgstr "1 canal"
+
+#~ msgid "Audio channels"
+#~ msgstr "Canales de audio"
+
+#~ msgid "Could not decode video for view (%s)"
+#~ msgstr "No se pudo decodificar el vÃdeo para mostrarlo (%s)"
+
+#~ msgid "Hz"
+#~ msgstr "Hz"
+
+#~ msgid "With Subtitles"
+#~ msgstr "Con subtÃtulos"
+
+#~ msgid "channels"
+#~ msgstr "canales"
+
#~ msgid "Default creator"
#~ msgstr "Creador por defecto"
#~ msgid "Add"
#~ msgstr "Añadir"
-#~ msgid "Duration"
-#~ msgstr "Duración"
-
#~ msgid "Edit"
#~ msgstr "Editar"
#~ msgid "Running"
#~ msgstr "Ejecutando"
-#, fuzzy
-#~ msgid "Start time"
-#~ msgstr "Inicio"
-
#~ msgid "A/B"
#~ msgstr "A/B"
#~ msgid "DVD-o-matic Preferences"
#~ msgstr "Preferencias DVD-o-matic"
-#~ msgid "End"
-#~ msgstr "Fin"
-
#~ msgid "Film"
#~ msgstr "PelÃcula"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.6\n"
-#: src/wx/subtitle_panel.cc:48 src/wx/subtitle_panel.cc:57
-#: src/wx/subtitle_panel.cc:66 src/wx/subtitle_panel.cc:75
+#: src/wx/subtitle_panel.cc:56 src/wx/subtitle_panel.cc:65
+#: src/wx/subtitle_panel.cc:74 src/wx/subtitle_panel.cc:83
msgid "%"
msgstr "%"
-#: src/wx/about_dialog.cc:78
+#: src/wx/about_dialog.cc:82
msgid ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2014 par Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole "
"Laursen"
-#: src/wx/config_dialog.cc:664
+#: src/wx/config_dialog.cc:1003
msgid "(password will be stored on disk in plaintext)"
msgstr "(le mot de passe sera enregistré sur le disque dur au format texte)"
-#: src/wx/config_dialog.cc:104
+#: src/wx/config_dialog.cc:111
msgid "(restart DCP-o-matic to see language changes)"
msgstr "(redémarrez DCP-o-matic pour appliquer le changement de langue)"
-#: src/wx/audio_mapping_view.cc:134
+#: src/wx/audio_mapping_view.cc:142
msgid "-6dB"
msgstr "-6dB"
msgid "1 / "
msgstr "1/"
-#: src/wx/audio_panel.cc:238
-msgid "1 channel"
-msgstr "1 canal"
-
-#: src/wx/video_panel.cc:197
+#: src/wx/video_panel.cc:193
msgid "2D"
msgstr "2D"
msgid "2D version of content available in 3D"
msgstr "Version 2D d'un contenu aussi disponible en 3D"
-#: src/wx/film_editor.cc:224
+#: src/wx/dcp_panel.cc:607
msgid "2K"
msgstr "2K"
-#: src/wx/film_editor.cc:171
+#: src/wx/dcp_panel.cc:555
msgid "3D"
msgstr "3D"
-#: src/wx/video_panel.cc:200
+#: src/wx/video_panel.cc:196
msgid "3D alternate"
msgstr "3D alternatif"
-#: src/wx/video_panel.cc:201
+#: src/wx/video_panel.cc:197
msgid "3D left only"
msgstr "3D gauche"
-#: src/wx/video_panel.cc:198
+#: src/wx/video_panel.cc:194
msgid "3D left/right"
msgstr "3D gauche/droite"
-#: src/wx/video_panel.cc:202
+#: src/wx/video_panel.cc:198
msgid "3D right only"
msgstr "3D droite"
-#: src/wx/video_panel.cc:199
+#: src/wx/video_panel.cc:195
msgid "3D top/bottom"
msgstr "3D dessus/dessous"
-#: src/wx/film_editor.cc:225
+#: src/wx/dcp_panel.cc:608
msgid "4K"
msgstr "4K"
msgid "A new version of DCP-o-matic is available."
msgstr "Une nouvelle version de DCP-o-matic est disponible"
-#: src/wx/about_dialog.cc:30
+#: src/wx/about_dialog.cc:34
msgid "About DCP-o-matic"
msgstr "À propos de DCP-o-matic"
msgid "Add Cinema..."
msgstr "Ajout cinéma"
+#: src/wx/content_menu.cc:57
+#, fuzzy
+msgid "Add KDM..."
+msgstr "Ajout cinéma"
+
#: src/wx/kdm_dialog.cc:86
msgid "Add Screen..."
msgstr "Ajout une salle"
-#: src/wx/film_editor.cc:280
+#: src/wx/content_panel.cc:67
msgid "Add file(s)..."
msgstr "Ajout fichier(s)..."
-#: src/wx/film_editor.cc:282
+#: src/wx/content_panel.cc:69
msgid "Add folder..."
msgstr "Ajout dossier..."
-#: src/wx/editable_list.h:62
+#: src/wx/config_dialog.cc:597 src/wx/editable_list.h:62
msgid "Add..."
msgstr "Ajouter..."
"tab."
msgstr ""
-#: src/wx/config_dialog.cc:799
+#: src/wx/config_dialog.cc:1141
msgid "Allow any DCP frame rate"
msgstr "Autoriser toutes cadences"
-#: src/wx/about_dialog.cc:111
+#: src/wx/about_dialog.cc:115
msgid "Artwork by"
msgstr "Thème par"
-#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:40
+#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:43 src/wx/dcp_panel.cc:86
msgid "Audio"
msgstr "Audio"
msgid "Audio Language (e.g. EN)"
msgstr "Langue audio (ex. FR)"
-#: src/wx/film_editor.cc:166
-msgid "Audio channels"
-msgstr "Canaux audios"
-
-#: src/wx/audio_mapping_view.cc:350
+#: src/wx/audio_mapping_view.cc:358
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d unaltered."
msgstr ""
"Le son du canal audio %d sera transféré au canal %d du DCP sans modification."
-#: src/wx/audio_mapping_view.cc:353
+#: src/wx/audio_mapping_view.cc:361
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d with gain "
"Le son du canal audio %d sera transféré au canal %d du DCP avec un gain de "
"%.1fdB."
-#: src/wx/config_dialog.cc:683
+#: src/wx/config_dialog.cc:1022
#, fuzzy
msgid "BCC address"
msgstr "Adresse CC"
msgid "Browse..."
msgstr "Parcourir..."
-#: src/wx/audio_mapping_view.cc:317
+#: src/wx/audio_mapping_view.cc:325
msgid "BsL"
msgstr "Ar.G"
-#: src/wx/audio_mapping_view.cc:321
+#: src/wx/audio_mapping_view.cc:329
msgid "BsR"
msgstr "Ar.D"
+#: src/wx/dcp_panel.cc:551
+msgid "Burn subtitles into image"
+msgstr ""
+
#: src/wx/gain_calculator_dialog.cc:32
msgid "But I have to use fader"
msgstr "Mais je dois mixer"
-#: src/wx/audio_mapping_view.cc:285
+#: src/wx/audio_mapping_view.cc:293
msgid "C"
msgstr "C"
-#: src/wx/config_dialog.cc:679
+#: src/wx/config_dialog.cc:1018
msgid "CC address"
msgstr "Adresse CC"
msgid "CPL annotation text"
msgstr "Commentaire CPL"
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
msgid "Calculate..."
msgstr "Calcul..."
msgid "Certificate"
msgstr "Certificat"
+#: src/wx/config_dialog.cc:564
+msgid "Certificate chain for signing DCPs and KDMs:"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:191
#: src/wx/doremi_certificate_dialog.cc:103
msgid "Certificate downloaded"
msgstr "Certificat téléchargé"
+#: src/wx/config_dialog.cc:625
+msgid "Certificate for decrypting DCPs"
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:65
msgid "Chain"
msgstr "Chaîne"
msgid "Channel gain"
msgstr "Gain Canal"
-#: src/wx/audio_dialog.cc:44
+#: src/wx/audio_dialog.cc:44 src/wx/dcp_panel.cc:623
msgid "Channels"
msgstr "Canaux"
-#: src/wx/config_dialog.cc:120
+#: src/wx/config_dialog.cc:126
msgid "Check for testing updates as well as stable ones"
msgstr "Recherche toutes mises à jour : test et stables."
-#: src/wx/config_dialog.cc:116
+#: src/wx/config_dialog.cc:122
msgid "Check for updates on startup"
msgstr "Recherche de mises à jour au démarrage."
-#: src/wx/content_menu.cc:182
+#: src/wx/content_menu.cc:198
msgid "Choose a file"
msgstr "Choisissez un fichier"
-#: src/wx/film_editor.cc:810
+#: src/wx/content_panel.cc:241
msgid "Choose a file or files"
msgstr "Choisissez un ou plusieurs fichiers"
-#: src/wx/content_menu.cc:175 src/wx/film_editor.cc:833
+#: src/wx/content_menu.cc:191 src/wx/content_panel.cc:264
msgid "Choose a folder"
msgstr "Choisissez un dossier"
msgid "Cinema"
msgstr "Cinéma"
-#: src/wx/config_dialog.cc:498
+#: src/wx/config_dialog.cc:504
msgid "Colour Conversions"
msgstr "Conversions Couleurs"
#: src/wx/content_colour_conversion_dialog.cc:34
-#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:162
+#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:167
msgid "Colour conversion"
msgstr "Espace Couleurs"
-#: src/wx/config_dialog.cc:819
+#: src/wx/config_dialog.cc:1161
msgid "Config|Timing"
msgstr "Temps"
-#: src/wx/film_editor.cc:134
+#: src/wx/dcp_panel.cc:532
msgid "Container"
msgstr "Format"
-#: src/wx/audio_mapping_view.cc:270 src/wx/film_editor.cc:85
+#: src/wx/audio_mapping_view.cc:278 src/wx/film_editor.cc:83
msgid "Content"
msgstr "Contenu"
-#: src/wx/film_editor.cc:139
+#: src/wx/dcp_panel.cc:77
msgid "Content Type"
msgstr "Type de Contenu"
-#: src/wx/video_panel.cc:332
+#: src/wx/video_panel.cc:353
#, c-format
msgid "Content frame rate %.4f\n"
msgstr "Cadence du contenu %.4f\n"
msgid "Content version"
msgstr "Version du contenu"
-#: src/wx/video_panel.cc:292
+#: src/wx/video_panel.cc:313
#, c-format
msgid "Content video is %dx%d (%.2f:1)\n"
msgstr "Le contenu vidéo est %dx%d (%.2f:1)\n"
msgid "Could not analyse audio."
msgstr "Analyse du son impossible"
-#: src/wx/film_viewer.cc:346
-#, c-format
-msgid "Could not decode video for view (%s)"
-msgstr "Décodage de la vidéo pour visualisation impossible (%s)"
+#: src/wx/content_panel.cc:280
+msgid "Could not find any images nor a DCP in that folder"
+msgstr ""
#: src/wx/job_wrapper.cc:39
#, c-format
msgid "Could not make DCP: %s"
msgstr "Impossible de créer le DCP : %s"
-#: src/wx/screen_dialog.cc:95
+#: src/wx/config_dialog.cc:688 src/wx/config_dialog.cc:783
+#: src/wx/config_dialog.cc:803 src/wx/screen_dialog.cc:95
#, c-format
msgid "Could not read certificate file (%s)"
msgstr "Lecture du ficher de certificat (%s) impossible"
+#: src/wx/config_dialog.cc:775 src/wx/config_dialog.cc:825
+#, fuzzy, c-format
+msgid "Could not read key file (%s)"
+msgstr "Lecture du ficher de certificat (%s) impossible"
+
#: src/wx/dolby_certificate_dialog.cc:39
msgid "Country"
msgstr "Pays"
msgid "Create in folder"
msgstr "Créer dans le dossier"
-#: src/wx/video_panel.cc:304
+#: src/wx/video_panel.cc:325
#, c-format
msgid "Cropped to %dx%d (%.2f:1)\n"
msgstr "Rognage de %dx%d (%.2f:1)\n"
-#: src/wx/video_panel.cc:244
+#: src/wx/video_panel.cc:243
msgid "Custom"
msgstr "Personnalisé"
-#: src/wx/film_editor.cc:87
+#: src/wx/film_editor.cc:85
msgid "DCP"
msgstr "DCP"
-#: src/wx/film_editor.cc:129
+#: src/wx/dcp_panel.cc:72
msgid "DCP Name"
msgstr "Nom du DCP"
msgid "DCP directory"
msgstr "Répertoire du DCP"
-#: src/wx/about_dialog.cc:45 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
+#: src/wx/about_dialog.cc:49 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic audio - %s"
msgstr "Son DCP-o-matic - %s"
-#: src/wx/config_dialog.cc:270
+#: src/wx/config_dialog.cc:276
msgid "Default ISDCF name details"
msgstr "Nom ISDCF par défaut"
-#: src/wx/config_dialog.cc:287
+#: src/wx/config_dialog.cc:293
msgid "Default JPEG2000 bandwidth"
msgstr "Qualité JPEG2000 par défaut"
-#: src/wx/config_dialog.cc:296
+#: src/wx/config_dialog.cc:302
msgid "Default audio delay"
msgstr "Délai audio par défaut"
-#: src/wx/config_dialog.cc:278
+#: src/wx/config_dialog.cc:284
msgid "Default container"
msgstr "Format par défaut"
-#: src/wx/config_dialog.cc:282
+#: src/wx/config_dialog.cc:288
msgid "Default content type"
msgstr "Catégorie par défaut"
-#: src/wx/config_dialog.cc:262
+#: src/wx/config_dialog.cc:268
msgid "Default directory for new films"
msgstr "Dossier par défaut pour les DCP"
-#: src/wx/config_dialog.cc:254
+#: src/wx/config_dialog.cc:260
msgid "Default duration of still images"
msgstr "Durée par défaut des images fixes"
-#: src/wx/config_dialog.cc:304
+#: src/wx/config_dialog.cc:310
msgid "Default issuer"
msgstr "Labo par défaut"
-#: src/wx/config_dialog.cc:274
+#: src/wx/config_dialog.cc:280
msgid "Default scale to"
msgstr ""
-#: src/wx/config_dialog.cc:235
+#: src/wx/config_dialog.cc:241
msgid "Defaults"
msgstr "Par défaut"
-#: src/wx/audio_panel.cc:67
+#: src/wx/audio_panel.cc:70
msgid "Delay"
msgstr "Délai"
-#: src/wx/film_editor.cc:125 src/wx/job_manager_view.cc:79
+#: src/wx/dcp_panel.cc:68 src/wx/job_manager_view.cc:79
msgid "Details..."
msgstr "Détails..."
msgid "Doremi serial numbers must have 6 digits"
msgstr "Les numéros de série Doremi doivent être composés de 6 chiffres"
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
msgid "Down"
msgstr "Descendre"
msgid "Edit Screen..."
msgstr "Éditer la salle"
-#: src/wx/audio_mapping_view.cc:135 src/wx/config_dialog.cc:271
-#: src/wx/video_panel.cc:155 src/wx/video_panel.cc:172
+#: src/wx/audio_mapping_view.cc:143 src/wx/config_dialog.cc:277
+#: src/wx/video_panel.cc:163 src/wx/video_panel.cc:170
#: src/wx/editable_list.h:66
msgid "Edit..."
msgstr "Éditer..."
msgid "Encoding Servers"
msgstr "Serveurs Encodage"
-#: src/wx/film_editor.cc:162
+#: src/wx/dcp_panel.cc:92
msgid "Encrypted"
msgstr "Crypté"
-#: src/wx/config_dialog.cc:817
+#: src/wx/subtitle_view.cc:47
+msgid "End"
+msgstr "Fin"
+
+#: src/wx/config_dialog.cc:1159
msgid "Errors"
msgstr "Erreurs"
+#: src/wx/config_dialog.cc:651
+msgid "Export DCP decryption certificate..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:50
msgid "Facility (e.g. DLA)"
msgstr "Laboratoire (ex. DLA)"
+#: src/wx/video_panel.cc:133
+msgid "Fade in"
+msgstr ""
+
+#: src/wx/video_panel.cc:138
+msgid "Fade out"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:76
#: src/wx/dolby_certificate_dialog.cc:100
#: src/wx/dolby_certificate_dialog.cc:123
msgid "Film name"
msgstr "Nom du Film"
-#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:146
+#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:160
msgid "Filters"
msgstr "Filtres"
-#: src/wx/content_menu.cc:52
+#: src/wx/content_menu.cc:55
msgid "Find missing..."
msgstr "Recherche de l'élément manquant..."
-#: src/wx/film_editor.cc:145
+#: src/wx/dcp_panel.cc:538
msgid "Frame Rate"
msgstr "Cadence image"
msgid "Frames already encoded"
msgstr "Images déjà encodées"
-#: src/wx/about_dialog.cc:61
+#: src/wx/about_dialog.cc:65
msgid "Free, open-source DCP generation from almost anything."
msgstr "Création de DCP libre et gratuit depuis presque tout."
msgid "From"
msgstr "À partir du"
-#: src/wx/config_dialog.cc:675
+#: src/wx/config_dialog.cc:1014
msgid "From address"
msgstr "Adresse source"
-#: src/wx/audio_mapping_view.cc:133
+#: src/wx/audio_mapping_view.cc:141
msgid "Full"
msgstr "Plein"
msgid "Full length"
msgstr "Durée totale"
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
msgid "Gain"
msgstr "Gain"
msgid "Gain for content channel %d in DCP channel %d"
msgstr "Gain pour le canal audio %d dans le canal du DCP %d"
-#: src/wx/properties_dialog.cc:52
+#: src/wx/properties_dialog.cc:51
msgid "Gb"
msgstr "Gb"
-#: src/wx/config_dialog.cc:813
+#: src/wx/config_dialog.cc:1155
msgid "General"
msgstr "Général"
-#: src/wx/audio_mapping_view.cc:301
+#: src/wx/audio_mapping_view.cc:309
msgid "HI"
msgstr "HI"
msgid "Host name or IP address"
msgstr "Nom de l'hôte ou adresse IP"
-#: src/wx/audio_panel.cc:242
-msgid "Hz"
-msgstr "Hz"
-
#: src/wx/gain_calculator_dialog.cc:29
msgid "I want to play this back at fader"
msgstr "Je veux lire avec une table de mixage"
-#: src/wx/config_dialog.cc:560
+#: src/wx/config_dialog.cc:899
msgid "IP address"
msgstr "Adresse IP"
-#: src/wx/config_dialog.cc:456
+#: src/wx/config_dialog.cc:462
msgid "IP address / host name"
msgstr "Adresse IP / Nom d'Hôte"
msgid "Input gamma"
msgstr "gamma source"
-#: src/wx/film_editor.cc:228
+#: src/wx/config_dialog.cc:727
+msgid "Intermediate"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:31
+msgid "Intermediate common name"
+msgstr ""
+
+#: src/wx/dcp_panel.cc:115
msgid "Interop"
msgstr "MXF-Interop"
-#: src/wx/film_editor.cc:181
+#: src/wx/dcp_panel.cc:565
msgid "JPEG2000 bandwidth"
msgstr "Qualité JPEG2000"
-#: src/wx/content_menu.cc:51
+#: src/wx/content_menu.cc:54
msgid "Join"
msgstr "Ajouter"
-#: src/wx/config_dialog.cc:627
+#: src/wx/config_dialog.cc:966
msgid "KDM Email"
msgstr "e-mail KDM"
msgid "KDM|Timing"
msgstr "Temps"
-#: src/wx/film_editor.cc:298
+#: src/wx/content_panel.cc:85
msgid "Keep video in sequence"
msgstr "Conserver la vidéo dans la séquence"
-#: src/wx/audio_mapping_view.cc:277
+#: src/wx/config_dialog.cc:548
+msgid "Keys"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:285
msgid "L"
msgstr "G"
-#: src/wx/audio_mapping_view.cc:309
+#: src/wx/audio_mapping_view.cc:317
msgid "Lc"
msgstr "CG"
-#: src/wx/video_panel.cc:88
+#: src/wx/config_dialog.cc:725
+msgid "Leaf"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:33
+msgid "Leaf common name"
+msgstr ""
+
+#: src/wx/video_panel.cc:89
msgid "Left crop"
msgstr "Rogner à gauche"
-#: src/wx/audio_mapping_view.cc:289
+#: src/wx/audio_mapping_view.cc:297
msgid "Lfe"
msgstr "BF"
msgid "Load from file..."
msgstr "Chargement depuis fichier..."
-#: src/wx/config_dialog.cc:807
+#: src/wx/config_dialog.cc:620 src/wx/config_dialog.cc:633
+#: src/wx/config_dialog.cc:646
+#, fuzzy
+msgid "Load..."
+msgstr "Ajouter..."
+
+#: src/wx/config_dialog.cc:1149
msgid "Log"
msgstr "Rapport"
-#: src/wx/config_dialog.cc:804
+#: src/wx/config_dialog.cc:1146
msgid "Log:"
msgstr ""
-#: src/wx/audio_mapping_view.cc:293
+#: src/wx/audio_mapping_view.cc:301
msgid "Ls"
msgstr "Sr.G"
-#: src/wx/film_editor.cc:784
+#: src/wx/content_panel.cc:438
msgid "MISSING: "
msgstr "MANQUANT:"
-#: src/wx/config_dialog.cc:660
+#: src/wx/config_dialog.cc:999
msgid "Mail password"
msgstr "Mot de passe Mail"
-#: src/wx/config_dialog.cc:656
+#: src/wx/config_dialog.cc:995
msgid "Mail user name"
msgstr "Nom Utilisateur Mail"
msgid "Make KDMs"
msgstr "Générer KDMs"
+#: src/wx/make_signer_chain_dialog.cc:23
+#, fuzzy
+msgid "Make certificate chain"
+msgstr "Sélectionner le certificat"
+
#: src/wx/isdcf_metadata_dialog.cc:71
msgid "Mastered luminance (e.g. 4fl)"
msgstr "Luminance masterisée (ex: 4fl)"
msgid "Matrix"
msgstr "Matrice"
-#: src/wx/config_dialog.cc:791
+#: src/wx/config_dialog.cc:1133
msgid "Maximum JPEG2000 bandwidth"
msgstr "Qualité JPEG2000 Maxi"
-#: src/wx/config_dialog.cc:291 src/wx/config_dialog.cc:795
-#: src/wx/film_editor.cc:185
+#: src/wx/config_dialog.cc:297 src/wx/config_dialog.cc:1137
+#: src/wx/dcp_panel.cc:569
msgid "Mbit/s"
msgstr "Mbit/s"
-#: src/wx/video_panel.cc:280
+#: src/wx/video_panel.cc:301
msgid "Multiple content selected"
msgstr "Contenus multiples sélectionnés"
msgid "My Documents"
msgstr "Mes Documents"
-#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:512
-#: src/wx/film_editor.cc:113 src/wx/preset_colour_conversion_dialog.cc:38
+#: src/wx/content_panel.cc:442
+msgid "NEEDS KDM: "
+msgstr ""
+
+#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:518
+#: src/wx/dcp_panel.cc:56 src/wx/preset_colour_conversion_dialog.cc:38
#: src/wx/screen_dialog.cc:38
msgid "Name"
msgstr "Nom"
msgid "New versions of DCP-o-matic are available."
msgstr "De nouvelles versions de DCP-o-matic sont disponibles."
-#: src/wx/audio_mapping_view.cc:348
+#: src/wx/audio_mapping_view.cc:356
#, c-format
msgid "No audio will be passed from content channel %d to DCP channel %d."
msgstr "Aucun son ne passera du canal audio %d au canal %d du DCP."
-#: src/wx/video_panel.cc:153 src/wx/video_panel.cc:249
+#: src/wx/audio_panel.cc:290 src/wx/video_panel.cc:161
+#: src/wx/video_panel.cc:248
msgid "None"
msgstr "Aucun"
-#: src/wx/audio_mapping_view.cc:132
+#: src/wx/audio_mapping_view.cc:140
msgid "Off"
msgstr "Eteint"
+#: src/wx/config_dialog.cc:1167
+msgid "Open console window"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:25
+#, fuzzy
+msgid "Organisation"
+msgstr "Durée"
+
+#: src/wx/make_signer_chain_dialog.cc:27
+msgid "Organisational unit"
+msgstr ""
+
#: src/wx/screen_dialog.cc:65
msgid "Other"
msgstr "Autre"
-#: src/wx/config_dialog.cc:652
+#: src/wx/config_dialog.cc:991
msgid "Outgoing mail server"
msgstr "Serveurs de messagerie sortant"
+#: src/wx/film_viewer.cc:61
+#, fuzzy
+msgid "Outline content"
+msgstr "Contenus multiples sélectionnés"
+
#: src/wx/kdm_dialog.cc:156
msgid "Output"
msgstr "Sortie"
msgid "Package Type (e.g. OV)"
msgstr "Type de paquet (ex. OV)"
-#: src/wx/video_panel.cc:325
+#: src/wx/video_panel.cc:346
#, c-format
msgid "Padded with black to %dx%d (%.2f:1)\n"
msgstr "Enveloppe noire de %dx%d (%.2f:1)\n"
-#: src/wx/config_dialog.cc:572
+#: src/wx/config_dialog.cc:911
msgid "Password"
msgstr "Mot de passe"
msgid "Peak"
msgstr "Crête"
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
msgid "Play"
msgstr "Lecture"
msgid "Pre-release"
msgstr "Avant sortie"
-#: src/wx/audio_mapping_view.cc:281
+#: src/wx/config_dialog.cc:638
+msgid "Private key for decrypting DCPs"
+msgstr ""
+
+#: src/wx/config_dialog.cc:612
+msgid "Private key for leaf certificate"
+msgstr ""
+
+#: src/wx/audio_panel.cc:89
+msgid "Process with"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:289
msgid "R"
msgstr "D"
msgid "Rating (e.g. 15)"
msgstr "Rating (ex. 15)"
-#: src/wx/audio_mapping_view.cc:313
+#: src/wx/audio_mapping_view.cc:321
msgid "Rc"
msgstr "CD"
+#: src/wx/content_menu.cc:56
+msgid "Re-examine..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:608
+#, fuzzy
+msgid "Re-make certificates..."
+msgstr "Téléchargement Certificat"
+
#: src/wx/isdcf_metadata_dialog.cc:62
msgid "Red band"
msgstr "Red Band"
-#: src/wx/content_menu.cc:54 src/wx/film_editor.cc:284
-#: src/wx/editable_list.h:68
+#: src/wx/config_dialog.cc:599 src/wx/content_menu.cc:59
+#: src/wx/content_panel.cc:71 src/wx/editable_list.h:68
msgid "Remove"
msgstr "Supprimer"
msgid "Repeat Content"
msgstr "Répéter le contenu"
-#: src/wx/content_menu.cc:50
+#: src/wx/content_menu.cc:53
msgid "Repeat..."
msgstr "Répéter..."
-#: src/wx/config_dialog.cc:690
+#: src/wx/config_dialog.cc:1029
msgid "Reset to default text"
msgstr "texte par défaut"
-#: src/wx/film_editor.cc:175
+#: src/wx/dcp_panel.cc:559
msgid "Resolution"
msgstr "Résolution"
msgid "Resume"
msgstr "Reprendre"
-#: src/wx/audio_mapping_view.cc:356
+#: src/wx/audio_mapping_view.cc:364
msgid "Right click to change gain."
msgstr "Cliquez droit pour modifier le gain."
msgid "Right crop"
msgstr "Rogner à droite"
-#: src/wx/audio_mapping_view.cc:297
+#: src/wx/config_dialog.cc:723
+msgid "Root"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:29
+msgid "Root common name"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:305
msgid "Rs"
msgstr "Sr.D"
-#: src/wx/film_editor.cc:227
+#: src/wx/dcp_panel.cc:114
msgid "SMPTE"
msgstr "SMPTE"
-#: src/wx/video_panel.cc:132
+#: src/wx/video_panel.cc:143
msgid "Scale to"
msgstr "Mise à l'échelle"
-#: src/wx/video_panel.cc:316
+#: src/wx/video_panel.cc:337
#, c-format
msgid "Scaled to %dx%d (%.2f:1)\n"
msgstr "Mis à l'échelle de %dx%d (%.2f:1)\n"
-#: src/wx/film_editor.cc:195
+#: src/wx/dcp_panel.cc:574
msgid "Scaler"
msgstr "Mise à l'échelle"
msgid "Select CPL XML file"
msgstr "Sélectionner le fichier CPL.xml"
-#: src/wx/screen_dialog.cc:102
+#: src/wx/config_dialog.cc:679 src/wx/config_dialog.cc:795
+#: src/wx/config_dialog.cc:840 src/wx/screen_dialog.cc:102
msgid "Select Certificate File"
msgstr "Sélectionner le certificat"
+#: src/wx/content_menu.cc:266
+msgid "Select KDM"
+msgstr ""
+
+#: src/wx/config_dialog.cc:769 src/wx/config_dialog.cc:817
+#, fuzzy
+msgid "Select Key File"
+msgstr "Sélectionner le certificat"
+
#: src/wx/kdm_dialog.cc:185
msgid "Send by email"
msgstr "Envoyé par e-mail"
msgid "Server serial number"
msgstr "Numéro de Série du Serveur"
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
msgid "Servers"
msgstr "Serveurs"
msgid "Set"
msgstr "Sélection"
-#: src/wx/config_dialog.cc:92
+#: src/wx/config_dialog.cc:99
msgid "Set language"
msgstr "Sélectionnez la langue"
-#: src/wx/audio_panel.cc:48
+#: src/wx/audio_panel.cc:51
msgid "Show Audio..."
msgstr "Afficher le son..."
-#: src/wx/film_editor.cc:158
+#: src/wx/dcp_panel.cc:88
msgid "Signed"
msgstr "Signé"
msgid "Smoothing"
msgstr "Lissage"
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
msgid "Snap"
msgstr "Magnetisme"
msgid "Stable version "
msgstr "Version Stable"
-#: src/wx/film_editor.cc:190
+#: src/wx/dcp_panel.cc:96
msgid "Standard"
msgstr "Standard"
-#: src/wx/audio_panel.cc:81 src/wx/subtitle_panel.cc:79
+#: src/wx/subtitle_view.cc:39
+#, fuzzy
+msgid "Start"
+msgstr "Début"
+
+#: src/wx/audio_panel.cc:84 src/wx/subtitle_panel.cc:87
msgid "Stream"
msgstr "Flux"
msgid "Studio (e.g. TCF)"
msgstr "Studio (ex. TCF)"
-#: src/wx/config_dialog.cc:671
+#: src/wx/config_dialog.cc:1010
msgid "Subject"
msgstr ""
+#: src/wx/subtitle_view.cc:55
+#, fuzzy
+msgid "Subtitle"
+msgstr "Sous-titres"
+
#: src/wx/isdcf_metadata_dialog.cc:38
msgid "Subtitle Language (e.g. FR)"
msgstr "Langue de sous-titres (ex. FR)"
-#: src/wx/subtitle_panel.cc:34
+#: src/wx/subtitle_panel.cc:41 src/wx/subtitle_view.cc:32
msgid "Subtitles"
msgstr "Sous-titres"
msgid "Supported by"
msgstr "Soutenu par"
-#: src/wx/config_dialog.cc:542
+#: src/wx/config_dialog.cc:881
msgid "TMS"
msgstr "TMS"
-#: src/wx/config_dialog.cc:564
+#: src/wx/config_dialog.cc:903
msgid "Target path"
msgstr "Chemin cible"
msgid "Tested by"
msgstr "Testé par"
-#: src/wx/content_menu.cc:223
+#: src/wx/content_menu.cc:252
msgid ""
"The content file(s) you specified are not the same as those that are "
"missing. Either try again with the correct content file or remove the "
msgid "There are no hints: everything looks good!"
msgstr "Il n'y a aucun avertissement: tout semble correct!"
-#: src/wx/film_viewer.cc:134
+#: src/wx/film_viewer.cc:142
msgid "There is not enough free memory to do that."
msgstr "Il n'y a pas assez de mémoire pour faire cela."
msgid "Threads"
msgstr "Processus"
-#: src/wx/config_dialog.cc:111
+#: src/wx/config_dialog.cc:118
msgid "Threads to use for encoding on this host"
msgstr "Nombre de processus à utiliser sur cet hôte"
+#: src/wx/config_dialog.cc:583
+msgid "Thumbprint"
+msgstr ""
+
#: src/wx/audio_plot.cc:165
msgid "Time"
msgstr "Durée"
-#: src/wx/timeline_dialog.cc:32
+#: src/wx/timeline_dialog.cc:33
msgid "Timeline"
msgstr "Timeline"
-#: src/wx/film_editor.cc:290
+#: src/wx/content_panel.cc:77
msgid "Timeline..."
msgstr "Timeline..."
msgid "Timing|Timing"
msgstr "Temps"
-#: src/wx/video_panel.cc:110
+#: src/wx/video_panel.cc:111
msgid "Top crop"
msgstr "Rogner en haut"
-#: src/wx/about_dialog.cc:107
+#: src/wx/about_dialog.cc:111
msgid "Translated by"
msgstr "Traduit par"
msgid "Trim from start"
msgstr "Rogner au début"
-#: src/wx/audio_dialog.cc:55 src/wx/video_panel.cc:75
+#: src/wx/audio_dialog.cc:55 src/wx/config_dialog.cc:575
+#: src/wx/video_panel.cc:76
msgid "Type"
msgstr "Type"
msgid "Until"
msgstr "Jusqu'au"
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
msgid "Up"
msgstr "Monter"
msgid "Update"
msgstr "Mise à jour"
-#: src/wx/film_editor.cc:123
+#: src/wx/dcp_panel.cc:66
msgid "Use ISDCF name"
msgstr "Utiliser le nom ISDCF"
-#: src/wx/config_dialog.cc:452
+#: src/wx/config_dialog.cc:458
msgid "Use all servers"
msgstr "Utiliser tous les serveurs"
-#: src/wx/film_editor.cc:152
+#: src/wx/dcp_panel.cc:545
msgid "Use best"
msgstr "Automatique"
msgid "Use preset"
msgstr "Utiliser le préréglage"
-#: src/wx/config_dialog.cc:568
+#: src/wx/subtitle_panel.cc:47
+#, fuzzy
+msgid "Use subtitles"
+msgstr "Sous-titres"
+
+#: src/wx/config_dialog.cc:907
msgid "User name"
msgstr "Nom d'utilisateur"
-#: src/wx/audio_mapping_view.cc:305
+#: src/wx/audio_mapping_view.cc:313
msgid "VI"
msgstr "VI"
-#: src/wx/video_panel.cc:68
+#: src/wx/dcp_panel.cc:85 src/wx/video_panel.cc:69
msgid "Video"
msgstr "Vidéo"
msgid "Video frame rate"
msgstr "Cadence vidéo"
-#: src/wx/config_dialog.cc:815
+#: src/wx/subtitle_panel.cc:91
+msgid "View..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:1157
msgid "Warnings"
msgstr "Avertissements"
-#: src/wx/subtitle_panel.cc:39
-msgid "With Subtitles"
-msgstr "Avec sous-titres"
-
#: src/wx/kdm_dialog.cc:172
msgid "Write to"
msgstr "Ecrire à "
-#: src/wx/about_dialog.cc:91
+#: src/wx/about_dialog.cc:95
msgid "Written by"
msgstr "Développé par"
-#: src/wx/subtitle_panel.cc:44
+#: src/wx/subtitle_panel.cc:52
msgid "X Offset"
msgstr "Position horizontale"
-#: src/wx/subtitle_panel.cc:62
+#: src/wx/subtitle_panel.cc:70
#, fuzzy
msgid "X Scale"
msgstr "Mise à l'échelle"
-#: src/wx/subtitle_panel.cc:53
+#: src/wx/subtitle_panel.cc:61
msgid "Y Offset"
msgstr "Position verticale"
-#: src/wx/subtitle_panel.cc:71
+#: src/wx/subtitle_panel.cc:79
#, fuzzy
msgid "Y Scale"
msgstr "Mise à l'échelle"
"Votre DCP a moins de 6 canaux audio. Cela peut créer des problèmes de "
"lecture sur certains projecteurs."
-#: src/wx/timeline.cc:220
+#: src/wx/timeline.cc:229
msgid "audio"
msgstr "audio"
-#: src/wx/audio_panel.cc:240
-msgid "channels"
-msgstr "canaux"
-
#: src/wx/properties_dialog.cc:46
msgid "counting..."
msgstr "calcul..."
-#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:62
+#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:65
msgid "dB"
msgstr "dB"
#. / TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
-#: src/wx/audio_panel.cc:78 src/wx/config_dialog.cc:300
+#: src/wx/audio_panel.cc:81 src/wx/config_dialog.cc:306
msgid "ms"
msgstr "ms"
-#: src/wx/config_dialog.cc:258
+#: src/wx/config_dialog.cc:264
msgid "s"
msgstr "s"
-#: src/wx/timeline.cc:243
+#: src/wx/timeline.cc:258
msgid "still"
msgstr "fixe"
+#: src/wx/timeline.cc:289
+#, fuzzy
+msgid "subtitles"
+msgstr "Sous-titres"
+
#: src/wx/repeat_dialog.cc:28
msgid "times"
msgstr "fois"
-#: src/wx/timeline.cc:241
+#: src/wx/timeline.cc:260
msgid "video"
msgstr "vidéo"
+#~ msgid "1 channel"
+#~ msgstr "1 canal"
+
+#~ msgid "Audio channels"
+#~ msgstr "Canaux audios"
+
+#~ msgid "Could not decode video for view (%s)"
+#~ msgstr "Décodage de la vidéo pour visualisation impossible (%s)"
+
+#~ msgid "Hz"
+#~ msgstr "Hz"
+
+#~ msgid "With Subtitles"
+#~ msgstr "Avec sous-titres"
+
+#~ msgid "channels"
+#~ msgstr "canaux"
+
#~ msgid "Default creator"
#~ msgstr "Créateur par défaut"
#~ msgid "Add"
#~ msgstr "Ajouter"
-#~ msgid "Duration"
-#~ msgstr "Durée"
-
#~ msgid "Edit"
#~ msgstr "Édition"
#~ msgid "Running"
#~ msgstr "Progression"
-#~ msgid "Start time"
-#~ msgstr "Début"
-
#~ msgid "A/B"
#~ msgstr "A/B"
#~ msgid "DVD-o-matic Preferences"
#~ msgstr "Préférences de DCP-o-matic"
-#~ msgid "End"
-#~ msgstr "Fin"
-
#~ msgid "Film"
#~ msgstr "Film"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.3\n"
-#: src/wx/subtitle_panel.cc:48 src/wx/subtitle_panel.cc:57
-#: src/wx/subtitle_panel.cc:66 src/wx/subtitle_panel.cc:75
+#: src/wx/subtitle_panel.cc:56 src/wx/subtitle_panel.cc:65
+#: src/wx/subtitle_panel.cc:74 src/wx/subtitle_panel.cc:83
msgid "%"
msgstr "%"
-#: src/wx/about_dialog.cc:78
+#: src/wx/about_dialog.cc:82
msgid ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
-#: src/wx/config_dialog.cc:664
+#: src/wx/config_dialog.cc:1003
msgid "(password will be stored on disk in plaintext)"
msgstr ""
-#: src/wx/config_dialog.cc:104
+#: src/wx/config_dialog.cc:111
msgid "(restart DCP-o-matic to see language changes)"
msgstr "(riavviare DCP-o-matic per vedere i cambiamenti di lingua)"
-#: src/wx/audio_mapping_view.cc:134
+#: src/wx/audio_mapping_view.cc:142
msgid "-6dB"
msgstr ""
msgid "1 / "
msgstr ""
-#: src/wx/audio_panel.cc:238
-msgid "1 channel"
-msgstr "1 canale"
-
-#: src/wx/video_panel.cc:197
+#: src/wx/video_panel.cc:193
msgid "2D"
msgstr ""
msgid "2D version of content available in 3D"
msgstr "Una nuova versione di DCP-o-matic è disponibile."
-#: src/wx/film_editor.cc:224
+#: src/wx/dcp_panel.cc:607
msgid "2K"
msgstr ""
-#: src/wx/film_editor.cc:171
+#: src/wx/dcp_panel.cc:555
msgid "3D"
msgstr ""
-#: src/wx/video_panel.cc:200
+#: src/wx/video_panel.cc:196
msgid "3D alternate"
msgstr ""
-#: src/wx/video_panel.cc:201
+#: src/wx/video_panel.cc:197
msgid "3D left only"
msgstr ""
-#: src/wx/video_panel.cc:198
+#: src/wx/video_panel.cc:194
msgid "3D left/right"
msgstr ""
-#: src/wx/video_panel.cc:202
+#: src/wx/video_panel.cc:198
msgid "3D right only"
msgstr ""
-#: src/wx/video_panel.cc:199
+#: src/wx/video_panel.cc:195
msgid "3D top/bottom"
msgstr ""
-#: src/wx/film_editor.cc:225
+#: src/wx/dcp_panel.cc:608
msgid "4K"
msgstr ""
msgid "A new version of DCP-o-matic is available."
msgstr "Una nuova versione di DCP-o-matic è disponibile"
-#: src/wx/about_dialog.cc:30
+#: src/wx/about_dialog.cc:34
msgid "About DCP-o-matic"
msgstr "Su DCP-o-matic"
msgid "Add Cinema..."
msgstr "Aggiungi Cinema"
+#: src/wx/content_menu.cc:57
+#, fuzzy
+msgid "Add KDM..."
+msgstr "Aggiungi Cinema"
+
#: src/wx/kdm_dialog.cc:86
msgid "Add Screen..."
msgstr "Aggiungi Schermo"
-#: src/wx/film_editor.cc:280
+#: src/wx/content_panel.cc:67
msgid "Add file(s)..."
msgstr "Aggiungi File"
-#: src/wx/film_editor.cc:282
+#: src/wx/content_panel.cc:69
msgid "Add folder..."
msgstr "Aggiungi cartella"
-#: src/wx/editable_list.h:62
+#: src/wx/config_dialog.cc:597 src/wx/editable_list.h:62
msgid "Add..."
msgstr "Aggiungi..."
"tab."
msgstr ""
-#: src/wx/config_dialog.cc:799
+#: src/wx/config_dialog.cc:1141
#, fuzzy
msgid "Allow any DCP frame rate"
msgstr "Frequenza fotogrammi video"
-#: src/wx/about_dialog.cc:111
+#: src/wx/about_dialog.cc:115
msgid "Artwork by"
msgstr ""
-#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:40
+#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:43 src/wx/dcp_panel.cc:86
msgid "Audio"
msgstr "Audio"
msgid "Audio Language (e.g. EN)"
msgstr "Lingua dell'audio (es. EN)"
-#: src/wx/film_editor.cc:166
-msgid "Audio channels"
-msgstr "Canali audio"
-
-#: src/wx/audio_mapping_view.cc:350
+#: src/wx/audio_mapping_view.cc:358
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d unaltered."
msgstr ""
"L' audio sarà trasferito dal canale %d sorgente al canale %d DCP inalterato."
-#: src/wx/audio_mapping_view.cc:353
+#: src/wx/audio_mapping_view.cc:361
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d with gain "
"L' audio sarà trasferito dal canale %d sorgente al canale %d DCP con "
"guadagno di %.1fdB."
-#: src/wx/config_dialog.cc:683
+#: src/wx/config_dialog.cc:1022
#, fuzzy
msgid "BCC address"
msgstr "Indirizzo IP"
msgid "Browse..."
msgstr "Sfoglia..."
-#: src/wx/audio_mapping_view.cc:317
+#: src/wx/audio_mapping_view.cc:325
msgid "BsL"
msgstr "BsL"
-#: src/wx/audio_mapping_view.cc:321
+#: src/wx/audio_mapping_view.cc:329
msgid "BsR"
msgstr "BsR"
+#: src/wx/dcp_panel.cc:551
+msgid "Burn subtitles into image"
+msgstr ""
+
#: src/wx/gain_calculator_dialog.cc:32
msgid "But I have to use fader"
msgstr "Ma dovrò riprodurre con il fader a"
-#: src/wx/audio_mapping_view.cc:285
+#: src/wx/audio_mapping_view.cc:293
msgid "C"
msgstr "C"
-#: src/wx/config_dialog.cc:679
+#: src/wx/config_dialog.cc:1018
#, fuzzy
msgid "CC address"
msgstr "Indirizzo IP"
msgid "CPL annotation text"
msgstr ""
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
msgid "Calculate..."
msgstr "Calcola..."
msgid "Certificate"
msgstr "Seleziona il file del Certificato"
+#: src/wx/config_dialog.cc:564
+msgid "Certificate chain for signing DCPs and KDMs:"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:191
#: src/wx/doremi_certificate_dialog.cc:103
#, fuzzy
msgid "Certificate downloaded"
msgstr "Seleziona il file del Certificato"
+#: src/wx/config_dialog.cc:625
+msgid "Certificate for decrypting DCPs"
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:65
msgid "Chain"
msgstr ""
msgid "Channel gain"
msgstr "Guadagno audio"
-#: src/wx/audio_dialog.cc:44
+#: src/wx/audio_dialog.cc:44 src/wx/dcp_panel.cc:623
msgid "Channels"
msgstr "Canali"
-#: src/wx/config_dialog.cc:120
+#: src/wx/config_dialog.cc:126
msgid "Check for testing updates as well as stable ones"
msgstr "Controlla per aggiornamenti test o stabili"
-#: src/wx/config_dialog.cc:116
+#: src/wx/config_dialog.cc:122
msgid "Check for updates on startup"
msgstr "Controlla gli aggiornamentio alla partenza"
-#: src/wx/content_menu.cc:182
+#: src/wx/content_menu.cc:198
msgid "Choose a file"
msgstr "Scegli un file"
-#: src/wx/film_editor.cc:810
+#: src/wx/content_panel.cc:241
msgid "Choose a file or files"
msgstr "Scegli uno o più file"
-#: src/wx/content_menu.cc:175 src/wx/film_editor.cc:833
+#: src/wx/content_menu.cc:191 src/wx/content_panel.cc:264
msgid "Choose a folder"
msgstr "Scegli una cartella"
msgid "Cinema"
msgstr "Aggiungi Cinema"
-#: src/wx/config_dialog.cc:498
+#: src/wx/config_dialog.cc:504
#, fuzzy
msgid "Colour Conversions"
msgstr "Conversioni colore"
#: src/wx/content_colour_conversion_dialog.cc:34
-#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:162
+#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:167
msgid "Colour conversion"
msgstr "Conversione colore"
-#: src/wx/config_dialog.cc:819
+#: src/wx/config_dialog.cc:1161
msgid "Config|Timing"
msgstr ""
-#: src/wx/film_editor.cc:134
+#: src/wx/dcp_panel.cc:532
msgid "Container"
msgstr "Contenitore"
-#: src/wx/audio_mapping_view.cc:270 src/wx/film_editor.cc:85
+#: src/wx/audio_mapping_view.cc:278 src/wx/film_editor.cc:83
msgid "Content"
msgstr "Sorgente"
-#: src/wx/film_editor.cc:139
+#: src/wx/dcp_panel.cc:77
msgid "Content Type"
msgstr "Tipo di sorgente"
-#: src/wx/video_panel.cc:332
+#: src/wx/video_panel.cc:353
#, c-format
msgid "Content frame rate %.4f\n"
msgstr "Freq. fotogrammi sorgente %.4f\n"
msgid "Content version"
msgstr "Tipo di sorgente"
-#: src/wx/video_panel.cc:292
+#: src/wx/video_panel.cc:313
#, fuzzy, c-format
msgid "Content video is %dx%d (%.2f:1)\n"
msgstr "Il video originale è %dx%d (%.2f:1)\n"
msgid "Could not analyse audio."
msgstr "Non posso analizzare l'audio."
-#: src/wx/film_viewer.cc:346
-#, c-format
-msgid "Could not decode video for view (%s)"
-msgstr "Non posso decodificare il video per guardarlo (%s)"
+#: src/wx/content_panel.cc:280
+msgid "Could not find any images nor a DCP in that folder"
+msgstr ""
#: src/wx/job_wrapper.cc:39
#, c-format
msgid "Could not make DCP: %s"
msgstr "Non posso creare il DCP: %s"
-#: src/wx/screen_dialog.cc:95
+#: src/wx/config_dialog.cc:688 src/wx/config_dialog.cc:783
+#: src/wx/config_dialog.cc:803 src/wx/screen_dialog.cc:95
#, fuzzy, c-format
msgid "Could not read certificate file (%s)"
msgstr "Non posso aprire il file del contenuto (%s)"
+#: src/wx/config_dialog.cc:775 src/wx/config_dialog.cc:825
+#, fuzzy, c-format
+msgid "Could not read key file (%s)"
+msgstr "Non posso aprire il file del contenuto (%s)"
+
#: src/wx/dolby_certificate_dialog.cc:39
msgid "Country"
msgstr ""
msgid "Create in folder"
msgstr "Crea nella cartella"
-#: src/wx/video_panel.cc:304
+#: src/wx/video_panel.cc:325
#, c-format
msgid "Cropped to %dx%d (%.2f:1)\n"
msgstr "Tagliato da %dx%d (%.2f:1)\n"
-#: src/wx/video_panel.cc:244
+#: src/wx/video_panel.cc:243
msgid "Custom"
msgstr ""
-#: src/wx/film_editor.cc:87
+#: src/wx/film_editor.cc:85
msgid "DCP"
msgstr ""
-#: src/wx/film_editor.cc:129
+#: src/wx/dcp_panel.cc:72
msgid "DCP Name"
msgstr "Nome del DCP"
msgid "DCP directory"
msgstr ""
-#: src/wx/about_dialog.cc:45 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
+#: src/wx/about_dialog.cc:49 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic audio - %s"
msgstr "Audio DCP-o-matic - %s"
-#: src/wx/config_dialog.cc:270
+#: src/wx/config_dialog.cc:276
#, fuzzy
msgid "Default ISDCF name details"
msgstr "Dettagli del nome DCI predefinito"
-#: src/wx/config_dialog.cc:287
+#: src/wx/config_dialog.cc:293
#, fuzzy
msgid "Default JPEG2000 bandwidth"
msgstr "Banda passante JPEG2000"
-#: src/wx/config_dialog.cc:296
+#: src/wx/config_dialog.cc:302
msgid "Default audio delay"
msgstr "Ritardo audio predefinito"
-#: src/wx/config_dialog.cc:278
+#: src/wx/config_dialog.cc:284
msgid "Default container"
msgstr "Contenitore predefinito"
-#: src/wx/config_dialog.cc:282
+#: src/wx/config_dialog.cc:288
msgid "Default content type"
msgstr "Tipo sorgente predefinito"
-#: src/wx/config_dialog.cc:262
+#: src/wx/config_dialog.cc:268
msgid "Default directory for new films"
msgstr "Cartella predefinita per i nuovi films"
-#: src/wx/config_dialog.cc:254
+#: src/wx/config_dialog.cc:260
msgid "Default duration of still images"
msgstr "Durata predefinita immagini statiche"
-#: src/wx/config_dialog.cc:304
+#: src/wx/config_dialog.cc:310
#, fuzzy
msgid "Default issuer"
msgstr "Predefiniti"
-#: src/wx/config_dialog.cc:274
+#: src/wx/config_dialog.cc:280
msgid "Default scale to"
msgstr ""
-#: src/wx/config_dialog.cc:235
+#: src/wx/config_dialog.cc:241
msgid "Defaults"
msgstr "Predefiniti"
-#: src/wx/audio_panel.cc:67
+#: src/wx/audio_panel.cc:70
#, fuzzy
msgid "Delay"
msgstr "Ritardo dell'audio"
-#: src/wx/film_editor.cc:125 src/wx/job_manager_view.cc:79
+#: src/wx/dcp_panel.cc:68 src/wx/job_manager_view.cc:79
msgid "Details..."
msgstr "Dettagli"
msgid "Doremi serial numbers must have 6 digits"
msgstr ""
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
msgid "Down"
msgstr ""
msgid "Edit Screen..."
msgstr "Modifica Schermo..."
-#: src/wx/audio_mapping_view.cc:135 src/wx/config_dialog.cc:271
-#: src/wx/video_panel.cc:155 src/wx/video_panel.cc:172
+#: src/wx/audio_mapping_view.cc:143 src/wx/config_dialog.cc:277
+#: src/wx/video_panel.cc:163 src/wx/video_panel.cc:170
#: src/wx/editable_list.h:66
msgid "Edit..."
msgstr "Modifica..."
msgid "Encoding Servers"
msgstr "Servers di codifica"
-#: src/wx/film_editor.cc:162
+#: src/wx/dcp_panel.cc:92
msgid "Encrypted"
msgstr "Criptato"
-#: src/wx/config_dialog.cc:817
+#: src/wx/subtitle_view.cc:47
+msgid "End"
+msgstr "Fine"
+
+#: src/wx/config_dialog.cc:1159
msgid "Errors"
msgstr ""
+#: src/wx/config_dialog.cc:651
+msgid "Export DCP decryption certificate..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:50
msgid "Facility (e.g. DLA)"
msgstr "Facility (es. DLA)"
+#: src/wx/video_panel.cc:133
+msgid "Fade in"
+msgstr ""
+
+#: src/wx/video_panel.cc:138
+msgid "Fade out"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:76
#: src/wx/dolby_certificate_dialog.cc:100
#: src/wx/dolby_certificate_dialog.cc:123
msgid "Film name"
msgstr "Nome del film"
-#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:146
+#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:160
msgid "Filters"
msgstr "Filtri"
-#: src/wx/content_menu.cc:52
+#: src/wx/content_menu.cc:55
msgid "Find missing..."
msgstr "Trova mancante..."
-#: src/wx/film_editor.cc:145
+#: src/wx/dcp_panel.cc:538
msgid "Frame Rate"
msgstr "Frequenza fotogrammi"
msgid "Frames already encoded"
msgstr "Fotogrammi già codificati"
-#: src/wx/about_dialog.cc:61
+#: src/wx/about_dialog.cc:65
msgid "Free, open-source DCP generation from almost anything."
msgstr ""
msgid "From"
msgstr "Da"
-#: src/wx/config_dialog.cc:675
+#: src/wx/config_dialog.cc:1014
#, fuzzy
msgid "From address"
msgstr "Indirizzo IP"
-#: src/wx/audio_mapping_view.cc:133
+#: src/wx/audio_mapping_view.cc:141
msgid "Full"
msgstr ""
msgid "Full length"
msgstr ""
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
msgid "Gain"
msgstr ""
msgid "Gain for content channel %d in DCP channel %d"
msgstr "Guadagno per il canale sorgente %d nel canale DCP %d"
-#: src/wx/properties_dialog.cc:52
+#: src/wx/properties_dialog.cc:51
msgid "Gb"
msgstr "Gb"
-#: src/wx/config_dialog.cc:813
+#: src/wx/config_dialog.cc:1155
msgid "General"
msgstr ""
-#: src/wx/audio_mapping_view.cc:301
+#: src/wx/audio_mapping_view.cc:309
msgid "HI"
msgstr "HI"
msgid "Host name or IP address"
msgstr "Nome dell'Host o indirizzo IP"
-#: src/wx/audio_panel.cc:242
-msgid "Hz"
-msgstr "Hz"
-
#: src/wx/gain_calculator_dialog.cc:29
msgid "I want to play this back at fader"
msgstr "Sto usando il fader a"
-#: src/wx/config_dialog.cc:560
+#: src/wx/config_dialog.cc:899
msgid "IP address"
msgstr "Indirizzo IP"
-#: src/wx/config_dialog.cc:456
+#: src/wx/config_dialog.cc:462
#, fuzzy
msgid "IP address / host name"
msgstr "Indirizzo IP"
msgid "Input gamma"
msgstr ""
-#: src/wx/film_editor.cc:228
+#: src/wx/config_dialog.cc:727
+msgid "Intermediate"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:31
+msgid "Intermediate common name"
+msgstr ""
+
+#: src/wx/dcp_panel.cc:115
msgid "Interop"
msgstr ""
-#: src/wx/film_editor.cc:181
+#: src/wx/dcp_panel.cc:565
msgid "JPEG2000 bandwidth"
msgstr "Banda passante JPEG2000"
-#: src/wx/content_menu.cc:51
+#: src/wx/content_menu.cc:54
msgid "Join"
msgstr ""
-#: src/wx/config_dialog.cc:627
+#: src/wx/config_dialog.cc:966
msgid "KDM Email"
msgstr ""
msgid "KDM|Timing"
msgstr ""
-#: src/wx/film_editor.cc:298
+#: src/wx/content_panel.cc:85
msgid "Keep video in sequence"
msgstr "Tieni i video in sequenza"
-#: src/wx/audio_mapping_view.cc:277
+#: src/wx/config_dialog.cc:548
+msgid "Keys"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:285
msgid "L"
msgstr "L"
-#: src/wx/audio_mapping_view.cc:309
+#: src/wx/audio_mapping_view.cc:317
msgid "Lc"
msgstr "Lc"
-#: src/wx/video_panel.cc:88
+#: src/wx/config_dialog.cc:725
+msgid "Leaf"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:33
+msgid "Leaf common name"
+msgstr ""
+
+#: src/wx/video_panel.cc:89
msgid "Left crop"
msgstr "Taglio a sinistra"
-#: src/wx/audio_mapping_view.cc:289
+#: src/wx/audio_mapping_view.cc:297
msgid "Lfe"
msgstr "Lfe"
msgid "Load from file..."
msgstr ""
-#: src/wx/config_dialog.cc:807
+#: src/wx/config_dialog.cc:620 src/wx/config_dialog.cc:633
+#: src/wx/config_dialog.cc:646
+#, fuzzy
+msgid "Load..."
+msgstr "Aggiungi..."
+
+#: src/wx/config_dialog.cc:1149
msgid "Log"
msgstr ""
-#: src/wx/config_dialog.cc:804
+#: src/wx/config_dialog.cc:1146
msgid "Log:"
msgstr ""
-#: src/wx/audio_mapping_view.cc:293
+#: src/wx/audio_mapping_view.cc:301
msgid "Ls"
msgstr "Ls"
-#: src/wx/film_editor.cc:784
+#: src/wx/content_panel.cc:438
msgid "MISSING: "
msgstr "MANCANTE:"
-#: src/wx/config_dialog.cc:660
+#: src/wx/config_dialog.cc:999
#, fuzzy
msgid "Mail password"
msgstr "Password del TMS"
-#: src/wx/config_dialog.cc:656
+#: src/wx/config_dialog.cc:995
#, fuzzy
msgid "Mail user name"
msgstr "Nome utente del TMS"
msgid "Make KDMs"
msgstr "Crea KDM"
+#: src/wx/make_signer_chain_dialog.cc:23
+#, fuzzy
+msgid "Make certificate chain"
+msgstr "Seleziona il file del Certificato"
+
#: src/wx/isdcf_metadata_dialog.cc:71
msgid "Mastered luminance (e.g. 4fl)"
msgstr ""
msgid "Matrix"
msgstr "Matrice"
-#: src/wx/config_dialog.cc:791
+#: src/wx/config_dialog.cc:1133
#, fuzzy
msgid "Maximum JPEG2000 bandwidth"
msgstr "Banda passante JPEG2000"
-#: src/wx/config_dialog.cc:291 src/wx/config_dialog.cc:795
-#: src/wx/film_editor.cc:185
+#: src/wx/config_dialog.cc:297 src/wx/config_dialog.cc:1137
+#: src/wx/dcp_panel.cc:569
msgid "Mbit/s"
msgstr ""
-#: src/wx/video_panel.cc:280
+#: src/wx/video_panel.cc:301
msgid "Multiple content selected"
msgstr "Molteplici sorgenti selezionate"
msgid "My Documents"
msgstr "Documenti"
-#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:512
-#: src/wx/film_editor.cc:113 src/wx/preset_colour_conversion_dialog.cc:38
+#: src/wx/content_panel.cc:442
+msgid "NEEDS KDM: "
+msgstr ""
+
+#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:518
+#: src/wx/dcp_panel.cc:56 src/wx/preset_colour_conversion_dialog.cc:38
#: src/wx/screen_dialog.cc:38
msgid "Name"
msgstr "Nome"
msgid "New versions of DCP-o-matic are available."
msgstr "Una nuova versione di DCP-o-matic è disponibile."
-#: src/wx/audio_mapping_view.cc:348
+#: src/wx/audio_mapping_view.cc:356
#, c-format
msgid "No audio will be passed from content channel %d to DCP channel %d."
msgstr "Nessun audio sarà passato dal canale %d sorgente al canale %d del DCP"
-#: src/wx/video_panel.cc:153 src/wx/video_panel.cc:249
+#: src/wx/audio_panel.cc:290 src/wx/video_panel.cc:161
+#: src/wx/video_panel.cc:248
msgid "None"
msgstr "Nessuno"
-#: src/wx/audio_mapping_view.cc:132
+#: src/wx/audio_mapping_view.cc:140
msgid "Off"
msgstr ""
+#: src/wx/config_dialog.cc:1167
+msgid "Open console window"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:25
+#, fuzzy
+msgid "Organisation"
+msgstr "Durata"
+
+#: src/wx/make_signer_chain_dialog.cc:27
+msgid "Organisational unit"
+msgstr ""
+
#: src/wx/screen_dialog.cc:65
msgid "Other"
msgstr ""
-#: src/wx/config_dialog.cc:652
+#: src/wx/config_dialog.cc:991
msgid "Outgoing mail server"
msgstr "Mail server posta in uscita"
+#: src/wx/film_viewer.cc:61
+#, fuzzy
+msgid "Outline content"
+msgstr "Molteplici sorgenti selezionate"
+
#: src/wx/kdm_dialog.cc:156
#, fuzzy
msgid "Output"
msgid "Package Type (e.g. OV)"
msgstr "Tipo di Package (es. OV)"
-#: src/wx/video_panel.cc:325
+#: src/wx/video_panel.cc:346
#, c-format
msgid "Padded with black to %dx%d (%.2f:1)\n"
msgstr "Riempito con nero a %dx%d (%.2f:1)\n"
-#: src/wx/config_dialog.cc:572
+#: src/wx/config_dialog.cc:911
#, fuzzy
msgid "Password"
msgstr "Password del TMS"
msgid "Peak"
msgstr "Picco"
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
msgid "Play"
msgstr "Riproduci"
msgid "Pre-release"
msgstr ""
-#: src/wx/audio_mapping_view.cc:281
+#: src/wx/config_dialog.cc:638
+msgid "Private key for decrypting DCPs"
+msgstr ""
+
+#: src/wx/config_dialog.cc:612
+msgid "Private key for leaf certificate"
+msgstr ""
+
+#: src/wx/audio_panel.cc:89
+msgid "Process with"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:289
msgid "R"
msgstr "R"
msgid "Rating (e.g. 15)"
msgstr "Classificazione (es. 15)"
-#: src/wx/audio_mapping_view.cc:313
+#: src/wx/audio_mapping_view.cc:321
msgid "Rc"
msgstr "Rc"
+#: src/wx/content_menu.cc:56
+msgid "Re-examine..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:608
+msgid "Re-make certificates..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:62
msgid "Red band"
msgstr ""
-#: src/wx/content_menu.cc:54 src/wx/film_editor.cc:284
-#: src/wx/editable_list.h:68
+#: src/wx/config_dialog.cc:599 src/wx/content_menu.cc:59
+#: src/wx/content_panel.cc:71 src/wx/editable_list.h:68
msgid "Remove"
msgstr "Rimuovi"
msgid "Repeat Content"
msgstr "Ripeti il contenuto"
-#: src/wx/content_menu.cc:50
+#: src/wx/content_menu.cc:53
msgid "Repeat..."
msgstr ""
-#: src/wx/config_dialog.cc:690
+#: src/wx/config_dialog.cc:1029
msgid "Reset to default text"
msgstr ""
-#: src/wx/film_editor.cc:175
+#: src/wx/dcp_panel.cc:559
msgid "Resolution"
msgstr "Risoluzione"
msgid "Resume"
msgstr ""
-#: src/wx/audio_mapping_view.cc:356
+#: src/wx/audio_mapping_view.cc:364
msgid "Right click to change gain."
msgstr "Clicca il tasto destro per cambiare guadagno."
msgid "Right crop"
msgstr "Taglio a destra"
-#: src/wx/audio_mapping_view.cc:297
+#: src/wx/config_dialog.cc:723
+msgid "Root"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:29
+msgid "Root common name"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:305
msgid "Rs"
msgstr "Rs"
-#: src/wx/film_editor.cc:227
+#: src/wx/dcp_panel.cc:114
msgid "SMPTE"
msgstr ""
-#: src/wx/video_panel.cc:132
+#: src/wx/video_panel.cc:143
msgid "Scale to"
msgstr "Scala a"
-#: src/wx/video_panel.cc:316
+#: src/wx/video_panel.cc:337
#, c-format
msgid "Scaled to %dx%d (%.2f:1)\n"
msgstr "Scalato a %dx%d (%.2f:1)\n"
-#: src/wx/film_editor.cc:195
+#: src/wx/dcp_panel.cc:574
msgid "Scaler"
msgstr "Scaler"
msgid "Select CPL XML file"
msgstr "Seleziona file audio"
-#: src/wx/screen_dialog.cc:102
+#: src/wx/config_dialog.cc:679 src/wx/config_dialog.cc:795
+#: src/wx/config_dialog.cc:840 src/wx/screen_dialog.cc:102
msgid "Select Certificate File"
msgstr "Seleziona il file del Certificato"
+#: src/wx/content_menu.cc:266
+msgid "Select KDM"
+msgstr ""
+
+#: src/wx/config_dialog.cc:769 src/wx/config_dialog.cc:817
+#, fuzzy
+msgid "Select Key File"
+msgstr "Seleziona il file del Certificato"
+
#: src/wx/kdm_dialog.cc:185
msgid "Send by email"
msgstr "Manda per email"
msgid "Server serial number"
msgstr ""
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
#, fuzzy
msgid "Servers"
msgstr "Server"
msgid "Set"
msgstr ""
-#: src/wx/config_dialog.cc:92
+#: src/wx/config_dialog.cc:99
msgid "Set language"
msgstr "Seleziona la lingua"
-#: src/wx/audio_panel.cc:48
+#: src/wx/audio_panel.cc:51
msgid "Show Audio..."
msgstr "Mostra Audio..."
-#: src/wx/film_editor.cc:158
+#: src/wx/dcp_panel.cc:88
msgid "Signed"
msgstr ""
msgid "Smoothing"
msgstr "Levigatura"
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
msgid "Snap"
msgstr ""
msgid "Stable version "
msgstr "Versione stabile"
-#: src/wx/film_editor.cc:190
+#: src/wx/dcp_panel.cc:96
msgid "Standard"
msgstr ""
-#: src/wx/audio_panel.cc:81 src/wx/subtitle_panel.cc:79
+#: src/wx/subtitle_view.cc:39
+#, fuzzy
+msgid "Start"
+msgstr "Inizio"
+
+#: src/wx/audio_panel.cc:84 src/wx/subtitle_panel.cc:87
#, fuzzy
msgid "Stream"
msgstr "Traccia"
msgid "Studio (e.g. TCF)"
msgstr "Studio (es. TCF)"
-#: src/wx/config_dialog.cc:671
+#: src/wx/config_dialog.cc:1010
msgid "Subject"
msgstr ""
+#: src/wx/subtitle_view.cc:55
+#, fuzzy
+msgid "Subtitle"
+msgstr "Sottotitoli"
+
#: src/wx/isdcf_metadata_dialog.cc:38
msgid "Subtitle Language (e.g. FR)"
msgstr "Lingua dei Sottotitoli (es. FR)"
-#: src/wx/subtitle_panel.cc:34
+#: src/wx/subtitle_panel.cc:41 src/wx/subtitle_view.cc:32
msgid "Subtitles"
msgstr "Sottotitoli"
msgid "Supported by"
msgstr ""
-#: src/wx/config_dialog.cc:542
+#: src/wx/config_dialog.cc:881
msgid "TMS"
msgstr "TMS"
-#: src/wx/config_dialog.cc:564
+#: src/wx/config_dialog.cc:903
#, fuzzy
msgid "Target path"
msgstr "Percorso di destinazione del TMS"
msgid "Tested by"
msgstr ""
-#: src/wx/content_menu.cc:223
+#: src/wx/content_menu.cc:252
msgid ""
"The content file(s) you specified are not the same as those that are "
"missing. Either try again with the correct content file or remove the "
msgid "There are no hints: everything looks good!"
msgstr "Non ci sono suggerimenti: sembra tutto a posto!"
-#: src/wx/film_viewer.cc:134
+#: src/wx/film_viewer.cc:142
msgid "There is not enough free memory to do that."
msgstr ""
msgid "Threads"
msgstr "Threads"
-#: src/wx/config_dialog.cc:111
+#: src/wx/config_dialog.cc:118
msgid "Threads to use for encoding on this host"
msgstr "Threads da usare per codificare su questo host"
+#: src/wx/config_dialog.cc:583
+msgid "Thumbprint"
+msgstr ""
+
#: src/wx/audio_plot.cc:165
msgid "Time"
msgstr "Tempo"
-#: src/wx/timeline_dialog.cc:32
+#: src/wx/timeline_dialog.cc:33
msgid "Timeline"
msgstr "Timeline"
-#: src/wx/film_editor.cc:290
+#: src/wx/content_panel.cc:77
msgid "Timeline..."
msgstr ""
msgid "Timing|Timing"
msgstr ""
-#: src/wx/video_panel.cc:110
+#: src/wx/video_panel.cc:111
msgid "Top crop"
msgstr "Taglio in alto"
-#: src/wx/about_dialog.cc:107
+#: src/wx/about_dialog.cc:111
msgid "Translated by"
msgstr ""
msgid "Trim from start"
msgstr "Taglia dall'inizio"
-#: src/wx/audio_dialog.cc:55 src/wx/video_panel.cc:75
+#: src/wx/audio_dialog.cc:55 src/wx/config_dialog.cc:575
+#: src/wx/video_panel.cc:76
msgid "Type"
msgstr "Tipo"
msgid "Until"
msgstr "Fino a"
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
msgid "Up"
msgstr "Su"
msgid "Update"
msgstr "Aggiorna"
-#: src/wx/film_editor.cc:123
+#: src/wx/dcp_panel.cc:66
#, fuzzy
msgid "Use ISDCF name"
msgstr "Usa nome DCI"
-#: src/wx/config_dialog.cc:452
+#: src/wx/config_dialog.cc:458
msgid "Use all servers"
msgstr "Usa tutti i server"
-#: src/wx/film_editor.cc:152
+#: src/wx/dcp_panel.cc:545
msgid "Use best"
msgstr "Usa la migliore"
msgid "Use preset"
msgstr "Usa predefinito"
-#: src/wx/config_dialog.cc:568
+#: src/wx/subtitle_panel.cc:47
+#, fuzzy
+msgid "Use subtitles"
+msgstr "Sottotitoli"
+
+#: src/wx/config_dialog.cc:907
msgid "User name"
msgstr "Nome utente"
-#: src/wx/audio_mapping_view.cc:305
+#: src/wx/audio_mapping_view.cc:313
msgid "VI"
msgstr "VI"
-#: src/wx/video_panel.cc:68
+#: src/wx/dcp_panel.cc:85 src/wx/video_panel.cc:69
msgid "Video"
msgstr "Video"
msgid "Video frame rate"
msgstr "Frequenza fotogrammi video"
-#: src/wx/config_dialog.cc:815
-msgid "Warnings"
+#: src/wx/subtitle_panel.cc:91
+msgid "View..."
msgstr ""
-#: src/wx/subtitle_panel.cc:39
-msgid "With Subtitles"
-msgstr "Con sottotitoli"
+#: src/wx/config_dialog.cc:1157
+msgid "Warnings"
+msgstr ""
#: src/wx/kdm_dialog.cc:172
msgid "Write to"
msgstr ""
-#: src/wx/about_dialog.cc:91
+#: src/wx/about_dialog.cc:95
msgid "Written by"
msgstr ""
-#: src/wx/subtitle_panel.cc:44
+#: src/wx/subtitle_panel.cc:52
msgid "X Offset"
msgstr "Spostamento X"
-#: src/wx/subtitle_panel.cc:62
+#: src/wx/subtitle_panel.cc:70
#, fuzzy
msgid "X Scale"
msgstr "Scaler"
-#: src/wx/subtitle_panel.cc:53
+#: src/wx/subtitle_panel.cc:61
msgid "Y Offset"
msgstr "Spostamento Y"
-#: src/wx/subtitle_panel.cc:71
+#: src/wx/subtitle_panel.cc:79
#, fuzzy
msgid "Y Scale"
msgstr "Scaler"
"Il vostro DCP ha meno di 6 canali audio. Questo può causare problemi su "
"alcuni proiettori."
-#: src/wx/timeline.cc:220
+#: src/wx/timeline.cc:229
#, fuzzy
msgid "audio"
msgstr "Audio"
-#: src/wx/audio_panel.cc:240
-msgid "channels"
-msgstr "canali"
-
#: src/wx/properties_dialog.cc:46
msgid "counting..."
msgstr "conteggio..."
-#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:62
+#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:65
msgid "dB"
msgstr "dB"
#. / TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
-#: src/wx/audio_panel.cc:78 src/wx/config_dialog.cc:300
+#: src/wx/audio_panel.cc:81 src/wx/config_dialog.cc:306
msgid "ms"
msgstr "ms"
-#: src/wx/config_dialog.cc:258
+#: src/wx/config_dialog.cc:264
msgid "s"
msgstr "s"
-#: src/wx/timeline.cc:243
+#: src/wx/timeline.cc:258
msgid "still"
msgstr ""
+#: src/wx/timeline.cc:289
+#, fuzzy
+msgid "subtitles"
+msgstr "Sottotitoli"
+
#: src/wx/repeat_dialog.cc:28
msgid "times"
msgstr ""
-#: src/wx/timeline.cc:241
+#: src/wx/timeline.cc:260
#, fuzzy
msgid "video"
msgstr "Video"
+#~ msgid "1 channel"
+#~ msgstr "1 canale"
+
+#~ msgid "Audio channels"
+#~ msgstr "Canali audio"
+
+#~ msgid "Could not decode video for view (%s)"
+#~ msgstr "Non posso decodificare il video per guardarlo (%s)"
+
+#~ msgid "Hz"
+#~ msgstr "Hz"
+
+#~ msgid "With Subtitles"
+#~ msgstr "Con sottotitoli"
+
+#~ msgid "channels"
+#~ msgstr "canali"
+
#, fuzzy
#~ msgid "Default creator"
#~ msgstr "Contenitore predefinito"
#~ msgid "Add"
#~ msgstr "Aggiungi"
-#~ msgid "Duration"
-#~ msgstr "Durata"
-
#~ msgid "Edit"
#~ msgstr "Modifica"
#~ msgid "Running"
#~ msgstr "In corso"
-#, fuzzy
-#~ msgid "Start time"
-#~ msgstr "Inizio"
-
#~ msgid "A/B"
#~ msgstr "A/B"
#~ msgid "DVD-o-matic Preferences"
#~ msgstr "Preferenze DVD-o-matic"
-#~ msgid "End"
-#~ msgstr "Fine"
-
#~ msgid "Film"
#~ msgstr "Film"
"X-Generator: Poedit 1.6.9\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: src/wx/subtitle_panel.cc:48 src/wx/subtitle_panel.cc:57
-#: src/wx/subtitle_panel.cc:66 src/wx/subtitle_panel.cc:75
+#: src/wx/subtitle_panel.cc:56 src/wx/subtitle_panel.cc:65
+#: src/wx/subtitle_panel.cc:74 src/wx/subtitle_panel.cc:83
msgid "%"
msgstr "%"
-#: src/wx/about_dialog.cc:78
+#: src/wx/about_dialog.cc:82
msgid ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/wx/config_dialog.cc:664
+#: src/wx/config_dialog.cc:1003
msgid "(password will be stored on disk in plaintext)"
msgstr "(wachtwoord wordt opgeslagen op disk in leesbare tekst)"
-#: src/wx/config_dialog.cc:104
+#: src/wx/config_dialog.cc:111
msgid "(restart DCP-o-matic to see language changes)"
msgstr "(herstart DCP-o-matic voor taal wijziging)"
-#: src/wx/audio_mapping_view.cc:134
+#: src/wx/audio_mapping_view.cc:142
msgid "-6dB"
msgstr "-6dB"
msgid "1 / "
msgstr "1 / "
-#: src/wx/audio_panel.cc:238
-msgid "1 channel"
-msgstr "1 channel"
-
-#: src/wx/video_panel.cc:197
+#: src/wx/video_panel.cc:193
msgid "2D"
msgstr "2D"
msgid "2D version of content available in 3D"
msgstr "2D versie van 3D content beschikbaar"
-#: src/wx/film_editor.cc:224
+#: src/wx/dcp_panel.cc:607
msgid "2K"
msgstr "2K"
-#: src/wx/film_editor.cc:171
+#: src/wx/dcp_panel.cc:555
msgid "3D"
msgstr "3D"
-#: src/wx/video_panel.cc:200
+#: src/wx/video_panel.cc:196
msgid "3D alternate"
msgstr "3D alternate"
-#: src/wx/video_panel.cc:201
+#: src/wx/video_panel.cc:197
msgid "3D left only"
msgstr "3D enkel Links"
-#: src/wx/video_panel.cc:198
+#: src/wx/video_panel.cc:194
msgid "3D left/right"
msgstr "3D links/rechts"
-#: src/wx/video_panel.cc:202
+#: src/wx/video_panel.cc:198
msgid "3D right only"
msgstr "3D enkel rechts"
-#: src/wx/video_panel.cc:199
+#: src/wx/video_panel.cc:195
msgid "3D top/bottom"
msgstr "3D boven/beneden"
-#: src/wx/film_editor.cc:225
+#: src/wx/dcp_panel.cc:608
msgid "4K"
msgstr "4K"
msgid "A new version of DCP-o-matic is available."
msgstr "Een nieuwe versie van DCP-o-matic is beschikbaar."
-#: src/wx/about_dialog.cc:30
+#: src/wx/about_dialog.cc:34
msgid "About DCP-o-matic"
msgstr "Over DCP-o-matic"
msgid "Add Cinema..."
msgstr "Voeg Bioscoop toe..."
+#: src/wx/content_menu.cc:57
+#, fuzzy
+msgid "Add KDM..."
+msgstr "Voeg Bioscoop toe..."
+
#: src/wx/kdm_dialog.cc:86
msgid "Add Screen..."
msgstr "Voeg Scherm toe..."
-#: src/wx/film_editor.cc:280
+#: src/wx/content_panel.cc:67
msgid "Add file(s)..."
msgstr "Voeg bestande(n) toe..."
-#: src/wx/film_editor.cc:282
+#: src/wx/content_panel.cc:69
msgid "Add folder..."
msgstr "Voeg map toe..."
-#: src/wx/editable_list.h:62
+#: src/wx/config_dialog.cc:597 src/wx/editable_list.h:62
msgid "Add..."
msgstr "Toevoegen.."
"beeld zullen komen. U kan best de DCP's container instellen in Scope "
"(2.39:1) in de \"DCP\"tab."
-#: src/wx/config_dialog.cc:799
+#: src/wx/config_dialog.cc:1141
msgid "Allow any DCP frame rate"
msgstr "Sta om het welke frame rate toe in DCP"
-#: src/wx/about_dialog.cc:111
+#: src/wx/about_dialog.cc:115
msgid "Artwork by"
msgstr "Artwork door"
-#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:40
+#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:43 src/wx/dcp_panel.cc:86
msgid "Audio"
msgstr "Audio"
msgid "Audio Language (e.g. EN)"
msgstr "AudioTaal (e.g. NL)"
-#: src/wx/film_editor.cc:166
-msgid "Audio channels"
-msgstr "Audio kanalen"
-
-#: src/wx/audio_mapping_view.cc:350
+#: src/wx/audio_mapping_view.cc:358
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d unaltered."
"Audio van content kanaal %d wordt onveranderd doorgestuurd naar DCP kanaal "
"%d"
-#: src/wx/audio_mapping_view.cc:353
+#: src/wx/audio_mapping_view.cc:361
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d with gain "
"Audio wordt doorgestuurd van content kanaal %d naar DCP kanaal %d met gain "
"%1fdB."
-#: src/wx/config_dialog.cc:683
+#: src/wx/config_dialog.cc:1022
msgid "BCC address"
msgstr "BCC adres"
msgid "Browse..."
msgstr "Verkennen..."
-#: src/wx/audio_mapping_view.cc:317
+#: src/wx/audio_mapping_view.cc:325
msgid "BsL"
msgstr "BsL"
-#: src/wx/audio_mapping_view.cc:321
+#: src/wx/audio_mapping_view.cc:329
msgid "BsR"
msgstr "BsR"
+#: src/wx/dcp_panel.cc:551
+msgid "Burn subtitles into image"
+msgstr ""
+
#: src/wx/gain_calculator_dialog.cc:32
msgid "But I have to use fader"
msgstr "Maak ik gebruik een fader"
-#: src/wx/audio_mapping_view.cc:285
+#: src/wx/audio_mapping_view.cc:293
msgid "C"
msgstr "C"
-#: src/wx/config_dialog.cc:679
+#: src/wx/config_dialog.cc:1018
msgid "CC address"
msgstr "CC adres"
msgid "CPL annotation text"
msgstr "CPL opmerkingen tekst"
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
msgid "Calculate..."
msgstr "Bereken..."
msgid "Certificate"
msgstr "certificaat"
+#: src/wx/config_dialog.cc:564
+msgid "Certificate chain for signing DCPs and KDMs:"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:191
#: src/wx/doremi_certificate_dialog.cc:103
msgid "Certificate downloaded"
msgstr "Certificaat gedownload"
+#: src/wx/config_dialog.cc:625
+msgid "Certificate for decrypting DCPs"
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:65
msgid "Chain"
msgstr "Ketting"
msgid "Channel gain"
msgstr "Kanaal versterking"
-#: src/wx/audio_dialog.cc:44
+#: src/wx/audio_dialog.cc:44 src/wx/dcp_panel.cc:623
msgid "Channels"
msgstr "Kanalen"
-#: src/wx/config_dialog.cc:120
+#: src/wx/config_dialog.cc:126
msgid "Check for testing updates as well as stable ones"
msgstr "Selecteer voor Test updates en Stabiele versies "
-#: src/wx/config_dialog.cc:116
+#: src/wx/config_dialog.cc:122
msgid "Check for updates on startup"
msgstr "Selecteer voor updates controle bij startup"
-#: src/wx/content_menu.cc:182
+#: src/wx/content_menu.cc:198
msgid "Choose a file"
msgstr "Kies een bestand"
-#: src/wx/film_editor.cc:810
+#: src/wx/content_panel.cc:241
msgid "Choose a file or files"
msgstr "Kies bestand(en)"
-#: src/wx/content_menu.cc:175 src/wx/film_editor.cc:833
+#: src/wx/content_menu.cc:191 src/wx/content_panel.cc:264
msgid "Choose a folder"
msgstr "Kies map"
msgid "Cinema"
msgstr "Bioscoop"
-#: src/wx/config_dialog.cc:498
+#: src/wx/config_dialog.cc:504
msgid "Colour Conversions"
msgstr "Colour Conversions"
#: src/wx/content_colour_conversion_dialog.cc:34
-#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:162
+#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:167
msgid "Colour conversion"
msgstr "Colour conversion"
-#: src/wx/config_dialog.cc:819
+#: src/wx/config_dialog.cc:1161
msgid "Config|Timing"
msgstr "Configureer|Timing"
-#: src/wx/film_editor.cc:134
+#: src/wx/dcp_panel.cc:532
msgid "Container"
msgstr "Container"
-#: src/wx/audio_mapping_view.cc:270 src/wx/film_editor.cc:85
+#: src/wx/audio_mapping_view.cc:278 src/wx/film_editor.cc:83
msgid "Content"
msgstr "Content"
-#: src/wx/film_editor.cc:139
+#: src/wx/dcp_panel.cc:77
msgid "Content Type"
msgstr "Content Type"
-#: src/wx/video_panel.cc:332
+#: src/wx/video_panel.cc:353
#, c-format
msgid "Content frame rate %.4f\n"
msgstr "Content frame rate %.4f\n"
msgid "Content version"
msgstr "Content versie"
-#: src/wx/video_panel.cc:292
+#: src/wx/video_panel.cc:313
#, c-format
msgid "Content video is %dx%d (%.2f:1)\n"
msgstr "Content video is %dx%d (%.2f:1)\n"
msgid "Could not analyse audio."
msgstr "Kan audio niet analyseren"
-#: src/wx/film_viewer.cc:346
-#, c-format
-msgid "Could not decode video for view (%s)"
-msgstr "Kan video niet decoderen voor preview (%s)"
+#: src/wx/content_panel.cc:280
+msgid "Could not find any images nor a DCP in that folder"
+msgstr ""
#: src/wx/job_wrapper.cc:39
#, c-format
msgid "Could not make DCP: %s"
msgstr "Kan geen DCP maken: %s"
-#: src/wx/screen_dialog.cc:95
+#: src/wx/config_dialog.cc:688 src/wx/config_dialog.cc:783
+#: src/wx/config_dialog.cc:803 src/wx/screen_dialog.cc:95
#, c-format
msgid "Could not read certificate file (%s)"
msgstr "Onleesbaar certificaat (%s)"
+#: src/wx/config_dialog.cc:775 src/wx/config_dialog.cc:825
+#, fuzzy, c-format
+msgid "Could not read key file (%s)"
+msgstr "Onleesbaar certificaat (%s)"
+
#: src/wx/dolby_certificate_dialog.cc:39
msgid "Country"
msgstr "Land"
msgid "Create in folder"
msgstr "Maak in map"
-#: src/wx/video_panel.cc:304
+#: src/wx/video_panel.cc:325
#, c-format
msgid "Cropped to %dx%d (%.2f:1)\n"
msgstr "Cropped naar %dx%d (%.2f:1)\n"
-#: src/wx/video_panel.cc:244
+#: src/wx/video_panel.cc:243
msgid "Custom"
msgstr "aangepast"
-#: src/wx/film_editor.cc:87
+#: src/wx/film_editor.cc:85
msgid "DCP"
msgstr "DCP"
-#: src/wx/film_editor.cc:129
+#: src/wx/dcp_panel.cc:72
msgid "DCP Name"
msgstr "DCP naam"
msgid "DCP directory"
msgstr "DCP map"
-#: src/wx/about_dialog.cc:45 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
+#: src/wx/about_dialog.cc:49 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic audio - %s"
msgstr "DCP-o-matic audio - %s"
-#: src/wx/config_dialog.cc:270
+#: src/wx/config_dialog.cc:276
msgid "Default ISDCF name details"
msgstr "Standaard ISDCF naam details"
-#: src/wx/config_dialog.cc:287
+#: src/wx/config_dialog.cc:293
msgid "Default JPEG2000 bandwidth"
msgstr "Standaard JPEG2000 bandbreedte"
-#: src/wx/config_dialog.cc:296
+#: src/wx/config_dialog.cc:302
msgid "Default audio delay"
msgstr "Standaard audio delay"
-#: src/wx/config_dialog.cc:278
+#: src/wx/config_dialog.cc:284
msgid "Default container"
msgstr "Standaard container"
-#: src/wx/config_dialog.cc:282
+#: src/wx/config_dialog.cc:288
msgid "Default content type"
msgstr "Standaard content type"
-#: src/wx/config_dialog.cc:262
+#: src/wx/config_dialog.cc:268
msgid "Default directory for new films"
msgstr "Standaard map voor nieuwe films"
-#: src/wx/config_dialog.cc:254
+#: src/wx/config_dialog.cc:260
msgid "Default duration of still images"
msgstr "Standaard lengte van stills"
-#: src/wx/config_dialog.cc:304
+#: src/wx/config_dialog.cc:310
msgid "Default issuer"
msgstr "Standaard uitgever"
-#: src/wx/config_dialog.cc:274
+#: src/wx/config_dialog.cc:280
msgid "Default scale to"
msgstr "Standaard schaal naar"
-#: src/wx/config_dialog.cc:235
+#: src/wx/config_dialog.cc:241
msgid "Defaults"
msgstr "Standaard instellingen"
-#: src/wx/audio_panel.cc:67
+#: src/wx/audio_panel.cc:70
msgid "Delay"
msgstr "Vertraging"
-#: src/wx/film_editor.cc:125 src/wx/job_manager_view.cc:79
+#: src/wx/dcp_panel.cc:68 src/wx/job_manager_view.cc:79
msgid "Details..."
msgstr "Details..."
msgid "Doremi serial numbers must have 6 digits"
msgstr "Doremi serial numbers moeten bestaan uit 6 digits"
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
msgid "Down"
msgstr "Naar beneden"
msgid "Edit Screen..."
msgstr "Edit scherm"
-#: src/wx/audio_mapping_view.cc:135 src/wx/config_dialog.cc:271
-#: src/wx/video_panel.cc:155 src/wx/video_panel.cc:172
+#: src/wx/audio_mapping_view.cc:143 src/wx/config_dialog.cc:277
+#: src/wx/video_panel.cc:163 src/wx/video_panel.cc:170
#: src/wx/editable_list.h:66
msgid "Edit..."
msgstr "Edit..."
msgid "Encoding Servers"
msgstr "Encoding Servers"
-#: src/wx/film_editor.cc:162
+#: src/wx/dcp_panel.cc:92
msgid "Encrypted"
msgstr "Encrypted"
-#: src/wx/config_dialog.cc:817
+#: src/wx/subtitle_view.cc:47
+msgid "End"
+msgstr ""
+
+#: src/wx/config_dialog.cc:1159
msgid "Errors"
msgstr "Fouten"
+#: src/wx/config_dialog.cc:651
+msgid "Export DCP decryption certificate..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:50
msgid "Facility (e.g. DLA)"
msgstr "faciliteit (e.g. DLA)"
+#: src/wx/video_panel.cc:133
+msgid "Fade in"
+msgstr ""
+
+#: src/wx/video_panel.cc:138
+msgid "Fade out"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:76
#: src/wx/dolby_certificate_dialog.cc:100
#: src/wx/dolby_certificate_dialog.cc:123
msgid "Film name"
msgstr "Film naam"
-#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:146
+#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:160
msgid "Filters"
msgstr "Filters"
-#: src/wx/content_menu.cc:52
+#: src/wx/content_menu.cc:55
msgid "Find missing..."
msgstr "Zoek ontbrekende..."
-#: src/wx/film_editor.cc:145
+#: src/wx/dcp_panel.cc:538
msgid "Frame Rate"
msgstr "Frame Rate"
msgid "Frames already encoded"
msgstr "Frames zijn al omgezet"
-#: src/wx/about_dialog.cc:61
+#: src/wx/about_dialog.cc:65
msgid "Free, open-source DCP generation from almost anything."
msgstr "Gratis, open-source DCP samensteller voor bijna alles."
msgid "From"
msgstr "Van"
-#: src/wx/config_dialog.cc:675
+#: src/wx/config_dialog.cc:1014
msgid "From address"
msgstr "Van adres"
-#: src/wx/audio_mapping_view.cc:133
+#: src/wx/audio_mapping_view.cc:141
msgid "Full"
msgstr "Volledig"
msgid "Full length"
msgstr "Volledige lengte"
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
msgid "Gain"
msgstr "Versterking"
msgid "Gain for content channel %d in DCP channel %d"
msgstr "Gain voor content kanaal %d in DCP kanaal %d"
-#: src/wx/properties_dialog.cc:52
+#: src/wx/properties_dialog.cc:51
msgid "Gb"
msgstr "Gb"
-#: src/wx/config_dialog.cc:813
+#: src/wx/config_dialog.cc:1155
msgid "General"
msgstr "Algemeen"
-#: src/wx/audio_mapping_view.cc:301
+#: src/wx/audio_mapping_view.cc:309
msgid "HI"
msgstr "HI"
msgid "Host name or IP address"
msgstr "Host naam of IP address"
-#: src/wx/audio_panel.cc:242
-msgid "Hz"
-msgstr "Hz"
-
#: src/wx/gain_calculator_dialog.cc:29
msgid "I want to play this back at fader"
msgstr "Ik wil dit afspelen met fader"
-#: src/wx/config_dialog.cc:560
+#: src/wx/config_dialog.cc:899
msgid "IP address"
msgstr "IP adres"
-#: src/wx/config_dialog.cc:456
+#: src/wx/config_dialog.cc:462
msgid "IP address / host name"
msgstr "IP adres / host naam"
msgid "Input gamma"
msgstr "Input gamma"
-#: src/wx/film_editor.cc:228
+#: src/wx/config_dialog.cc:727
+msgid "Intermediate"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:31
+msgid "Intermediate common name"
+msgstr ""
+
+#: src/wx/dcp_panel.cc:115
msgid "Interop"
msgstr "Interop"
-#: src/wx/film_editor.cc:181
+#: src/wx/dcp_panel.cc:565
msgid "JPEG2000 bandwidth"
msgstr "JPEG2000 bandbreedte"
-#: src/wx/content_menu.cc:51
+#: src/wx/content_menu.cc:54
msgid "Join"
msgstr "Samenvoegen"
-#: src/wx/config_dialog.cc:627
+#: src/wx/config_dialog.cc:966
msgid "KDM Email"
msgstr "KDM Email"
msgid "KDM|Timing"
msgstr "KDM|Timing"
-#: src/wx/film_editor.cc:298
+#: src/wx/content_panel.cc:85
msgid "Keep video in sequence"
msgstr "Behoud video in tijdlijn"
-#: src/wx/audio_mapping_view.cc:277
+#: src/wx/config_dialog.cc:548
+msgid "Keys"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:285
msgid "L"
msgstr "L"
-#: src/wx/audio_mapping_view.cc:309
+#: src/wx/audio_mapping_view.cc:317
msgid "Lc"
msgstr "Lc"
-#: src/wx/video_panel.cc:88
+#: src/wx/config_dialog.cc:725
+msgid "Leaf"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:33
+msgid "Leaf common name"
+msgstr ""
+
+#: src/wx/video_panel.cc:89
msgid "Left crop"
msgstr "Left crop"
-#: src/wx/audio_mapping_view.cc:289
+#: src/wx/audio_mapping_view.cc:297
msgid "Lfe"
msgstr "Lfe"
msgid "Load from file..."
msgstr "Laad van bestand..."
-#: src/wx/config_dialog.cc:807
+#: src/wx/config_dialog.cc:620 src/wx/config_dialog.cc:633
+#: src/wx/config_dialog.cc:646
+#, fuzzy
+msgid "Load..."
+msgstr "Toevoegen.."
+
+#: src/wx/config_dialog.cc:1149
msgid "Log"
msgstr "Log"
-#: src/wx/config_dialog.cc:804
+#: src/wx/config_dialog.cc:1146
msgid "Log:"
msgstr ""
-#: src/wx/audio_mapping_view.cc:293
+#: src/wx/audio_mapping_view.cc:301
msgid "Ls"
msgstr "Ls"
-#: src/wx/film_editor.cc:784
+#: src/wx/content_panel.cc:438
msgid "MISSING: "
msgstr "ONTBREEKT:"
-#: src/wx/config_dialog.cc:660
+#: src/wx/config_dialog.cc:999
msgid "Mail password"
msgstr "Mail wachtwoord"
-#: src/wx/config_dialog.cc:656
+#: src/wx/config_dialog.cc:995
msgid "Mail user name"
msgstr "Mail gebruikersnaam"
msgid "Make KDMs"
msgstr "Maak KDM's"
+#: src/wx/make_signer_chain_dialog.cc:23
+#, fuzzy
+msgid "Make certificate chain"
+msgstr "Selecteer Certificaat bestand"
+
#: src/wx/isdcf_metadata_dialog.cc:71
msgid "Mastered luminance (e.g. 4fl)"
msgstr "Gemasterde helderheid (bv. 4fl)"
msgid "Matrix"
msgstr "Matrix"
-#: src/wx/config_dialog.cc:791
+#: src/wx/config_dialog.cc:1133
msgid "Maximum JPEG2000 bandwidth"
msgstr "Maximum JPEG2000 bandbreedte"
-#: src/wx/config_dialog.cc:291 src/wx/config_dialog.cc:795
-#: src/wx/film_editor.cc:185
+#: src/wx/config_dialog.cc:297 src/wx/config_dialog.cc:1137
+#: src/wx/dcp_panel.cc:569
msgid "Mbit/s"
msgstr "Mbit/s"
-#: src/wx/video_panel.cc:280
+#: src/wx/video_panel.cc:301
msgid "Multiple content selected"
msgstr "Meedere content geselecteerd"
msgid "My Documents"
msgstr "Mijn documenten"
-#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:512
-#: src/wx/film_editor.cc:113 src/wx/preset_colour_conversion_dialog.cc:38
+#: src/wx/content_panel.cc:442
+msgid "NEEDS KDM: "
+msgstr ""
+
+#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:518
+#: src/wx/dcp_panel.cc:56 src/wx/preset_colour_conversion_dialog.cc:38
#: src/wx/screen_dialog.cc:38
msgid "Name"
msgstr "Naam"
msgid "New versions of DCP-o-matic are available."
msgstr "Er zijn nieuwe versies van DCP -o- matic beschikbaar."
-#: src/wx/audio_mapping_view.cc:348
+#: src/wx/audio_mapping_view.cc:356
#, c-format
msgid "No audio will be passed from content channel %d to DCP channel %d."
msgstr ""
"Er wordt geen audio van het content kanaal %d doorgestuurd naar DCP kanaal "
"%d."
-#: src/wx/video_panel.cc:153 src/wx/video_panel.cc:249
+#: src/wx/audio_panel.cc:290 src/wx/video_panel.cc:161
+#: src/wx/video_panel.cc:248
msgid "None"
msgstr "Geen"
-#: src/wx/audio_mapping_view.cc:132
+#: src/wx/audio_mapping_view.cc:140
msgid "Off"
msgstr "Uit"
+#: src/wx/config_dialog.cc:1167
+msgid "Open console window"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:25
+msgid "Organisation"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:27
+msgid "Organisational unit"
+msgstr ""
+
#: src/wx/screen_dialog.cc:65
msgid "Other"
msgstr "Andere"
-#: src/wx/config_dialog.cc:652
+#: src/wx/config_dialog.cc:991
msgid "Outgoing mail server"
msgstr "Uitgaande mail server"
+#: src/wx/film_viewer.cc:61
+#, fuzzy
+msgid "Outline content"
+msgstr "Meedere content geselecteerd"
+
#: src/wx/kdm_dialog.cc:156
msgid "Output"
msgstr "Output"
msgid "Package Type (e.g. OV)"
msgstr "Package Type (e.g. OV)"
-#: src/wx/video_panel.cc:325
+#: src/wx/video_panel.cc:346
#, c-format
msgid "Padded with black to %dx%d (%.2f:1)\n"
msgstr "Opgevuld met zwart tot %dx%d (%.2f:1)\n"
-#: src/wx/config_dialog.cc:572
+#: src/wx/config_dialog.cc:911
msgid "Password"
msgstr "Wachtwoord"
msgid "Peak"
msgstr "Piek"
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
msgid "Play"
msgstr "Afspelen"
msgid "Pre-release"
msgstr "Voor-uitgave"
-#: src/wx/audio_mapping_view.cc:281
+#: src/wx/config_dialog.cc:638
+msgid "Private key for decrypting DCPs"
+msgstr ""
+
+#: src/wx/config_dialog.cc:612
+msgid "Private key for leaf certificate"
+msgstr ""
+
+#: src/wx/audio_panel.cc:89
+msgid "Process with"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:289
msgid "R"
msgstr "R"
msgid "Rating (e.g. 15)"
msgstr "Beoordeling (e.g. 15)"
-#: src/wx/audio_mapping_view.cc:313
+#: src/wx/audio_mapping_view.cc:321
msgid "Rc"
msgstr "Rc"
+#: src/wx/content_menu.cc:56
+msgid "Re-examine..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:608
+#, fuzzy
+msgid "Re-make certificates..."
+msgstr "Download certificaat"
+
#: src/wx/isdcf_metadata_dialog.cc:62
msgid "Red band"
msgstr "Rode tape"
-#: src/wx/content_menu.cc:54 src/wx/film_editor.cc:284
-#: src/wx/editable_list.h:68
+#: src/wx/config_dialog.cc:599 src/wx/content_menu.cc:59
+#: src/wx/content_panel.cc:71 src/wx/editable_list.h:68
msgid "Remove"
msgstr "Verwijder"
msgid "Repeat Content"
msgstr "Herhaal content"
-#: src/wx/content_menu.cc:50
+#: src/wx/content_menu.cc:53
msgid "Repeat..."
msgstr "Herhaal..."
-#: src/wx/config_dialog.cc:690
+#: src/wx/config_dialog.cc:1029
msgid "Reset to default text"
msgstr "Herinstellen oorspronkelijke tekst"
-#: src/wx/film_editor.cc:175
+#: src/wx/dcp_panel.cc:559
msgid "Resolution"
msgstr "Resolutie"
msgid "Resume"
msgstr "Vervolg"
-#: src/wx/audio_mapping_view.cc:356
+#: src/wx/audio_mapping_view.cc:364
msgid "Right click to change gain."
msgstr "Klik Rechtermuis om gain te veranderen"
msgid "Right crop"
msgstr "Right crop"
-#: src/wx/audio_mapping_view.cc:297
+#: src/wx/config_dialog.cc:723
+msgid "Root"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:29
+msgid "Root common name"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:305
msgid "Rs"
msgstr "Rs"
-#: src/wx/film_editor.cc:227
+#: src/wx/dcp_panel.cc:114
msgid "SMPTE"
msgstr "SMPTE"
-#: src/wx/video_panel.cc:132
+#: src/wx/video_panel.cc:143
msgid "Scale to"
msgstr "Schaal tot"
-#: src/wx/video_panel.cc:316
+#: src/wx/video_panel.cc:337
#, c-format
msgid "Scaled to %dx%d (%.2f:1)\n"
msgstr "Geschaald naar %dx%d (%.2f:1)\n"
-#: src/wx/film_editor.cc:195
+#: src/wx/dcp_panel.cc:574
msgid "Scaler"
msgstr "Schaler"
msgid "Select CPL XML file"
msgstr "Selekteer CPL XML bestand"
-#: src/wx/screen_dialog.cc:102
+#: src/wx/config_dialog.cc:679 src/wx/config_dialog.cc:795
+#: src/wx/config_dialog.cc:840 src/wx/screen_dialog.cc:102
msgid "Select Certificate File"
msgstr "Selecteer Certificaat bestand"
+#: src/wx/content_menu.cc:266
+msgid "Select KDM"
+msgstr ""
+
+#: src/wx/config_dialog.cc:769 src/wx/config_dialog.cc:817
+#, fuzzy
+msgid "Select Key File"
+msgstr "Selecteer Certificaat bestand"
+
#: src/wx/kdm_dialog.cc:185
msgid "Send by email"
msgstr "Stuur per Email"
msgid "Server serial number"
msgstr "Server serie nummer"
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
msgid "Servers"
msgstr "Servers"
msgid "Set"
msgstr "Instellen"
-#: src/wx/config_dialog.cc:92
+#: src/wx/config_dialog.cc:99
msgid "Set language"
msgstr "Taal Instellen"
-#: src/wx/audio_panel.cc:48
+#: src/wx/audio_panel.cc:51
msgid "Show Audio..."
msgstr "Toon Audio..."
-#: src/wx/film_editor.cc:158
+#: src/wx/dcp_panel.cc:88
msgid "Signed"
msgstr "Signed"
msgid "Smoothing"
msgstr "Smoothing"
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
msgid "Snap"
msgstr "Snap"
msgid "Stable version "
msgstr "Stabiele versie"
-#: src/wx/film_editor.cc:190
+#: src/wx/dcp_panel.cc:96
msgid "Standard"
msgstr "Standaard"
-#: src/wx/audio_panel.cc:81 src/wx/subtitle_panel.cc:79
+#: src/wx/subtitle_view.cc:39
+msgid "Start"
+msgstr ""
+
+#: src/wx/audio_panel.cc:84 src/wx/subtitle_panel.cc:87
msgid "Stream"
msgstr "Stroom"
msgid "Studio (e.g. TCF)"
msgstr "Studio (e.g. TCF)"
-#: src/wx/config_dialog.cc:671
+#: src/wx/config_dialog.cc:1010
msgid "Subject"
msgstr "Onderwerp"
+#: src/wx/subtitle_view.cc:55
+#, fuzzy
+msgid "Subtitle"
+msgstr "Ondertitels"
+
#: src/wx/isdcf_metadata_dialog.cc:38
msgid "Subtitle Language (e.g. FR)"
msgstr "Ondertitel Taal (vb NL)"
-#: src/wx/subtitle_panel.cc:34
+#: src/wx/subtitle_panel.cc:41 src/wx/subtitle_view.cc:32
msgid "Subtitles"
msgstr "Ondertitels"
msgid "Supported by"
msgstr "Ondersteund door"
-#: src/wx/config_dialog.cc:542
+#: src/wx/config_dialog.cc:881
msgid "TMS"
msgstr "TMS"
-#: src/wx/config_dialog.cc:564
+#: src/wx/config_dialog.cc:903
msgid "Target path"
msgstr "Doel pad"
msgid "Tested by"
msgstr "Getest door"
-#: src/wx/content_menu.cc:223
+#: src/wx/content_menu.cc:252
msgid ""
"The content file(s) you specified are not the same as those that are "
"missing. Either try again with the correct content file or remove the "
msgid "There are no hints: everything looks good!"
msgstr "Er zijn geen tips, alles lijkt goed!"
-#: src/wx/film_viewer.cc:134
+#: src/wx/film_viewer.cc:142
msgid "There is not enough free memory to do that."
msgstr "Er is niet genoeg geheugen om dat te doen."
msgid "Threads"
msgstr "CPU belasting"
-#: src/wx/config_dialog.cc:111
+#: src/wx/config_dialog.cc:118
msgid "Threads to use for encoding on this host"
msgstr "CPU cores beschikbaar voor encoding op deze host"
+#: src/wx/config_dialog.cc:583
+msgid "Thumbprint"
+msgstr ""
+
#: src/wx/audio_plot.cc:165
msgid "Time"
msgstr "Tijd"
-#: src/wx/timeline_dialog.cc:32
+#: src/wx/timeline_dialog.cc:33
msgid "Timeline"
msgstr "Tijdlijn"
-#: src/wx/film_editor.cc:290
+#: src/wx/content_panel.cc:77
msgid "Timeline..."
msgstr "Tijdlijn..."
msgid "Timing|Timing"
msgstr "Timing|Timing"
-#: src/wx/video_panel.cc:110
+#: src/wx/video_panel.cc:111
msgid "Top crop"
msgstr "Top crop"
-#: src/wx/about_dialog.cc:107
+#: src/wx/about_dialog.cc:111
msgid "Translated by"
msgstr "Vertaald door"
msgid "Trim from start"
msgstr "Trim vanaf begin"
-#: src/wx/audio_dialog.cc:55 src/wx/video_panel.cc:75
+#: src/wx/audio_dialog.cc:55 src/wx/config_dialog.cc:575
+#: src/wx/video_panel.cc:76
msgid "Type"
msgstr "Type"
msgid "Until"
msgstr "Totdat"
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
msgid "Up"
msgstr "Omhoog"
msgid "Update"
msgstr "Update"
-#: src/wx/film_editor.cc:123
+#: src/wx/dcp_panel.cc:66
msgid "Use ISDCF name"
msgstr "Gebruik ISDCF naam"
-#: src/wx/config_dialog.cc:452
+#: src/wx/config_dialog.cc:458
msgid "Use all servers"
msgstr "Gebruik alle servers"
-#: src/wx/film_editor.cc:152
+#: src/wx/dcp_panel.cc:545
msgid "Use best"
msgstr "Gebruik de beste"
msgid "Use preset"
msgstr "Gebruik preset"
-#: src/wx/config_dialog.cc:568
+#: src/wx/subtitle_panel.cc:47
+#, fuzzy
+msgid "Use subtitles"
+msgstr "Ondertitels"
+
+#: src/wx/config_dialog.cc:907
msgid "User name"
msgstr "Gebruikersnaam"
-#: src/wx/audio_mapping_view.cc:305
+#: src/wx/audio_mapping_view.cc:313
msgid "VI"
msgstr "VI"
-#: src/wx/video_panel.cc:68
+#: src/wx/dcp_panel.cc:85 src/wx/video_panel.cc:69
msgid "Video"
msgstr "Video"
msgid "Video frame rate"
msgstr "Video frame rate"
-#: src/wx/config_dialog.cc:815
+#: src/wx/subtitle_panel.cc:91
+msgid "View..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:1157
msgid "Warnings"
msgstr "Waarschuwingen"
-#: src/wx/subtitle_panel.cc:39
-msgid "With Subtitles"
-msgstr "Met ondertiteling"
-
#: src/wx/kdm_dialog.cc:172
msgid "Write to"
msgstr "Schrijf naar"
-#: src/wx/about_dialog.cc:91
+#: src/wx/about_dialog.cc:95
msgid "Written by"
msgstr "Geschreven door"
-#: src/wx/subtitle_panel.cc:44
+#: src/wx/subtitle_panel.cc:52
msgid "X Offset"
msgstr "X offset"
-#: src/wx/subtitle_panel.cc:62
+#: src/wx/subtitle_panel.cc:70
#, fuzzy
msgid "X Scale"
msgstr "Schaal"
-#: src/wx/subtitle_panel.cc:53
+#: src/wx/subtitle_panel.cc:61
msgid "Y Offset"
msgstr "Y offset"
-#: src/wx/subtitle_panel.cc:71
+#: src/wx/subtitle_panel.cc:79
#, fuzzy
msgid "Y Scale"
msgstr "Schaal"
"Uw DCP heeft minder dan 6 audio kanalen. This kan problemen geven op sommige "
"projectors."
-#: src/wx/timeline.cc:220
+#: src/wx/timeline.cc:229
msgid "audio"
msgstr "audio"
-#: src/wx/audio_panel.cc:240
-msgid "channels"
-msgstr "kanalen"
-
#: src/wx/properties_dialog.cc:46
msgid "counting..."
msgstr "tellen..."
-#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:62
+#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:65
msgid "dB"
msgstr "dB"
#. / TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
-#: src/wx/audio_panel.cc:78 src/wx/config_dialog.cc:300
+#: src/wx/audio_panel.cc:81 src/wx/config_dialog.cc:306
msgid "ms"
msgstr "ms"
-#: src/wx/config_dialog.cc:258
+#: src/wx/config_dialog.cc:264
msgid "s"
msgstr "s"
-#: src/wx/timeline.cc:243
+#: src/wx/timeline.cc:258
msgid "still"
msgstr "still"
+#: src/wx/timeline.cc:289
+#, fuzzy
+msgid "subtitles"
+msgstr "Ondertitels"
+
#: src/wx/repeat_dialog.cc:28
msgid "times"
msgstr "tijden"
-#: src/wx/timeline.cc:241
+#: src/wx/timeline.cc:260
msgid "video"
msgstr "video"
+#~ msgid "1 channel"
+#~ msgstr "1 channel"
+
+#~ msgid "Audio channels"
+#~ msgstr "Audio kanalen"
+
+#~ msgid "Could not decode video for view (%s)"
+#~ msgstr "Kan video niet decoderen voor preview (%s)"
+
+#~ msgid "Hz"
+#~ msgstr "Hz"
+
+#~ msgid "With Subtitles"
+#~ msgstr "Met ondertiteling"
+
+#~ msgid "channels"
+#~ msgstr "kanalen"
+
#~ msgid "Default creator"
#~ msgstr "Standaard maker"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.6.3\n"
-#: src/wx/subtitle_panel.cc:48 src/wx/subtitle_panel.cc:57
-#: src/wx/subtitle_panel.cc:66 src/wx/subtitle_panel.cc:75
+#: src/wx/subtitle_panel.cc:56 src/wx/subtitle_panel.cc:65
+#: src/wx/subtitle_panel.cc:74 src/wx/subtitle_panel.cc:83
msgid "%"
msgstr "%"
-#: src/wx/about_dialog.cc:78
+#: src/wx/about_dialog.cc:82
msgid ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2014 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/wx/config_dialog.cc:664
+#: src/wx/config_dialog.cc:1003
msgid "(password will be stored on disk in plaintext)"
msgstr "(lösenord sparas på disk i klartext)"
-#: src/wx/config_dialog.cc:104
+#: src/wx/config_dialog.cc:111
msgid "(restart DCP-o-matic to see language changes)"
msgstr "(starta om DCP-o-matic för att se språkändringar)"
-#: src/wx/audio_mapping_view.cc:134
+#: src/wx/audio_mapping_view.cc:142
msgid "-6dB"
msgstr ""
msgid "1 / "
msgstr "1 / "
-#: src/wx/audio_panel.cc:238
-msgid "1 channel"
-msgstr "1 kanal"
-
-#: src/wx/video_panel.cc:197
+#: src/wx/video_panel.cc:193
msgid "2D"
msgstr "2D"
msgid "2D version of content available in 3D"
msgstr "Nya versioner av DCP-o-matic finns tillgängligt."
-#: src/wx/film_editor.cc:224
+#: src/wx/dcp_panel.cc:607
msgid "2K"
msgstr "2K"
-#: src/wx/film_editor.cc:171
+#: src/wx/dcp_panel.cc:555
msgid "3D"
msgstr "3D"
-#: src/wx/video_panel.cc:200
+#: src/wx/video_panel.cc:196
msgid "3D alternate"
msgstr ""
-#: src/wx/video_panel.cc:201
+#: src/wx/video_panel.cc:197
msgid "3D left only"
msgstr ""
-#: src/wx/video_panel.cc:198
+#: src/wx/video_panel.cc:194
msgid "3D left/right"
msgstr "3D left/right"
-#: src/wx/video_panel.cc:202
+#: src/wx/video_panel.cc:198
#, fuzzy
msgid "3D right only"
msgstr "3D left/right"
-#: src/wx/video_panel.cc:199
+#: src/wx/video_panel.cc:195
msgid "3D top/bottom"
msgstr "3D top/bottom"
-#: src/wx/film_editor.cc:225
+#: src/wx/dcp_panel.cc:608
msgid "4K"
msgstr "4K"
msgid "A new version of DCP-o-matic is available."
msgstr "En ny version av DCP-o-matic finns tillgänglig."
-#: src/wx/about_dialog.cc:30
+#: src/wx/about_dialog.cc:34
msgid "About DCP-o-matic"
msgstr "Om DCP-o-matic"
msgid "Add Cinema..."
msgstr "Lägg till Cinema..."
+#: src/wx/content_menu.cc:57
+#, fuzzy
+msgid "Add KDM..."
+msgstr "Lägg till Cinema..."
+
#: src/wx/kdm_dialog.cc:86
msgid "Add Screen..."
msgstr "Lägg till Skärm..."
-#: src/wx/film_editor.cc:280
+#: src/wx/content_panel.cc:67
msgid "Add file(s)..."
msgstr "Lägg till fil(er)..."
-#: src/wx/film_editor.cc:282
+#: src/wx/content_panel.cc:69
msgid "Add folder..."
msgstr "Lägg till folder..."
-#: src/wx/editable_list.h:62
+#: src/wx/config_dialog.cc:597 src/wx/editable_list.h:62
msgid "Add..."
msgstr "Lägg till..."
"tab."
msgstr ""
-#: src/wx/config_dialog.cc:799
+#: src/wx/config_dialog.cc:1141
#, fuzzy
msgid "Allow any DCP frame rate"
msgstr "bildhastighet"
-#: src/wx/about_dialog.cc:111
+#: src/wx/about_dialog.cc:115
msgid "Artwork by"
msgstr ""
-#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:40
+#: src/wx/audio_dialog.cc:33 src/wx/audio_panel.cc:43 src/wx/dcp_panel.cc:86
msgid "Audio"
msgstr "Audio"
msgid "Audio Language (e.g. EN)"
msgstr "Audiospråk (ex. SV)"
-#: src/wx/film_editor.cc:166
-msgid "Audio channels"
-msgstr "Audio-kanaler"
-
-#: src/wx/audio_mapping_view.cc:350
+#: src/wx/audio_mapping_view.cc:358
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d unaltered."
"Audio kommer att överföras från innehållskanal %d till DCP-kanal %d "
"oförändrad."
-#: src/wx/audio_mapping_view.cc:353
+#: src/wx/audio_mapping_view.cc:361
#, c-format
msgid ""
"Audio will be passed from content channel %d to DCP channel %d with gain "
"Audio kommer att överföras från innehållskanal %d till DCP-kanal %d med "
"förstärkning %.1fdB."
-#: src/wx/config_dialog.cc:683
+#: src/wx/config_dialog.cc:1022
#, fuzzy
msgid "BCC address"
msgstr "IP-adress"
msgid "Browse..."
msgstr "Bläddra..."
-#: src/wx/audio_mapping_view.cc:317
+#: src/wx/audio_mapping_view.cc:325
msgid "BsL"
msgstr "BsL"
-#: src/wx/audio_mapping_view.cc:321
+#: src/wx/audio_mapping_view.cc:329
msgid "BsR"
msgstr "BsR"
+#: src/wx/dcp_panel.cc:551
+msgid "Burn subtitles into image"
+msgstr ""
+
#: src/wx/gain_calculator_dialog.cc:32
msgid "But I have to use fader"
msgstr "Men jag måste använda mixervolym"
-#: src/wx/audio_mapping_view.cc:285
+#: src/wx/audio_mapping_view.cc:293
msgid "C"
msgstr "C"
-#: src/wx/config_dialog.cc:679
+#: src/wx/config_dialog.cc:1018
#, fuzzy
msgid "CC address"
msgstr "IP-adress"
msgid "CPL annotation text"
msgstr ""
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
msgid "Calculate..."
msgstr "Beräkna..."
msgid "Certificate"
msgstr "Välj certifikatfil"
+#: src/wx/config_dialog.cc:564
+msgid "Certificate chain for signing DCPs and KDMs:"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:191
#: src/wx/doremi_certificate_dialog.cc:103
#, fuzzy
msgid "Certificate downloaded"
msgstr "Välj certifikatfil"
+#: src/wx/config_dialog.cc:625
+msgid "Certificate for decrypting DCPs"
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:65
msgid "Chain"
msgstr ""
msgid "Channel gain"
msgstr "Kanalförstärkning"
-#: src/wx/audio_dialog.cc:44
+#: src/wx/audio_dialog.cc:44 src/wx/dcp_panel.cc:623
msgid "Channels"
msgstr "Kanaler"
-#: src/wx/config_dialog.cc:120
+#: src/wx/config_dialog.cc:126
msgid "Check for testing updates as well as stable ones"
msgstr "sök efter både test- och stabila uppdateringar"
-#: src/wx/config_dialog.cc:116
+#: src/wx/config_dialog.cc:122
msgid "Check for updates on startup"
msgstr "Sök efter uppdateringar vid start"
-#: src/wx/content_menu.cc:182
+#: src/wx/content_menu.cc:198
msgid "Choose a file"
msgstr "Välj en fil"
-#: src/wx/film_editor.cc:810
+#: src/wx/content_panel.cc:241
msgid "Choose a file or files"
msgstr "Välj en fil eller filer"
-#: src/wx/content_menu.cc:175 src/wx/film_editor.cc:833
+#: src/wx/content_menu.cc:191 src/wx/content_panel.cc:264
msgid "Choose a folder"
msgstr "Välj en folder"
msgid "Cinema"
msgstr "Lägg till Cinema..."
-#: src/wx/config_dialog.cc:498
+#: src/wx/config_dialog.cc:504
#, fuzzy
msgid "Colour Conversions"
msgstr "Färgkonverteringar"
#: src/wx/content_colour_conversion_dialog.cc:34
-#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:162
+#: src/wx/preset_colour_conversion_dialog.cc:30 src/wx/video_panel.cc:167
msgid "Colour conversion"
msgstr "Färgkonvertering"
# Svengelska
-#: src/wx/config_dialog.cc:819
+#: src/wx/config_dialog.cc:1161
#, fuzzy
msgid "Config|Timing"
msgstr "Tajming"
-#: src/wx/film_editor.cc:134
+#: src/wx/dcp_panel.cc:532
msgid "Container"
msgstr "Innehåll"
-#: src/wx/audio_mapping_view.cc:270 src/wx/film_editor.cc:85
+#: src/wx/audio_mapping_view.cc:278 src/wx/film_editor.cc:83
msgid "Content"
msgstr "Innehåll"
-#: src/wx/film_editor.cc:139
+#: src/wx/dcp_panel.cc:77
msgid "Content Type"
msgstr "Innehållstyp"
-#: src/wx/video_panel.cc:332
+#: src/wx/video_panel.cc:353
#, c-format
msgid "Content frame rate %.4f\n"
msgstr "Innehållets bildhastighet %.4f\n"
msgid "Content version"
msgstr "Innehållsversion"
-#: src/wx/video_panel.cc:292
+#: src/wx/video_panel.cc:313
#, c-format
msgid "Content video is %dx%d (%.2f:1)\n"
msgstr "Original-videon är %dx%d (%.2f:1)\n"
msgid "Could not analyse audio."
msgstr "Kunde inte analysera audio."
-#: src/wx/film_viewer.cc:346
-#, c-format
-msgid "Could not decode video for view (%s)"
-msgstr "Kunde inte avkoda video för visning (%s)"
+#: src/wx/content_panel.cc:280
+msgid "Could not find any images nor a DCP in that folder"
+msgstr ""
#: src/wx/job_wrapper.cc:39
#, c-format
msgid "Could not make DCP: %s"
msgstr "Kunde inte skapa DCP: %s"
-#: src/wx/screen_dialog.cc:95
+#: src/wx/config_dialog.cc:688 src/wx/config_dialog.cc:783
+#: src/wx/config_dialog.cc:803 src/wx/screen_dialog.cc:95
#, fuzzy, c-format
msgid "Could not read certificate file (%s)"
msgstr "Kunde inte öppna innehållsfilen (%s)"
+#: src/wx/config_dialog.cc:775 src/wx/config_dialog.cc:825
+#, fuzzy, c-format
+msgid "Could not read key file (%s)"
+msgstr "Kunde inte öppna innehållsfilen (%s)"
+
#: src/wx/dolby_certificate_dialog.cc:39
msgid "Country"
msgstr ""
msgid "Create in folder"
msgstr "Skapa i katalog"
-#: src/wx/video_panel.cc:304
+#: src/wx/video_panel.cc:325
#, c-format
msgid "Cropped to %dx%d (%.2f:1)\n"
msgstr "Beskuren till %dx%d (%.2f:1)\n"
# sammanhang?
-#: src/wx/video_panel.cc:244
+#: src/wx/video_panel.cc:243
msgid "Custom"
msgstr "Special"
-#: src/wx/film_editor.cc:87
+#: src/wx/film_editor.cc:85
msgid "DCP"
msgstr "DCP"
-#: src/wx/film_editor.cc:129
+#: src/wx/dcp_panel.cc:72
msgid "DCP Name"
msgstr "DCP-namn"
msgid "DCP directory"
msgstr ""
-#: src/wx/about_dialog.cc:45 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
+#: src/wx/about_dialog.cc:49 src/wx/wx_util.cc:87 src/wx/wx_util.cc:95
msgid "DCP-o-matic"
msgstr "DCP-o-matic"
msgid "DCP-o-matic audio - %s"
msgstr "DCP-o-matic audio - %s"
-#: src/wx/config_dialog.cc:270
+#: src/wx/config_dialog.cc:276
#, fuzzy
msgid "Default ISDCF name details"
msgstr "Detaljer om förvalda DCI-namn"
-#: src/wx/config_dialog.cc:287
+#: src/wx/config_dialog.cc:293
msgid "Default JPEG2000 bandwidth"
msgstr "Förvald JPEG2000-bandbredd"
-#: src/wx/config_dialog.cc:296
+#: src/wx/config_dialog.cc:302
msgid "Default audio delay"
msgstr "Förvald audiofördröjning"
-#: src/wx/config_dialog.cc:278
+#: src/wx/config_dialog.cc:284
msgid "Default container"
msgstr "Förvald innehållstyp"
-#: src/wx/config_dialog.cc:282
+#: src/wx/config_dialog.cc:288
msgid "Default content type"
msgstr "Förvald innehållstyp"
-#: src/wx/config_dialog.cc:262
+#: src/wx/config_dialog.cc:268
msgid "Default directory for new films"
msgstr "Förvald katalog för nya filmer"
-#: src/wx/config_dialog.cc:254
+#: src/wx/config_dialog.cc:260
msgid "Default duration of still images"
msgstr "Förvald varaktighet på stillbilder"
-#: src/wx/config_dialog.cc:304
+#: src/wx/config_dialog.cc:310
#, fuzzy
msgid "Default issuer"
msgstr "Standardval"
-#: src/wx/config_dialog.cc:274
+#: src/wx/config_dialog.cc:280
msgid "Default scale to"
msgstr ""
-#: src/wx/config_dialog.cc:235
+#: src/wx/config_dialog.cc:241
msgid "Defaults"
msgstr "Standardval"
-#: src/wx/audio_panel.cc:67
+#: src/wx/audio_panel.cc:70
#, fuzzy
msgid "Delay"
msgstr "Audio Fördröjning"
-#: src/wx/film_editor.cc:125 src/wx/job_manager_view.cc:79
+#: src/wx/dcp_panel.cc:68 src/wx/job_manager_view.cc:79
msgid "Details..."
msgstr "Detaljer..."
msgid "Doremi serial numbers must have 6 digits"
msgstr ""
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
msgid "Down"
msgstr "Ner"
msgid "Edit Screen..."
msgstr "Redigera Skärm..."
-#: src/wx/audio_mapping_view.cc:135 src/wx/config_dialog.cc:271
-#: src/wx/video_panel.cc:155 src/wx/video_panel.cc:172
+#: src/wx/audio_mapping_view.cc:143 src/wx/config_dialog.cc:277
+#: src/wx/video_panel.cc:163 src/wx/video_panel.cc:170
#: src/wx/editable_list.h:66
msgid "Edit..."
msgstr "Redigera..."
msgid "Encoding Servers"
msgstr "Kodningsservrar"
-#: src/wx/film_editor.cc:162
+#: src/wx/dcp_panel.cc:92
msgid "Encrypted"
msgstr "Krypterad"
-#: src/wx/config_dialog.cc:817
+#: src/wx/subtitle_view.cc:47
+msgid "End"
+msgstr "Slut"
+
+#: src/wx/config_dialog.cc:1159
msgid "Errors"
msgstr ""
+#: src/wx/config_dialog.cc:651
+msgid "Export DCP decryption certificate..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:50
msgid "Facility (e.g. DLA)"
msgstr "Företag (ex. DLA)"
+#: src/wx/video_panel.cc:133
+msgid "Fade in"
+msgstr ""
+
+#: src/wx/video_panel.cc:138
+msgid "Fade out"
+msgstr ""
+
#: src/wx/dolby_certificate_dialog.cc:76
#: src/wx/dolby_certificate_dialog.cc:100
#: src/wx/dolby_certificate_dialog.cc:123
msgid "Film name"
msgstr "Filmnamn"
-#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:146
+#: src/wx/filter_dialog.cc:32 src/wx/video_panel.cc:160
msgid "Filters"
msgstr "Filter"
-#: src/wx/content_menu.cc:52
+#: src/wx/content_menu.cc:55
msgid "Find missing..."
msgstr "Hitta saknade..."
-#: src/wx/film_editor.cc:145
+#: src/wx/dcp_panel.cc:538
msgid "Frame Rate"
msgstr "Bildhastighet"
msgid "Frames already encoded"
msgstr "Bildrutor redan kodade"
-#: src/wx/about_dialog.cc:61
+#: src/wx/about_dialog.cc:65
msgid "Free, open-source DCP generation from almost anything."
msgstr "Fri, öppen-källkods DCP-generering från nästan vad som helst."
msgid "From"
msgstr "Avsändare"
-#: src/wx/config_dialog.cc:675
+#: src/wx/config_dialog.cc:1014
#, fuzzy
msgid "From address"
msgstr "IP-adress"
-#: src/wx/audio_mapping_view.cc:133
+#: src/wx/audio_mapping_view.cc:141
msgid "Full"
msgstr "Full"
msgid "Full length"
msgstr "Full längd"
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
msgid "Gain"
msgstr ""
msgid "Gain for content channel %d in DCP channel %d"
msgstr "Förstärkning för innehållskanal %d i DCP-kanal %d"
-#: src/wx/properties_dialog.cc:52
+#: src/wx/properties_dialog.cc:51
msgid "Gb"
msgstr "Gb"
-#: src/wx/config_dialog.cc:813
+#: src/wx/config_dialog.cc:1155
msgid "General"
msgstr ""
-#: src/wx/audio_mapping_view.cc:301
+#: src/wx/audio_mapping_view.cc:309
msgid "HI"
msgstr "HI"
msgid "Host name or IP address"
msgstr "Datornamn eller IP-adress"
-#: src/wx/audio_panel.cc:242
-msgid "Hz"
-msgstr "Hz"
-
#: src/wx/gain_calculator_dialog.cc:29
msgid "I want to play this back at fader"
msgstr "Jag vill spela upp detta med mixervolym"
-#: src/wx/config_dialog.cc:560
+#: src/wx/config_dialog.cc:899
msgid "IP address"
msgstr "IP-adress"
-#: src/wx/config_dialog.cc:456
+#: src/wx/config_dialog.cc:462
msgid "IP address / host name"
msgstr "IP-adress / datornamn"
msgid "Input gamma"
msgstr "Indata gamma"
-#: src/wx/film_editor.cc:228
+#: src/wx/config_dialog.cc:727
+msgid "Intermediate"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:31
+msgid "Intermediate common name"
+msgstr ""
+
+#: src/wx/dcp_panel.cc:115
msgid "Interop"
msgstr "Interop"
-#: src/wx/film_editor.cc:181
+#: src/wx/dcp_panel.cc:565
msgid "JPEG2000 bandwidth"
msgstr "JPEG2000-bandbredd"
-#: src/wx/content_menu.cc:51
+#: src/wx/content_menu.cc:54
msgid "Join"
msgstr "Anslut"
-#: src/wx/config_dialog.cc:627
+#: src/wx/config_dialog.cc:966
#, fuzzy
msgid "KDM Email"
msgstr "KDM mejl"
msgstr "Tajming"
# "sekvens" eller "ordning"?
-#: src/wx/film_editor.cc:298
+#: src/wx/content_panel.cc:85
msgid "Keep video in sequence"
msgstr "Behåll video i sekvens"
-#: src/wx/audio_mapping_view.cc:277
+#: src/wx/config_dialog.cc:548
+msgid "Keys"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:285
msgid "L"
msgstr "V"
-#: src/wx/audio_mapping_view.cc:309
+#: src/wx/audio_mapping_view.cc:317
msgid "Lc"
msgstr "Vc"
-#: src/wx/video_panel.cc:88
+#: src/wx/config_dialog.cc:725
+msgid "Leaf"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:33
+msgid "Leaf common name"
+msgstr ""
+
+#: src/wx/video_panel.cc:89
msgid "Left crop"
msgstr "Vänster beskärning"
-#: src/wx/audio_mapping_view.cc:289
+#: src/wx/audio_mapping_view.cc:297
msgid "Lfe"
msgstr "Lfe"
msgid "Load from file..."
msgstr ""
-#: src/wx/config_dialog.cc:807
+#: src/wx/config_dialog.cc:620 src/wx/config_dialog.cc:633
+#: src/wx/config_dialog.cc:646
+#, fuzzy
+msgid "Load..."
+msgstr "Lägg till..."
+
+#: src/wx/config_dialog.cc:1149
msgid "Log"
msgstr ""
-#: src/wx/config_dialog.cc:804
+#: src/wx/config_dialog.cc:1146
msgid "Log:"
msgstr ""
-#: src/wx/audio_mapping_view.cc:293
+#: src/wx/audio_mapping_view.cc:301
msgid "Ls"
msgstr "Vs"
-#: src/wx/film_editor.cc:784
+#: src/wx/content_panel.cc:438
msgid "MISSING: "
msgstr "SAKNAS:"
-#: src/wx/config_dialog.cc:660
+#: src/wx/config_dialog.cc:999
msgid "Mail password"
msgstr "Mejl-lösenord"
-#: src/wx/config_dialog.cc:656
+#: src/wx/config_dialog.cc:995
msgid "Mail user name"
msgstr "Mejl-användarnamn"
msgid "Make KDMs"
msgstr "Skapa KDM:er"
+#: src/wx/make_signer_chain_dialog.cc:23
+#, fuzzy
+msgid "Make certificate chain"
+msgstr "Välj certifikatfil"
+
#: src/wx/isdcf_metadata_dialog.cc:71
msgid "Mastered luminance (e.g. 4fl)"
msgstr ""
msgid "Matrix"
msgstr "Matris"
-#: src/wx/config_dialog.cc:791
+#: src/wx/config_dialog.cc:1133
#, fuzzy
msgid "Maximum JPEG2000 bandwidth"
msgstr "JPEG2000-bandbredd"
-#: src/wx/config_dialog.cc:291 src/wx/config_dialog.cc:795
-#: src/wx/film_editor.cc:185
+#: src/wx/config_dialog.cc:297 src/wx/config_dialog.cc:1137
+#: src/wx/dcp_panel.cc:569
msgid "Mbit/s"
msgstr ""
# LÃ¥ter mysko
-#: src/wx/video_panel.cc:280
+#: src/wx/video_panel.cc:301
msgid "Multiple content selected"
msgstr "Flera innehåll valda"
msgid "My Documents"
msgstr "Mina Dokument"
-#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:512
-#: src/wx/film_editor.cc:113 src/wx/preset_colour_conversion_dialog.cc:38
+#: src/wx/content_panel.cc:442
+msgid "NEEDS KDM: "
+msgstr ""
+
+#: src/wx/cinema_dialog.cc:28 src/wx/config_dialog.cc:518
+#: src/wx/dcp_panel.cc:56 src/wx/preset_colour_conversion_dialog.cc:38
#: src/wx/screen_dialog.cc:38
msgid "Name"
msgstr "Namn"
msgid "New versions of DCP-o-matic are available."
msgstr "Nya versioner av DCP-o-matic finns tillgängligt."
-#: src/wx/audio_mapping_view.cc:348
+#: src/wx/audio_mapping_view.cc:356
#, c-format
msgid "No audio will be passed from content channel %d to DCP channel %d."
msgstr ""
"Ingen audio kommer att överföras från innehållskanalen %d till DCP-kanalen "
"%d."
-#: src/wx/video_panel.cc:153 src/wx/video_panel.cc:249
+#: src/wx/audio_panel.cc:290 src/wx/video_panel.cc:161
+#: src/wx/video_panel.cc:248
msgid "None"
msgstr "Inget"
-#: src/wx/audio_mapping_view.cc:132
+#: src/wx/audio_mapping_view.cc:140
msgid "Off"
msgstr "Av"
+#: src/wx/config_dialog.cc:1167
+msgid "Open console window"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:25
+#, fuzzy
+msgid "Organisation"
+msgstr "Längd"
+
+#: src/wx/make_signer_chain_dialog.cc:27
+msgid "Organisational unit"
+msgstr ""
+
#: src/wx/screen_dialog.cc:65
msgid "Other"
msgstr ""
-#: src/wx/config_dialog.cc:652
+#: src/wx/config_dialog.cc:991
msgid "Outgoing mail server"
msgstr "Utgående mejlserver"
+# LÃ¥ter mysko
+#: src/wx/film_viewer.cc:61
+#, fuzzy
+msgid "Outline content"
+msgstr "Flera innehåll valda"
+
#: src/wx/kdm_dialog.cc:156
#, fuzzy
msgid "Output"
msgid "Package Type (e.g. OV)"
msgstr "Förpackningstyp (ex. OV)"
-#: src/wx/video_panel.cc:325
+#: src/wx/video_panel.cc:346
#, c-format
msgid "Padded with black to %dx%d (%.2f:1)\n"
msgstr "Svarta kanter tillagda för %dx%d (%.2f:1)\n"
-#: src/wx/config_dialog.cc:572
+#: src/wx/config_dialog.cc:911
msgid "Password"
msgstr "Lösenord"
msgid "Peak"
msgstr "Topp"
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
msgid "Play"
msgstr "Spela"
msgid "Pre-release"
msgstr ""
-#: src/wx/audio_mapping_view.cc:281
+#: src/wx/config_dialog.cc:638
+msgid "Private key for decrypting DCPs"
+msgstr ""
+
+#: src/wx/config_dialog.cc:612
+msgid "Private key for leaf certificate"
+msgstr ""
+
+#: src/wx/audio_panel.cc:89
+msgid "Process with"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:289
msgid "R"
msgstr "H"
msgid "Rating (e.g. 15)"
msgstr "Klassificering (ex. 15)"
-#: src/wx/audio_mapping_view.cc:313
+#: src/wx/audio_mapping_view.cc:321
msgid "Rc"
msgstr "Hc"
+#: src/wx/content_menu.cc:56
+msgid "Re-examine..."
+msgstr ""
+
+#: src/wx/config_dialog.cc:608
+msgid "Re-make certificates..."
+msgstr ""
+
#: src/wx/isdcf_metadata_dialog.cc:62
msgid "Red band"
msgstr ""
-#: src/wx/content_menu.cc:54 src/wx/film_editor.cc:284
-#: src/wx/editable_list.h:68
+#: src/wx/config_dialog.cc:599 src/wx/content_menu.cc:59
+#: src/wx/content_panel.cc:71 src/wx/editable_list.h:68
msgid "Remove"
msgstr "Ta bort"
msgid "Repeat Content"
msgstr "Repetera Innehåll"
-#: src/wx/content_menu.cc:50
+#: src/wx/content_menu.cc:53
msgid "Repeat..."
msgstr "Upprepa..."
-#: src/wx/config_dialog.cc:690
+#: src/wx/config_dialog.cc:1029
msgid "Reset to default text"
msgstr ""
-#: src/wx/film_editor.cc:175
+#: src/wx/dcp_panel.cc:559
msgid "Resolution"
msgstr "Upplösning"
msgid "Resume"
msgstr "Fortsätt"
-#: src/wx/audio_mapping_view.cc:356
+#: src/wx/audio_mapping_view.cc:364
msgid "Right click to change gain."
msgstr "Högerklicka för att ändra förstärkning."
msgid "Right crop"
msgstr "Höger beskärning"
-#: src/wx/audio_mapping_view.cc:297
+#: src/wx/config_dialog.cc:723
+msgid "Root"
+msgstr ""
+
+#: src/wx/make_signer_chain_dialog.cc:29
+msgid "Root common name"
+msgstr ""
+
+#: src/wx/audio_mapping_view.cc:305
msgid "Rs"
msgstr "Hs"
-#: src/wx/film_editor.cc:227
+#: src/wx/dcp_panel.cc:114
msgid "SMPTE"
msgstr "SMPTE"
-#: src/wx/video_panel.cc:132
+#: src/wx/video_panel.cc:143
msgid "Scale to"
msgstr "Skala om till"
-#: src/wx/video_panel.cc:316
+#: src/wx/video_panel.cc:337
#, c-format
msgid "Scaled to %dx%d (%.2f:1)\n"
msgstr "Skalad till %dx%d (%.2f:1)\n"
-#: src/wx/film_editor.cc:195
+#: src/wx/dcp_panel.cc:574
msgid "Scaler"
msgstr "Omskalare"
msgid "Select CPL XML file"
msgstr "Välj audiofil"
-#: src/wx/screen_dialog.cc:102
+#: src/wx/config_dialog.cc:679 src/wx/config_dialog.cc:795
+#: src/wx/config_dialog.cc:840 src/wx/screen_dialog.cc:102
msgid "Select Certificate File"
msgstr "Välj certifikatfil"
+#: src/wx/content_menu.cc:266
+msgid "Select KDM"
+msgstr ""
+
+#: src/wx/config_dialog.cc:769 src/wx/config_dialog.cc:817
+#, fuzzy
+msgid "Select Key File"
+msgstr "Välj certifikatfil"
+
#: src/wx/kdm_dialog.cc:185
msgid "Send by email"
msgstr "Skicka med mejl"
msgid "Server serial number"
msgstr ""
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
#, fuzzy
msgid "Servers"
msgstr "Server"
msgid "Set"
msgstr "Sätt"
-#: src/wx/config_dialog.cc:92
+#: src/wx/config_dialog.cc:99
msgid "Set language"
msgstr "Välj språk"
-#: src/wx/audio_panel.cc:48
+#: src/wx/audio_panel.cc:51
msgid "Show Audio..."
msgstr "Visa Audio..."
-#: src/wx/film_editor.cc:158
+#: src/wx/dcp_panel.cc:88
msgid "Signed"
msgstr "Signerad"
msgstr "Utjämning"
# sammanhang?
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
msgid "Snap"
msgstr "Snap"
msgid "Stable version "
msgstr "Stabil version"
-#: src/wx/film_editor.cc:190
+#: src/wx/dcp_panel.cc:96
msgid "Standard"
msgstr "Standard"
-#: src/wx/audio_panel.cc:81 src/wx/subtitle_panel.cc:79
+#: src/wx/subtitle_view.cc:39
+#, fuzzy
+msgid "Start"
+msgstr "Start"
+
+#: src/wx/audio_panel.cc:84 src/wx/subtitle_panel.cc:87
#, fuzzy
msgid "Stream"
msgstr "Audioström"
msgid "Studio (e.g. TCF)"
msgstr "Studio (ex. TCF)"
-#: src/wx/config_dialog.cc:671
+#: src/wx/config_dialog.cc:1010
msgid "Subject"
msgstr ""
+#: src/wx/subtitle_view.cc:55
+#, fuzzy
+msgid "Subtitle"
+msgstr "Undertexter"
+
#: src/wx/isdcf_metadata_dialog.cc:38
msgid "Subtitle Language (e.g. FR)"
msgstr "Undertextspråk (ex. SV)"
-#: src/wx/subtitle_panel.cc:34
+#: src/wx/subtitle_panel.cc:41 src/wx/subtitle_view.cc:32
msgid "Subtitles"
msgstr "Undertexter"
msgid "Supported by"
msgstr "Stöd från"
-#: src/wx/config_dialog.cc:542
+#: src/wx/config_dialog.cc:881
msgid "TMS"
msgstr "TMS"
-#: src/wx/config_dialog.cc:564
+#: src/wx/config_dialog.cc:903
msgid "Target path"
msgstr "Målsökväg"
msgid "Tested by"
msgstr "Översatt av"
-#: src/wx/content_menu.cc:223
+#: src/wx/content_menu.cc:252
msgid ""
"The content file(s) you specified are not the same as those that are "
"missing. Either try again with the correct content file or remove the "
msgid "There are no hints: everything looks good!"
msgstr "Det finns inga råd: allt verkar bra!"
-#: src/wx/film_viewer.cc:134
+#: src/wx/film_viewer.cc:142
msgid "There is not enough free memory to do that."
msgstr ""
msgid "Threads"
msgstr "Trådar"
-#: src/wx/config_dialog.cc:111
+#: src/wx/config_dialog.cc:118
msgid "Threads to use for encoding on this host"
msgstr "Antal trådar att använda vid kodning på denna maskin"
+#: src/wx/config_dialog.cc:583
+msgid "Thumbprint"
+msgstr ""
+
#: src/wx/audio_plot.cc:165
msgid "Time"
msgstr "Tid"
-#: src/wx/timeline_dialog.cc:32
+#: src/wx/timeline_dialog.cc:33
msgid "Timeline"
msgstr "Tidslinje"
-#: src/wx/film_editor.cc:290
+#: src/wx/content_panel.cc:77
msgid "Timeline..."
msgstr "Tidslinje..."
msgid "Timing|Timing"
msgstr "Tajming"
-#: src/wx/video_panel.cc:110
+#: src/wx/video_panel.cc:111
msgid "Top crop"
msgstr "Övre beskärning"
-#: src/wx/about_dialog.cc:107
+#: src/wx/about_dialog.cc:111
msgid "Translated by"
msgstr "Översatt av"
msgid "Trim from start"
msgstr "Trimma från start"
-#: src/wx/audio_dialog.cc:55 src/wx/video_panel.cc:75
+#: src/wx/audio_dialog.cc:55 src/wx/config_dialog.cc:575
+#: src/wx/video_panel.cc:76
msgid "Type"
msgstr "Typ"
msgid "Until"
msgstr "Tills"
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
msgid "Up"
msgstr "Upp"
msgid "Update"
msgstr "Uppdatera"
-#: src/wx/film_editor.cc:123
+#: src/wx/dcp_panel.cc:66
#, fuzzy
msgid "Use ISDCF name"
msgstr "Använd DCI-namnet"
-#: src/wx/config_dialog.cc:452
+#: src/wx/config_dialog.cc:458
msgid "Use all servers"
msgstr "Använd alla servrar"
-#: src/wx/film_editor.cc:152
+#: src/wx/dcp_panel.cc:545
msgid "Use best"
msgstr "Använd bästa"
msgid "Use preset"
msgstr "Använd förhandsinställning"
-#: src/wx/config_dialog.cc:568
+#: src/wx/subtitle_panel.cc:47
+#, fuzzy
+msgid "Use subtitles"
+msgstr "Undertexter"
+
+#: src/wx/config_dialog.cc:907
msgid "User name"
msgstr "Användarnamn"
-#: src/wx/audio_mapping_view.cc:305
+#: src/wx/audio_mapping_view.cc:313
msgid "VI"
msgstr "VI"
-#: src/wx/video_panel.cc:68
+#: src/wx/dcp_panel.cc:85 src/wx/video_panel.cc:69
msgid "Video"
msgstr "Video"
msgid "Video frame rate"
msgstr "bildhastighet"
-#: src/wx/config_dialog.cc:815
-msgid "Warnings"
+#: src/wx/subtitle_panel.cc:91
+msgid "View..."
msgstr ""
-#: src/wx/subtitle_panel.cc:39
-msgid "With Subtitles"
-msgstr "Med Undertexter"
+#: src/wx/config_dialog.cc:1157
+msgid "Warnings"
+msgstr ""
#: src/wx/kdm_dialog.cc:172
msgid "Write to"
msgstr "Skriv till"
-#: src/wx/about_dialog.cc:91
+#: src/wx/about_dialog.cc:95
msgid "Written by"
msgstr "Skriven av"
-#: src/wx/subtitle_panel.cc:44
+#: src/wx/subtitle_panel.cc:52
#, fuzzy
msgid "X Offset"
msgstr "Undertext Förskjutning"
-#: src/wx/subtitle_panel.cc:62
+#: src/wx/subtitle_panel.cc:70
#, fuzzy
msgid "X Scale"
msgstr "Omskalare"
-#: src/wx/subtitle_panel.cc:53
+#: src/wx/subtitle_panel.cc:61
#, fuzzy
msgid "Y Offset"
msgstr "Undertext Förskjutning"
-#: src/wx/subtitle_panel.cc:71
+#: src/wx/subtitle_panel.cc:79
#, fuzzy
msgid "Y Scale"
msgstr "Omskalare"
"Din DCP har mindre än 6 audiokanaler. Detta kan medföra problem på vissa "
"projektorer."
-#: src/wx/timeline.cc:220
+#: src/wx/timeline.cc:229
msgid "audio"
msgstr "audio"
-#: src/wx/audio_panel.cc:240
-msgid "channels"
-msgstr "kanaler"
-
#: src/wx/properties_dialog.cc:46
msgid "counting..."
msgstr "räknar..."
-#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:62
+#: src/wx/audio_gain_dialog.cc:30 src/wx/audio_panel.cc:65
msgid "dB"
msgstr "dB"
#. / TRANSLATORS: this is an abbreviation for milliseconds, the unit of time
-#: src/wx/audio_panel.cc:78 src/wx/config_dialog.cc:300
+#: src/wx/audio_panel.cc:81 src/wx/config_dialog.cc:306
msgid "ms"
msgstr "ms"
-#: src/wx/config_dialog.cc:258
+#: src/wx/config_dialog.cc:264
msgid "s"
msgstr "s"
-#: src/wx/timeline.cc:243
+#: src/wx/timeline.cc:258
msgid "still"
msgstr "stillbild"
+#: src/wx/timeline.cc:289
+#, fuzzy
+msgid "subtitles"
+msgstr "Undertexter"
+
# Sammanhang?
#: src/wx/repeat_dialog.cc:28
msgid "times"
msgstr "tider"
-#: src/wx/timeline.cc:241
+#: src/wx/timeline.cc:260
msgid "video"
msgstr "video"
+#~ msgid "1 channel"
+#~ msgstr "1 kanal"
+
+#~ msgid "Audio channels"
+#~ msgstr "Audio-kanaler"
+
+#~ msgid "Could not decode video for view (%s)"
+#~ msgstr "Kunde inte avkoda video för visning (%s)"
+
+#~ msgid "Hz"
+#~ msgstr "Hz"
+
+#~ msgid "With Subtitles"
+#~ msgstr "Med Undertexter"
+
+#~ msgid "channels"
+#~ msgstr "kanaler"
+
#, fuzzy
#~ msgid "Default creator"
#~ msgstr "Förvald innehållstyp"
#~ msgid "Add"
#~ msgstr "Lägg till"
-#~ msgid "Duration"
-#~ msgstr "Längd"
-
#~ msgid "Edit"
#~ msgstr "Redigera"
#~ msgid "Running"
#~ msgstr "Körs"
-#, fuzzy
-#~ msgid "Start time"
-#~ msgstr "Start"
-
#~ msgid "A/B"
#~ msgstr "A/B"
#~ msgid "DVD-o-matic Preferences"
#~ msgstr "DVD-o-matic Inställningar"
-#~ msgid "End"
-#~ msgstr "Slut"
-
#~ msgid "Film"
#~ msgstr "Film"
add (_("Frames already encoded"), true);
_encoded = add (new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this)));
_encoded->Finished.connect (boost::bind (&PropertiesDialog::layout, this));
-
- _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->time_to_video_frames (_film->length()))));
+ _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().frames (_film->video_frame_rate ()))));
double const disk = double (_film->required_disk_space()) / 1073741824.0f;
SafeStringStream s;
s << fixed << setprecision (1) << disk << wx_to_std (_("Gb"));
} catch (boost::thread_interrupted &) {
return "";
}
-
- if (_film->length()) {
+
+ uint64_t const frames = _film->length().frames (_film->video_frame_rate ());
+ if (frames) {
/* XXX: encoded_frames() should check which frames have been encoded */
- u << " (" << (_film->encoded_frames() * 100 / _film->time_to_video_frames (_film->length())) << "%)";
+ u << " (" << (_film->encoded_frames() * 100 / frames) << "%)";
}
return u.str ();
}
#include <wx/filepicker.h>
#include <wx/validate.h>
-#include <libdcp/exceptions.h>
+#include <dcp/exceptions.h>
#include "lib/compose.hpp"
#include "lib/util.h"
#include "screen_dialog.h"
using std::string;
using std::cout;
-using boost::shared_ptr;
+using boost::optional;
-ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_ptr<libdcp::Certificate> certificate)
+ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, optional<dcp::Certificate> certificate)
: TableDialog (parent, std_to_wx (title), 2, true)
, _certificate (certificate)
{
return wx_to_std (_name->GetValue());
}
-shared_ptr<libdcp::Certificate>
+optional<dcp::Certificate>
ScreenDialog::certificate () const
{
return _certificate;
ScreenDialog::load_certificate (boost::filesystem::path file)
{
try {
- _certificate.reset (new libdcp::Certificate (file));
+ _certificate = dcp::Certificate (dcp::file_to_string (file));
_certificate_text->SetValue (_certificate->certificate ());
- } catch (libdcp::MiscError& e) {
+ } catch (dcp::MiscError& e) {
error_dialog (this, wxString::Format (_("Could not read certificate file (%s)"), e.what()));
}
}
#include <wx/wx.h>
#include <boost/shared_ptr.hpp>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
#include "table_dialog.h"
class Progress;
class ScreenDialog : public TableDialog
{
public:
- ScreenDialog (wxWindow *, std::string, std::string name = "", boost::shared_ptr<libdcp::Certificate> c = boost::shared_ptr<libdcp::Certificate> ());
+ ScreenDialog (wxWindow *, std::string, std::string name = "", boost::optional<dcp::Certificate> c = boost::optional<dcp::Certificate> ());
std::string name () const;
- boost::shared_ptr<libdcp::Certificate> certificate () const;
+ boost::optional<dcp::Certificate> certificate () const;
private:
void select_certificate ();
wxButton* _download_certificate;
wxTextCtrl* _certificate_text;
- boost::shared_ptr<libdcp::Certificate> _certificate;
+ boost::optional<dcp::Certificate> _certificate;
};
#include <boost/lexical_cast.hpp>
#include <wx/spinctrl.h>
#include "lib/ffmpeg_content.h"
+#include "lib/subrip_content.h"
+#include "lib/ffmpeg_subtitle_stream.h"
+#include "lib/dcp_subtitle_content.h"
+#include "lib/subrip_decoder.h"
+#include "lib/dcp_subtitle_decoder.h"
#include "subtitle_panel.h"
#include "film_editor.h"
#include "wx_util.h"
+#include "subtitle_view.h"
+#include "content_panel.h"
using std::vector;
using std::string;
using boost::lexical_cast;
using boost::dynamic_pointer_cast;
-SubtitlePanel::SubtitlePanel (FilmEditor* e)
- : FilmEditorPanel (e, _("Subtitles"))
+SubtitlePanel::SubtitlePanel (ContentPanel* p)
+ : ContentSubPanel (p, _("Subtitles"))
+ , _view (0)
{
wxFlexGridSizer* grid = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
_sizer->Add (grid, 0, wxALL, 8);
- _with_subtitles = new wxCheckBox (this, wxID_ANY, _("With Subtitles"));
- grid->Add (_with_subtitles, 1);
+ _use = new wxCheckBox (this, wxID_ANY, _("Use subtitles"));
+ grid->Add (_use);
grid->AddSpacer (0);
-
+
{
add_label_to_sizer (grid, this, _("X Offset"), true);
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
add_label_to_sizer (grid, this, _("Stream"), true);
_stream = new wxChoice (this, wxID_ANY);
grid->Add (_stream, 1, wxEXPAND);
+
+ _view_button = new wxButton (this, wxID_ANY, _("View..."));
+ grid->Add (_view_button);
_x_offset->SetRange (-100, 100);
_y_offset->SetRange (-100, 100);
_x_scale->SetRange (10, 1000);
_y_scale->SetRange (10, 1000);
- _with_subtitles->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::with_subtitles_toggled, this));
- _x_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_offset_changed, this));
- _y_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_offset_changed, this));
- _x_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_scale_changed, this));
- _y_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_scale_changed, this));
- _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&SubtitlePanel::stream_changed, this));
+ _use->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&SubtitlePanel::use_toggled, this));
+ _x_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_offset_changed, this));
+ _y_offset->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_offset_changed, this));
+ _x_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::x_scale_changed, this));
+ _y_scale->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&SubtitlePanel::y_scale_changed, this));
+ _stream->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&SubtitlePanel::stream_changed, this));
+ _view_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&SubtitlePanel::view_clicked, this));
}
void
SubtitlePanel::film_changed (Film::Property property)
{
- switch (property) {
- case Film::CONTENT:
- setup_sensitivity ();
- break;
- case Film::WITH_SUBTITLES:
- checked_set (_with_subtitles, _editor->film()->with_subtitles ());
+ if (property == Film::CONTENT) {
setup_sensitivity ();
- break;
- default:
- break;
}
}
void
SubtitlePanel::film_content_changed (int property)
{
- FFmpegContentList fc = _editor->selected_ffmpeg_content ();
- SubtitleContentList sc = _editor->selected_subtitle_content ();
+ FFmpegContentList fc = _parent->selected_ffmpeg ();
+ SubtitleContentList sc = _parent->selected_subtitle ();
shared_ptr<FFmpegContent> fcs;
if (fc.size() == 1) {
if (sc.size() == 1) {
scs = sc.front ();
}
-
+
if (property == FFmpegContentProperty::SUBTITLE_STREAMS) {
_stream->Clear ();
if (fcs) {
}
}
setup_sensitivity ();
+ } else if (property == SubtitleContentProperty::USE_SUBTITLES) {
+ checked_set (_use, scs ? scs->use_subtitles() : false);
+ setup_sensitivity ();
} else if (property == SubtitleContentProperty::SUBTITLE_X_OFFSET) {
checked_set (_x_offset, scs ? (scs->subtitle_x_offset() * 100) : 0);
} else if (property == SubtitleContentProperty::SUBTITLE_Y_OFFSET) {
}
void
-SubtitlePanel::with_subtitles_toggled ()
+SubtitlePanel::use_toggled ()
{
- if (!_editor->film()) {
- return;
+ SubtitleContentList c = _parent->selected_subtitle ();
+ for (SubtitleContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ (*i)->set_use_subtitles (_use->GetValue());
}
-
- _editor->film()->set_with_subtitles (_with_subtitles->GetValue ());
}
void
SubtitlePanel::setup_sensitivity ()
{
- bool h = false;
- bool j = false;
- if (_editor->film()) {
- h = _editor->film()->has_subtitles ();
- j = _editor->film()->with_subtitles ();
+ int any_subs = 0;
+ int ffmpeg_subs = 0;
+ int subrip_or_dcp_subs = 0;
+ SubtitleContentList c = _parent->selected_subtitle ();
+ for (SubtitleContentList::const_iterator i = c.begin(); i != c.end(); ++i) {
+ shared_ptr<const FFmpegContent> fc = boost::dynamic_pointer_cast<const FFmpegContent> (*i);
+ shared_ptr<const SubRipContent> sc = boost::dynamic_pointer_cast<const SubRipContent> (*i);
+ shared_ptr<const DCPSubtitleContent> dsc = boost::dynamic_pointer_cast<const DCPSubtitleContent> (*i);
+ if (fc) {
+ if (fc->has_subtitles ()) {
+ ++ffmpeg_subs;
+ ++any_subs;
+ }
+ } else if (sc || dsc) {
+ ++subrip_or_dcp_subs;
+ ++any_subs;
+ } else {
+ ++any_subs;
+ }
}
+
+ _use->Enable (any_subs > 0);
+ bool const use = _use->GetValue ();
- _with_subtitles->Enable (h);
- _x_offset->Enable (j);
- _y_offset->Enable (j);
- _x_scale->Enable (j);
- _y_scale->Enable (j);
- _stream->Enable (j);
+ _x_offset->Enable (any_subs > 0 && use);
+ _y_offset->Enable (any_subs > 0 && use);
+ _x_scale->Enable (any_subs > 0 && use);
+ _y_scale->Enable (any_subs > 0 && use);
+ _stream->Enable (ffmpeg_subs == 1);
+ _view_button->Enable (subrip_or_dcp_subs == 1);
}
void
SubtitlePanel::stream_changed ()
{
- FFmpegContentList fc = _editor->selected_ffmpeg_content ();
+ FFmpegContentList fc = _parent->selected_ffmpeg ();
if (fc.size() != 1) {
return;
}
void
SubtitlePanel::x_offset_changed ()
{
- SubtitleContentList c = _editor->selected_subtitle_content ();
- if (c.size() == 1) {
- c.front()->set_subtitle_x_offset (_x_offset->GetValue() / 100.0);
+ SubtitleContentList c = _parent->selected_subtitle ();
+ for (SubtitleContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ (*i)->set_subtitle_x_offset (_x_offset->GetValue() / 100.0);
}
}
void
SubtitlePanel::y_offset_changed ()
{
- SubtitleContentList c = _editor->selected_subtitle_content ();
- if (c.size() == 1) {
- c.front()->set_subtitle_y_offset (_y_offset->GetValue() / 100.0);
+ SubtitleContentList c = _parent->selected_subtitle ();
+ for (SubtitleContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ (*i)->set_subtitle_y_offset (_y_offset->GetValue() / 100.0);
}
}
void
SubtitlePanel::x_scale_changed ()
{
- SubtitleContentList c = _editor->selected_subtitle_content ();
+ SubtitleContentList c = _parent->selected_subtitle ();
if (c.size() == 1) {
c.front()->set_subtitle_x_scale (_x_scale->GetValue() / 100.0);
}
void
SubtitlePanel::y_scale_changed ()
{
- SubtitleContentList c = _editor->selected_subtitle_content ();
- if (c.size() == 1) {
- c.front()->set_subtitle_y_scale (_y_scale->GetValue() / 100.0);
+ SubtitleContentList c = _parent->selected_subtitle ();
+ for (SubtitleContentList::iterator i = c.begin(); i != c.end(); ++i) {
+ (*i)->set_subtitle_y_scale (_y_scale->GetValue() / 100.0);
}
}
SubtitlePanel::content_selection_changed ()
{
film_content_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
+ film_content_changed (SubtitleContentProperty::USE_SUBTITLES);
film_content_changed (SubtitleContentProperty::SUBTITLE_X_OFFSET);
film_content_changed (SubtitleContentProperty::SUBTITLE_Y_OFFSET);
film_content_changed (SubtitleContentProperty::SUBTITLE_X_SCALE);
film_content_changed (SubtitleContentProperty::SUBTITLE_Y_SCALE);
}
+
+void
+SubtitlePanel::view_clicked ()
+{
+ if (_view) {
+ _view->Destroy ();
+ _view = 0;
+ }
+
+ SubtitleContentList c = _parent->selected_subtitle ();
+ assert (c.size() == 1);
+
+ shared_ptr<SubtitleDecoder> decoder;
+
+ shared_ptr<SubRipContent> sr = dynamic_pointer_cast<SubRipContent> (c.front ());
+ if (sr) {
+ decoder.reset (new SubRipDecoder (sr));
+ }
+
+ shared_ptr<DCPSubtitleContent> dc = dynamic_pointer_cast<DCPSubtitleContent> (c.front ());
+ if (dc) {
+ decoder.reset (new DCPSubtitleDecoder (dc));
+ }
+
+ if (decoder) {
+ _view = new SubtitleView (this, _parent->film(), decoder, c.front()->position ());
+ _view->Show ();
+ }
+}
/*
- 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 "film_editor_panel.h"
+#include "content_sub_panel.h"
class wxCheckBox;
class wxSpinCtrl;
+class SubtitleView;
-class SubtitlePanel : public FilmEditorPanel
+class SubtitlePanel : public ContentSubPanel
{
public:
- SubtitlePanel (FilmEditor *);
+ SubtitlePanel (ContentPanel *);
void film_changed (Film::Property);
void film_content_changed (int);
void content_selection_changed ();
private:
- void with_subtitles_toggled ();
+ void use_toggled ();
void x_offset_changed ();
void y_offset_changed ();
void x_scale_changed ();
void y_scale_changed ();
void stream_changed ();
+ void view_clicked ();
void setup_sensitivity ();
- wxCheckBox* _with_subtitles;
+ wxCheckBox* _use;
wxSpinCtrl* _x_offset;
wxSpinCtrl* _y_offset;
wxSpinCtrl* _x_scale;
wxSpinCtrl* _y_scale;
wxChoice* _stream;
+ wxButton* _view_button;
+ SubtitleView* _view;
};
--- /dev/null
+/*
+ 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 "lib/subrip_decoder.h"
+#include "lib/content_subtitle.h"
+#include "lib/film.h"
+#include "lib/subrip_content.h"
+#include "subtitle_view.h"
+#include "wx_util.h"
+
+using std::list;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+SubtitleView::SubtitleView (wxWindow* parent, shared_ptr<Film> film, shared_ptr<SubtitleDecoder> decoder, DCPTime position)
+ : wxDialog (parent, wxID_ANY, _("Subtitles"))
+{
+ _list = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
+
+ {
+ wxListItem ip;
+ ip.SetId (0);
+ ip.SetText (_("Start"));
+ ip.SetWidth (100);
+ _list->InsertColumn (0, ip);
+ }
+
+ {
+ wxListItem ip;
+ ip.SetId (1);
+ ip.SetText (_("End"));
+ ip.SetWidth (100);
+ _list->InsertColumn (1, ip);
+ }
+
+ {
+ wxListItem ip;
+ ip.SetId (2);
+ ip.SetText (_("Subtitle"));
+ ip.SetWidth (640);
+ _list->InsertColumn (2, ip);
+ }
+
+ wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+ sizer->Add (_list, 1, wxEXPAND);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
+ if (buttons) {
+ sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ list<ContentTextSubtitle> subs = decoder->get_text_subtitles (ContentTimePeriod (ContentTime(), ContentTime::max ()), true);
+ FrameRateChange const frc = film->active_frame_rate_change (position);
+ int n = 0;
+ for (list<ContentTextSubtitle>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
+ for (list<dcp::SubtitleString>::const_iterator j = i->subs.begin(); j != i->subs.end(); ++j) {
+ wxListItem list_item;
+ list_item.SetId (n);
+ _list->InsertItem (list_item);
+ ContentTimePeriod const p = i->period ();
+ _list->SetItem (n, 0, std_to_wx (p.from.timecode (frc.source)));
+ _list->SetItem (n, 1, std_to_wx (p.to.timecode (frc.source)));
+ _list->SetItem (n, 2, std_to_wx (j->text ()));
+ ++n;
+ }
+ }
+
+ SetSizerAndFit (sizer);
+}
+
--- /dev/null
+/*
+ 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/shared_ptr.hpp>
+#include <wx/wx.h>
+#include <wx/listctrl.h>
+
+class SubtitleDecoder;
+
+class SubtitleView : public wxDialog
+{
+public:
+ SubtitleView (wxWindow *, boost::shared_ptr<Film>, boost::shared_ptr<SubtitleDecoder>, DCPTime position);
+
+private:
+ wxListCtrl* _list;
+};
/*
- 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/lexical_cast.hpp>
#include "lib/util.h"
#include "timecode.h"
#include "wx_util.h"
+#include <boost/lexical_cast.hpp>
using std::string;
using std::cout;
using boost::lexical_cast;
-Timecode::Timecode (wxWindow* parent)
+TimecodeBase::TimecodeBase (wxWindow* parent)
: wxPanel (parent)
{
wxClientDC dc (parent);
_fixed = add_label_to_sizer (_sizer, this, wxT ("42"), false);
- _hours->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _minutes->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _seconds->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _frames->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&Timecode::changed, this));
- _set_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&Timecode::set_clicked, this));
+ _hours->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TimecodeBase::changed, this));
+ _minutes->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TimecodeBase::changed, this));
+ _seconds->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TimecodeBase::changed, this));
+ _frames->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&TimecodeBase::changed, this));
+ _set_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&TimecodeBase::set_clicked, this));
_set_button->Enable (false);
}
void
-Timecode::set (Time t, int fps)
-{
- /* Do this calculation with frames so that we can round
- to a frame boundary at the start rather than the end.
- */
- int64_t f = divide_with_round (t * fps, TIME_HZ);
-
- int const h = f / (3600 * fps);
- f -= h * 3600 * fps;
- int const m = f / (60 * fps);
- f -= m * 60 * fps;
- int const s = f / fps;
- f -= s * fps;
-
- checked_set (_hours, lexical_cast<string> (h));
- checked_set (_minutes, lexical_cast<string> (m));
- checked_set (_seconds, lexical_cast<string> (s));
- checked_set (_frames, lexical_cast<string> (f));
-
- _fixed->SetLabel (wxString::Format ("%02d:%02d:%02d.%02" wxLongLongFmtSpec "d", h, m, s, f));
-}
-
-Time
-Timecode::get (int fps) const
-{
- Time t = 0;
- string const h = wx_to_std (_hours->GetValue ());
- t += lexical_cast<int> (h.empty() ? "0" : h) * 3600 * TIME_HZ;
- string const m = wx_to_std (_minutes->GetValue());
- t += lexical_cast<int> (m.empty() ? "0" : m) * 60 * TIME_HZ;
- string const s = wx_to_std (_seconds->GetValue());
- t += lexical_cast<int> (s.empty() ? "0" : s) * TIME_HZ;
- string const f = wx_to_std (_frames->GetValue());
- t += lexical_cast<int> (f.empty() ? "0" : f) * TIME_HZ / fps;
-
- return t;
-}
-
-void
-Timecode::clear ()
+TimecodeBase::clear ()
{
checked_set (_hours, "");
checked_set (_minutes, "");
}
void
-Timecode::changed ()
+TimecodeBase::changed ()
{
_set_button->Enable (true);
}
void
-Timecode::set_clicked ()
+TimecodeBase::set_clicked ()
{
Changed ();
_set_button->Enable (false);
}
void
-Timecode::set_editable (bool e)
+TimecodeBase::set_editable (bool e)
{
_editable->Show (e);
_fixed->Show (!e);
/*
- 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/signals2.hpp>
-#include <wx/wx.h>
+#ifndef DCPOMATIC_WX_TIMECODE_H
+#define DCPOMATIC_WX_TIMECODE_H
+
+#include "wx_util.h"
#include "lib/types.h"
+#include <wx/wx.h>
+#include <boost/signals2.hpp>
+#include <boost/lexical_cast.hpp>
-class Timecode : public wxPanel
+class TimecodeBase : public wxPanel
{
public:
- Timecode (wxWindow *);
+ TimecodeBase (wxWindow *);
- void set (Time, int);
- Time get (int) const;
void clear ();
void set_editable (bool);
boost::signals2::signal<void ()> Changed;
-private:
+protected:
void changed ();
void set_clicked ();
wxStaticText* _fixed;
};
+template <class T>
+class Timecode : public TimecodeBase
+{
+public:
+ Timecode (wxWindow* parent)
+ : TimecodeBase (parent)
+ {
+
+ }
+
+ void set (T t, int fps)
+ {
+ int h;
+ int m;
+ int s;
+ int f;
+ t.split (fps, h, m, s, f);
+
+ checked_set (_hours, boost::lexical_cast<std::string> (h));
+ checked_set (_minutes, boost::lexical_cast<std::string> (m));
+ checked_set (_seconds, boost::lexical_cast<std::string> (s));
+ checked_set (_frames, boost::lexical_cast<std::string> (f));
+
+ _fixed->SetLabel (std_to_wx (t.timecode (fps)));
+ }
+
+ T get (int fps) const
+ {
+ T t;
+ std::string const h = wx_to_std (_hours->GetValue ());
+ t += T::from_seconds (boost::lexical_cast<int> (h.empty() ? "0" : h) * 3600);
+ std::string const m = wx_to_std (_minutes->GetValue());
+ t += T::from_seconds (boost::lexical_cast<int> (m.empty() ? "0" : m) * 60);
+ std::string const s = wx_to_std (_seconds->GetValue());
+ t += T::from_seconds (boost::lexical_cast<int> (s.empty() ? "0" : s));
+ std::string const f = wx_to_std (_frames->GetValue());
+ t += T::from_seconds (boost::lexical_cast<double> (f.empty() ? "0" : f) / fps);
+
+ return t;
+ }
+};
+
+#endif
#include <boost/weak_ptr.hpp>
#include "lib/film.h"
#include "lib/playlist.h"
+#include "lib/image_content.h"
#include "film_editor.h"
#include "timeline.h"
+#include "content_panel.h"
#include "wx_util.h"
using std::list;
using boost::bind;
using boost::optional;
-/** Parent class for components of the timeline (e.g. a piece of content or an axis) */
+/** @class View
+ * @brief Parent class for components of the timeline (e.g. a piece of content or an axis).
+ */
class View : public boost::noncopyable
{
public:
protected:
virtual void do_paint (wxGraphicsContext *) = 0;
- int time_x (Time t) const
+ int time_x (DCPTime t) const
{
- return _timeline.tracks_position().x + t * _timeline.pixels_per_time_unit().get_value_or (0);
+ return _timeline.tracks_position().x + t.seconds() * _timeline.pixels_per_second().get_value_or (0);
}
Timeline& _timeline;
};
-/** Parent class for views of pieces of content */
+/** @class ContentView
+ * @brief Parent class for views of pieces of content.
+ */
class ContentView : public View
{
public:
return dcpomatic::Rect<int> (
time_x (content->position ()) - 8,
y_pos (_track.get()) - 8,
- content->length_after_trim () * _timeline.pixels_per_time_unit().get_value_or(0) + 16,
+ content->length_after_trim().seconds() * _timeline.pixels_per_second().get_value_or(0) + 16,
_timeline.track_height() + 16
);
}
}
virtual wxString type () const = 0;
- virtual wxColour colour () const = 0;
+ virtual wxColour background_colour () const = 0;
+ virtual wxColour foreground_colour () const = 0;
private:
return;
}
- Time const position = cont->position ();
- Time const len = cont->length_after_trim ();
+ DCPTime const position = cont->position ();
+ DCPTime const len = cont->length_after_trim ();
- wxColour selected (colour().Red() / 2, colour().Green() / 2, colour().Blue() / 2);
+ wxColour selected (background_colour().Red() / 2, background_colour().Green() / 2, background_colour().Blue() / 2);
- gc->SetPen (*wxBLACK_PEN);
-
- gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, wxPENSTYLE_SOLID));
+ gc->SetPen (*wxThePenList->FindOrCreatePen (foreground_colour(), 4, wxPENSTYLE_SOLID));
if (_selected) {
gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (selected, wxBRUSHSTYLE_SOLID));
} else {
- gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (colour(), wxBRUSHSTYLE_SOLID));
+ gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (background_colour(), wxBRUSHSTYLE_SOLID));
}
wxGraphicsPath path = gc->CreatePath ();
gc->StrokePath (path);
gc->FillPath (path);
- wxString name = wxString::Format (wxT ("%s [%s]"), std_to_wx (cont->path_summary()).data(), type().data());
+ wxString name = wxString::Format (wxT ("%s [%s]"), std_to_wx (cont->summary()).data(), type().data());
wxDouble name_width;
wxDouble name_height;
wxDouble name_descent;
wxDouble name_leading;
gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
- gc->Clip (wxRegion (time_x (position), y_pos (_track.get()), len * _timeline.pixels_per_time_unit().get_value_or(0), _timeline.track_height()));
+ gc->Clip (wxRegion (time_x (position), y_pos (_track.get()), len.seconds() * _timeline.pixels_per_second().get_value_or(0), _timeline.track_height()));
+ gc->SetFont (gc->CreateFont (*wxNORMAL_FONT, foreground_colour ()));
gc->DrawText (name, time_x (position) + 12, y_pos (_track.get() + 1) - name_height - 4);
gc->ResetClip ();
}
}
if (!frequent) {
- _timeline.setup_pixels_per_time_unit ();
+ _timeline.setup_pixels_per_second ();
_timeline.Refresh ();
}
}
boost::signals2::scoped_connection _content_connection;
};
+/** @class AudioContentView
+ * @brief Timeline view for AudioContent.
+ */
class AudioContentView : public ContentView
{
public:
return _("audio");
}
- wxColour colour () const
+ wxColour background_colour () const
{
return wxColour (149, 121, 232, 255);
}
+
+ wxColour foreground_colour () const
+ {
+ return wxColour (0, 0, 0, 255);
+ }
};
+/** @class AudioContentView
+ * @brief Timeline view for VideoContent.
+ */
class VideoContentView : public ContentView
{
public:
wxString type () const
{
- if (dynamic_pointer_cast<FFmpegContent> (content ())) {
- return _("video");
- } else {
+ if (dynamic_pointer_cast<ImageContent> (content ()) && content()->number_of_paths() == 1) {
return _("still");
+ } else {
+ return _("video");
}
}
- wxColour colour () const
+ wxColour background_colour () const
{
return wxColour (242, 92, 120, 255);
}
+
+ wxColour foreground_colour () const
+ {
+ return wxColour (0, 0, 0, 255);
+ }
+};
+
+/** @class AudioContentView
+ * @brief Timeline view for SubtitleContent.
+ */
+class SubtitleContentView : public ContentView
+{
+public:
+ SubtitleContentView (Timeline& tl, shared_ptr<SubtitleContent> c)
+ : ContentView (tl, c)
+ , _subtitle_content (c)
+ {}
+
+private:
+ wxString type () const
+ {
+ return _("subtitles");
+ }
+
+ wxColour background_colour () const
+ {
+ shared_ptr<SubtitleContent> sc = _subtitle_content.lock ();
+ if (!sc || !sc->use_subtitles ()) {
+ return wxColour (210, 210, 210, 128);
+ }
+
+ return wxColour (163, 255, 154, 255);
+ }
+
+ wxColour foreground_colour () const
+ {
+ shared_ptr<SubtitleContent> sc = _subtitle_content.lock ();
+ if (!sc || !sc->use_subtitles ()) {
+ return wxColour (180, 180, 180, 128);
+ }
+
+ return wxColour (0, 0, 0, 255);
+ }
+
+ boost::weak_ptr<SubtitleContent> _subtitle_content;
};
class TimeAxisView : public View
void do_paint (wxGraphicsContext* gc)
{
- if (!_timeline.pixels_per_time_unit()) {
+ if (!_timeline.pixels_per_second()) {
return;
}
- double const pptu = _timeline.pixels_per_time_unit().get ();
+ double const pps = _timeline.pixels_per_second().get ();
gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
- int mark_interval = rint (128 / (TIME_HZ * pptu));
+ double mark_interval = rint (128 / pps);
if (mark_interval > 5) {
- mark_interval -= mark_interval % 5;
+ mark_interval -= int (rint (mark_interval)) % 5;
}
if (mark_interval > 10) {
- mark_interval -= mark_interval % 10;
+ mark_interval -= int (rint (mark_interval)) % 10;
}
if (mark_interval > 60) {
- mark_interval -= mark_interval % 60;
+ mark_interval -= int (rint (mark_interval)) % 60;
}
if (mark_interval > 3600) {
- mark_interval -= mark_interval % 3600;
+ mark_interval -= int (rint (mark_interval)) % 3600;
}
if (mark_interval < 1) {
path.AddLineToPoint (_timeline.width(), _y);
gc->StrokePath (path);
- Time t = 0;
- while ((t * pptu) < _timeline.width()) {
+ gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
+
+ /* Time in seconds */
+ DCPTime t;
+ while ((t.seconds() * pps) < _timeline.width()) {
wxGraphicsPath path = gc->CreatePath ();
path.MoveToPoint (time_x (t), _y - 4);
path.AddLineToPoint (time_x (t), _y + 4);
gc->StrokePath (path);
- int tc = t / TIME_HZ;
+ double tc = t.seconds ();
int const h = tc / 3600;
tc -= h * 3600;
int const m = tc / 60;
wxDouble str_leading;
gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading);
- int const tx = _timeline.x_offset() + t * pptu;
+ int const tx = _timeline.x_offset() + t.seconds() * pps;
if ((tx + str_width) < _timeline.width()) {
gc->DrawText (str, time_x (t), _y + 16);
}
- t += mark_interval * TIME_HZ;
+ t += DCPTime::from_seconds (mark_interval);
}
}
};
-Timeline::Timeline (wxWindow* parent, FilmEditor* ed, shared_ptr<Film> film)
+Timeline::Timeline (wxWindow* parent, ContentPanel* cp, shared_ptr<Film> film)
: wxPanel (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
- , _film_editor (ed)
+ , _content_panel (cp)
, _film (film)
, _time_axis_view (new TimeAxisView (*this, 32))
, _tracks (0)
return;
}
- gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
-
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
(*i)->paint (gc);
}
if (dynamic_pointer_cast<AudioContent> (*i)) {
_views.push_back (shared_ptr<View> (new AudioContentView (*this, *i)));
}
+
+ shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (*i);
+ if (sc && sc->has_subtitles ()) {
+ _views.push_back (shared_ptr<View> (new SubtitleContentView (*this, sc)));
+ }
}
assign_tracks ();
- setup_pixels_per_time_unit ();
+ setup_pixels_per_second ();
Refresh ();
}
if (property == ContentProperty::POSITION) {
assign_tracks ();
- setup_pixels_per_time_unit ();
+ setup_pixels_per_second ();
Refresh ();
}
}
}
void
-Timeline::setup_pixels_per_time_unit ()
+Timeline::setup_pixels_per_second ()
{
shared_ptr<const Film> film = _film.lock ();
- if (!film || film->length() == 0) {
+ if (!film || film->length() == DCPTime ()) {
return;
}
- _pixels_per_time_unit = static_cast<double>(width() - x_offset() * 2) / film->length ();
+ _pixels_per_second = static_cast<double>(width() - x_offset() * 2) / film->length().seconds ();
}
shared_ptr<View>
}
if (view == *i) {
- _film_editor->set_selection (cv->content ());
+ _content_panel->set_selection (cv->content ());
}
}
void
Timeline::set_position_from_event (wxMouseEvent& ev)
{
- if (!_pixels_per_time_unit) {
+ if (!_pixels_per_second) {
return;
}
- double const pptu = _pixels_per_time_unit.get ();
+ double const pps = _pixels_per_second.get ();
wxPoint const p = ev.GetPosition();
return;
}
- Time new_position = _down_view_position + (p.x - _down_point.x) / pptu;
+ DCPTime new_position = _down_view_position + DCPTime::from_seconds ((p.x - _down_point.x) / pps);
if (_snap) {
bool first = true;
- Time nearest_distance = TIME_MAX;
- Time nearest_new_position = TIME_MAX;
+ DCPTime nearest_distance = DCPTime::max ();
+ DCPTime nearest_new_position = DCPTime::max ();
/* Find the nearest content edge; this is inefficient */
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
{
/* Snap starts to ends */
- Time const d = abs (cv->content()->end() - new_position);
+ DCPTime const d = DCPTime (cv->content()->end() - new_position).abs ();
if (first || d < nearest_distance) {
nearest_distance = d;
nearest_new_position = cv->content()->end();
{
/* Snap ends to starts */
- Time const d = abs (cv->content()->position() - (new_position + _down_view->content()->length_after_trim()));
+ DCPTime const d = DCPTime (
+ cv->content()->position() - (new_position + _down_view->content()->length_after_trim())
+ ).abs ();
+
if (d < nearest_distance) {
nearest_distance = d;
nearest_new_position = cv->content()->position() - _down_view->content()->length_after_trim ();
if (!first) {
/* Snap if it's close; `close' means within a proportion of the time on the timeline */
- if (nearest_distance < (width() / pptu) / 32) {
+ if (nearest_distance < DCPTime::from_seconds ((width() / pps) / 32)) {
new_position = nearest_new_position;
}
}
}
- if (new_position < 0) {
- new_position = 0;
+ if (new_position < DCPTime ()) {
+ new_position = DCPTime ();
}
_down_view->content()->set_position (new_position);
void
Timeline::resized ()
{
- setup_pixels_per_time_unit ();
+ setup_pixels_per_second ();
}
void
class Film;
class View;
class ContentView;
-class FilmEditor;
+class ContentPanel;
class TimeAxisView;
class Timeline : public wxPanel
{
public:
- Timeline (wxWindow *, FilmEditor *, boost::shared_ptr<Film>);
+ Timeline (wxWindow *, ContentPanel *, boost::shared_ptr<Film>);
boost::shared_ptr<const Film> film () const;
return 48;
}
- boost::optional<double> pixels_per_time_unit () const {
- return _pixels_per_time_unit;
+ boost::optional<double> pixels_per_second () const {
+ return _pixels_per_second;
}
Position<int> tracks_position () const {
int tracks () const;
- void setup_pixels_per_time_unit ();
+ void setup_pixels_per_second ();
void set_snap (bool s) {
_snap = s;
ContentViewList selected_views () const;
ContentList selected_content () const;
- FilmEditor* _film_editor;
+ ContentPanel* _content_panel;
boost::weak_ptr<Film> _film;
ViewList _views;
boost::shared_ptr<TimeAxisView> _time_axis_view;
int _tracks;
- boost::optional<double> _pixels_per_time_unit;
+ boost::optional<double> _pixels_per_second;
bool _left_down;
wxPoint _down_point;
boost::shared_ptr<ContentView> _down_view;
- Time _down_view_position;
+ DCPTime _down_view_position;
bool _first_move;
ContentMenu _menu;
bool _snap;
#include "film_editor.h"
#include "timeline_dialog.h"
#include "wx_util.h"
+#include "content_panel.h"
using std::list;
using std::cout;
using boost::shared_ptr;
-TimelineDialog::TimelineDialog (FilmEditor* ed, shared_ptr<Film> film)
- : wxDialog (ed, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
- , _timeline (this, ed, film)
+TimelineDialog::TimelineDialog (ContentPanel* cp, shared_ptr<Film> film)
+ : wxDialog (cp->panel(), wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
+ , _timeline (this, cp, film)
{
wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
class TimelineDialog : public wxDialog
{
public:
- TimelineDialog (FilmEditor *, boost::shared_ptr<Film>);
+ TimelineDialog (ContentPanel *, boost::shared_ptr<Film>);
private:
void snap_toggled ();
*/
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
#include "lib/content.h"
#include "lib/image_content.h"
#include "timing_panel.h"
#include "wx_util.h"
#include "timecode.h"
-#include "film_editor.h"
+#include "content_panel.h"
using std::cout;
using std::string;
using std::set;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
-using libdcp::raw_convert;
+using dcp::raw_convert;
-TimingPanel::TimingPanel (FilmEditor* e)
+TimingPanel::TimingPanel (ContentPanel* p)
/* horrid hack for apparent lack of context support with wxWidgets i18n code */
- : FilmEditorPanel (e, S_("Timing|Timing"))
+ : ContentSubPanel (p, S_("Timing|Timing"))
{
wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
_sizer->Add (grid, 0, wxALL, 8);
add_label_to_sizer (grid, this, _("Position"), true);
- _position = new Timecode (this);
+ _position = new Timecode<DCPTime> (this);
grid->Add (_position);
add_label_to_sizer (grid, this, _("Full length"), true);
- _full_length = new Timecode (this);
+ _full_length = new Timecode<DCPTime> (this);
grid->Add (_full_length);
add_label_to_sizer (grid, this, _("Trim from start"), true);
- _trim_start = new Timecode (this);
+ _trim_start = new Timecode<DCPTime> (this);
grid->Add (_trim_start);
add_label_to_sizer (grid, this, _("Trim from end"), true);
- _trim_end = new Timecode (this);
+ _trim_end = new Timecode<DCPTime> (this);
grid->Add (_trim_end);
add_label_to_sizer (grid, this, _("Play length"), true);
- _play_length = new Timecode (this);
+ _play_length = new Timecode<DCPTime> (this);
grid->Add (_play_length);
{
void
TimingPanel::film_content_changed (int property)
{
- ContentList cl = _editor->selected_content ();
- int const film_video_frame_rate = _editor->film()->video_frame_rate ();
+ ContentList cl = _parent->selected ();
+ int const film_video_frame_rate = _parent->film()->video_frame_rate ();
/* Here we check to see if we have exactly one different value of various
properties, and fill the controls with that value if so.
if (property == ContentProperty::POSITION) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->position ());
}
property == VideoContentProperty::VIDEO_FRAME_TYPE
) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->full_length ());
}
} else if (property == ContentProperty::TRIM_START) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->trim_start ());
}
} else if (property == ContentProperty::TRIM_END) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->trim_end ());
}
if (check.size() == 1) {
_trim_end->set (cl.front()->trim_end (), film_video_frame_rate);
} else {
- _trim_end->set (0, 24);
+ _trim_end->clear ();
}
}
property == VideoContentProperty::VIDEO_FRAME_TYPE
) {
- set<Time> check;
+ set<DCPTime> check;
for (ContentList::const_iterator i = cl.begin (); i != cl.end(); ++i) {
check.insert ((*i)->length_after_trim ());
}
void
TimingPanel::position_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_position (_position->get (_editor->film()->video_frame_rate ()));
+ (*i)->set_position (_position->get (_parent->film()->video_frame_rate ()));
}
}
void
TimingPanel::full_length_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (*i);
if (ic && ic->still ()) {
- ic->set_video_length (rint (_full_length->get (_editor->film()->video_frame_rate()) * ic->video_frame_rate() / TIME_HZ));
+ /* XXX: No effective FRC here... is this right? */
+ ic->set_video_length (ContentTime (_full_length->get (_parent->film()->video_frame_rate()), FrameRateChange (1, 1)));
}
}
}
void
TimingPanel::trim_start_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_trim_start (_trim_start->get (_editor->film()->video_frame_rate ()));
+ (*i)->set_trim_start (_trim_start->get (_parent->film()->video_frame_rate ()));
}
}
void
TimingPanel::trim_end_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_trim_end (_trim_end->get (_editor->film()->video_frame_rate ()));
+ (*i)->set_trim_end (_trim_end->get (_parent->film()->video_frame_rate ()));
}
}
void
TimingPanel::play_length_changed ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
- (*i)->set_trim_end ((*i)->full_length() - _play_length->get (_editor->film()->video_frame_rate()) - (*i)->trim_start());
+ (*i)->set_trim_end ((*i)->full_length() - _play_length->get (_parent->film()->video_frame_rate()) - (*i)->trim_start());
}
}
void
TimingPanel::set_video_frame_rate ()
{
- ContentList c = _editor->selected_content ();
+ ContentList c = _parent->selected ();
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
if (vc) {
void
TimingPanel::content_selection_changed ()
{
- bool const e = !_editor->selected_content().empty ();
+ bool const e = !_parent->selected().empty ();
_position->Enable (e);
_full_length->Enable (e);
*/
-#include "film_editor_panel.h"
+#include "content_sub_panel.h"
+#include "timecode.h"
-class Timecode;
-
-class TimingPanel : public FilmEditorPanel
+class TimingPanel : public ContentSubPanel
{
public:
- TimingPanel (FilmEditor *);
+ TimingPanel (ContentPanel *);
void film_content_changed (int);
void content_selection_changed ();
void video_frame_rate_changed ();
void set_video_frame_rate ();
- Timecode* _position;
- Timecode* _full_length;
- Timecode* _trim_start;
- Timecode* _trim_end;
- Timecode* _play_length;
+ Timecode<DCPTime>* _position;
+ Timecode<DCPTime>* _full_length;
+ Timecode<DCPTime>* _trim_start;
+ Timecode<DCPTime>* _trim_end;
+ Timecode<DCPTime>* _play_length;
wxTextCtrl* _video_frame_rate;
wxButton* _set_video_frame_rate;
};
#include "filter_dialog.h"
#include "video_panel.h"
#include "wx_util.h"
-#include "film_editor.h"
#include "content_colour_conversion_dialog.h"
#include "content_widget.h"
+#include "content_panel.h"
using std::vector;
using std::string;
using std::pair;
using std::cout;
using std::list;
+using std::set;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
using boost::bind;
assert (false);
}
-VideoPanel::VideoPanel (FilmEditor* e)
- : FilmEditorPanel (e, _("Video"))
+VideoPanel::VideoPanel (ContentPanel* p)
+ : ContentSubPanel (p, _("Video"))
{
wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
_sizer->Add (grid, 0, wxALL, 8);
&caster<int, VideoFrameType>,
&caster<VideoFrameType, int>
);
- _frame_type->add (grid, wxGBPosition (r, 1));
+ _frame_type->add (grid, wxGBPosition (r, 1), wxGBSpan (1, 2));
++r;
add_label_to_grid_bag_sizer (grid, this, _("Left crop"), true, wxGBPosition (r, 0));
boost::mem_fn (&VideoContent::set_left_crop)
);
_left_crop->add (grid, wxGBPosition (r, 1));
- ++r;
- add_label_to_grid_bag_sizer (grid, this, _("Right crop"), true, wxGBPosition (r, 0));
+ add_label_to_grid_bag_sizer (grid, this, _("Right crop"), true, wxGBPosition (r, 2));
_right_crop = new ContentSpinCtrl<VideoContent> (
this,
new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)),
boost::mem_fn (&VideoContent::right_crop),
boost::mem_fn (&VideoContent::set_right_crop)
);
- _right_crop->add (grid, wxGBPosition (r, 1));
+ _right_crop->add (grid, wxGBPosition (r, 3));
+
++r;
add_label_to_grid_bag_sizer (grid, this, _("Top crop"), true, wxGBPosition (r, 0));
boost::mem_fn (&VideoContent::top_crop),
boost::mem_fn (&VideoContent::set_top_crop)
);
- _top_crop->add (grid, wxGBPosition (r,1 ));
- ++r;
+ _top_crop->add (grid, wxGBPosition (r, 1));
- add_label_to_grid_bag_sizer (grid, this, _("Bottom crop"), true, wxGBPosition (r, 0));
+ add_label_to_grid_bag_sizer (grid, this, _("Bottom crop"), true, wxGBPosition (r, 2));
_bottom_crop = new ContentSpinCtrl<VideoContent> (
this,
new wxSpinCtrl (this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)),
boost::mem_fn (&VideoContent::bottom_crop),
boost::mem_fn (&VideoContent::set_bottom_crop)
);
- _bottom_crop->add (grid, wxGBPosition (r, 1));
+ _bottom_crop->add (grid, wxGBPosition (r, 3));
+
++r;
+ add_label_to_grid_bag_sizer (grid, this, _("Fade in"), true, wxGBPosition (r, 0));
+ _fade_in = new Timecode<ContentTime> (this);
+ grid->Add (_fade_in, wxGBPosition (r, 1), wxGBSpan (1, 3));
+ ++r;
+
+ add_label_to_grid_bag_sizer (grid, this, _("Fade out"), true, wxGBPosition (r, 0));
+ _fade_out = new Timecode<ContentTime> (this);
+ grid->Add (_fade_out, wxGBPosition (r, 1), wxGBSpan (1, 3));
+ ++r;
+
add_label_to_grid_bag_sizer (grid, this, _("Scale to"), true, wxGBPosition (r, 0));
_scale = new ContentChoice<VideoContent, VideoContentScale> (
this,
&index_to_scale,
&scale_to_index
);
- _scale->add (grid, wxGBPosition (r, 1));
+ _scale->add (grid, wxGBPosition (r, 1), wxGBSpan (1, 2));
++r;
- {
- add_label_to_grid_bag_sizer (grid, this, _("Filters"), true, wxGBPosition (r, 0));
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
- wxClientDC dc (this);
- wxSize size = dc.GetTextExtent (wxT ("A quite long name"));
- size.SetHeight (-1);
-
- _filters = new wxStaticText (this, wxID_ANY, _("None"), wxDefaultPosition, size);
- s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
- _filters_button = new wxButton (this, wxID_ANY, _("Edit..."));
- s->Add (_filters_button, 0, wxALIGN_CENTER_VERTICAL);
- grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- }
+ wxClientDC dc (this);
+ wxSize size = dc.GetTextExtent (wxT ("A quite long name"));
+ size.SetHeight (-1);
+
+ add_label_to_grid_bag_sizer (grid, this, _("Filters"), true, wxGBPosition (r, 0));
+ _filters = new wxStaticText (this, wxID_ANY, _("None"), wxDefaultPosition, size);
+ grid->Add (_filters, wxGBPosition (r, 1), wxGBSpan (1, 2), wxALIGN_CENTER_VERTICAL);
+ _filters_button = new wxButton (this, wxID_ANY, _("Edit..."));
+ grid->Add (_filters_button, wxGBPosition (r, 3), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
++r;
- {
- add_label_to_grid_bag_sizer (grid, this, _("Colour conversion"), true, wxGBPosition (r, 0));
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
-
- wxClientDC dc (this);
- wxSize size = dc.GetTextExtent (wxT ("A quite long name"));
- size.SetHeight (-1);
-
- _colour_conversion = new wxStaticText (this, wxID_ANY, wxT (""), wxDefaultPosition, size);
-
- s->Add (_colour_conversion, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6);
- _colour_conversion_button = new wxButton (this, wxID_ANY, _("Edit..."));
- s->Add (_colour_conversion_button, 0, wxALIGN_CENTER_VERTICAL);
- grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- }
+ add_label_to_grid_bag_sizer (grid, this, _("Colour conversion"), true, wxGBPosition (r, 0));
+ _colour_conversion = new wxStaticText (this, wxID_ANY, wxT (""), wxDefaultPosition, size);
+ grid->Add (_colour_conversion, wxGBPosition (r, 1), wxGBSpan (1, 2), wxALIGN_CENTER_VERTICAL);
+ _colour_conversion_button = new wxButton (this, wxID_ANY, _("Edit..."));
+ grid->Add (_colour_conversion_button, wxGBPosition (r, 3), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
++r;
_description = new wxStaticText (this, wxID_ANY, wxT ("\n \n \n \n \n"), wxDefaultPosition, wxDefaultSize);
- grid->Add (_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
+ grid->Add (_description, wxGBPosition (r, 0), wxGBSpan (1, 4), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6);
wxFont font = _description->GetFont();
font.SetStyle(wxFONTSTYLE_ITALIC);
font.SetPointSize(font.GetPointSize() - 1);
_frame_type->wrapped()->Append (_("3D left only"));
_frame_type->wrapped()->Append (_("3D right only"));
+ _fade_in->Changed.connect (boost::bind (&VideoPanel::fade_in_changed, this));
+ _fade_out->Changed.connect (boost::bind (&VideoPanel::fade_out_changed, this));
+
_filters_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&VideoPanel::edit_filters_clicked, this));
_colour_conversion_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&VideoPanel::edit_colour_conversion_clicked, this));
}
void
VideoPanel::film_content_changed (int property)
{
- VideoContentList vc = _editor->selected_video_content ();
+ VideoContentList vc = _parent->selected_video ();
shared_ptr<VideoContent> vcs;
shared_ptr<FFmpegContent> fcs;
if (!vc.empty ()) {
_filters->SetLabel (std_to_wx (p));
}
}
+ } else if (property == VideoContentProperty::VIDEO_FADE_IN) {
+ set<ContentTime> check;
+ for (VideoContentList::const_iterator i = vc.begin (); i != vc.end(); ++i) {
+ check.insert ((*i)->fade_in ());
+ }
+
+ if (check.size() == 1) {
+ _fade_in->set (vc.front()->fade_in (), vc.front()->video_frame_rate ());
+ } else {
+ _fade_in->clear ();
+ }
+ } else if (property == VideoContentProperty::VIDEO_FADE_OUT) {
+ set<ContentTime> check;
+ for (VideoContentList::const_iterator i = vc.begin (); i != vc.end(); ++i) {
+ check.insert ((*i)->fade_out ());
+ }
+
+ if (check.size() == 1) {
+ _fade_out->set (vc.front()->fade_out (), vc.front()->video_frame_rate ());
+ } else {
+ _fade_out->clear ();
+ }
}
}
void
VideoPanel::edit_filters_clicked ()
{
- FFmpegContentList c = _editor->selected_ffmpeg_content ();
+ FFmpegContentList c = _parent->selected_ffmpeg ();
if (c.size() != 1) {
return;
}
void
VideoPanel::setup_description ()
{
- VideoContentList vc = _editor->selected_video_content ();
+ VideoContentList vc = _parent->selected_video ();
if (vc.empty ()) {
_description->SetLabel ("");
return;
}
Crop const crop = vcs->crop ();
- if ((crop.left || crop.right || crop.top || crop.bottom) && vcs->video_size() != libdcp::Size (0, 0)) {
- libdcp::Size cropped = vcs->video_size_after_crop ();
+ if ((crop.left || crop.right || crop.top || crop.bottom) && vcs->video_size() != dcp::Size (0, 0)) {
+ dcp::Size cropped = vcs->video_size_after_crop ();
d << wxString::Format (
_("Cropped to %dx%d (%.2f:1)\n"),
cropped.width, cropped.height,
++lines;
}
- libdcp::Size const container_size = _editor->film()->frame_size ();
- libdcp::Size const scaled = vcs->scale().size (vcs, container_size, container_size);
+ dcp::Size const container_size = _parent->film()->frame_size ();
+ dcp::Size const scaled = vcs->scale().size (vcs, container_size, container_size, 1);
if (scaled != vcs->video_size_after_crop ()) {
d << wxString::Format (
d << wxString::Format (_("Content frame rate %.4f\n"), vcs->video_frame_rate ());
++lines;
- FrameRateChange frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
+ FrameRateChange frc (vcs->video_frame_rate(), _parent->film()->video_frame_rate ());
d << std_to_wx (frc.description ()) << "\n";
++lines;
void
VideoPanel::edit_colour_conversion_clicked ()
{
- VideoContentList vc = _editor->selected_video_content ();
+ VideoContentList vc = _parent->selected_video ();
if (vc.size() != 1) {
return;
}
void
VideoPanel::content_selection_changed ()
{
- VideoContentList video_sel = _editor->selected_video_content ();
- FFmpegContentList ffmpeg_sel = _editor->selected_ffmpeg_content ();
+ VideoContentList video_sel = _parent->selected_video ();
+ FFmpegContentList ffmpeg_sel = _parent->selected_ffmpeg ();
bool const single = video_sel.size() == 1;
film_content_changed (VideoContentProperty::VIDEO_CROP);
film_content_changed (VideoContentProperty::VIDEO_FRAME_RATE);
film_content_changed (VideoContentProperty::COLOUR_CONVERSION);
+ film_content_changed (VideoContentProperty::VIDEO_FADE_IN);
+ film_content_changed (VideoContentProperty::VIDEO_FADE_OUT);
film_content_changed (FFmpegContentProperty::FILTERS);
}
+
+void
+VideoPanel::fade_in_changed ()
+{
+ VideoContentList vc = _parent->selected_video ();
+ for (VideoContentList::const_iterator i = vc.begin(); i != vc.end(); ++i) {
+ (*i)->set_fade_in (_fade_in->get (_parent->film()->video_frame_rate ()));
+ }
+}
+
+void
+VideoPanel::fade_out_changed ()
+{
+ VideoContentList vc = _parent->selected_video ();
+ for (VideoContentList::const_iterator i = vc.begin(); i != vc.end(); ++i) {
+ (*i)->set_fade_out (_fade_out->get (_parent->film()->video_frame_rate ()));
+ }
+}
/*
- 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 "lib/film.h"
-#include "film_editor_panel.h"
+/** @file src/lib/video_panel.h
+ * @brief VideoPanel class.
+ */
+
+#include "content_sub_panel.h"
#include "content_widget.h"
+#include "timecode.h"
+#include "lib/film.h"
class wxChoice;
class wxStaticText;
class wxSpinCtrl;
class wxButton;
-class VideoPanel : public FilmEditorPanel
+/** @class VideoPanel
+ * @brief The video tab of the film editor.
+ */
+class VideoPanel : public ContentSubPanel
{
public:
- VideoPanel (FilmEditor *);
+ VideoPanel (ContentPanel *);
void film_changed (Film::Property);
void film_content_changed (int);
private:
void edit_filters_clicked ();
void edit_colour_conversion_clicked ();
+ void fade_in_changed ();
+ void fade_out_changed ();
void setup_description ();
ContentSpinCtrl<VideoContent>* _right_crop;
ContentSpinCtrl<VideoContent>* _top_crop;
ContentSpinCtrl<VideoContent>* _bottom_crop;
+ Timecode<ContentTime>* _fade_in;
+ Timecode<ContentTime>* _fade_out;
ContentChoice<VideoContent, VideoContentScale>* _scale;
wxStaticText* _description;
wxStaticText* _filters;
config_dialog.cc
content_colour_conversion_dialog.cc
content_menu.cc
+ content_panel.cc
+ content_sub_panel.cc
+ dcp_panel.cc
isdcf_metadata_dialog.cc
dir_picker_ctrl.cc
dolby_certificate_dialog.cc
doremi_certificate_dialog.cc
download_certificate_dialog.cc
film_editor.cc
- film_editor_panel.cc
film_viewer.cc
filter_dialog.cc
filter_editor.cc
job_manager_view.cc
job_wrapper.cc
kdm_dialog.cc
+ make_signer_chain_dialog.cc
new_film_dialog.cc
preset_colour_conversion_dialog.cc
properties_dialog.cc
server_dialog.cc
servers_list_dialog.cc
subtitle_panel.cc
+ subtitle_view.cc
table_dialog.cc
timecode.cc
timeline.cc
else:
obj = bld(features = 'cxx cxxshlib')
- obj.name = 'libdcpomatic-wx'
+ obj.name = 'libdcpomatic2-wx'
obj.export_includes = ['..']
obj.uselib = 'WXWIDGETS DCP'
if bld.env.TARGET_LINUX:
obj.uselib += ' GTK'
- obj.use = 'libdcpomatic'
+ obj.use = 'libdcpomatic2'
obj.source = sources
- obj.target = 'dcpomatic-wx'
+ obj.target = 'dcpomatic2-wx'
- i18n.po_to_mo(os.path.join('src', 'wx'), 'libdcpomatic-wx', bld)
+ i18n.po_to_mo(os.path.join('src', 'wx'), 'libdcpomatic2-wx', bld)
def pot(bld):
i18n.pot(os.path.join('src', 'wx'), sources + " editable_list.h", 'libdcpomatic-wx')
/*
- 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
/*
- 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
class wxEvtHandler;
+/** @class wxUISignaller
+ * @brief UISignaller for the wxWidgets event loop
+ */
+
class wxUISignaller : public UISignaller
{
public:
* @param initial Initial text for the wxStaticText while the computation is being run.
* @param fn Function which works out what the wxStaticText content should be and returns it.
*/
-ThreadedStaticText::ThreadedStaticText (wxWindow* parent, wxString initial, function<string ()> fn)
+ThreadedStaticText::ThreadedStaticText (wxWindow* parent, wxString initial, boost::function<string ()> fn)
: wxStaticText (parent, wxID_ANY, initial)
{
Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ThreadedStaticText::thread_finished, this, _1), _update_event_id);
/** Run our thread and post the result to the GUI thread via AddPendingEvent */
void
-ThreadedStaticText::run (function<string ()> fn)
+ThreadedStaticText::run (boost::function<string ()> fn)
try
{
wxCommandEvent ev (wxEVT_COMMAND_TEXT_UPDATED, _update_event_id);
/*
- 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 test/4k_test.cc
+ * @brief Run a 4K encode from a simple input.
+ *
+ * The output is checked against test/data/4k_test.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/film.h"
#include "lib/ffmpeg_content.h"
--- /dev/null
+DCP-o-matic unit tests
+----------------------
+
+They can be grouped roughly into the following:
+
+* Self-contained tests of single classes / method sets
+
+AudioAnalysis: audio_analysis_test
+AudioBuffers: audio_buffers_test
+AudioDecoder: audio_decoder_test
+AudioMapping: audio_mapping_test
+ColourConversion: colour_conversion_test
+FileGroup: file_group_test
+Image: image_test, pixel_formats_test, make_black_test
+Player: player_test
+Job/JobManager: job_test
+SubRip: subrip_test
+Ratio: ratio_test
+Resampler: resampler_test
+util.cc: util_test
+
+* "Complete" builds of DCPs with various characteristics, aiming
+to test broad areas of code
+
+4k_test
+threed_test
+
+* Tests of fairly specific areas
+
+audio_delay_test
+black_fill_test
+client_server_test
+film_metadata_test
+frame_rate_test
+recover_test
+repeat_frame_test
+scaling_test
+silence_padding_test
+skip_frame_test
+
+ - FFmpeg decoding
+
+ ffmpeg_audio_test
+ ffmpeg_dcp_test
+ ffmpeg_decoder_seek_test
+ ffmpeg_decoder_sequential_test
+ ffmpeg_examiner_test
+ ffmpeg_pts_offset_test
+ seek_zero_test.cc
+ stream_test
/*
- 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 test/audio_analysis_test.cc
+ * @brief Check audio analysis code.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/audio_analysis.h"
+#include "lib/film.h"
+#include "lib/sndfile_content.h"
+#include "lib/dcp_content_type.h"
+#include "lib/ratio.h"
+#include "test.h"
+
+using boost::shared_ptr;
static float
random_float ()
return (float (rand ()) / RAND_MAX) * 2 - 1;
}
-/* Check serialisation of audio analyses */
-BOOST_AUTO_TEST_CASE (audio_analysis_test)
+BOOST_AUTO_TEST_CASE (audio_analysis_serialisation_test)
{
int const channels = 3;
int const points = 4096;
}
}
- a.write ("build/test/audio_analysis_test");
+ a.write ("build/test/audio_analysis_serialisation_test");
srand (1);
- AudioAnalysis b ("build/test/audio_analysis_test");
+ AudioAnalysis b ("build/test/audio_analysis_serialisation_test");
for (int i = 0; i < channels; ++i) {
- BOOST_CHECK (b.points(i) == points);
+ BOOST_CHECK_EQUAL (b.points(i), points);
for (int j = 0; j < points; ++j) {
AudioPoint p = b.get_point (i, j);
BOOST_CHECK_CLOSE (p[AudioPoint::PEAK], random_float (), 1);
}
}
}
+
+void
+finished ()
+{
+
+}
+
+BOOST_AUTO_TEST_CASE (audio_analysis_test)
+{
+ shared_ptr<Film> film = new_test_film ("audio_analysis_test");
+ film->set_dcp_content_type (DCPContentType::from_isdcf_name ("FTR"));
+ film->set_container (Ratio::from_id ("185"));
+ film->set_name ("audio_analysis_test");
+ boost::filesystem::path p = private_data / "betty_L.wav";
+
+ shared_ptr<SndfileContent> c (new SndfileContent (film, p));
+ film->examine_and_add_content (c);
+ wait_for_jobs ();
+
+ c->analyse_audio (boost::bind (&finished));
+ wait_for_jobs ();
+}
--- /dev/null
+/*
+ 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 test/audio_buffers_test.cc
+ * @brief Test AudioBuffers in various ways.
+ */
+
+#include <cmath>
+#include <boost/test/unit_test.hpp>
+#include "lib/audio_buffers.h"
+
+using std::pow;
+
+static float tolerance = 1e-3;
+
+static float
+random_float ()
+{
+ return float (rand ()) / RAND_MAX;
+}
+
+static void
+random_fill (AudioBuffers& buffers)
+{
+ for (int i = 0; i < buffers.frames(); ++i) {
+ for (int j = 0; j < buffers.channels(); ++j) {
+ buffers.data(j)[i] = random_float ();
+ }
+ }
+}
+
+static void
+random_check (AudioBuffers& buffers, int from, int frames)
+{
+ for (int i = from; i < (from + frames); ++i) {
+ for (int j = 0; j < buffers.channels(); ++j) {
+ BOOST_CHECK_CLOSE (buffers.data(j)[i], random_float (), tolerance);
+ }
+ }
+}
+
+/** Basic setup */
+BOOST_AUTO_TEST_CASE (audio_buffers_setup_test)
+{
+ AudioBuffers buffers (4, 9155);
+
+ BOOST_CHECK (buffers.data ());
+ for (int i = 0; i < 4; ++i) {
+ BOOST_CHECK (buffers.data (i));
+ }
+
+ BOOST_CHECK_EQUAL (buffers.channels(), 4);
+ BOOST_CHECK_EQUAL (buffers.frames(), 9155);
+}
+
+/** Extending some buffers */
+BOOST_AUTO_TEST_CASE (audio_buffers_extend_test)
+{
+ AudioBuffers buffers (3, 150);
+ srand (1);
+ random_fill (buffers);
+
+ /* Extend */
+ buffers.ensure_size (299);
+
+ srand (1);
+ random_check (buffers, 0, 150);
+
+ /* New space should be silent */
+ for (int i = 150; i < 299; ++i) {
+ for (int c = 0; c < 3; ++c) {
+ BOOST_CHECK_EQUAL (buffers.data(c)[i], 0);
+ }
+ }
+}
+
+/** make_silent() */
+BOOST_AUTO_TEST_CASE (audio_buffers_make_silent_test)
+{
+ AudioBuffers buffers (9, 9933);
+ srand (2);
+ random_fill (buffers);
+
+ buffers.make_silent ();
+
+ for (int i = 0; i < 9933; ++i) {
+ for (int c = 0; c < 9; ++c) {
+ BOOST_CHECK_EQUAL (buffers.data(c)[i], 0);
+ }
+ }
+}
+
+/** make_silent (int c) */
+BOOST_AUTO_TEST_CASE (audio_buffers_make_silent_channel_test)
+{
+ AudioBuffers buffers (9, 9933);
+ srand (3);
+ random_fill (buffers);
+
+ buffers.make_silent (4);
+
+ srand (3);
+ for (int i = 0; i < 9933; ++i) {
+ for (int c = 0; c < 9; ++c) {
+ if (c == 4) {
+ random_float ();
+ BOOST_CHECK_EQUAL (buffers.data(c)[i], 0);
+ } else {
+ BOOST_CHECK_CLOSE (buffers.data(c)[i], random_float (), tolerance);
+ }
+ }
+ }
+}
+
+/** make_silent (int from, int frames) */
+BOOST_AUTO_TEST_CASE (audio_buffers_make_silent_part_test)
+{
+ AudioBuffers buffers (9, 9933);
+ srand (4);
+ random_fill (buffers);
+
+ buffers.make_silent (145, 833);
+
+ srand (4);
+ for (int i = 0; i < 145; ++i) {
+ for (int c = 0; c < 9; ++c) {
+ BOOST_CHECK_EQUAL (buffers.data(c)[i], random_float ());
+ }
+ }
+
+ for (int i = 145; i < (145 + 833); ++i) {
+ for (int c = 0; c < 9; ++c) {
+ random_float ();
+ BOOST_CHECK_EQUAL (buffers.data(c)[i], 0);
+ }
+ }
+
+ for (int i = (145 + 833); i < 9933; ++i) {
+ for (int c = 0; c < 9; ++c) {
+ BOOST_CHECK_EQUAL (buffers.data(c)[i], random_float ());
+ }
+ }
+}
+
+/* apply_gain */
+BOOST_AUTO_TEST_CASE (audio_buffers_apply_gain)
+{
+ AudioBuffers buffers (2, 417315);
+ srand (9);
+ random_fill (buffers);
+
+ buffers.apply_gain (5.4);
+
+ srand (9);
+ for (int i = 0; i < 417315; ++i) {
+ for (int c = 0; c < 2; ++c) {
+ BOOST_CHECK_CLOSE (buffers.data(c)[i], random_float() * pow (10, 5.4 / 20), tolerance);
+ }
+ }
+}
+
+/* copy_from */
+BOOST_AUTO_TEST_CASE (audio_buffers_copy_from)
+{
+ AudioBuffers a (5, 63711);
+ AudioBuffers b (5, 12345);
+
+ srand (42);
+ random_fill (a);
+
+ srand (99);
+ random_fill (b);
+
+ a.copy_from (&b, 517, 233, 194);
+
+ /* Re-seed a's generator and check the numbers that came from it */
+
+ /* First part; not copied-over */
+ srand (42);
+ random_check (a, 0, 194);
+
+ /* Second part; copied-over (just burn generator a's numbers) */
+ for (int i = 0; i < (517 * 5); ++i) {
+ random_float ();
+ }
+
+ /* Third part; not copied-over */
+ random_check (a, 194 + 517, a.frames() - 194 - 517);
+
+ /* Re-seed b's generator and check the numbers that came from it */
+ srand (99);
+
+ /* First part; burn */
+ for (int i = 0; i < 194 * 5; ++i) {
+ random_float ();
+ }
+
+ /* Second part; copied */
+ random_check (b, 194, 517);
+}
+
+/* move */
+BOOST_AUTO_TEST_CASE (audio_buffers_move)
+{
+ AudioBuffers buffers (7, 65536);
+
+ srand (84);
+ random_fill (buffers);
+
+ int const from = 888;
+ int const to = 666;
+ int const frames = 444;
+
+ buffers.move (from, to, frames);
+
+ /* Re-seed and check the un-moved parts */
+ srand (84);
+
+ random_check (buffers, 0, to);
+
+ /* Burn a few */
+ for (int i = 0; i < (from - to + frames) * 7; ++i) {
+ random_float ();
+ }
+
+ random_check (buffers, from + frames, 65536 - frames - from);
+
+ /* Re-seed and check the moved part */
+ srand (84);
+
+ /* Burn a few */
+ for (int i = 0; i < from * 7; ++i) {
+ random_float ();
+ }
+
+ random_check (buffers, to, frames);
+}
+
+/** accumulate_channel */
+BOOST_AUTO_TEST_CASE (audio_buffers_accumulate_channel)
+{
+ AudioBuffers a (3, 256);
+ srand (38);
+ random_fill (a);
+
+ AudioBuffers b (3, 256);
+ random_fill (b);
+
+ a.accumulate_channel (&b, 2, 1, 1.2);
+
+ srand (38);
+ for (int i = 0; i < 256; ++i) {
+ for (int c = 0; c < 3; ++c) {
+ float const A = random_float ();
+ if (c == 1) {
+ BOOST_CHECK_CLOSE (a.data(c)[i], A + b.data(2)[i] * 1.2, tolerance);
+ } else {
+ BOOST_CHECK_CLOSE (a.data(c)[i], A, tolerance);
+ }
+ }
+ }
+}
+
+/** accumulate_frames */
+BOOST_AUTO_TEST_CASE (audio_buffers_accumulate_frames)
+{
+ AudioBuffers a (3, 256);
+ srand (38);
+ random_fill (a);
+
+ AudioBuffers b (3, 256);
+ random_fill (b);
+
+ a.accumulate_frames (&b, 91, 44, 129);
+
+ srand (38);
+ for (int i = 0; i < 256; ++i) {
+ for (int c = 0; c < 3; ++c) {
+ float const A = random_float ();
+ if (i < 44 || i >= (44 + 129)) {
+ BOOST_CHECK_CLOSE (a.data(c)[i], A, tolerance);
+ } else {
+ BOOST_CHECK_CLOSE (a.data(c)[i], A + b.data(c)[i + 91 - 44], tolerance);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ 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 test/audio_decoder_test.cc
+ * @brief Tests of the AudioDecoder class.
+ */
+
+#include <cassert>
+#include <boost/test/unit_test.hpp>
+#include "test.h"
+#include "lib/audio_decoder.h"
+#include "lib/audio_content.h"
+
+using std::string;
+using std::cout;
+using std::min;
+using boost::shared_ptr;
+
+class TestAudioDecoder : public AudioDecoder
+{
+public:
+ TestAudioDecoder (shared_ptr<AudioContent> content)
+ : AudioDecoder (content)
+ , _position (0)
+ {}
+
+ bool pass ()
+ {
+ AudioFrame const N = min (
+ AudioFrame (2000),
+ _audio_content->audio_length().frames (_audio_content->resampled_audio_frame_rate ()) - _position
+ );
+
+ shared_ptr<AudioBuffers> buffers (new AudioBuffers (_audio_content->audio_channels(), N));
+ for (int i = 0; i < _audio_content->audio_channels(); ++i) {
+ for (int j = 0; j < N; ++j) {
+ buffers->data(i)[j] = j + _position;
+ }
+ }
+
+ audio (buffers, ContentTime::from_frames (_position, _audio_content->resampled_audio_frame_rate ()));
+ _position += N;
+
+ return N < 2000;
+ }
+
+ void seek (ContentTime t, bool accurate)
+ {
+ AudioDecoder::seek (t, accurate);
+ _position = t.frames (_audio_content->resampled_audio_frame_rate ());
+ }
+
+private:
+ AudioFrame _position;
+};
+
+class TestAudioContent : public AudioContent
+{
+public:
+ TestAudioContent (shared_ptr<Film> film)
+ : Content (film)
+ , AudioContent (film, DCPTime ())
+ {}
+
+ string summary () const {
+ return "";
+ }
+
+ string information () const {
+ return "";
+ }
+
+ DCPTime full_length () const {
+ return DCPTime (audio_length().get ());
+ }
+
+ int audio_channels () const {
+ return 2;
+ }
+
+ ContentTime audio_length () const {
+ return ContentTime::from_seconds (61.2942);
+ }
+
+ int audio_frame_rate () const {
+ return 48000;
+ }
+
+ AudioMapping audio_mapping () const {
+ return AudioMapping (audio_channels ());
+ }
+
+ void set_audio_mapping (AudioMapping) {}
+};
+
+shared_ptr<TestAudioContent> content;
+shared_ptr<TestAudioDecoder> decoder;
+
+static shared_ptr<ContentAudio>
+get (AudioFrame from, AudioFrame length)
+{
+ decoder->seek (ContentTime::from_frames (from, content->resampled_audio_frame_rate ()), true);
+ shared_ptr<ContentAudio> ca = decoder->get_audio (from, length, true);
+ BOOST_CHECK_EQUAL (ca->frame, from);
+ return ca;
+}
+
+static void
+check (AudioFrame from, AudioFrame length)
+{
+ shared_ptr<ContentAudio> ca = get (from, length);
+ for (int i = 0; i < content->audio_channels(); ++i) {
+ for (int j = 0; j < length; ++j) {
+ BOOST_CHECK_EQUAL (ca->audio->data(i)[j], j + from);
+ assert (ca->audio->data(i)[j] == j + from);
+ }
+ }
+}
+
+/** Check the logic in AudioDecoder::get_audio */
+BOOST_AUTO_TEST_CASE (audio_decoder_get_audio_test)
+{
+ shared_ptr<Film> film = new_test_film ("audio_decoder_test");
+
+ content.reset (new TestAudioContent (film));
+ decoder.reset (new TestAudioDecoder (content));
+
+ /* Simple reads */
+ check (0, 48000);
+ check (44, 9123);
+ check (9991, 22);
+
+ /* Read off the end */
+
+ AudioFrame const from = content->resampled_audio_frame_rate() * 61;
+ AudioFrame const length = content->resampled_audio_frame_rate() * 4;
+ shared_ptr<ContentAudio> ca = get (from, length);
+
+ for (int i = 0; i < content->audio_channels(); ++i) {
+ for (int j = 0; j < ca->audio->frames(); ++j) {
+ BOOST_CHECK_EQUAL (ca->audio->data(i)[j], j + from);
+ assert (ca->audio->data(i)[j] == j + from);
+ }
+ }
+}
/*
- 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 test/audio_delay_test.cc
+ * @brief Test encode using some SndfileContents which have audio delays.
+ *
+ * The output is checked algorithmically using knowledge of the input.
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/sound_frame.h>
-#include <libdcp/cpl.h>
-#include <libdcp/reel.h>
-#include <libdcp/sound_asset.h>
+#include <dcp/sound_frame.h>
+#include <dcp/cpl.h>
+#include <dcp/reel.h>
+#include <dcp/sound_mxf.h>
+#include <dcp/reel_sound_asset.h>
#include "lib/sndfile_content.h"
#include "lib/dcp_content_type.h"
#include "lib/ratio.h"
boost::filesystem::path path = "build/test";
path /= film_name;
path /= film->dcp_name ();
- libdcp::DCP check (path.string ());
+ dcp::DCP check (path.string ());
check.read ();
- shared_ptr<const libdcp::SoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
+ shared_ptr<const dcp::ReelSoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
BOOST_CHECK (sound_asset);
/* Sample index in the DCP */
/* Delay in frames */
int const delay_in_frames = delay_in_ms * 48000 / 1000;
- while (n < sound_asset->intrinsic_duration()) {
- shared_ptr<const libdcp::SoundFrame> sound_frame = sound_asset->get_frame (frame++);
+ while (n < sound_asset->mxf()->intrinsic_duration()) {
+ shared_ptr<const dcp::SoundFrame> sound_frame = sound_asset->mxf()->get_frame (frame++);
uint8_t const * d = sound_frame->data ();
- for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->channels())) {
+ for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->mxf()->channels())) {
/* Mono input so it will appear on centre */
int const sample = d[i + 7] | (d[i + 8] << 8);
}
}
-
/* Test audio delay when specified in a piece of audio content */
BOOST_AUTO_TEST_CASE (audio_delay_test)
{
--- /dev/null
+/*
+ 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 test/audio_filter_test.cc
+ * @brief Basic tests of audio filters.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "lib/audio_filter.h"
+#include "lib/audio_buffers.h"
+
+using boost::shared_ptr;
+
+static void
+audio_filter_impulse_test_one (AudioFilter& f, int block_size, int num_blocks)
+{
+ int c = 0;
+
+ for (int i = 0; i < num_blocks; ++i) {
+
+ shared_ptr<AudioBuffers> in (new AudioBuffers (1, block_size));
+ for (int j = 0; j < block_size; ++j) {
+ in->data()[0][j] = c + j;
+ }
+
+ shared_ptr<AudioBuffers> out = f.run (in);
+
+ for (int j = 0; j < out->frames(); ++j) {
+ BOOST_CHECK_EQUAL (out->data()[0][j], c + j);
+ }
+
+ c += block_size;
+ }
+}
+
+/** Create a filter with an impulse as a kernel and check that it
+ * passes data through unaltered.
+ */
+BOOST_AUTO_TEST_CASE (audio_filter_impulse_kernel_test)
+{
+ AudioFilter f (0.02);
+ f._ir.resize (f._M + 1);
+
+ f._ir[0] = 1;
+ for (int i = 1; i <= f._M; ++i) {
+ f._ir[i] = 0;
+ }
+
+ audio_filter_impulse_test_one (f, 32, 1);
+ audio_filter_impulse_test_one (f, 256, 1);
+ audio_filter_impulse_test_one (f, 2048, 1);
+}
+
+/** Create filters and pass them impulses as input and check that
+ * the filter kernels comes back.
+ */
+BOOST_AUTO_TEST_CASE (audio_filter_impulse_input_test)
+{
+ LowPassAudioFilter lpf (0.02, 0.3);
+
+ shared_ptr<AudioBuffers> in (new AudioBuffers (1, 1751));
+ in->make_silent ();
+ in->data(0)[0] = 1;
+
+ shared_ptr<AudioBuffers> out = lpf.run (in);
+ for (int j = 0; j < out->frames(); ++j) {
+ if (j <= lpf._M) {
+ BOOST_CHECK_EQUAL (out->data(0)[j], lpf._ir[j]);
+ } else {
+ BOOST_CHECK_EQUAL (out->data(0)[j], 0);
+ }
+ }
+
+ HighPassAudioFilter hpf (0.02, 0.3);
+
+ in.reset (new AudioBuffers (1, 9133));
+ in->make_silent ();
+ in->data(0)[0] = 1;
+
+ out = hpf.run (in);
+ for (int j = 0; j < out->frames(); ++j) {
+ if (j <= hpf._M) {
+ BOOST_CHECK_EQUAL (out->data(0)[j], hpf._ir[j]);
+ } else {
+ BOOST_CHECK_EQUAL (out->data(0)[j], 0);
+ }
+ }
+}
*/
+/** @file test/audio_mapping_test.cc
+ * @brief Basic tests of the AudioMapping class, which itself doesn't really do much.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/audio_mapping.h"
#include "lib/util.h"
-/* Basic tests of the AudioMapping class, which itself
- doesn't really do much.
-*/
BOOST_AUTO_TEST_CASE (audio_mapping_test)
{
AudioMapping none;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < MAX_DCP_AUDIO_CHANNELS; ++j) {
- BOOST_CHECK_EQUAL (four.get (i, static_cast<libdcp::Channel> (j)), i == j ? 1 : 0);
+ BOOST_CHECK_EQUAL (four.get (i, static_cast<dcp::Channel> (j)), i == j ? 1 : 0);
}
}
- four.set (0, libdcp::RIGHT, 1);
- BOOST_CHECK_EQUAL (four.get (0, libdcp::RIGHT), 1);
+ four.set (0, dcp::RIGHT, 1);
+ BOOST_CHECK_EQUAL (four.get (0, dcp::RIGHT), 1);
}
+++ /dev/null
-/*
- 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 <boost/bind.hpp>
-#include <boost/function.hpp>
-#include <boost/signals2.hpp>
-#include "lib/audio_merger.h"
-#include "lib/audio_buffers.h"
-
-using boost::shared_ptr;
-using boost::bind;
-
-static shared_ptr<const AudioBuffers> last_audio;
-
-static int
-pass_through (int x)
-{
- return x;
-}
-
-BOOST_AUTO_TEST_CASE (audio_merger_test1)
-{
- AudioMerger<int, int> merger (1, bind (&pass_through, _1), boost::bind (&pass_through, _1));
-
- /* Push 64 samples, 0 -> 63 at time 0 */
- shared_ptr<AudioBuffers> buffers (new AudioBuffers (1, 64));
- for (int i = 0; i < 64; ++i) {
- buffers->data()[0][i] = i;
- }
- merger.push (buffers, 0);
-
- /* Push 64 samples, 0 -> 63 at time 22 */
- merger.push (buffers, 22);
-
- TimedAudioBuffers<int> tb = merger.pull (22);
- BOOST_CHECK (tb.audio != shared_ptr<const AudioBuffers> ());
- BOOST_CHECK_EQUAL (tb.audio->frames(), 22);
- BOOST_CHECK_EQUAL (tb.time, 0);
-
- /* And they should be a staircase */
- for (int i = 0; i < 22; ++i) {
- BOOST_CHECK_EQUAL (tb.audio->data()[0][i], i);
- }
-
- tb = merger.flush ();
-
- /* That flush should give us 64 samples at 22 */
- BOOST_CHECK_EQUAL (tb.audio->frames(), 64);
- BOOST_CHECK_EQUAL (tb.time, 22);
-
- /* Check the sample values */
- for (int i = 0; i < 64; ++i) {
- int correct = i;
- if (i < (64 - 22)) {
- correct += i + 22;
- }
- BOOST_CHECK_EQUAL (tb.audio->data()[0][i], correct);
- }
-}
-
-BOOST_AUTO_TEST_CASE (audio_merger_test2)
-{
- AudioMerger<int, int> merger (1, bind (&pass_through, _1), boost::bind (&pass_through, _1));
-
- /* Push 64 samples, 0 -> 63 at time 9 */
- shared_ptr<AudioBuffers> buffers (new AudioBuffers (1, 64));
- for (int i = 0; i < 64; ++i) {
- buffers->data()[0][i] = i;
- }
- merger.push (buffers, 9);
-
- TimedAudioBuffers<int> tb = merger.pull (9);
- BOOST_CHECK_EQUAL (tb.audio->frames(), 9);
- BOOST_CHECK_EQUAL (tb.time, 0);
-
- for (int i = 0; i < 9; ++i) {
- BOOST_CHECK_EQUAL (tb.audio->data()[0][i], 0);
- }
-
- tb = merger.flush ();
-
- /* That flush should give us 64 samples at 9 */
- BOOST_CHECK_EQUAL (tb.audio->frames(), 64);
- BOOST_CHECK_EQUAL (tb.time, 9);
-
- /* Check the sample values */
- for (int i = 0; i < 64; ++i) {
- BOOST_CHECK_EQUAL (tb.audio->data()[0][i], i);
- }
-}
/*
- 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 "test.h"
/** @file test/black_fill_test.cc
- * @brief Test insertion of black frames between video content.
+ * @brief Test insertion of black frames between separate bits of video content.
*/
using boost::shared_ptr;
film->examine_and_add_content (contentB);
wait_for_jobs ();
- contentA->set_video_length (3);
- contentA->set_position (film->video_frames_to_time (2));
- contentB->set_video_length (1);
- contentB->set_position (film->video_frames_to_time (7));
+ contentA->set_video_length (ContentTime::from_frames (3, 24));
+ contentA->set_position (DCPTime::from_frames (2, film->video_frame_rate ()));
+ contentB->set_video_length (ContentTime::from_frames (1, 24));
+ contentB->set_position (DCPTime::from_frames (7, film->video_frame_rate ()));
film->make_dcp ();
--- /dev/null
+/*
+ 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 test/burnt_subtitle_test.cc
+ * @brief Test the burning of subtitles into the DCP.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "lib/subrip_content.h"
+#include "lib/dcp_subtitle_content.h"
+#include "lib/film.h"
+#include "lib/ratio.h"
+#include "lib/dcp_content_type.h"
+#include "test.h"
+
+using std::cout;
+using boost::shared_ptr;
+
+/** Build a small DCP with no picture and a single subtitle overlaid onto it from a SubRip file */
+BOOST_AUTO_TEST_CASE (burnt_subtitle_test_subrip)
+{
+ shared_ptr<Film> film = new_test_film ("burnt_subtitle_test_subrip");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ film->set_name ("frobozz");
+ film->set_burn_subtitles (true);
+ shared_ptr<SubRipContent> content (new SubRipContent (film, "test/data/subrip2.srt"));
+ content->set_use_subtitles (true);
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+ film->make_dcp ();
+ wait_for_jobs ();
+
+ check_dcp ("test/data/burnt_subtitle_test_subrip", film->dir (film->dcp_name ()));
+}
+
+/** Build a small DCP with no picture and a single subtitle overlaid onto it from a DCP XML file */
+BOOST_AUTO_TEST_CASE (burnt_subtitle_test_dcp)
+{
+ shared_ptr<Film> film = new_test_film ("burnt_subtitle_test_dcp");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ film->set_name ("frobozz");
+ film->set_burn_subtitles (true);
+ shared_ptr<DCPSubtitleContent> content (new DCPSubtitleContent (film, "test/data/dcp_sub.xml"));
+ content->set_use_subtitles (true);
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+ film->make_dcp ();
+ wait_for_jobs ();
+
+ check_dcp ("test/data/burnt_subtitle_test_dcp", film->dir (film->dcp_name ()));
+}
*/
+/** @file test/client_server_test.cc
+ * @brief Test the server class.
+ *
+ * Create a test image and then encode it using the standard mechanism
+ * and also using a Server object running on localhost. Compare the resulting
+ * encoded data to check that they are the same.
+ */
+
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
#include "lib/server.h"
#include "lib/image.h"
#include "lib/cross.h"
-#include "lib/dcp_video_frame.h"
+#include "lib/dcp_video.h"
#include "lib/scaler.h"
-#include "lib/player_video_frame.h"
-#include "lib/image_proxy.h"
+#include "lib/player_video.h"
+#include "lib/raw_image_proxy.h"
+#include "lib/encoded_data.h"
using std::list;
using boost::shared_ptr;
using boost::thread;
+using boost::optional;
void
-do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription description, shared_ptr<EncodedData> locally_encoded)
+do_remote_encode (shared_ptr<DCPVideo> frame, ServerDescription description, shared_ptr<EncodedData> locally_encoded)
{
shared_ptr<EncodedData> remotely_encoded;
BOOST_CHECK_NO_THROW (remotely_encoded = frame->encode_remotely (description));
BOOST_CHECK (remotely_encoded);
BOOST_CHECK_EQUAL (locally_encoded->size(), remotely_encoded->size());
- BOOST_CHECK (memcmp (locally_encoded->data(), remotely_encoded->data(), locally_encoded->size()) == 0);
+ BOOST_CHECK_EQUAL (memcmp (locally_encoded->data(), remotely_encoded->data(), locally_encoded->size()), 0);
}
BOOST_AUTO_TEST_CASE (client_server_test_rgb)
{
- shared_ptr<Image> image (new Image (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_RGB24, dcp::Size (1998, 1080), true));
uint8_t* p = image->data()[0];
for (int y = 0; y < 1080; ++y) {
p += image->stride()[0];
}
- shared_ptr<Image> sub_image (new Image (PIX_FMT_RGBA, libdcp::Size (100, 200), true));
+ shared_ptr<Image> sub_image (new Image (PIX_FMT_RGBA, dcp::Size (100, 200), true));
p = sub_image->data()[0];
for (int y = 0; y < 200; ++y) {
uint8_t* q = p;
shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test_rgb.log"));
- shared_ptr<PlayerVideoFrame> pvf (
- new PlayerVideoFrame (
+ shared_ptr<PlayerVideo> pvf (
+ new PlayerVideo (
shared_ptr<ImageProxy> (new RawImageProxy (image, log)),
+ DCPTime (),
Crop (),
- libdcp::Size (1998, 1080),
- libdcp::Size (1998, 1080),
+ optional<float> (),
+ dcp::Size (1998, 1080),
+ dcp::Size (1998, 1080),
Scaler::from_id ("bicubic"),
EYES_BOTH,
PART_WHOLE,
)
);
- pvf->set_subtitle (sub_image, Position<int> (50, 60));
+ pvf->set_subtitle (PositionImage (sub_image, Position<int> (50, 60)));
- shared_ptr<DCPVideoFrame> frame (
- new DCPVideoFrame (
+ shared_ptr<DCPVideo> frame (
+ new DCPVideo (
pvf,
0,
24,
200000000,
RESOLUTION_2K,
+ true,
log
)
);
for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
delete *i;
}
+
+ delete server;
}
BOOST_AUTO_TEST_CASE (client_server_test_yuv)
{
- shared_ptr<Image> image (new Image (PIX_FMT_YUV420P, libdcp::Size (1998, 1080), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_YUV420P, dcp::Size (1998, 1080), true));
uint8_t* p = image->data()[0];
for (int i = 0; i < image->components(); ++i) {
}
}
- shared_ptr<Image> sub_image (new Image (PIX_FMT_RGBA, libdcp::Size (100, 200), true));
+ shared_ptr<Image> sub_image (new Image (PIX_FMT_RGBA, dcp::Size (100, 200), true));
p = sub_image->data()[0];
for (int y = 0; y < 200; ++y) {
uint8_t* q = p;
shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test_yuv.log"));
- shared_ptr<PlayerVideoFrame> pvf (
- new PlayerVideoFrame (
+ shared_ptr<PlayerVideo> pvf (
+ new PlayerVideo (
shared_ptr<ImageProxy> (new RawImageProxy (image, log)),
+ DCPTime (),
Crop (),
- libdcp::Size (1998, 1080),
- libdcp::Size (1998, 1080),
+ optional<float> (),
+ dcp::Size (1998, 1080),
+ dcp::Size (1998, 1080),
Scaler::from_id ("bicubic"),
EYES_BOTH,
PART_WHOLE,
)
);
- pvf->set_subtitle (sub_image, Position<int> (50, 60));
+ pvf->set_subtitle (PositionImage (sub_image, Position<int> (50, 60)));
- shared_ptr<DCPVideoFrame> frame (
- new DCPVideoFrame (
+ shared_ptr<DCPVideo> frame (
+ new DCPVideo (
pvf,
0,
24,
200000000,
RESOLUTION_2K,
+ true,
log
)
);
for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
delete *i;
}
+
+ delete server;
}
/*
- 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 test/colour_conversion_test.cc
+ * @brief Basic test of identifier() for ColourConversion (i.e. a hash of the numbers)
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/colour_matrix.h>
+#include <dcp/colour_matrix.h>
#include "lib/colour_conversion.h"
using std::cout;
-/* Basic test of identifier() for ColourConversion (i.e. a hash of the numbers) */
BOOST_AUTO_TEST_CASE (colour_conversion_test)
{
- ColourConversion A (2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6);
- ColourConversion B (2.4, false, libdcp::colour_matrix::srgb_to_xyz, 2.6);
+ ColourConversion A (2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6);
+ ColourConversion B (2.4, false, dcp::colour_matrix::srgb_to_xyz, 2.6);
BOOST_CHECK_EQUAL (A.identifier(), "1e720d2d99add654d7816f3b72da815e");
BOOST_CHECK_EQUAL (B.identifier(), "18751a247b22682b725bf9c4caf71522");
--- /dev/null
+/*
+ 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 test/dcp_subtitle_test.cc
+ * @brief Test DCP subtitle content in various ways.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "lib/film.h"
+#include "lib/dcp_subtitle_content.h"
+#include "lib/ratio.h"
+#include "lib/dcp_content_type.h"
+#include "test.h"
+
+using std::cout;
+using boost::shared_ptr;
+
+/** Test load of very simple DCP subtitle file */
+BOOST_AUTO_TEST_CASE (dcp_subtitle_test)
+{
+ shared_ptr<Film> film = new_test_film ("dcp_subtitle_test");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ film->set_name ("frobozz");
+ shared_ptr<DCPSubtitleContent> content (new DCPSubtitleContent (film, "test/data/dcp_sub.xml"));
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+
+ BOOST_CHECK_EQUAL (content->full_length(), DCPTime::from_seconds (2));
+}
/*
- 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 test/ffmpeg_audio_test.cc
+ * @brief A simple test of reading audio from an FFmpeg file.
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/cpl.h>
-#include <libdcp/dcp.h>
-#include <libdcp/sound_asset.h>
-#include <libdcp/sound_frame.h>
-#include <libdcp/reel.h>
+#include <dcp/cpl.h>
+#include <dcp/dcp.h>
+#include <dcp/sound_mxf.h>
+#include <dcp/sound_frame.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/reel.h>
#include "lib/sndfile_content.h"
#include "lib/film.h"
#include "lib/dcp_content_type.h"
boost::filesystem::path path = "build/test";
path /= "ffmpeg_audio_test";
path /= film->dcp_name ();
- libdcp::DCP check (path.string ());
+ dcp::DCP check (path.string ());
check.read ();
- shared_ptr<const libdcp::SoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
+ shared_ptr<const dcp::ReelSoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
BOOST_CHECK (sound_asset);
- BOOST_CHECK (sound_asset->channels () == 6);
+ BOOST_CHECK_EQUAL (sound_asset->mxf()->channels (), 6);
/* Sample index in the DCP */
int n = 0;
/* DCP sound asset frame */
int frame = 0;
- while (n < sound_asset->intrinsic_duration()) {
- shared_ptr<const libdcp::SoundFrame> sound_frame = sound_asset->get_frame (frame++);
+ while (n < sound_asset->mxf()->intrinsic_duration()) {
+ shared_ptr<const dcp::SoundFrame> sound_frame = sound_asset->mxf()->get_frame (frame++);
uint8_t const * d = sound_frame->data ();
- for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->channels())) {
+ for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->mxf()->channels())) {
- if (sound_asset->channels() > 0) {
+ if (sound_asset->mxf()->channels() > 0) {
/* L should be silent */
int const sample = d[i + 0] | (d[i + 1] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 1) {
+ if (sound_asset->mxf()->channels() > 1) {
/* R should be silent */
int const sample = d[i + 2] | (d[i + 3] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 2) {
+ if (sound_asset->mxf()->channels() > 2) {
/* Mono input so it will appear on centre */
int const sample = d[i + 7] | (d[i + 8] << 8);
BOOST_CHECK_EQUAL (sample, n);
}
- if (sound_asset->channels() > 3) {
+ if (sound_asset->mxf()->channels() > 3) {
/* Lfe should be silent */
int const sample = d[i + 9] | (d[i + 10] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 4) {
+ if (sound_asset->mxf()->channels() > 4) {
/* Ls should be silent */
int const sample = d[i + 11] | (d[i + 12] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 5) {
+ if (sound_asset->mxf()->channels() > 5) {
/* Rs should be silent */
int const sample = d[i + 13] | (d[i + 14] << 8);
BOOST_CHECK_EQUAL (sample, 0);
/*
- 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 test/ffmpeg_dcp_test.cc
+ * @brief Test creation of a very simple DCP from some FFmpegContent (data/test.mp4).
+ *
+ * Also a quick test of Film::have_dcp ().
+ */
+
#include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp>
#include "lib/film.h"
using boost::shared_ptr;
-/** @file test/ffmpeg_dcp_test.cc
- * @brief Test scaling and black-padding of images from a still-image source.
- */
-
BOOST_AUTO_TEST_CASE (ffmpeg_dcp_test)
{
shared_ptr<Film> film = new_test_film ("ffmpeg_dcp_test");
--- /dev/null
+/*
+ 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 test/ffmpeg_decoder_seek_test.cc
+ * @brief Check that get_video() returns the frame indexes that we ask for
+ * for FFmpegDecoder.
+ *
+ * This doesn't check that the contents of those frames are right, which
+ * it probably should.
+ */
+
+#include <vector>
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include "lib/ffmpeg_content.h"
+#include "lib/ffmpeg_decoder.h"
+#include "lib/log.h"
+#include "lib/film.h"
+#include "test.h"
+
+using std::cerr;
+using std::vector;
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+
+static void
+check (FFmpegDecoder& decoder, int frame)
+{
+ list<ContentVideo> v;
+ v = decoder.get_video (frame, true);
+ BOOST_CHECK (v.size() == 1);
+ BOOST_CHECK_EQUAL (v.front().frame, frame);
+}
+
+static void
+test (boost::filesystem::path file, vector<int> frames)
+{
+ boost::filesystem::path path = private_data / file;
+ if (!boost::filesystem::exists (path)) {
+ cerr << "Skipping test: " << path.string() << " not found.\n";
+ return;
+ }
+
+ shared_ptr<Film> film = new_test_film ("ffmpeg_decoder_seek_test_" + file.string());
+ shared_ptr<FFmpegContent> content (new FFmpegContent (film, path));
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+ shared_ptr<Log> log (new NullLog);
+ FFmpegDecoder decoder (content, log);
+
+ for (vector<int>::const_iterator i = frames.begin(); i != frames.end(); ++i) {
+ check (decoder, *i);
+ }
+}
+
+BOOST_AUTO_TEST_CASE (ffmpeg_decoder_seek_test)
+{
+ vector<int> frames;
+
+ frames.clear ();
+ frames.push_back (0);
+ frames.push_back (42);
+ frames.push_back (999);
+ frames.push_back (0);
+
+ test ("boon_telly.mkv", frames);
+ test ("Sintel_Trailer1.480p.DivX_Plus_HD.mkv", frames);
+
+ frames.clear ();
+ frames.push_back (15);
+ frames.push_back (42);
+ frames.push_back (999);
+ frames.push_back (15);
+
+ test ("prophet_clip.mkv", frames);
+}
+
--- /dev/null
+/*
+ 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 test/ffmpeg_decoder_sequential_test.cc
+ * @brief Check that the FFmpeg decoder produces sequential frames without gaps or dropped frames;
+ * (dropped frames being checked by assert() in VideoDecoder). Also that the decoder picks up frame rates correctly.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <boost/filesystem.hpp>
+#include "lib/ffmpeg_content.h"
+#include "lib/ffmpeg_decoder.h"
+#include "lib/log.h"
+#include "lib/film.h"
+#include "test.h"
+
+using std::cout;
+using std::cerr;
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+
+/** @param black Frame index of first frame in the video */
+static void
+test (boost::filesystem::path file, float fps, int first)
+{
+ boost::filesystem::path path = private_data / file;
+ if (!boost::filesystem::exists (path)) {
+ cerr << "Skipping test: " << path.string() << " not found.\n";
+ return;
+ }
+
+ shared_ptr<Film> film = new_test_film ("ffmpeg_decoder_seek_test_" + file.string());
+ shared_ptr<FFmpegContent> content (new FFmpegContent (film, path));
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+ shared_ptr<Log> log (new NullLog);
+ FFmpegDecoder decoder (content, log);
+
+ BOOST_CHECK_CLOSE (decoder.video_content()->video_frame_rate(), fps, 0.01);
+
+ VideoFrame const N = decoder.video_content()->video_length().frames (decoder.video_content()->video_frame_rate ());
+#ifdef DCPOMATIC_DEBUG
+ decoder.test_gaps = 0;
+#endif
+ for (VideoFrame i = 0; i < N; ++i) {
+ list<ContentVideo> v;
+ v = decoder.get_video (i, true);
+ if (i < first) {
+ BOOST_CHECK (v.empty ());
+ } else {
+ BOOST_CHECK (v.size() == 1);
+ BOOST_CHECK_EQUAL (v.front().frame, i);
+ }
+ }
+#ifdef DCPOMATIC_DEBUG
+ BOOST_CHECK_EQUAL (decoder.test_gaps, 0);
+#endif
+}
+
+BOOST_AUTO_TEST_CASE (ffmpeg_decoder_sequential_test)
+{
+ test ("boon_telly.mkv", 29.97, 0);
+ test ("Sintel_Trailer1.480p.DivX_Plus_HD.mkv", 24, 0);
+ test ("prophet_clip.mkv", 23.976, 12);
+}
+
/*
- 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 test/ffmpeg_examiner_test.cc
+ * @brief Check that the FFmpegExaminer can extract the first video and audio time
+ * correctly from data/count300bd24.m2ts.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/ffmpeg_examiner.h"
#include "lib/ffmpeg_content.h"
+#include "lib/ffmpeg_audio_stream.h"
#include "test.h"
using boost::shared_ptr;
shared_ptr<FFmpegContent> content (new FFmpegContent (film, "test/data/count300bd24.m2ts"));
shared_ptr<FFmpegExaminer> examiner (new FFmpegExaminer (content));
- BOOST_CHECK_EQUAL (examiner->first_video().get(), 600);
+ BOOST_CHECK_EQUAL (examiner->first_video().get(), ContentTime::from_seconds (600));
BOOST_CHECK_EQUAL (examiner->audio_streams().size(), 1);
- BOOST_CHECK_EQUAL (examiner->audio_streams()[0]->first_audio.get(), 600);
+ BOOST_CHECK_EQUAL (examiner->audio_streams()[0]->first_audio.get(), ContentTime::from_seconds (600));
}
+++ /dev/null
-/*
- 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 "lib/film.h"
-#include "lib/ffmpeg_decoder.h"
-#include "lib/ffmpeg_content.h"
-#include "test.h"
-
-using boost::shared_ptr;
-
-BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test)
-{
- shared_ptr<Film> film = new_test_film ("ffmpeg_pts_offset_test");
- shared_ptr<FFmpegContent> content (new FFmpegContent (film, "test/data/test.mp4"));
- content->_audio_stream.reset (new FFmpegAudioStream);
- content->_video_frame_rate = 24;
-
- {
- /* Sound == video so no offset required */
- content->_first_video = 0;
- content->_audio_stream->first_audio = 0;
- FFmpegDecoder decoder (film, content, true, true);
- BOOST_CHECK_EQUAL (decoder._pts_offset, 0);
- BOOST_CHECK_EQUAL (decoder._pts_offset, 0);
- }
-
- {
- /* Common offset should be removed */
- content->_first_video = 600;
- content->_audio_stream->first_audio = 600;
- FFmpegDecoder decoder (film, content, true, true);
- BOOST_CHECK_EQUAL (decoder._pts_offset, -600);
- BOOST_CHECK_EQUAL (decoder._pts_offset, -600);
- }
-
- {
- /* Video is on a frame boundary */
- content->_first_video = 1.0 / 24.0;
- content->_audio_stream->first_audio = 0;
- FFmpegDecoder decoder (film, content, true, true);
- BOOST_CHECK_EQUAL (decoder._pts_offset, 0);
- BOOST_CHECK_EQUAL (decoder._pts_offset, 0);
- }
-
- {
- /* Video is off a frame boundary */
- double const frame = 1.0 / 24.0;
- content->_first_video = frame + 0.0215;
- content->_audio_stream->first_audio = 0;
- FFmpegDecoder decoder (film, content, true, true);
- BOOST_CHECK_CLOSE (decoder._pts_offset, (frame - 0.0215), 0.00001);
- BOOST_CHECK_CLOSE (decoder._pts_offset, (frame - 0.0215), 0.00001);
- }
-
- {
- /* Video is off a frame boundary and both have a common offset */
- double const frame = 1.0 / 24.0;
- content->_first_video = frame + 0.0215 + 4.1;
- content->_audio_stream->first_audio = 4.1;
- FFmpegDecoder decoder (film, content, true, true);
- BOOST_CHECK_EQUAL (decoder._pts_offset, (frame - 0.0215) - 4.1);
- BOOST_CHECK_EQUAL (decoder._pts_offset, (frame - 0.0215) - 4.1);
- }
-}
--- /dev/null
+/*
+ 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 test/ffmpeg_pts_offset_test.cc
+ * @brief Check the computation of _pts_offset in FFmpegDecoder.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "lib/film.h"
+#include "lib/ffmpeg_decoder.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/ffmpeg_audio_stream.h"
+#include "test.h"
+
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (ffmpeg_pts_offset_test)
+{
+ shared_ptr<Film> film = new_test_film ("ffmpeg_pts_offset_test");
+ shared_ptr<FFmpegContent> content (new FFmpegContent (film, "test/data/test.mp4"));
+ content->_audio_stream.reset (new FFmpegAudioStream);
+ content->_video_frame_rate = 24;
+
+ {
+ /* Sound == video so no offset required */
+ content->_first_video = ContentTime ();
+ content->_audio_stream->first_audio = ContentTime ();
+ FFmpegDecoder decoder (content, film->log());
+ BOOST_CHECK_EQUAL (decoder._pts_offset, ContentTime ());
+ }
+
+ {
+ /* Common offset should be removed */
+ content->_first_video = ContentTime::from_seconds (600);
+ content->_audio_stream->first_audio = ContentTime::from_seconds (600);
+ FFmpegDecoder decoder (content, film->log());
+ BOOST_CHECK_EQUAL (decoder._pts_offset, ContentTime::from_seconds (-600));
+ }
+
+ {
+ /* Video is on a frame boundary */
+ content->_first_video = ContentTime::from_frames (1, 24);
+ content->_audio_stream->first_audio = ContentTime ();
+ FFmpegDecoder decoder (content, film->log());
+ BOOST_CHECK_EQUAL (decoder._pts_offset, ContentTime ());
+ }
+
+ {
+ /* Video is off a frame boundary */
+ double const frame = 1.0 / 24.0;
+ content->_first_video = ContentTime::from_seconds (frame + 0.0215);
+ content->_audio_stream->first_audio = ContentTime ();
+ FFmpegDecoder decoder (content, film->log());
+ BOOST_CHECK_CLOSE (decoder._pts_offset.seconds(), (frame - 0.0215), 0.00001);
+ }
+
+ {
+ /* Video is off a frame boundary and both have a common offset */
+ double const frame = 1.0 / 24.0;
+ content->_first_video = ContentTime::from_seconds (frame + 0.0215 + 4.1);
+ content->_audio_stream->first_audio = ContentTime::from_seconds (4.1);
+ FFmpegDecoder decoder (content, film->log());
+ BOOST_CHECK_CLOSE (decoder._pts_offset.seconds(), (frame - 0.0215) - 4.1, 0.1);
+ }
+}
/*
- 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 test/file_group_test.cc
+ * @brief Check that FileGroup works.
+ */
+
#include <stdint.h>
#include <cstdio>
#include <boost/test/unit_test.hpp>
/*
- 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 test/film_metadata_test.cc
+ * @brief Test some basic reading/writing of film metadata.
+ */
+
#include <boost/test/unit_test.hpp>
#include <boost/filesystem.hpp>
#include <boost/date_time.hpp>
BOOST_AUTO_TEST_CASE (film_metadata_test)
{
- string const test_film = "build/test/film_metadata_test";
-
- if (boost::filesystem::exists (test_film)) {
- boost::filesystem::remove_all (test_film);
- }
+ shared_ptr<Film> f = new_test_film ("film_metadata_test");
+ boost::filesystem::path dir = test_film_dir ("film_metadata_test");
- shared_ptr<Film> f (new Film (test_film));
f->_isdcf_date = boost::gregorian::from_undelimited_string ("20130211");
BOOST_CHECK (f->container() == 0);
BOOST_CHECK (f->dcp_content_type() == 0);
list<string> ignore;
ignore.push_back ("Key");
- check_xml ("test/data/metadata.xml.ref", test_film + "/metadata.xml", ignore);
+ check_xml ("test/data/metadata.xml.ref", dir.string() + "/metadata.xml", ignore);
- shared_ptr<Film> g (new Film (test_film));
+ shared_ptr<Film> g (new Film (dir));
g->read_metadata ();
BOOST_CHECK_EQUAL (g->name(), "fred");
BOOST_CHECK_EQUAL (g->container(), Ratio::from_id ("185"));
g->write_metadata ();
- check_xml ("test/data/metadata.xml.ref", test_film + "/metadata.xml", ignore);
+ check_xml ("test/data/metadata.xml.ref", dir.string() + "/metadata.xml", ignore);
}
/*
- 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 test/frame_rate_test.cc
+ * @brief Tests for FrameRateChange and the computation of the best
+ * frame rate for the DCP.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/film.h"
#include "lib/config.h"
#include "lib/ffmpeg_content.h"
#include "lib/playlist.h"
+#include "lib/ffmpeg_audio_stream.h"
#include "lib/frame_rate_change.h"
#include "test.h"
BOOST_CHECK_EQUAL (frc.skip, true);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 50;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, true);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 48;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, true);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 30;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 29.97;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, true);
+ BOOST_CHECK_CLOSE (frc.speed_up, 30 / 29.97, 0.1);
content->_video_frame_rate = 25;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 24;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 14.5;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 2);
BOOST_CHECK_EQUAL (frc.change_speed, true);
+ BOOST_CHECK_CLOSE (frc.speed_up, 15 / 14.5, 0.1);
content->_video_frame_rate = 12.6;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 2);
BOOST_CHECK_EQUAL (frc.change_speed, true);
+ BOOST_CHECK_CLOSE (frc.speed_up, 25 / 25.2, 0.1);
content->_video_frame_rate = 12.4;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 2);
BOOST_CHECK_EQUAL (frc.change_speed, true);
+ BOOST_CHECK_CLOSE (frc.speed_up, 25 / 24.8, 0.1);
content->_video_frame_rate = 12;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 2);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
/* Now add some more rates and see if it will use them
in preference to skip/repeat.
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 50;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
content->_video_frame_rate = 48;
best = film->playlist()->best_dcp_frame_rate ();
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, false);
+ BOOST_CHECK_CLOSE (frc.speed_up, 1, 0.1);
/* Check some out-there conversions (not the best) */
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 2);
BOOST_CHECK_EQUAL (frc.change_speed, true);
+ BOOST_CHECK_CLOSE (frc.speed_up, 24 / (2 * 14.99), 0.1);
/* Check some conversions with limited DCP targets */
BOOST_CHECK_EQUAL (frc.skip, false);
BOOST_CHECK_EQUAL (frc.repeat, 1);
BOOST_CHECK_EQUAL (frc.change_speed, true);
+ BOOST_CHECK_CLOSE (frc.speed_up, 24.0 / 25, 0.1);
}
/* Test Playlist::best_dcp_frame_rate and FrameRateChange
content->_video_frame_rate = 24;
film->set_video_frame_rate (24);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 48000);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 48000);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 48000);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 48000);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 96000);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 96000);
content->_video_frame_rate = 23.976;
film->set_video_frame_rate (24);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 47952);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 47952);
content->_video_frame_rate = 29.97;
film->set_video_frame_rate (30);
BOOST_CHECK_EQUAL (film->video_frame_rate (), 30);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 47952);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 47952);
content->_video_frame_rate = 25;
film->set_video_frame_rate (24);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 50000);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 50000);
content->_video_frame_rate = 25;
film->set_video_frame_rate (24);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), 50000);
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), 50000);
/* Check some out-there conversions (not the best) */
content->_video_frame_rate = 14.99;
film->set_video_frame_rate (25);
content->set_audio_stream (shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
- /* The FrameRateChange within output_audio_frame_rate should choose to double-up
+ /* The FrameRateChange within resampled_audio_frame_rate should choose to double-up
the 14.99 fps video to 30 and then run it slow at 25.
*/
- BOOST_CHECK_EQUAL (content->output_audio_frame_rate(), rint (48000 * 2 * 14.99 / 25));
+ BOOST_CHECK_EQUAL (content->resampled_audio_frame_rate(), rint (48000 * 2 * 14.99 / 25));
}
/*
- 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 test/image_test.cc
+ * @brief Tests of the Image class.
+ *
+ * @see test/make_black_test.cc, test/pixel_formats_test.cc
+ */
+
#include <boost/test/unit_test.hpp>
#include <Magick++.h>
#include "lib/image.h"
#include "lib/scaler.h"
using std::string;
+using std::list;
using std::cout;
using boost::shared_ptr;
BOOST_AUTO_TEST_CASE (aligned_image_test)
{
- Image* s = new Image (PIX_FMT_RGB24, libdcp::Size (50, 50), true);
+ Image* s = new Image (PIX_FMT_RGB24, dcp::Size (50, 50), true);
BOOST_CHECK_EQUAL (s->components(), 1);
/* 160 is 150 aligned to the nearest 32 bytes */
BOOST_CHECK_EQUAL (s->stride()[0], 160);
BOOST_CHECK (t->data() != s->data());
BOOST_CHECK (t->data()[0] != s->data()[0]);
BOOST_CHECK (t->line_size() != s->line_size());
- BOOST_CHECK (t->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK_EQUAL (t->line_size()[0], s->line_size()[0]);
BOOST_CHECK (t->stride() != s->stride());
- BOOST_CHECK (t->stride()[0] == s->stride()[0]);
+ BOOST_CHECK_EQUAL (t->stride()[0], s->stride()[0]);
/* assignment operator */
- Image* u = new Image (PIX_FMT_YUV422P, libdcp::Size (150, 150), false);
+ Image* u = new Image (PIX_FMT_YUV422P, dcp::Size (150, 150), false);
*u = *s;
BOOST_CHECK_EQUAL (u->components(), 1);
BOOST_CHECK_EQUAL (u->stride()[0], 160);
BOOST_CHECK (u->data() != s->data());
BOOST_CHECK (u->data()[0] != s->data()[0]);
BOOST_CHECK (u->line_size() != s->line_size());
- BOOST_CHECK (u->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK_EQUAL (u->line_size()[0], s->line_size()[0]);
BOOST_CHECK (u->stride() != s->stride());
- BOOST_CHECK (u->stride()[0] == s->stride()[0]);
+ BOOST_CHECK_EQUAL (u->stride()[0], s->stride()[0]);
delete s;
delete t;
BOOST_AUTO_TEST_CASE (compact_image_test)
{
- Image* s = new Image (PIX_FMT_RGB24, libdcp::Size (50, 50), false);
+ Image* s = new Image (PIX_FMT_RGB24, dcp::Size (50, 50), false);
BOOST_CHECK_EQUAL (s->components(), 1);
BOOST_CHECK_EQUAL (s->stride()[0], 50 * 3);
BOOST_CHECK_EQUAL (s->line_size()[0], 50 * 3);
BOOST_CHECK (t->data() != s->data());
BOOST_CHECK (t->data()[0] != s->data()[0]);
BOOST_CHECK (t->line_size() != s->line_size());
- BOOST_CHECK (t->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK_EQUAL (t->line_size()[0], s->line_size()[0]);
BOOST_CHECK (t->stride() != s->stride());
- BOOST_CHECK (t->stride()[0] == s->stride()[0]);
+ BOOST_CHECK_EQUAL (t->stride()[0], s->stride()[0]);
/* assignment operator */
- Image* u = new Image (PIX_FMT_YUV422P, libdcp::Size (150, 150), true);
+ Image* u = new Image (PIX_FMT_YUV422P, dcp::Size (150, 150), true);
*u = *s;
BOOST_CHECK_EQUAL (u->components(), 1);
BOOST_CHECK_EQUAL (u->stride()[0], 50 * 3);
BOOST_CHECK (u->data() != s->data());
BOOST_CHECK (u->data()[0] != s->data()[0]);
BOOST_CHECK (u->line_size() != s->line_size());
- BOOST_CHECK (u->line_size()[0] == s->line_size()[0]);
+ BOOST_CHECK_EQUAL (u->line_size()[0], s->line_size()[0]);
BOOST_CHECK (u->stride() != s->stride());
- BOOST_CHECK (u->stride()[0] == s->stride()[0]);
+ BOOST_CHECK_EQUAL (u->stride()[0], s->stride()[0]);
delete s;
delete t;
BOOST_AUTO_TEST_CASE (crop_image_test)
{
/* This was to check out a bug with valgrind, and is probably not very useful */
- shared_ptr<Image> image (new Image (PIX_FMT_YUV420P, libdcp::Size (16, 16), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_YUV420P, dcp::Size (16, 16), true));
image->make_black ();
Crop crop;
crop.top = 3;
BOOST_AUTO_TEST_CASE (crop_image_test2)
{
/* Here's a 1998 x 1080 image which is black */
- shared_ptr<Image> image (new Image (PIX_FMT_YUV420P, libdcp::Size (1998, 1080), true));
+ shared_ptr<Image> image (new Image (PIX_FMT_YUV420P, dcp::Size (1998, 1080), true));
image->make_black ();
/* Crop it by 1 pixel */
read_file (string file)
{
Magick::Image magick_image (file.c_str ());
- libdcp::Size size (magick_image.columns(), magick_image.rows());
+ dcp::Size size (magick_image.columns(), magick_image.rows());
boost::shared_ptr<Image> image (new Image (PIX_FMT_RGB24, size, true));
static
void
-crop_scale_window_single (AVPixelFormat in_format, libdcp::Size in_size, Crop crop, libdcp::Size inter_size, libdcp::Size out_size)
+crop_scale_window_single (AVPixelFormat in_format, dcp::Size in_size, Crop crop, dcp::Size inter_size, dcp::Size out_size)
{
/* Set up our test image */
shared_ptr<Image> test (new Image (in_format, in_size, true));
/** Test Image::crop_scale_window against separate calls to crop/scale/copy */
BOOST_AUTO_TEST_CASE (crop_scale_window_test)
{
- crop_scale_window_single (AV_PIX_FMT_YUV422P, libdcp::Size (640, 480), Crop (), libdcp::Size (640, 480), libdcp::Size (640, 480));
- crop_scale_window_single (AV_PIX_FMT_YUV422P, libdcp::Size (640, 480), Crop (2, 4, 6, 8), libdcp::Size (640, 480), libdcp::Size (640, 480));
- crop_scale_window_single (AV_PIX_FMT_YUV422P, libdcp::Size (640, 480), Crop (2, 4, 6, 8), libdcp::Size (1920, 1080), libdcp::Size (1998, 1080));
- crop_scale_window_single (AV_PIX_FMT_YUV422P, libdcp::Size (640, 480), Crop (1, 4, 6, 8), libdcp::Size (1920, 1080), libdcp::Size (1998, 1080));
- crop_scale_window_single (AV_PIX_FMT_YUV420P, libdcp::Size (640, 480), Crop (16, 16, 0, 0), libdcp::Size (1920, 1080), libdcp::Size (1998, 1080));
- crop_scale_window_single (AV_PIX_FMT_YUV420P, libdcp::Size (640, 480), Crop (16, 3, 3, 0), libdcp::Size (1920, 1080), libdcp::Size (1998, 1080));
- crop_scale_window_single (AV_PIX_FMT_RGB24, libdcp::Size (1000, 800), Crop (0, 0, 0, 0), libdcp::Size (1920, 1080), libdcp::Size (1998, 1080));
- crop_scale_window_single (AV_PIX_FMT_RGB24, libdcp::Size (1000, 800), Crop (55, 0, 1, 9), libdcp::Size (1920, 1080), libdcp::Size (1998, 1080));
+ crop_scale_window_single (AV_PIX_FMT_YUV422P, dcp::Size (640, 480), Crop (), dcp::Size (640, 480), dcp::Size (640, 480));
+ crop_scale_window_single (AV_PIX_FMT_YUV422P, dcp::Size (640, 480), Crop (2, 4, 6, 8), dcp::Size (640, 480), dcp::Size (640, 480));
+ crop_scale_window_single (AV_PIX_FMT_YUV422P, dcp::Size (640, 480), Crop (2, 4, 6, 8), dcp::Size (1920, 1080), dcp::Size (1998, 1080));
+ crop_scale_window_single (AV_PIX_FMT_YUV422P, dcp::Size (640, 480), Crop (1, 4, 6, 8), dcp::Size (1920, 1080), dcp::Size (1998, 1080));
+ crop_scale_window_single (AV_PIX_FMT_YUV420P, dcp::Size (640, 480), Crop (16, 16, 0, 0), dcp::Size (1920, 1080), dcp::Size (1998, 1080));
+ crop_scale_window_single (AV_PIX_FMT_YUV420P, dcp::Size (640, 480), Crop (16, 3, 3, 0), dcp::Size (1920, 1080), dcp::Size (1998, 1080));
+ crop_scale_window_single (AV_PIX_FMT_RGB24, dcp::Size (1000, 800), Crop (0, 0, 0, 0), dcp::Size (1920, 1080), dcp::Size (1998, 1080));
+ crop_scale_window_single (AV_PIX_FMT_RGB24, dcp::Size (1000, 800), Crop (55, 0, 1, 9), dcp::Size (1920, 1080), dcp::Size (1998, 1080));
+}
+
+/** Test Image::alpha_blend */
+BOOST_AUTO_TEST_CASE (alpha_blend_test)
+{
+ int const stride = 48 * 4;
+
+ shared_ptr<Image> A (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 48), false));
+ A->make_black ();
+ uint8_t* a = A->data()[0];
+
+ for (int y = 0; y < 48; ++y) {
+ uint8_t* p = a + y * stride;
+ for (int x = 0; x < 16; ++x) {
+ p[x * 4] = 255;
+ p[(x + 16) * 4 + 1] = 255;
+ p[(x + 32) * 4 + 2] = 255;
+ }
+ }
+
+ shared_ptr<Image> B (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 48), true));
+ B->make_transparent ();
+ uint8_t* b = B->data()[0];
+
+ for (int y = 32; y < 48; ++y) {
+ uint8_t* p = b + y * stride;
+ for (int x = 0; x < 48; ++x) {
+ p[x * 4] = 255;
+ p[x * 4 + 1] = 255;
+ p[x * 4 + 2] = 255;
+ p[x * 4 + 3] = 255;
+ }
+ }
+
+ A->alpha_blend (B, Position<int> (0, 0));
+
+ for (int y = 0; y < 32; ++y) {
+ uint8_t* p = a + y * stride;
+ for (int x = 0; x < 16; ++x) {
+ BOOST_CHECK_EQUAL (p[x * 4], 255);
+ BOOST_CHECK_EQUAL (p[(x + 16) * 4 + 1], 255);
+ BOOST_CHECK_EQUAL (p[(x + 32) * 4 + 2], 255);
+ }
+ }
+
+ for (int y = 32; y < 48; ++y) {
+ uint8_t* p = a + y * stride;
+ for (int x = 0; x < 48; ++x) {
+ BOOST_CHECK_EQUAL (p[x * 4], 255);
+ BOOST_CHECK_EQUAL (p[x * 4 + 1], 255);
+ BOOST_CHECK_EQUAL (p[x * 4 + 2], 255);
+ BOOST_CHECK_EQUAL (p[x * 4 + 3], 255);
+ }
+ }
+}
+
+/** Test merge (list<PositionImage>) with a single image */
+BOOST_AUTO_TEST_CASE (merge_test1)
+{
+ int const stride = 48 * 4;
+
+ shared_ptr<Image> A (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 48), false));
+ A->make_transparent ();
+ uint8_t* a = A->data()[0];
+
+ for (int y = 0; y < 48; ++y) {
+ uint8_t* p = a + y * stride;
+ for (int x = 0; x < 16; ++x) {
+ /* red */
+ p[x * 4] = 255;
+ /* opaque */
+ p[x * 4 + 3] = 255;
+ }
+ }
+
+ list<PositionImage> all;
+ all.push_back (PositionImage (A, Position<int> (0, 0)));
+ PositionImage merged = merge (all);
+
+ BOOST_CHECK (merged.position == Position<int> (0, 0));
+ BOOST_CHECK_EQUAL (memcmp (merged.image->data()[0], A->data()[0], stride * 48), 0);
+}
+
+/** Test merge (list<PositionImage>) with two images */
+BOOST_AUTO_TEST_CASE (merge_test2)
+{
+ shared_ptr<Image> A (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 1), false));
+ A->make_transparent ();
+ uint8_t* a = A->data()[0];
+ for (int x = 0; x < 16; ++x) {
+ /* red */
+ a[x * 4] = 255;
+ /* opaque */
+ a[x * 4 + 3] = 255;
+ }
+
+ shared_ptr<Image> B (new Image (AV_PIX_FMT_RGBA, dcp::Size (48, 1), false));
+ B->make_transparent ();
+ uint8_t* b = B->data()[0];
+ for (int x = 0; x < 16; ++x) {
+ /* blue */
+ b[(x + 32) * 4 + 2] = 255;
+ /* opaque */
+ b[(x + 32) * 4 + 3] = 255;
+ }
+
+ list<PositionImage> all;
+ all.push_back (PositionImage (A, Position<int> (0, 0)));
+ all.push_back (PositionImage (B, Position<int> (0, 0)));
+ PositionImage merged = merge (all);
+
+ BOOST_CHECK (merged.position == Position<int> (0, 0));
+
+ uint8_t* m = merged.image->data()[0];
+
+ for (int x = 0; x < 16; ++x) {
+ BOOST_CHECK_EQUAL (m[x * 4], 255);
+ BOOST_CHECK_EQUAL (m[x * 4 + 3], 255);
+ BOOST_CHECK_EQUAL (m[(x + 16) * 4 + 3], 0);
+ BOOST_CHECK_EQUAL (m[(x + 32) * 4 + 2], 255);
+ BOOST_CHECK_EQUAL (m[(x + 32) * 4 + 3], 255);
+ }
}
--- /dev/null
+/*
+ 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 <dcp/cpl.h>
+#include "lib/film.h"
+#include "lib/dcp_subtitle_content.h"
+#include "lib/ratio.h"
+#include "lib/dcp_content_type.h"
+#include "lib/dcp_content.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/config.h"
+#include "test.h"
+
+using boost::shared_ptr;
+
+/** Make an encrypted DCP, import it and make a new unencrypted DCP */
+BOOST_AUTO_TEST_CASE (import_dcp_test)
+{
+ shared_ptr<Film> A = new_test_film ("import_dcp_test");
+ A->set_container (Ratio::from_id ("185"));
+ A->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ A->set_name ("frobozz");
+
+ shared_ptr<FFmpegContent> c (new FFmpegContent (A, "test/data/test.mp4"));
+ A->examine_and_add_content (c);
+ A->set_encrypted (true);
+ wait_for_jobs ();
+
+ A->make_dcp ();
+ wait_for_jobs ();
+
+ dcp::DCP A_dcp ("build/test/import_dcp_test/" + A->dcp_name());
+ A_dcp.read ();
+
+ dcp::EncryptedKDM kdm = A->make_kdm (
+ Config::instance()->decryption_certificate(),
+ A_dcp.cpls().front()->file (),
+ dcp::LocalTime ("2014-07-21T00:00:00+00:00"),
+ dcp::LocalTime ("2024-07-21T00:00:00+00:00"),
+ dcp::MODIFIED_TRANSITIONAL_1
+ );
+
+ shared_ptr<Film> B = new_test_film ("import_dcp_test2");
+ B->set_container (Ratio::from_id ("185"));
+ B->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ B->set_name ("frobozz");
+
+ shared_ptr<DCPContent> d (new DCPContent (B, "build/test/import_dcp_test/" + A->dcp_name()));
+ d->add_kdm (kdm);
+ B->examine_and_add_content (d);
+ wait_for_jobs ();
+
+ B->make_dcp ();
+ wait_for_jobs ();
+
+ check_dcp ("build/test/import_dcp_test2/" + B->dcp_name(), "test/data/import_dcp_test2");
+}
/*
- 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 test/job_test.cc
+ * @brief Basic tests of Job and JobManager.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/job.h"
#include "lib/job_manager.h"
*/
+/** @file test/make_black_test.cc
+ * @brief Check that Image::make_black works, and doesn't use values which crash
+ * sws_scale().
+ *
+ * @see test/image_test.cc
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
extern "C" {
#include <libavutil/pixfmt.h>
}
using std::list;
-/* Check that Image::make_black works, and doesn't use values which crash
- sws_scale().
-*/
BOOST_AUTO_TEST_CASE (make_black_test)
{
- libdcp::Size in_size (512, 512);
- libdcp::Size out_size (1024, 1024);
+ dcp::Size in_size (512, 512);
+ dcp::Size out_size (1024, 1024);
list<AVPixelFormat> pix_fmts;
pix_fmts.push_back (AV_PIX_FMT_RGB24); // 2
/*
- 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/pixel_formats_test.cc
+ * @brief Make sure that Image::lines() and Image::bytes_per_pixel() return the right
+ * things for various pixel formats.
+ *
+ * @see test/image_test.cc
+ */
+
#include <boost/test/unit_test.hpp>
#include <list>
extern "C" {
using std::list;
using std::cout;
+/** @struct Case
+ * @brief A test case for pixel_formats_test.
+ */
struct Case
{
Case (AVPixelFormat f, int c, int l0, int l1, int l2, float b0, float b1, float b2)
--- /dev/null
+/*
+ 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 test/player_test.cc
+ * @brief Various tests of Player.
+ */
+
+#include <iostream>
+#include <boost/test/unit_test.hpp>
+#include "lib/film.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/dcp_content_type.h"
+#include "lib/ratio.h"
+#include "lib/audio_buffers.h"
+#include "lib/player.h"
+#include "test.h"
+
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+
+/** Player::overlaps */
+BOOST_AUTO_TEST_CASE (player_overlaps_test)
+{
+ shared_ptr<Film> film = new_test_film ("player_overlaps_test");
+ film->set_container (Ratio::from_id ("185"));
+ shared_ptr<FFmpegContent> A (new FFmpegContent (film, "test/data/test.mp4"));
+ shared_ptr<FFmpegContent> B (new FFmpegContent (film, "test/data/test.mp4"));
+ shared_ptr<FFmpegContent> C (new FFmpegContent (film, "test/data/test.mp4"));
+
+ film->examine_and_add_content (A);
+ film->examine_and_add_content (B);
+ film->examine_and_add_content (C);
+ wait_for_jobs ();
+
+ BOOST_CHECK_EQUAL (A->full_length(), DCPTime (288000));
+
+ A->set_position (DCPTime::from_seconds (0));
+ B->set_position (DCPTime::from_seconds (10));
+ C->set_position (DCPTime::from_seconds (20));
+
+ shared_ptr<Player> player = film->make_player ();
+
+ list<shared_ptr<Piece> > o = player->overlaps<FFmpegContent> (DCPTime::from_seconds (0), DCPTime::from_seconds (5));
+ BOOST_CHECK_EQUAL (o.size(), 1);
+ BOOST_CHECK_EQUAL (o.front()->content, A);
+
+ o = player->overlaps<FFmpegContent> (DCPTime::from_seconds (5), DCPTime::from_seconds (8));
+ BOOST_CHECK_EQUAL (o.size(), 0);
+
+ o = player->overlaps<FFmpegContent> (DCPTime::from_seconds (8), DCPTime::from_seconds (12));
+ BOOST_CHECK_EQUAL (o.size(), 1);
+ BOOST_CHECK_EQUAL (o.front()->content, B);
+
+ o = player->overlaps<FFmpegContent> (DCPTime::from_seconds (2), DCPTime::from_seconds (12));
+ BOOST_CHECK_EQUAL (o.size(), 2);
+ BOOST_CHECK_EQUAL (o.front()->content, A);
+ BOOST_CHECK_EQUAL (o.back()->content, B);
+
+ o = player->overlaps<FFmpegContent> (DCPTime::from_seconds (8), DCPTime::from_seconds (11));
+ BOOST_CHECK_EQUAL (o.size(), 1);
+ BOOST_CHECK_EQUAL (o.front()->content, B);
+}
+
+/** Check that the Player correctly generates silence when used with a silent FFmpegContent */
+BOOST_AUTO_TEST_CASE (player_silence_padding_test)
+{
+ shared_ptr<Film> film = new_test_film ("player_silence_padding_test");
+ film->set_name ("player_silence_padding_test");
+ shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/test.mp4"));
+ film->set_container (Ratio::from_id ("185"));
+ film->set_audio_channels (6);
+
+ film->examine_and_add_content (c);
+ wait_for_jobs ();
+
+ shared_ptr<Player> player = film->make_player ();
+ shared_ptr<AudioBuffers> test = player->get_audio (DCPTime (0), DCPTime::from_seconds (1), true);
+ BOOST_CHECK_EQUAL (test->frames(), 48000);
+ BOOST_CHECK_EQUAL (test->channels(), film->audio_channels ());
+
+ for (int i = 0; i < test->frames(); ++i) {
+ for (int c = 0; c < test->channels(); ++c) {
+ BOOST_CHECK_EQUAL (test->data()[c][i], 0);
+ }
+ }
+}
+
/*
- 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 test/ratio_test.cc
+ * @brief Test Ratio and fit_ratio_within().
+ */
+
#include <iostream>
#include <boost/test/unit_test.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
#include "lib/ratio.h"
#include "lib/util.h"
using std::ostream;
-namespace libdcp {
-
-ostream&
-operator<< (ostream& s, libdcp::Size const & t)
-{
- s << t.width << "x" << t.height;
- return s;
-}
-
-}
-
BOOST_AUTO_TEST_CASE (ratio_test)
{
Ratio::setup_ratios ();
Ratio const * r = Ratio::from_id ("119");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1290, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1290, 1080));
r = Ratio::from_id ("133");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1440, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1440, 1080));
r = Ratio::from_id ("137");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1480, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1480, 1080));
r = Ratio::from_id ("138");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1485, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1485, 1080));
r = Ratio::from_id ("166");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1800, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1800, 1080));
r = Ratio::from_id ("178");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1920, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1920, 1080));
r = Ratio::from_id ("185");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (1998, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (1998, 1080));
r = Ratio::from_id ("239");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (2048, 858));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (2048, 858));
r = Ratio::from_id ("full-frame");
BOOST_CHECK (r);
- BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), libdcp::Size (2048, 1080)), libdcp::Size (2048, 1080));
+ BOOST_CHECK_EQUAL (fit_ratio_within (r->ratio(), dcp::Size (2048, 1080), 1), dcp::Size (2048, 1080));
}
*/
+/** @file test/recover_test.cc
+ * @brief Test recovery of a DCP transcode after a crash.
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/stereo_picture_asset.h>
+#include <dcp/stereo_picture_mxf.h>
#include "lib/film.h"
#include "lib/dcp_content_type.h"
#include "lib/image_content.h"
using boost::shared_ptr;
static void
-note (libdcp::NoteType, string n)
+note (dcp::NoteType t, string n)
{
- cout << n << "\n";
+ if (t == dcp::DCP_ERROR) {
+ cout << n << "\n";
+ }
}
-/** Test recovery of a DCP transcode after a crash */
BOOST_AUTO_TEST_CASE (recover_test)
{
shared_ptr<Film> film = new_test_film ("recover_test");
film->make_dcp ();
wait_for_jobs ();
+ boost::filesystem::path const video = "build/test/recover_test/video/185_2K_1133fd57e751ce3e82146492466365f9_24_bicubic_100000000_P_S_3D.mxf";
+
boost::filesystem::copy_file (
- "build/test/recover_test/video/185_2K_58a090f8d70a2b410c534120d35e5256_24_bicubic_200000000_P_S_3D.mxf",
+ video,
"build/test/recover_test/original.mxf"
);
- boost::filesystem::resize_file ("build/test/recover_test/video/185_2K_58a090f8d70a2b410c534120d35e5256_24_bicubic_200000000_P_S_3D.mxf", 2 * 1024 * 1024);
+ boost::filesystem::resize_file (video, 2 * 1024 * 1024);
film->make_dcp ();
wait_for_jobs ();
- shared_ptr<libdcp::StereoPictureAsset> A (new libdcp::StereoPictureAsset ("build/test/recover_test", "original.mxf"));
- shared_ptr<libdcp::StereoPictureAsset> B (new libdcp::StereoPictureAsset ("build/test/recover_test/video", "185_2K_58a090f8d70a2b410c534120d35e5256_24_bicubic_200000000_P_S_3D.mxf"));
+ shared_ptr<dcp::StereoPictureMXF> A (new dcp::StereoPictureMXF ("build/test/recover_test/original.mxf"));
+ shared_ptr<dcp::StereoPictureMXF> B (new dcp::StereoPictureMXF (video));
- libdcp::EqualityOptions eq;
+ dcp::EqualityOptions eq;
eq.mxf_names_can_differ = true;
BOOST_CHECK (A->equals (B, eq, boost::bind (¬e, _1, _2)));
}
--- /dev/null
+/*
+ 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 test/repeat_frame_test.cc
+ * @brief Test the repeat of frames by the player when putting a 24fps
+ * source into a 48fps DCP.
+ *
+ * @see test/skip_frame_test.cc
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "test.h"
+#include "lib/film.h"
+#include "lib/ratio.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/dcp_content_type.h"
+
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (repeat_frame_test)
+{
+ shared_ptr<Film> film = new_test_film ("repeat_frame_test");
+ film->set_name ("repeat_frame_test");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
+ shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/red_24.mp4"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
+ film->examine_and_add_content (c);
+
+ wait_for_jobs ();
+
+ film->set_video_frame_rate (48);
+ film->make_dcp ();
+ wait_for_jobs ();
+
+ check_dcp ("test/data/repeat_frame_test", film->dir (film->dcp_name ()));
+}
+
/*
- 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 test/resampler_test.cc
+ * @brief Check that the timings that come back from the resampler correspond
+ * to the number of samples it generates.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/audio_buffers.h"
#include "lib/resampler.h"
/* 3 hours */
int64_t const N = int64_t (from) * 60 * 60 * 3;
-
+
+ /* XXX: no longer checks anything */
for (int64_t i = 0; i < N; i += 1000) {
shared_ptr<AudioBuffers> a (new AudioBuffers (1, 1000));
a->make_silent ();
- pair<shared_ptr<const AudioBuffers>, AudioContent::Frame> r = resamp.run (a, i);
- BOOST_CHECK_EQUAL (r.second, total_out);
- total_out += r.first->frames ();
+ shared_ptr<const AudioBuffers> r = resamp.run (a);
+ total_out += r->frames ();
}
}
-/** Check that the timings that come back from the resampler correspond
- to the number of samples it generates.
-*/
BOOST_AUTO_TEST_CASE (resampler_test)
{
resampler_test_one (44100, 48000);
/*
- 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 test/scaling_test.cc
+ * @brief Test scaling and black-padding of images from a still-image source.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/image_content.h"
#include "lib/ratio.h"
#include "lib/dcp_content_type.h"
#include "test.h"
-/** @file test/scaling_test.cc
- * @brief Test scaling and black-padding of images from a still-image source.
- */
-
using std::string;
using boost::shared_ptr;
wait_for_jobs ();
- imc->set_video_length (1);
+ imc->set_video_length (ContentTime::from_frames (1, 24));
scaling_test_for (film, imc, "133", "185");
scaling_test_for (film, imc, "185", "185");
--- /dev/null
+/*
+ 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 test/seek_zero_test.cc
+ * @brief Test seek to zero with a raw FFmpegDecoder (without the player
+ * confusing things as it might in ffmpeg_seek_test).
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "lib/film.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/ratio.h"
+#include "lib/dcp_content_type.h"
+#include "lib/ffmpeg_decoder.h"
+#include "lib/ffmpeg_audio_stream.h"
+#include "lib/content_video.h"
+#include "test.h"
+
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::optional;
+
+BOOST_AUTO_TEST_CASE (seek_zero_test)
+{
+ shared_ptr<Film> film = new_test_film ("seek_zero_test");
+ film->set_name ("seek_zero_test");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
+ shared_ptr<FFmpegContent> content (new FFmpegContent (film, "test/data/count300bd48.m2ts"));
+ content->set_scale (VideoContentScale (Ratio::from_id ("185")));
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+
+ /* Work out the first video frame index that we will be given, taking into account
+ * the difference between first video and first audio.
+ */
+ ContentTime video_delay = content->first_video().get() - content->audio_stream()->first_audio.get();
+ if (video_delay < ContentTime ()) {
+ video_delay = ContentTime ();
+ }
+
+ VideoFrame const first_frame = video_delay.round_up (content->video_frame_rate ()).frames (content->video_frame_rate ());
+
+ FFmpegDecoder decoder (content, film->log());
+ list<ContentVideo> a = decoder.get_video (first_frame, true);
+ BOOST_CHECK (a.size() == 1);
+ BOOST_CHECK_EQUAL (a.front().frame, first_frame);
+}
/*
- 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 test/silence_padding_test.cc
+ * @brief Test the padding (with silence) of a mono source to a 6-channel DCP.
+ */
+
#include <boost/test/unit_test.hpp>
-#include <libdcp/cpl.h>
-#include <libdcp/dcp.h>
-#include <libdcp/sound_asset.h>
-#include <libdcp/sound_frame.h>
-#include <libdcp/reel.h>
+#include <dcp/cpl.h>
+#include <dcp/dcp.h>
+#include <dcp/sound_mxf.h>
+#include <dcp/sound_frame.h>
+#include <dcp/reel.h>
+#include <dcp/reel_sound_asset.h>
#include "lib/sndfile_content.h"
#include "lib/film.h"
#include "lib/dcp_content_type.h"
using boost::lexical_cast;
using boost::shared_ptr;
-static void test_silence_padding (int channels)
+static void
+test_silence_padding (int channels)
{
string const film_name = "silence_padding_test_" + lexical_cast<string> (channels);
shared_ptr<Film> film = new_test_film (film_name);
boost::filesystem::path path = "build/test";
path /= film_name;
path /= film->dcp_name ();
- libdcp::DCP check (path.string ());
+ dcp::DCP check (path.string ());
check.read ();
- shared_ptr<const libdcp::SoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
+ shared_ptr<const dcp::ReelSoundAsset> sound_asset = check.cpls().front()->reels().front()->main_sound ();
BOOST_CHECK (sound_asset);
- BOOST_CHECK (sound_asset->channels () == channels);
+ BOOST_CHECK_EQUAL (sound_asset->mxf()->channels (), channels);
/* Sample index in the DCP */
int n = 0;
/* DCP sound asset frame */
int frame = 0;
- while (n < sound_asset->intrinsic_duration()) {
- shared_ptr<const libdcp::SoundFrame> sound_frame = sound_asset->get_frame (frame++);
+ while (n < sound_asset->mxf()->intrinsic_duration()) {
+ shared_ptr<const dcp::SoundFrame> sound_frame = sound_asset->mxf()->get_frame (frame++);
uint8_t const * d = sound_frame->data ();
- for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->channels())) {
+ for (int i = 0; i < sound_frame->size(); i += (3 * sound_asset->mxf()->channels())) {
- if (sound_asset->channels() > 0) {
+ if (sound_asset->mxf()->channels() > 0) {
/* L should be silent */
int const sample = d[i + 0] | (d[i + 1] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 1) {
+ if (sound_asset->mxf()->channels() > 1) {
/* R should be silent */
int const sample = d[i + 2] | (d[i + 3] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 2) {
+ if (sound_asset->mxf()->channels() > 2) {
/* Mono input so it will appear on centre */
int const sample = d[i + 7] | (d[i + 8] << 8);
BOOST_CHECK_EQUAL (sample, n);
}
- if (sound_asset->channels() > 3) {
+ if (sound_asset->mxf()->channels() > 3) {
/* Lfe should be silent */
int const sample = d[i + 9] | (d[i + 10] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 4) {
+ if (sound_asset->mxf()->channels() > 4) {
/* Ls should be silent */
int const sample = d[i + 11] | (d[i + 12] << 8);
BOOST_CHECK_EQUAL (sample, 0);
}
- if (sound_asset->channels() > 5) {
+ if (sound_asset->mxf()->channels() > 5) {
/* Rs should be silent */
int const sample = d[i + 13] | (d[i + 14] << 8);
BOOST_CHECK_EQUAL (sample, 0);
--- /dev/null
+/*
+ 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 test/skip_frame_test.cc
+ * @brief Test the skip of frames by the player when putting a 48fps
+ * source into a 24fps DCP.
+ *
+ * @see test/repeat_frame_test.cc
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "test.h"
+#include "lib/film.h"
+#include "lib/ratio.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/dcp_content_type.h"
+
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (skip_frame_test)
+{
+ shared_ptr<Film> film = new_test_film ("skip_frame_test");
+ film->set_name ("skip_frame_test");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
+ shared_ptr<FFmpegContent> c (new FFmpegContent (film, "test/data/count300bd48.m2ts"));
+ c->set_scale (VideoContentScale (Ratio::from_id ("185")));
+ film->examine_and_add_content (c);
+
+ wait_for_jobs ();
+ film->write_metadata ();
+
+ film->set_video_frame_rate (24);
+ film->make_dcp ();
+ wait_for_jobs ();
+
+ check_dcp ("test/data/skip_frame_test", film->dir (film->dcp_name ()));
+}
+
/*
- 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
*/
+/** @test test/stream_test.cc
+ * @brief Some simple tests of FFmpegAudioStream.
+ */
+
#include <boost/test/unit_test.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
#include "lib/ffmpeg_content.h"
+#include "lib/ffmpeg_audio_stream.h"
#include "lib/film.h"
using std::pair;
map->add_child("DCP")->add_child_text ("2");
}
- FFmpegAudioStream a (shared_ptr<cxml::Node> (new cxml::Node (root)), 5);
+ FFmpegAudioStream a (cxml::NodePtr (new cxml::Node (root)), 5);
BOOST_CHECK_EQUAL (a.identifier(), "4");
- BOOST_CHECK_EQUAL (a.frame_rate, 44100);
- BOOST_CHECK_EQUAL (a.channels, 2);
+ BOOST_CHECK_EQUAL (a.frame_rate(), 44100);
+ BOOST_CHECK_EQUAL (a.channels(), 2);
BOOST_CHECK_EQUAL (a.name, "hello there world");
- BOOST_CHECK_EQUAL (a.mapping.content_channels(), 2);
+ BOOST_CHECK_EQUAL (a.mapping().content_channels(), 2);
- BOOST_CHECK_EQUAL (a.mapping.get (0, libdcp::LEFT), 1);
- BOOST_CHECK_EQUAL (a.mapping.get (0, libdcp::RIGHT), 0);
- BOOST_CHECK_EQUAL (a.mapping.get (0, libdcp::CENTRE), 1);
- BOOST_CHECK_EQUAL (a.mapping.get (1, libdcp::LEFT), 0);
- BOOST_CHECK_EQUAL (a.mapping.get (1, libdcp::RIGHT), 1);
- BOOST_CHECK_EQUAL (a.mapping.get (1, libdcp::CENTRE), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (0, dcp::LEFT), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (0, dcp::RIGHT), 0);
+ BOOST_CHECK_EQUAL (a.mapping().get (0, dcp::CENTRE), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (1, dcp::LEFT), 0);
+ BOOST_CHECK_EQUAL (a.mapping().get (1, dcp::RIGHT), 1);
+ BOOST_CHECK_EQUAL (a.mapping().get (1, dcp::CENTRE), 1);
}
--- /dev/null
+/*
+ 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 test/subrip_test.cc
+ * @brief Various tests of the subrip code.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include <dcp/subtitle_content.h>
+#include "lib/subrip.h"
+#include "lib/subrip_content.h"
+#include "lib/subrip_decoder.h"
+#include "lib/render_subtitles.h"
+#include "test.h"
+
+using std::list;
+using std::vector;
+using std::string;
+using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+
+/** Test rendering of a SubRip subtitle */
+BOOST_AUTO_TEST_CASE (subrip_render_test)
+{
+ shared_ptr<Film> film = new_test_film ("subrip_render_test");
+ shared_ptr<SubRipContent> content (new SubRipContent (film, "test/data/subrip.srt"));
+ content->examine (shared_ptr<Job> ());
+ BOOST_CHECK_EQUAL (content->full_length(), DCPTime::from_seconds ((3 * 60) + 56.471));
+
+ shared_ptr<SubRipDecoder> decoder (new SubRipDecoder (content));
+ list<ContentTextSubtitle> cts = decoder->get_text_subtitles (
+ ContentTimePeriod (
+ ContentTime::from_seconds (109), ContentTime::from_seconds (110)
+ ), false
+ );
+ BOOST_CHECK_EQUAL (cts.size(), 1);
+
+ PositionImage image = render_subtitles (cts.front().subs, dcp::Size (1998, 1080));
+ write_image (image.image, "build/test/subrip_render_test.png");
+ check_file ("build/test/subrip_render_test.png", "test/data/subrip_render_test.png");
+}
/*
- 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 test/test.cc
+ * @brief Overall test stuff and useful methods for tests.
+ */
+
#include <vector>
#include <list>
+#include <Magick++.h>
+#include <sndfile.h>
#include <libxml++/libxml++.h>
-#include <libdcp/dcp.h>
+#include <dcp/dcp.h>
#include "lib/config.h"
#include "lib/util.h"
#include "lib/ui_signaller.h"
#include "lib/job.h"
#include "lib/cross.h"
#include "lib/server_finder.h"
+#include "lib/image.h"
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE dcpomatic_test
#include <boost/test/unit_test.hpp>
using std::list;
using boost::shared_ptr;
+boost::filesystem::path private_data = boost::filesystem::path ("..") / boost::filesystem::path ("dcpomatic-test-private");
+
class TestUISignaller : public UISignaller
{
public:
struct TestConfig
{
- TestConfig()
+ TestConfig ()
{
- dcpomatic_setup();
+ dcpomatic_setup ();
Config::instance()->set_num_local_encoding_threads (1);
Config::instance()->set_server_port_base (61920);
Config::instance()->set_default_isdcf_metadata (ISDCFMetadata ());
Config::instance()->set_default_container (static_cast<Ratio*> (0));
Config::instance()->set_default_dcp_content_type (static_cast<DCPContentType*> (0));
+ Config::instance()->set_default_audio_delay (0);
+ Config::instance()->set_default_j2k_bandwidth (100000000);
ServerFinder::instance()->disable ();
ui_signaller = new TestUISignaller ();
}
+
+ ~TestConfig ()
+ {
+ JobManager::drop ();
+ }
};
BOOST_GLOBAL_FIXTURE (TestConfig);
return f;
}
+void
+check_audio_file (boost::filesystem::path ref, boost::filesystem::path check)
+{
+ SF_INFO ref_info;
+ ref_info.format = 0;
+ SNDFILE* ref_file = sf_open (ref.string().c_str(), SFM_READ, &ref_info);
+ BOOST_CHECK (ref_file);
+
+ SF_INFO check_info;
+ check_info.format = 0;
+ SNDFILE* check_file = sf_open (check.string().c_str(), SFM_READ, &check_info);
+ BOOST_CHECK (check_file);
+
+ BOOST_CHECK_EQUAL (ref_info.frames, check_info.frames);
+ BOOST_CHECK_EQUAL (ref_info.samplerate, check_info.samplerate);
+ BOOST_CHECK_EQUAL (ref_info.channels, check_info.channels);
+ BOOST_CHECK_EQUAL (ref_info.format, check_info.format);
+
+ /* buffer_size is in frames */
+ sf_count_t const buffer_size = 65536 * ref_info.channels;
+ int32_t* ref_buffer = new int32_t[buffer_size];
+ int32_t* check_buffer = new int32_t[buffer_size];
+
+ sf_count_t N = ref_info.frames;
+ while (N) {
+ sf_count_t this_time = min (buffer_size, N);
+ sf_count_t r = sf_readf_int (ref_file, ref_buffer, this_time);
+ BOOST_CHECK_EQUAL (r, this_time);
+ r = sf_readf_int (check_file, check_buffer, this_time);
+ BOOST_CHECK_EQUAL (r, this_time);
+
+ for (sf_count_t i = 0; i < this_time; ++i) {
+ BOOST_CHECK (fabs (ref_buffer[i] - check_buffer[i]) <= 65536);
+ }
+
+ N -= this_time;
+ }
+}
+
void
check_file (boost::filesystem::path ref, boost::filesystem::path check)
{
uintmax_t N = boost::filesystem::file_size (ref);
- BOOST_CHECK_EQUAL (N, boost::filesystem::file_size(check));
+ BOOST_CHECK_EQUAL (N, boost::filesystem::file_size (check));
FILE* ref_file = fopen_boost (ref, "rb");
BOOST_CHECK (ref_file);
FILE* check_file = fopen_boost (check, "rb");
uint8_t* ref_buffer = new uint8_t[buffer_size];
uint8_t* check_buffer = new uint8_t[buffer_size];
+ SafeStringStream error;
+ error << "File " << check.string() << " differs from reference " << ref.string();
+
while (N) {
uintmax_t this_time = min (uintmax_t (buffer_size), N);
size_t r = fread (ref_buffer, 1, this_time, ref_file);
r = fread (check_buffer, 1, this_time, check_file);
BOOST_CHECK_EQUAL (r, this_time);
- BOOST_CHECK_EQUAL (memcmp (ref_buffer, check_buffer, this_time), 0);
+ BOOST_CHECK_MESSAGE (memcmp (ref_buffer, check_buffer, this_time) == 0, error.str ());
+ if (memcmp (ref_buffer, check_buffer, this_time)) {
+ break;
+ }
+
N -= this_time;
}
}
static void
-note (libdcp::NoteType t, string n)
+note (dcp::NoteType t, string n)
{
- if (t == libdcp::ERROR) {
+ if (t == dcp::DCP_ERROR) {
cerr << n << "\n";
}
}
void
-check_dcp (string ref, string check)
+check_dcp (boost::filesystem::path ref, boost::filesystem::path check)
{
- libdcp::DCP ref_dcp (ref);
+ dcp::DCP ref_dcp (ref);
ref_dcp.read ();
- libdcp::DCP check_dcp (check);
+ dcp::DCP check_dcp (check);
check_dcp.read ();
- libdcp::EqualityOptions options;
+ dcp::EqualityOptions options;
options.max_mean_pixel_error = 5;
options.max_std_dev_pixel_error = 5;
options.max_audio_sample_error = 255;
- options.cpl_names_can_differ = true;
+ options.cpl_annotation_texts_can_differ = true;
options.mxf_names_can_differ = true;
+ options.reel_hashes_can_differ = true;
BOOST_CHECK (ref_dcp.equals (check_dcp, options, boost::bind (note, _1, _2)));
}
xmlpp::Element::NodeList::iterator k = ref_children.begin ();
xmlpp::Element::NodeList::iterator l = test_children.begin ();
- while (k != ref_children.end ()) {
+ while (k != ref_children.end () && l != test_children.end ()) {
/* XXX: should be doing xmlpp::EntityReference, xmlpp::XIncludeEnd, xmlpp::XIncludeStart */
++k;
++l;
}
+
+ BOOST_CHECK (k == ref_children.end ());
+ BOOST_CHECK (l == test_children.end ());
}
void
ui_signaller->ui_idle ();
}
if (jm->errors ()) {
+ int N = 0;
+ for (list<shared_ptr<Job> >::iterator i = jm->_jobs.begin(); i != jm->_jobs.end(); ++i) {
+ if ((*i)->finished_in_error ()) {
+ ++N;
+ }
+ }
+ cerr << N << " errors.\n";
+
for (list<shared_ptr<Job> >::iterator i = jm->_jobs.begin(); i != jm->_jobs.end(); ++i) {
if ((*i)->finished_in_error ()) {
- cerr << (*i)->error_summary () << "\n"
- << (*i)->error_details () << "\n";
+ cerr << (*i)->name() << ":\n"
+ << "\tsummary: " << (*i)->error_summary () << "\n"
+ << "\tdetails: " << (*i)->error_details () << "\n";
}
}
}
ui_signaller->ui_idle ();
}
+
+void
+write_image (shared_ptr<const Image> image, boost::filesystem::path file)
+{
+ using namespace MagickCore;
+
+ Magick::Image m (image->size().width, image->size().height, "ARGB", CharPixel, (void *) image->data()[0]);
+ m.write (file.string ());
+}
#include <boost/filesystem.hpp>
class Film;
+class Image;
+
+extern boost::filesystem::path private_data;
extern void wait_for_jobs ();
extern boost::shared_ptr<Film> new_test_film (std::string);
-extern void check_dcp (std::string, std::string);
+extern void check_dcp (boost::filesystem::path, boost::filesystem::path);
+extern void check_file (boost::filesystem::path ref, boost::filesystem::path check);
+extern void check_audio_file (boost::filesystem::path ref, boost::filesystem::path check);
extern void check_xml (boost::filesystem::path, boost::filesystem::path, std::list<std::string>);
extern void check_file (boost::filesystem::path, boost::filesystem::path);
extern boost::filesystem::path test_film_dir (std::string);
+extern void write_image (boost::shared_ptr<const Image> image, boost::filesystem::path file);
/*
- 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 test/threed_test.cc
+ * @brief Create a 3D DCP (without comparing the result to anything).
+ */
+
#include <boost/test/unit_test.hpp>
#include "test.h"
#include "lib/film.h"
--- /dev/null
+/*
+ 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 <sndfile.h>
+#include "lib/film.h"
+#include "lib/ratio.h"
+#include "lib/dcp_content_type.h"
+#include "lib/sndfile_content.h"
+#include "lib/player.h"
+#include "lib/audio_buffers.h"
+#include "lib/upmixer_a.h"
+#include "test.h"
+
+using boost::shared_ptr;
+
+BOOST_AUTO_TEST_CASE (upmixer_a_test)
+{
+ shared_ptr<Film> film = new_test_film ("upmixer_a_test");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ film->set_name ("frobozz");
+ shared_ptr<SndfileContent> content (new SndfileContent (film, "test/data/white.wav"));
+ content->set_audio_processor (AudioProcessor::from_id ("stereo-5.1-upmix-a"));
+ film->examine_and_add_content (content);
+
+ wait_for_jobs ();
+
+ SF_INFO info;
+ info.samplerate = 48000;
+ info.channels = 1;
+ info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
+ SNDFILE* L = sf_open ("build/test/upmixer_a_test/L.wav", SFM_WRITE, &info);
+ SNDFILE* R = sf_open ("build/test/upmixer_a_test/R.wav", SFM_WRITE, &info);
+ SNDFILE* C = sf_open ("build/test/upmixer_a_test/C.wav", SFM_WRITE, &info);
+ SNDFILE* Lfe = sf_open ("build/test/upmixer_a_test/Lfe.wav", SFM_WRITE, &info);
+ SNDFILE* Ls = sf_open ("build/test/upmixer_a_test/Ls.wav", SFM_WRITE, &info);
+ SNDFILE* Rs = sf_open ("build/test/upmixer_a_test/Rs.wav", SFM_WRITE, &info);
+
+ shared_ptr<Player> player = film->make_player ();
+ for (DCPTime t; t < film->length(); t += DCPTime::from_seconds (1)) {
+ shared_ptr<AudioBuffers> b = player->get_audio (t, DCPTime::from_seconds (1), true);
+ sf_write_float (L, b->data(0), b->frames());
+ sf_write_float (R, b->data(1), b->frames());
+ sf_write_float (C, b->data(2), b->frames());
+ sf_write_float (Lfe, b->data(3), b->frames());
+ sf_write_float (Ls, b->data(4), b->frames());
+ sf_write_float (Rs, b->data(5), b->frames());
+ }
+
+ sf_close (L);
+ sf_close (R);
+ sf_close (C);
+ sf_close (Lfe);
+ sf_close (Ls);
+ sf_close (Rs);
+
+ check_audio_file ("test/data/upmixer_a_test/L.wav", "build/test/upmixer_a_test/L.wav");
+ check_audio_file ("test/data/upmixer_a_test/R.wav", "build/test/upmixer_a_test/R.wav");
+ check_audio_file ("test/data/upmixer_a_test/C.wav", "build/test/upmixer_a_test/C.wav");
+ check_audio_file ("test/data/upmixer_a_test/Lfe.wav", "build/test/upmixer_a_test/Lfe.wav");
+ check_audio_file ("test/data/upmixer_a_test/Ls.wav", "build/test/upmixer_a_test/Ls.wav");
+ check_audio_file ("test/data/upmixer_a_test/Rs.wav", "build/test/upmixer_a_test/Rs.wav");
+}
/*
- 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 test/util_test.cc
+ * @brief Test various utility methods.
+ */
+
#include <boost/test/unit_test.hpp>
#include "lib/util.h"
#include "lib/exceptions.h"
BOOST_CHECK_THROW (md5_digest (p, shared_ptr<Job> ()), std::runtime_error);
}
+/* Straightforward test of DCPTime::round_up */
+BOOST_AUTO_TEST_CASE (dcptime_round_up_test)
+{
+ BOOST_CHECK_EQUAL (DCPTime (0).round_up (DCPTime::HZ / 2), DCPTime (0));
+ BOOST_CHECK_EQUAL (DCPTime (1).round_up (DCPTime::HZ / 2), DCPTime (2));
+ BOOST_CHECK_EQUAL (DCPTime (2).round_up (DCPTime::HZ / 2), DCPTime (2));
+ BOOST_CHECK_EQUAL (DCPTime (3).round_up (DCPTime::HZ / 2), DCPTime (4));
+
+ BOOST_CHECK_EQUAL (DCPTime (0).round_up (DCPTime::HZ / 42), DCPTime (0));
+ BOOST_CHECK_EQUAL (DCPTime (1).round_up (DCPTime::HZ / 42), DCPTime (42));
+ BOOST_CHECK_EQUAL (DCPTime (42).round_up (DCPTime::HZ / 42), DCPTime (42));
+ BOOST_CHECK_EQUAL (DCPTime (43).round_up (DCPTime::HZ / 42), DCPTime (84));
+
+ /* Check that rounding up to non-integer frame rates works */
+ BOOST_CHECK_EQUAL (DCPTime (45312).round_up (29.976), DCPTime (48045));
+}
+
+
BOOST_AUTO_TEST_CASE (divide_with_round_test)
{
BOOST_CHECK_EQUAL (divide_with_round (0, 4), 0);
BOOST_CHECK_EQUAL (divide_with_round (1000, 500), 2);
}
+BOOST_AUTO_TEST_CASE (timecode_test)
+{
+ DCPTime t = DCPTime::from_seconds (2 * 60 * 60 + 4 * 60 + 31) + DCPTime::from_frames (19, 24);
+ BOOST_CHECK_EQUAL (t.timecode (24), "02:04:31:19");
+}
+
BOOST_AUTO_TEST_CASE (seconds_to_approximate_hms_test)
{
BOOST_CHECK_EQUAL (seconds_to_approximate_hms (1), "1 second");
""", msg = 'Checking for boost unit testing library', lib = 'boost_unit_test_framework%s' % boost_test_suffix, uselib_store = 'BOOST_TEST')
def build(bld):
- obj = bld(features = 'cxx cxxprogram')
+ obj = bld(features='cxx cxxprogram')
obj.name = 'unit-tests'
- obj.uselib = 'BOOST_TEST BOOST_THREAD DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML'
- obj.use = 'libdcpomatic'
+ obj.uselib = 'BOOST_TEST BOOST_THREAD DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML MAGICK SUB'
+ obj.use = 'libdcpomatic2'
obj.source = """
4k_test.cc
audio_analysis_test.cc
+ audio_buffers_test.cc
audio_delay_test.cc
+ audio_decoder_test.cc
+ audio_filter_test.cc
audio_mapping_test.cc
- audio_merger_test.cc
black_fill_test.cc
+ burnt_subtitle_test.cc
client_server_test.cc
colour_conversion_test.cc
+ dcp_subtitle_test.cc
ffmpeg_audio_test.cc
ffmpeg_dcp_test.cc
+ ffmpeg_decoder_seek_test.cc
+ ffmpeg_decoder_sequential_test.cc
ffmpeg_examiner_test.cc
- ffmpeg_pts_offset.cc
+ ffmpeg_pts_offset_test.cc
file_group_test.cc
film_metadata_test.cc
frame_rate_test.cc
image_test.cc
+ import_dcp_test.cc
isdcf_name_test.cc
job_test.cc
make_black_test.cc
+ player_test.cc
pixel_formats_test.cc
- play_test.cc
ratio_test.cc
+ repeat_frame_test.cc
recover_test.cc
resampler_test.cc
scaling_test.cc
+ seek_zero_test.cc
silence_padding_test.cc
+ skip_frame_test.cc
stream_test.cc
+ subrip_test.cc
test.cc
threed_test.cc
+ upmixer_a_test.cc
util_test.cc
+ xml_subtitle_test.cc
"""
obj.target = 'unit-tests'
obj.install_path = ''
+
+ obj = bld(features='cxx cxxprogram')
+ obj.name = 'long-unit-tests'
+ obj.uselib = 'BOOST_TEST DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML SUB'
+ obj.use = 'libdcpomatic2'
+ obj.source = """
+ test.cc
+ """
+
+ obj.target = 'long-unit-tests'
+ obj.install_path = ''
--- /dev/null
+/*
+ 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 test/burnt_subtitle_test.cc
+ * @brief Test creation of XML DCP subtitles.
+ */
+
+#include <boost/test/unit_test.hpp>
+#include "lib/subrip_content.h"
+#include "lib/film.h"
+#include "lib/ratio.h"
+#include "lib/dcp_content_type.h"
+#include "test.h"
+
+using std::cout;
+using boost::shared_ptr;
+
+/** Build a small DCP with no picture and a single subtitle overlaid onto it */
+BOOST_AUTO_TEST_CASE (xml_subtitle_test)
+{
+ shared_ptr<Film> film = new_test_film ("xml_subtitle_test");
+ film->set_container (Ratio::from_id ("185"));
+ film->set_dcp_content_type (DCPContentType::from_isdcf_name ("TLR"));
+ film->set_name ("frobozz");
+ film->set_burn_subtitles (false);
+ shared_ptr<SubRipContent> content (new SubRipContent (film, "test/data/subrip2.srt"));
+ content->set_use_subtitles (true);
+ film->examine_and_add_content (content);
+ wait_for_jobs ();
+ film->make_dcp ();
+ wait_for_jobs ();
+
+ check_dcp ("test/data/xml_subtitle_test", film->dir (film->dcp_name ()));
+}
import distutils.spawn
APPNAME = 'dcpomatic'
-VERSION = '1.76.2devel'
+VERSION = '2.0.14devel'
def options(opt):
opt.load('compiler_cxx')
conf.check_cfg(package='libopenjpeg', args='--cflags --libs', atleast_version='1.5.0', uselib_store='OPENJPEG', mandatory=True)
conf.check_cfg(package='libopenjpeg', args='--cflags --libs', max_version='1.5.2', mandatory=True)
+def static_sub(conf):
+ conf.check_cfg(package='libsub', atleast_version='0.01.0', args='--cflags', uselib_store='SUB', mandatory=True)
+ conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB]
+ conf.env.STLIB_SUB = ['sub']
+
def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh):
- conf.check_cfg(package='libdcp', atleast_version='0.98', args='--cflags', uselib_store='DCP', mandatory=True)
+ conf.check_cfg(package='libdcp-1.0', atleast_version='1.0', args='--cflags', uselib_store='DCP', mandatory=True)
conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
- conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp']
+ conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-libdcp-1.0', 'kumu-libdcp-1.0']
conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt']
if static_boost:
conf.env.LIB_DCP.append('ssh')
def dynamic_dcp(conf):
- conf.check_cfg(package='libdcp', atleast_version='0.97.0', args='--cflags --libs', uselib_store='DCP', mandatory=True)
+ conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True)
conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
+def dynamic_sub(conf):
+ conf.check_cfg(package='libsub', atleast_version='0.01.0', args='--cflags --libs', uselib_store='SUB', mandatory=True)
+ conf.env.DEFINES_SUB = [f.replace('\\', '') for f in conf.env.DEFINES_SUB]
+
def dynamic_ssh(conf):
conf.check_cc(fragment="""
#include <libssh/libssh.h>\n
if conf.env.TARGET_LINUX or conf.env.TARGET_OSX:
conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_POSIX')
conf.env.append_value('CXXFLAGS', '-DPOSIX_LOCALE_PREFIX="%s/share/locale"' % conf.env['INSTALL_PREFIX'])
- conf.env.append_value('CXXFLAGS', '-DPOSIX_ICON_PREFIX="%s/share/dcpomatic"' % conf.env['INSTALL_PREFIX'])
+ conf.env.append_value('CXXFLAGS', '-DPOSIX_ICON_PREFIX="%s/share/dcpomatic2"' % conf.env['INSTALL_PREFIX'])
boost_lib_suffix = ''
boost_thread = 'boost_thread'
conf.env.append_value('LINKFLAGS', '-pthread')
if conf.env.TARGET_DEBIAN:
# libxml2 seems to be linked against this on Ubuntu but it doesn't mention it in its .pc file
conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True)
+
+ if conf.env.TARGET_CENTOS_6 or conf.env.TARGET_CENTOS_7:
+ # libavcodec seems to be linked against this on Centos
+ conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True)
if not conf.env.DISABLE_GUI and conf.env.TARGET_LINUX:
conf.check_cfg(package='gtk+-2.0', args='--cflags --libs', uselib_store='GTK', mandatory=True)
conf.env.STLIB_QUICKMAIL = ['quickmail']
static_ffmpeg(conf)
static_openjpeg(conf)
+ static_sub(conf)
static_dcp(conf, False, False, False, False)
dynamic_boost(conf, boost_lib_suffix, boost_thread)
conf.env.LIB_QUICKMAIL = ['ssh2', 'idn']
static_ffmpeg(conf)
static_openjpeg(conf)
+ static_sub(conf)
static_dcp(conf, True, True, True, True)
static_boost(conf, boost_lib_suffix)
conf.env.LIB_XMLSEC = ['ltdl']
static_ffmpeg(conf)
static_openjpeg(conf)
+ static_sub(conf)
static_dcp(conf, False, True, True, True)
dynamic_boost(conf, boost_lib_suffix, boost_thread)
dynamic_ffmpeg(conf)
dynamic_openjpeg(conf)
dynamic_dcp(conf)
+ dynamic_sub(conf)
dynamic_ssh(conf)
# Not packaging; just a straight build
dynamic_boost(conf, boost_lib_suffix, boost_thread)
dynamic_ffmpeg(conf)
dynamic_dcp(conf)
+ dynamic_sub(conf)
dynamic_openjpeg(conf)
dynamic_ssh(conf)
conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_GRAPHICS_MAGICK')
conf.check_cfg(package='libzip', args='--cflags --libs', uselib_store='ZIP', mandatory=True)
+ conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
+ conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
conf.check_cc(fragment="""
#include <glib.h>
bld.recurse('platform/osx')
for r in ['22x22', '32x32', '48x48', '64x64', '128x128']:
- bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic.png' % r)
+ bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic2.png' % r)
if not bld.env.TARGET_WINDOWS:
- bld.install_files('${PREFIX}/share/dcpomatic', 'icons/taskbar_icon.png')
+ bld.install_files('${PREFIX}/share/dcpomatic2', 'icons/taskbar_icon.png')
bld.add_post_fun(post)
bld.recurse('src')
def tags(bld):
- os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h')
+ os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h test/*.cc')