Merge master.
authorCarl Hetherington <cth@carlh.net>
Tue, 21 Oct 2014 18:14:58 +0000 (19:14 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 21 Oct 2014 18:14:58 +0000 (19:14 +0100)
334 files changed:
ChangeLog
Doxyfile
cscript
debian/changelog
doc/design/Attic/content.tex [new file with mode: 0644]
doc/design/audio_path.svg [new file with mode: 0644]
doc/design/content.tex [deleted file]
doc/design/player_get_audio.svg [new file with mode: 0644]
doc/design/resampling.tex
doc/design/timing.svg [new file with mode: 0644]
doc/design/who_fills_the_gaps.tex [new file with mode: 0644]
doc/mainpage.txt
doc/manual/Makefile
doc/manual/dcpomatic.xml
doc/manual/screenshots/prefs-keys.png [new file with mode: 0644]
icons/128x128/dcpomatic.png [deleted file]
icons/128x128/dcpomatic2.png [new file with mode: 0644]
icons/16x16/dcpomatic.png [deleted file]
icons/16x16/dcpomatic2.png [new file with mode: 0644]
icons/22x22/dcpomatic.png [deleted file]
icons/22x22/dcpomatic2.png [new file with mode: 0644]
icons/32x32/dcpomatic.png [deleted file]
icons/32x32/dcpomatic2.png [new file with mode: 0644]
icons/48x48/dcpomatic.png [deleted file]
icons/48x48/dcpomatic2.png [new file with mode: 0644]
icons/64x64/dcpomatic.png [deleted file]
icons/64x64/dcpomatic2.png [new file with mode: 0644]
icons/kdm_email.png
icons/kdm_email.svg
icons/keys.png [new file with mode: 0644]
icons/keys.svg [new file with mode: 0644]
platform/linux/dcpomatic.desktop.in
platform/linux/dcpomatic.spec.in
platform/linux/wscript
platform/osx/Info.plist.in
platform/osx/make_dmg.sh
platform/windows/wscript
run/dcpomatic
run/dcpomatic_cli
src/lib/analyse_audio_job.cc
src/lib/analyse_audio_job.h
src/lib/audio_analysis.h
src/lib/audio_buffers.cc
src/lib/audio_buffers.h
src/lib/audio_content.cc
src/lib/audio_content.h
src/lib/audio_decoder.cc
src/lib/audio_decoder.h
src/lib/audio_examiner.h [new file with mode: 0644]
src/lib/audio_filter.cc [new file with mode: 0644]
src/lib/audio_filter.h [new file with mode: 0644]
src/lib/audio_mapping.cc
src/lib/audio_mapping.h
src/lib/audio_merger.h [deleted file]
src/lib/audio_processor.cc [new file with mode: 0644]
src/lib/audio_processor.h [new file with mode: 0644]
src/lib/channel_count.h [new file with mode: 0644]
src/lib/cinema.cc
src/lib/cinema.h
src/lib/cinema_sound_processor.cc [new file with mode: 0644]
src/lib/cinema_sound_processor.h [new file with mode: 0644]
src/lib/colour_conversion.cc
src/lib/config.cc
src/lib/config.h
src/lib/content.cc
src/lib/content.h
src/lib/content_audio.h [new file with mode: 0644]
src/lib/content_factory.cc
src/lib/content_factory.h
src/lib/content_subtitle.cc [new file with mode: 0644]
src/lib/content_subtitle.h [new file with mode: 0644]
src/lib/content_video.h [new file with mode: 0644]
src/lib/cross.cc
src/lib/cross.h
src/lib/dcp_content.cc [new file with mode: 0644]
src/lib/dcp_content.h [new file with mode: 0644]
src/lib/dcp_content_type.cc
src/lib/dcp_content_type.h
src/lib/dcp_decoder.cc [new file with mode: 0644]
src/lib/dcp_decoder.h [new file with mode: 0644]
src/lib/dcp_examiner.cc [new file with mode: 0644]
src/lib/dcp_examiner.h [new file with mode: 0644]
src/lib/dcp_subtitle_content.cc [new file with mode: 0644]
src/lib/dcp_subtitle_content.h [new file with mode: 0644]
src/lib/dcp_subtitle_decoder.cc [new file with mode: 0644]
src/lib/dcp_subtitle_decoder.h [new file with mode: 0644]
src/lib/dcp_video.cc [new file with mode: 0644]
src/lib/dcp_video.h [new file with mode: 0644]
src/lib/dcp_video_frame.cc [deleted file]
src/lib/dcp_video_frame.h [deleted file]
src/lib/dcpomatic_time.cc [new file with mode: 0644]
src/lib/dcpomatic_time.h [new file with mode: 0644]
src/lib/decoder.cc [deleted file]
src/lib/decoder.h
src/lib/dolby_cp750.cc
src/lib/dolby_cp750.h
src/lib/encoded_data.cc [new file with mode: 0644]
src/lib/encoded_data.h [new file with mode: 0644]
src/lib/encoder.cc
src/lib/encoder.h
src/lib/exceptions.cc
src/lib/exceptions.h
src/lib/ffmpeg.cc
src/lib/ffmpeg.h
src/lib/ffmpeg_audio_stream.cc [new file with mode: 0644]
src/lib/ffmpeg_audio_stream.h [new file with mode: 0644]
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/ffmpeg_examiner.cc
src/lib/ffmpeg_examiner.h
src/lib/ffmpeg_stream.cc [new file with mode: 0644]
src/lib/ffmpeg_stream.h [new file with mode: 0644]
src/lib/ffmpeg_subtitle_stream.cc [new file with mode: 0644]
src/lib/ffmpeg_subtitle_stream.h [new file with mode: 0644]
src/lib/file_group.cc
src/lib/file_group.h
src/lib/film.cc
src/lib/film.h
src/lib/filter_graph.cc
src/lib/filter_graph.h
src/lib/image.cc
src/lib/image.h
src/lib/image_content.cc
src/lib/image_content.h
src/lib/image_decoder.cc
src/lib/image_decoder.h
src/lib/image_examiner.cc
src/lib/image_examiner.h
src/lib/image_proxy.cc
src/lib/image_proxy.h
src/lib/image_subtitle.h [new file with mode: 0644]
src/lib/isdcf_metadata.cc
src/lib/isdcf_metadata.h
src/lib/j2k_image_proxy.cc [new file with mode: 0644]
src/lib/j2k_image_proxy.h [new file with mode: 0644]
src/lib/job.cc
src/lib/kdm.cc
src/lib/kdm.h
src/lib/magick_image_proxy.cc [new file with mode: 0644]
src/lib/magick_image_proxy.h [new file with mode: 0644]
src/lib/mid_side_decoder.cc [new file with mode: 0644]
src/lib/mid_side_decoder.h [new file with mode: 0644]
src/lib/piece.cc [deleted file]
src/lib/piece.h
src/lib/player.cc
src/lib/player.h
src/lib/player_subtitles.h [new file with mode: 0644]
src/lib/player_video.cc [new file with mode: 0644]
src/lib/player_video.h [new file with mode: 0644]
src/lib/player_video_frame.h [deleted file]
src/lib/playlist.cc
src/lib/playlist.h
src/lib/po/de_DE.po
src/lib/po/es_ES.po
src/lib/po/fr_FR.po
src/lib/po/it_IT.po
src/lib/po/nl_NL.po
src/lib/po/sv_SE.po
src/lib/position.h
src/lib/position_image.cc [new file with mode: 0644]
src/lib/position_image.h [new file with mode: 0644]
src/lib/ratio.cc
src/lib/ratio.h
src/lib/raw_image_proxy.cc [new file with mode: 0644]
src/lib/raw_image_proxy.h [new file with mode: 0644]
src/lib/rect.h
src/lib/render_subtitles.cc [new file with mode: 0644]
src/lib/render_subtitles.h [new file with mode: 0644]
src/lib/resampler.cc
src/lib/resampler.h
src/lib/safe_stringstream.h
src/lib/send_kdm_email_job.cc
src/lib/send_kdm_email_job.h
src/lib/server.cc
src/lib/server.h
src/lib/server_finder.cc
src/lib/single_stream_audio_content.cc [new file with mode: 0644]
src/lib/single_stream_audio_content.h [new file with mode: 0644]
src/lib/sndfile_content.cc
src/lib/sndfile_content.h
src/lib/sndfile_decoder.cc
src/lib/sndfile_decoder.h
src/lib/sound_processor.cc [deleted file]
src/lib/sound_processor.h [deleted file]
src/lib/subrip.cc [new file with mode: 0644]
src/lib/subrip.h [new file with mode: 0644]
src/lib/subrip_content.cc [new file with mode: 0644]
src/lib/subrip_content.h [new file with mode: 0644]
src/lib/subrip_decoder.cc [new file with mode: 0644]
src/lib/subrip_decoder.h [new file with mode: 0644]
src/lib/subrip_subtitle.h [new file with mode: 0644]
src/lib/subtitle.h [deleted file]
src/lib/subtitle_content.cc
src/lib/subtitle_content.h
src/lib/subtitle_decoder.cc
src/lib/subtitle_decoder.h
src/lib/transcode_job.cc
src/lib/transcoder.cc
src/lib/transcoder.h
src/lib/types.cc
src/lib/types.h
src/lib/update.cc
src/lib/update.h
src/lib/upmixer_a.cc [new file with mode: 0644]
src/lib/upmixer_a.h [new file with mode: 0644]
src/lib/util.cc
src/lib/util.h
src/lib/video_content.cc
src/lib/video_content.h
src/lib/video_content_scale.cc
src/lib/video_content_scale.h
src/lib/video_decoder.cc
src/lib/video_decoder.h
src/lib/video_examiner.h
src/lib/writer.cc
src/lib/writer.h
src/lib/wscript
src/tools/dcpomatic.cc
src/tools/dcpomatic_cli.cc
src/tools/dcpomatic_create.cc
src/tools/dcpomatic_kdm.cc
src/tools/dcpomatic_server_cli.cc
src/tools/po/de_DE.po
src/tools/po/es_ES.po
src/tools/po/fr_FR.po
src/tools/po/it_IT.po
src/tools/po/nl_NL.po
src/tools/po/sv_SE.po
src/tools/server_test.cc
src/tools/wscript
src/wx/about_dialog.cc
src/wx/about_dialog.h
src/wx/audio_mapping_view.cc
src/wx/audio_mapping_view.h
src/wx/audio_panel.cc
src/wx/audio_panel.h
src/wx/config_dialog.cc
src/wx/content_menu.cc
src/wx/content_menu.h
src/wx/content_panel.cc [new file with mode: 0644]
src/wx/content_panel.h [new file with mode: 0644]
src/wx/content_sub_panel.cc [new file with mode: 0644]
src/wx/content_sub_panel.h [new file with mode: 0644]
src/wx/content_widget.h
src/wx/dcp_panel.cc [new file with mode: 0644]
src/wx/dcp_panel.h [new file with mode: 0644]
src/wx/editable_list.h
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/film_editor_panel.cc [deleted file]
src/wx/film_editor_panel.h [deleted file]
src/wx/film_viewer.cc
src/wx/film_viewer.h
src/wx/kdm_dialog.cc
src/wx/kdm_dialog.h
src/wx/make_signer_chain_dialog.cc [new file with mode: 0644]
src/wx/make_signer_chain_dialog.h [new file with mode: 0644]
src/wx/po/de_DE.po
src/wx/po/es_ES.po
src/wx/po/fr_FR.po
src/wx/po/it_IT.po
src/wx/po/nl_NL.po
src/wx/po/sv_SE.po
src/wx/properties_dialog.cc
src/wx/screen_dialog.cc
src/wx/screen_dialog.h
src/wx/subtitle_panel.cc
src/wx/subtitle_panel.h
src/wx/subtitle_view.cc [new file with mode: 0644]
src/wx/subtitle_view.h [new file with mode: 0644]
src/wx/timecode.cc
src/wx/timecode.h
src/wx/timeline.cc
src/wx/timeline.h
src/wx/timeline_dialog.cc
src/wx/timeline_dialog.h
src/wx/timing_panel.cc
src/wx/timing_panel.h
src/wx/video_panel.cc
src/wx/video_panel.h
src/wx/wscript
src/wx/wx_ui_signaller.cc
src/wx/wx_ui_signaller.h
src/wx/wx_util.cc
test/4k_test.cc
test/README [new file with mode: 0644]
test/audio_analysis_test.cc
test/audio_buffers_test.cc [new file with mode: 0644]
test/audio_decoder_test.cc [new file with mode: 0644]
test/audio_delay_test.cc
test/audio_filter_test.cc [new file with mode: 0644]
test/audio_mapping_test.cc
test/audio_merger_test.cc [deleted file]
test/black_fill_test.cc
test/burnt_subtitle_test.cc [new file with mode: 0644]
test/client_server_test.cc
test/colour_conversion_test.cc
test/dcp_subtitle_test.cc [new file with mode: 0644]
test/ffmpeg_audio_test.cc
test/ffmpeg_dcp_test.cc
test/ffmpeg_decoder_seek_test.cc [new file with mode: 0644]
test/ffmpeg_decoder_sequential_test.cc [new file with mode: 0644]
test/ffmpeg_examiner_test.cc
test/ffmpeg_pts_offset.cc [deleted file]
test/ffmpeg_pts_offset_test.cc [new file with mode: 0644]
test/file_group_test.cc
test/film_metadata_test.cc
test/frame_rate_test.cc
test/image_test.cc
test/import_dcp_test.cc [new file with mode: 0644]
test/job_test.cc
test/make_black_test.cc
test/pixel_formats_test.cc
test/player_test.cc [new file with mode: 0644]
test/ratio_test.cc
test/recover_test.cc
test/repeat_frame_test.cc [new file with mode: 0644]
test/resampler_test.cc
test/scaling_test.cc
test/seek_zero_test.cc [new file with mode: 0644]
test/silence_padding_test.cc
test/skip_frame_test.cc [new file with mode: 0644]
test/stream_test.cc
test/subrip_test.cc [new file with mode: 0644]
test/test.cc
test/test.h
test/threed_test.cc
test/upmixer_a_test.cc [new file with mode: 0644]
test/util_test.cc
test/wscript
test/xml_subtitle_test.cc [new file with mode: 0644]
wscript

index 5bb0711b48dda50626403c9f940268b5422490ea..873ad89dffe7361b40e682306fd9abd0c55ebb09 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
        * 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>
 
index 4ed65e4f14c5bb940b5eb4bfa894339dcbab1908..a49c8451fc0875cc7e88b48cc5e46e90be0a5092 100644 (file)
--- a/Doxyfile
+++ b/Doxyfile
-# 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
-# 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
 
@@ -112,174 +123,204 @@ 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
 
@@ -287,67 +328,61 @@ 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
 
@@ -356,341 +391,394 @@ 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.
 
@@ -699,14 +787,16 @@ EXCLUDE                =
 # 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       =
 
@@ -715,765 +805,1080 @@ 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 =
 
@@ -1481,106 +1886,128 @@ 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
 
@@ -1588,222 +2015,293 @@ 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
diff --git a/cscript b/cscript
index 495d7723ac090efbf05efad1e956cb87c4410e3b..a5c0900c121fd73a830445c3207206e3682032a9 100644 (file)
--- a/cscript
+++ b/cscript
@@ -156,7 +156,8 @@ def make_control(debian_version, bits, filename, debug):
 
 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
@@ -229,7 +230,7 @@ def package_centos(target, cpu, version):
         "%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":
index 05d8e42bf2c7a47c5e15a78d7f24f1ae7386e339..9842aacb1bde7a3ea88e935d98f511d924fcf1ec 100644 (file)
@@ -1,4 +1,4 @@
-dcpomatic (1.76.2-1) UNRELEASED; urgency=low
+dcpomatic (2.0.14-1) UNRELEASED; urgency=low
 
   * New upstream release.
   * New upstream release.
diff --git a/doc/design/Attic/content.tex b/doc/design/Attic/content.tex
new file mode 100644 (file)
index 0000000..0f5f170
--- /dev/null
@@ -0,0 +1,195 @@
+\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}
diff --git a/doc/design/audio_path.svg b/doc/design/audio_path.svg
new file mode 100644 (file)
index 0000000..c75d505
--- /dev/null
@@ -0,0 +1,408 @@
+<?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>
diff --git a/doc/design/content.tex b/doc/design/content.tex
deleted file mode 100644 (file)
index 0f5f170..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-\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}
diff --git a/doc/design/player_get_audio.svg b/doc/design/player_get_audio.svg
new file mode 100644 (file)
index 0000000..fe7bdd5
--- /dev/null
@@ -0,0 +1,399 @@
+<?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-&gt;frame</tspan></text>
+  </g>
+</svg>
index 44aeee9b1b4c8abcdea917a7aeeebe66620c7815..cf9cfb1edc220bcdfa6f242b4aeee94427c2a0d5 100644 (file)
@@ -1,4 +1,5 @@
 \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$.  
@@ -18,6 +19,7 @@ $C_V$ is a DCI rate, $C_A$ is not.  e.g.\ if $C_V = 24$, $C_A = 44.1\times{}10^3
 \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$,
@@ -31,5 +33,24 @@ resample audio to $25 * 48\times{}10^3 / 24 = 50\times{}10^3$.
 \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}
diff --git a/doc/design/timing.svg b/doc/design/timing.svg
new file mode 100644 (file)
index 0000000..30325e7
--- /dev/null
@@ -0,0 +1,645 @@
+<?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>
diff --git a/doc/design/who_fills_the_gaps.tex b/doc/design/who_fills_the_gaps.tex
new file mode 100644 (file)
index 0000000..00e8ac3
--- /dev/null
@@ -0,0 +1,30 @@
+\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}
index 649c9c60913506e43cb4c4084ce9dc4209edf453..1e371852b21ba69ee34f0c36e417792fc6a8b9d9 100644 (file)
@@ -1,7 +1,7 @@
 /** @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/
  */
index 55d888b2e3ba2d66f06e21cec0572cd359794aad..5ca5700f9dca9f6544070f1753d121ece8633661 100644 (file)
@@ -9,7 +9,7 @@ SCREENSHOTS := file-new.png video-new-film.png still-new-film.png video-select-c
                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
 
index cd27bef726f816e4e104ce2d7dfca74308ea9527..489f0ba041dd55c069578069bb20daff207d70af 100644 (file)
@@ -1376,7 +1376,7 @@ methods to understand it.
 
 <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>
 
@@ -1533,6 +1533,9 @@ generate the KDMs.
 
 
 <!-- ============================================================== -->
+<!-- PREFERENCES                                                    -->
+<!-- ============================================================== -->
+
 <chapter xml:id="ch-preferences" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
 <title>Preferences</title>
 
@@ -1549,7 +1552,7 @@ behaviour.  This chapter explains those options.
 <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>
 
 <!-- ============================================================== -->
@@ -1619,7 +1622,7 @@ available
 <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 &lsquo;released&rsquo; This is useful if you
+those that are formally &lsquo;released&rsquo;. This is useful if you
 like to live on the bleeding edge!
 </para>
 </section>
@@ -1688,6 +1691,61 @@ converting from common input colour spaces to XYZ.
 </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>
@@ -1889,12 +1947,12 @@ with minimal loss in quality.
 </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
@@ -1926,7 +1984,7 @@ For very low or high frame rates, DCP-o-matic can also skip or duplicate frames.
 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>
diff --git a/doc/manual/screenshots/prefs-keys.png b/doc/manual/screenshots/prefs-keys.png
new file mode 100644 (file)
index 0000000..1309e49
Binary files /dev/null and b/doc/manual/screenshots/prefs-keys.png differ
diff --git a/icons/128x128/dcpomatic.png b/icons/128x128/dcpomatic.png
deleted file mode 100644 (file)
index 9936b39..0000000
Binary files a/icons/128x128/dcpomatic.png and /dev/null differ
diff --git a/icons/128x128/dcpomatic2.png b/icons/128x128/dcpomatic2.png
new file mode 100644 (file)
index 0000000..9936b39
Binary files /dev/null and b/icons/128x128/dcpomatic2.png differ
diff --git a/icons/16x16/dcpomatic.png b/icons/16x16/dcpomatic.png
deleted file mode 100644 (file)
index 3c5a10f..0000000
Binary files a/icons/16x16/dcpomatic.png and /dev/null differ
diff --git a/icons/16x16/dcpomatic2.png b/icons/16x16/dcpomatic2.png
new file mode 100644 (file)
index 0000000..3c5a10f
Binary files /dev/null and b/icons/16x16/dcpomatic2.png differ
diff --git a/icons/22x22/dcpomatic.png b/icons/22x22/dcpomatic.png
deleted file mode 100644 (file)
index dddb862..0000000
Binary files a/icons/22x22/dcpomatic.png and /dev/null differ
diff --git a/icons/22x22/dcpomatic2.png b/icons/22x22/dcpomatic2.png
new file mode 100644 (file)
index 0000000..dddb862
Binary files /dev/null and b/icons/22x22/dcpomatic2.png differ
diff --git a/icons/32x32/dcpomatic.png b/icons/32x32/dcpomatic.png
deleted file mode 100644 (file)
index 8cecf08..0000000
Binary files a/icons/32x32/dcpomatic.png and /dev/null differ
diff --git a/icons/32x32/dcpomatic2.png b/icons/32x32/dcpomatic2.png
new file mode 100644 (file)
index 0000000..8cecf08
Binary files /dev/null and b/icons/32x32/dcpomatic2.png differ
diff --git a/icons/48x48/dcpomatic.png b/icons/48x48/dcpomatic.png
deleted file mode 100644 (file)
index 07bf2d1..0000000
Binary files a/icons/48x48/dcpomatic.png and /dev/null differ
diff --git a/icons/48x48/dcpomatic2.png b/icons/48x48/dcpomatic2.png
new file mode 100644 (file)
index 0000000..07bf2d1
Binary files /dev/null and b/icons/48x48/dcpomatic2.png differ
diff --git a/icons/64x64/dcpomatic.png b/icons/64x64/dcpomatic.png
deleted file mode 100644 (file)
index 35564a8..0000000
Binary files a/icons/64x64/dcpomatic.png and /dev/null differ
diff --git a/icons/64x64/dcpomatic2.png b/icons/64x64/dcpomatic2.png
new file mode 100644 (file)
index 0000000..35564a8
Binary files /dev/null and b/icons/64x64/dcpomatic2.png differ
index 28701ee494d2aaf8815ba4d214eafe88ab3933e7..a6c2157e8071eed98d11b39b5b79d2425e54d8d0 100644 (file)
Binary files a/icons/kdm_email.png and b/icons/kdm_email.png differ
index ace413dae555e8686c35bf4407f0041de243ebf8..c09d94e81622d24f350225d7393baa66e3a5717e 100644 (file)
 <?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 &lt;a href="http://tango.freedesktop.org/Tango_Desktop_Project"&gt; Tango Project &lt;/a&gt; &#13;\n&lt;br&gt;&lt;br&gt;&#13;\nSince version 0.8.90 Tango Project icons are Public Domain: &lt;a href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"&gt; Tango Project FAQ &lt;/a&gt;</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
+>
diff --git a/icons/keys.png b/icons/keys.png
new file mode 100644 (file)
index 0000000..28701ee
Binary files /dev/null and b/icons/keys.png differ
diff --git a/icons/keys.svg b/icons/keys.svg
new file mode 100644 (file)
index 0000000..ace413d
--- /dev/null
@@ -0,0 +1,197 @@
+<?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>
index 76e62940456aba2fb5faf0a47f52f752139e415e..a950f35e9cc65f1da914e0bd567239549886b06f 100644 (file)
@@ -3,8 +3,8 @@ Encoding=UTF-8
 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
index da179628c76d0e12125fdc0d10c93d457369d195..dae0e90b6b6c1481a8e66d03a1348af211cbffb6 100644 (file)
@@ -1,5 +1,5 @@
 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
@@ -13,40 +13,40 @@ files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant
 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@
index 3aab4f7fb349120fb85f96ec2f27f6b35581a941..336c1bcb002aefee8c18fe4a478832e3cd186336 100644 (file)
@@ -1,25 +1,25 @@
 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:
@@ -27,4 +27,4 @@ def build(bld):
     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'])
index f2675e3f630fc4ad44d5d55005935e82cabf8888..e420d3620a47b778dfbb4d42a7580d87b56ff868 100644 (file)
@@ -5,7 +5,7 @@
        <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>
@@ -15,7 +15,7 @@
        <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>
index 52bff13494af84aef430ae3ef40a63d5b952239b..78c3bb819d5539dcfb69af18bb923ba7bcc5d76a 100644 (file)
@@ -15,7 +15,7 @@ WORK=build/platform/osx
 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"
@@ -53,16 +53,16 @@ function universal_copy_lib {
     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"
@@ -105,13 +105,16 @@ universal_copy_lib $ENV libffi "$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"`
@@ -129,6 +132,7 @@ cp icons/defaults.png "$WORK/$resources"
 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
@@ -169,7 +173,7 @@ echo '
            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
index 24b6ed099a057ddcddd04a09d4870cd372b94de2..7e92fb2d55c3442013cbda2cce822100310f8617 100644 (file)
@@ -22,7 +22,7 @@ def write_installer(bits, version):
 !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"
@@ -40,7 +40,7 @@ ${If} ${RunningX64}
    ; 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)
 
@@ -102,25 +102,34 @@ File "%static_deps%/bin/openssl.exe"
 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.
@@ -129,66 +138,71 @@ SetOutPath "$PROFILE\\.magick"
 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"
@@ -211,13 +225,13 @@ LangString DESC_SEC_SERVER ${LANG_ENGLISH} "DCP-o-matic encode server"
 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 batch converter.lnk"
+Delete "$DESKTOP\\DCP-o-matic 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)
     
index 278ee8c5f80e1b5d91afbaaa48727f1a5b1e8ad3..74714865ae0b05224e2562b957f3a912aa44c5c6 100755 (executable)
@@ -90,24 +90,24 @@ else
   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
 
index ea2a987631238e71d949d6430f52070cc5294995..e1c0ebd1485e1bf232326447f306e43c7bfb77f3 100755 (executable)
@@ -3,11 +3,11 @@
 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
index ab985bdf75468ee557a81f0307d09a919df390e0..60b10e7b6e05ba69dc418894fa10b4ca8ef7781e 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include "audio_analysis.h"
+#include "audio_buffers.h"
 #include "analyse_audio_job.h"
 #include "compose.hpp"
 #include "film.h"
@@ -59,19 +60,18 @@ AnalyseAudioJob::run ()
        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 ());
@@ -81,7 +81,7 @@ AnalyseAudioJob::run ()
 }
 
 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) {
index 3d4881983b5234572ce40a47455c5b849a620328..a218cb3400d563354b751101eeb349bf87f81b59 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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:
@@ -33,10 +45,10 @@ 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;
 
index 824472dda08c80daf1037a0f5635b5db7eefa556..b91a1cf5123d3c43c3a1b40418e279ff273b4630 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/lib/audio_analysis.h
+ *  @brief AudioAnalysis and AudioPoint classes.
+ */
+
 #ifndef DCPOMATIC_AUDIO_ANALYSIS_H
 #define DCPOMATIC_AUDIO_ANALYSIS_H
 
@@ -24,6 +28,9 @@
 #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:
@@ -48,6 +55,14 @@ private:
        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:
index a1c9b81ac099259bbe86e2f9f8dcea1340bbb9ae..56ca7a94b18c5860e3800f03740baa2c09480d59 100644 (file)
@@ -73,6 +73,9 @@ AudioBuffers::~AudioBuffers ()
 void
 AudioBuffers::allocate (int channels, int frames)
 {
+       assert (frames >= 0);
+       assert (channels >= 0);
+
        _channels = channels;
        _frames = frames;
        _allocated_frames = frames;
@@ -172,6 +175,11 @@ AudioBuffers::make_silent (int from, int 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);
@@ -255,6 +263,8 @@ void
 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) {
@@ -275,3 +285,29 @@ AudioBuffers::apply_gain (float dB)
                }
        }
 }
+
+/** @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;
+}
index c9030dbfa9e84af247dd760f276c3ee33eaa5969..8cd67aaa729d52afc42a8ff5b11f3d42327de414 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#ifndef 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>
 
@@ -35,6 +39,9 @@ public:
 
        AudioBuffers & operator= (AudioBuffers const &);
 
+       boost::shared_ptr<AudioBuffers> clone () const;
+       boost::shared_ptr<AudioBuffers> channel (int) const;
+
        void ensure_size (int);
 
        float** data () const {
@@ -60,8 +67,9 @@ public:
        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:
index 29d159a29a9603f330eb6100ed6c42743a33c630..d02728b00018f5ed6d8bc023698b6fe4a94c7536 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@
 */
 
 #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"
@@ -26,6 +26,7 @@
 #include "exceptions.h"
 #include "config.h"
 #include "frame_rate_change.h"
+#include "audio_processor.h"
 
 #include "i18n.h"
 
@@ -34,7 +35,7 @@ using std::cout;
 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;
@@ -42,11 +43,22 @@ int const AudioContentProperty::AUDIO_FRAME_RATE = 202;
 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)
 {
 
 }
@@ -55,15 +67,20 @@ AudioContent::AudioContent (shared_ptr<const Film> f, boost::filesystem::path p)
        : 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)
@@ -86,6 +103,7 @@ AudioContent::AudioContent (shared_ptr<const Film> f, vector<shared_ptr<Content>
 
        _audio_gain = ref->audio_gain ();
        _audio_delay = ref->audio_delay ();
+       _audio_processor = ref->audio_processor ();
 }
 
 void
@@ -94,6 +112,9 @@ AudioContent::as_xml (xmlpp::Node* node) const
        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 ());
+       }
 }
 
 
@@ -119,6 +140,22 @@ AudioContent::set_audio_delay (int d)
        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)
 {
@@ -148,24 +185,38 @@ AudioContent::audio_analysis_path () const
 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) {
@@ -175,3 +226,13 @@ AudioContent::output_audio_frame_rate () const
        return rint (t);
 }
 
+int
+AudioContent::processed_audio_channels () const
+{
+       if (!audio_processor ()) {
+               return audio_channels ();
+       }
+
+       return audio_processor()->out_channels (audio_channels ());
+}
+
index 2c324a3a4966e0dad899c10f24f3f99115317420..57085a7651bcc285c9b32618cc768f36fc5cce33 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/lib/audio_content.h
+ *  @brief AudioContent and AudioContentProperty classes.
+ */
+
 #ifndef DCPOMATIC_AUDIO_CONTENT_H
 #define DCPOMATIC_AUDIO_CONTENT_H
 
@@ -27,6 +31,11 @@ namespace cxml {
        class Node;
 }
 
+class AudioProcessor;
+
+/** @class AudioContentProperty
+ *  @brief Names for properties of AudioContent.
+ */
 class AudioContentProperty
 {
 public:
@@ -36,34 +45,44 @@ 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);
@@ -75,11 +94,17 @@ public:
                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
index 30bd2540a01ac361523fabc792dafe9ac1e27caa..f3251f306bf47284a71bc1d53917631e917c827b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <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 ();
+       }
 }
index ab6c4b8a931e12cfe892c8cb74d86ae7162d1a10..f8438df524cdbdfabb4bcfe6ddc91145db5f60aa 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "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.
@@ -36,18 +38,38 @@ class AudioBuffers;
 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
diff --git a/src/lib/audio_examiner.h b/src/lib/audio_examiner.h
new file mode 100644 (file)
index 0000000..d6d4dbe
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+    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;
+};
diff --git a/src/lib/audio_filter.cc b/src/lib/audio_filter.cc
new file mode 100644 (file)
index 0000000..59b5684
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+    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;
+}
diff --git a/src/lib/audio_filter.h b/src/lib/audio_filter.h
new file mode 100644 (file)
index 0000000..9fc69da
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+    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);
+};
index e35c1ae9464cf67061426ddb065cfb8c9d162b7a..e86e2e2ac6820499cd551ddaed17ffe8e7f30019 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -19,7 +19,7 @@
 
 #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"
@@ -32,7 +32,7 @@ using std::string;
 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)
@@ -40,12 +40,12 @@ AudioMapping::AudioMapping ()
 
 }
 
-/** 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
@@ -70,16 +70,16 @@ AudioMapping::make_default ()
 
        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"));
 
@@ -87,14 +87,14 @@ AudioMapping::AudioMapping (shared_ptr<const cxml::Node> node, int state_version
                /* 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 ())
                                );
                }
@@ -102,13 +102,13 @@ AudioMapping::AudioMapping (shared_ptr<const cxml::Node> node, int state_version
 }
 
 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];
 }
@@ -123,7 +123,7 @@ AudioMapping::as_xml (xmlpp::Node* node) const
                        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))));
                }
        }
 }
index b0b75ac063372c8f258dc235a6675f44ad2b8e81..a76d83a370f703dceded47e1d5aff6e90cd472e3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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;
@@ -32,7 +37,9 @@ namespace cxml {
        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.
@@ -41,8 +48,8 @@ class AudioMapping
 {
 public:
        AudioMapping ();
-       AudioMapping (int);
-       AudioMapping (boost::shared_ptr<const cxml::Node>, int);
+       AudioMapping (int channels);
+       AudioMapping (cxml::ConstNodePtr, int);
 
        /* Default copy constructor is fine */
        
@@ -50,8 +57,8 @@ public:
 
        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;
diff --git a/src/lib/audio_merger.h b/src/lib/audio_merger.h
deleted file mode 100644 (file)
index 226601e..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "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;
-};
diff --git a/src/lib/audio_processor.cc b/src/lib/audio_processor.cc
new file mode 100644 (file)
index 0000000..f350cc2
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+    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;
+}
diff --git a/src/lib/audio_processor.h b/src/lib/audio_processor.h
new file mode 100644 (file)
index 0000000..9b332e7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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
diff --git a/src/lib/channel_count.h b/src/lib/channel_count.h
new file mode 100644 (file)
index 0000000..4247fc0
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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
index fca6b6afda36c80c653940eaaaa8819818327b4b..62023618676a6f83de20bfde5f6be6acd651e1b7 100644 (file)
@@ -24,7 +24,7 @@
 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"))
 {
@@ -35,7 +35,7 @@ Cinema::Cinema (shared_ptr<const cxml::Node> node)
    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) {
@@ -67,17 +67,21 @@ Cinema::remove_screen (shared_ptr<Screen> s)
        _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));
+       }
 }
 
                
index 40dc15ae02ebaf66decb9f837ce845b5d40dcc99..8421f468751f72548560c9705f27cefa9bb8fcaa 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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:
@@ -51,9 +64,9 @@ 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;
 
diff --git a/src/lib/cinema_sound_processor.cc b/src/lib/cinema_sound_processor.cc
new file mode 100644 (file)
index 0000000..6a79051
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+    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];
+}
diff --git a/src/lib/cinema_sound_processor.h b/src/lib/cinema_sound_processor.h
new file mode 100644 (file)
index 0000000..f735b12
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @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
index e5b1104ff9427525dcd374c76d146eedccfb1775..c836cc2715728e8c75f09eb5271d4b003748dbc3 100644 (file)
@@ -18,8 +18,8 @@
 */
 
 #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"
@@ -34,7 +34,7 @@ using std::cout;
 using std::vector;
 using boost::shared_ptr;
 using boost::optional;
-using libdcp::raw_convert;
+using dcp::raw_convert;
 
 ColourConversion::ColourConversion ()
        : input_gamma (2.4)
@@ -44,7 +44,7 @@ ColourConversion::ColourConversion ()
 {
        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];
                }
        }
 }
index 878fedaa4c31ed394bad0c43819ea7f961c1fdcb..2b7b81cfeec2a3b067ab482c264eeea49a30ec6b 100644 (file)
 #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"
 
@@ -51,7 +54,7 @@ using boost::shared_ptr;
 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;
 
@@ -61,7 +64,7 @@ Config::Config ()
        , _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")))
@@ -73,6 +76,9 @@ Config::Config ()
        , _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);
@@ -81,9 +87,9 @@ Config::Config ()
        _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 ();
 }
@@ -91,13 +97,16 @@ Config::Config ()
 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");
@@ -130,7 +139,11 @@ Config::read ()
 
        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");
@@ -180,7 +193,7 @@ Config::read ()
                /* 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");
@@ -209,99 +222,64 @@ Config::read ()
        _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;
 }
 
@@ -347,8 +325,8 @@ Config::write () const
        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());
@@ -391,12 +369,25 @@ Config::write () const
        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
index 0639382a05040abc85e7e4fc1a9075a591dd974c..55a172d78df40ed7c62025ff60c5422c7e9406fc 100644 (file)
 #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;
@@ -104,9 +106,9 @@ public:
                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 {
@@ -193,6 +195,18 @@ public:
                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;
        }
@@ -209,6 +223,12 @@ public:
                return _log_types;
        }
 
+#ifdef DCPOMATIC_WINDOWS       
+       bool win32_console () const {
+               return _win32_console;
+       }
+#endif 
+
        std::vector<boost::filesystem::path> history () const {
                return _history;
        }
@@ -371,6 +391,21 @@ public:
 
        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 ();
@@ -391,6 +426,13 @@ public:
                changed ();
        }
 
+#ifdef DCPOMATIC_WINDOWS       
+       void set_win32_console (bool c) {
+               _win32_console = c;
+               changed ();
+       }
+#endif 
+
        void clear_history () {
                _history.clear ();
                changed ();
@@ -398,8 +440,6 @@ public:
 
        void add_to_history (boost::filesystem::path p);
        
-       boost::filesystem::path signer_chain_directory () const;
-
        void changed ();
        boost::signals2::signal<void ()> Changed;
 
@@ -408,10 +448,10 @@ public:
 
 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;
@@ -433,8 +473,8 @@ private:
        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;
@@ -458,12 +498,18 @@ private:
        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 */
index 11a4b21cca3026706911cb0c51ea9230eabc8797..21e49a2c955ae7e74091ec5c1a43f4e9e238b857 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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"
@@ -38,7 +42,7 @@ using std::cout;
 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;
@@ -56,7 +60,7 @@ Content::Content (shared_ptr<const Film> f)
 
 }
 
-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)
@@ -76,7 +80,7 @@ Content::Content (shared_ptr<const Film> f, boost::filesystem::path p)
        _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)
 {
@@ -85,9 +89,9 @@ Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
                _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)
@@ -98,11 +102,11 @@ 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."));
                }
 
@@ -121,9 +125,9 @@ Content::as_xml (xmlpp::Node* node) const
                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
@@ -148,7 +152,7 @@ Content::signal_changed (int p)
 }
 
 void
-Content::set_position (Time p)
+Content::set_position (DCPTime p)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
@@ -163,7 +167,7 @@ Content::set_position (Time p)
 }
 
 void
-Content::set_trim_start (Time t)
+Content::set_trim_start (DCPTime t)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
@@ -174,7 +178,7 @@ Content::set_trim_start (Time t)
 }
 
 void
-Content::set_trim_end (Time t)
+Content::set_trim_end (DCPTime t)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
@@ -206,22 +210,13 @@ Content::clone () const
 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
@@ -233,9 +228,9 @@ Content::identifier () const
        SafeStringStream s;
        
        s << Content::digest()
-         << "_" << position()
-         << "_" << trim_start()
-         << "_" << trim_end();
+         << "_" << position().get()
+         << "_" << trim_start().get()
+         << "_" << trim_end().get();
 
        return s.str ();
 }
index 596a0a905c95217d4daaf8ba116b8a6f518c6555..f7e97feac9a3489d1083f96278a4eea3e491d367 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/lib/content.h
+ *  @brief Content class.
+ */
+
 #ifndef DCPOMATIC_CONTENT_H
 #define DCPOMATIC_CONTENT_H
 
@@ -26,7 +30,9 @@
 #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;
@@ -45,25 +51,38 @@ public:
        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;
@@ -95,41 +114,43 @@ public:
                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;
 
@@ -139,8 +160,8 @@ protected:
        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 */
@@ -148,9 +169,9 @@ protected:
        
 private:
        std::string _digest;
-       Time _position;
-       Time _trim_start;
-       Time _trim_end;
+       DCPTime _position;
+       DCPTime _trim_start;
+       DCPTime _trim_end;
        bool _change_signals_frequent;
 };
 
diff --git a/src/lib/content_audio.h b/src/lib/content_audio.h
new file mode 100644 (file)
index 0000000..535c0b4
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file  src/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;
+};
index 98b1dd859536643c83c524e3a22a4df45b039c8a..16340adb43b0e74f8f14d4b2bc039e258d8e58a9 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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)
 {
@@ -40,20 +54,38 @@ content_factory (shared_ptr<const Film> film, cxml::NodePtr node, int version, l
                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));
        }
index 2eeebbc9f8924c9ebb45c04df47bfdbefa9de22c..fae7648eabfa8910baa7157a695cca1035dba2cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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> &);
diff --git a/src/lib/content_subtitle.cc b/src/lib/content_subtitle.cc
new file mode 100644 (file)
index 0000000..93e0677
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+    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)
+               );
+}
diff --git a/src/lib/content_subtitle.h b/src/lib/content_subtitle.h
new file mode 100644 (file)
index 0000000..8868618
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+    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
diff --git a/src/lib/content_video.h b/src/lib/content_video.h
new file mode 100644 (file)
index 0000000..a7f7359
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+    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
index 9b7d5594f392ecde245ff6646ccce4766133a1f2..d84c17c55b25318ef5b32a0a06eda8ee23ac1d16 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
index 1c77545033fdb0c7e2ee17db2a297b9e8100659f..c206fa55dbf3064cf17bcf68e719a45bb1765e2d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/lib/cross.h
+ *  @brief Cross-platform compatibility code.
+ */
+
 #ifndef DCPOMATIC_CROSS_H
 #define DCPOMATIC_CROSS_H
 
@@ -42,7 +46,9 @@ extern boost::filesystem::path app_contents ();
 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.
  */
diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc
new file mode 100644 (file)
index 0000000..a5b5f37
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+    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;
+}
diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h
new file mode 100644 (file)
index 0000000..da78e6d
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+    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
index f24ed95ea24de85c74dd4d6b646fe744cc526f36..e5466e1398936c1e4917efe11e95fa1cf5b12472 100644 (file)
@@ -30,7 +30,7 @@ using namespace std;
 
 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)
@@ -41,16 +41,16 @@ DCPContentType::DCPContentType (string p, libdcp::ContentKind k, string 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 *
index 88f3c4a857e18d8e1574846906118ec1921c344f..ebfe09518a5577189e745d05d0a37bfd6d1ce04c 100644 (file)
@@ -26,7 +26,7 @@
 
 #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;
        }
 
@@ -58,7 +58,7 @@ public:
 
 private:
        std::string _pretty_name;
-       libdcp::ContentKind _libdcp_kind;
+       dcp::ContentKind _libdcp_kind;
        std::string _isdcf_name;
 
        /** All available DCP content types */
diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc
new file mode 100644 (file)
index 0000000..bf016ef
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <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> ();
+}
diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h
new file mode 100644 (file)
index 0000000..d81b20b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    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;
+};
diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc
new file mode 100644 (file)
index 0000000..1e4cc89
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+    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"));
+               }
+       }
+}
diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h
new file mode 100644 (file)
index 0000000..03d43d0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+    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;
+};
diff --git a/src/lib/dcp_subtitle_content.cc b/src/lib/dcp_subtitle_content.cc
new file mode 100644 (file)
index 0000000..83b0d20
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+    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 ()));
+}
diff --git a/src/lib/dcp_subtitle_content.h b/src/lib/dcp_subtitle_content.h
new file mode 100644 (file)
index 0000000..5794b59
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    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;
+};
diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc
new file mode 100644 (file)
index 0000000..20a9f32
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    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;
+}
+
diff --git a/src/lib/dcp_subtitle_decoder.h b/src/lib/dcp_subtitle_decoder.h
new file mode 100644 (file)
index 0000000..0705624
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    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;
+};
diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc
new file mode 100644 (file)
index 0000000..f6c671f
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+    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 (&parameters);
+
+       /* 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, &parameters, 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);
+}
diff --git a/src/lib/dcp_video.h b/src/lib/dcp_video.h
new file mode 100644 (file)
index 0000000..d517a8f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+    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
+};
diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc
deleted file mode 100644 (file)
index fac247a..0000000
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
-    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 (&parameters);
-
-       /* 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, &parameters, 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)
-{
-
-}
diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h
deleted file mode 100644 (file)
index e4006d9..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
-    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
-};
diff --git a/src/lib/dcpomatic_time.cc b/src/lib/dcpomatic_time.cc
new file mode 100644 (file)
index 0000000..812c756
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+    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);
+}
diff --git a/src/lib/dcpomatic_time.h b/src/lib/dcpomatic_time.h
new file mode 100644 (file)
index 0000000..55476d5
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+    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
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc
deleted file mode 100644 (file)
index 3f4cda6..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file  src/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)
-{
-
-}
index d67592ed812544c644b8766bcb1b1be1c03e84de..583a92636443ba7cd1bd650eb3cf61144017394c 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 /** @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.
@@ -36,21 +38,19 @@ class Film;
 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
index aeb469d293a6e6385b82160e61596b35198b50a1..317d129d99f0abcba3af6c78d4bf904baf05010c 100644 (file)
@@ -24,7 +24,7 @@
 using namespace std;
 
 DolbyCP750::DolbyCP750 ()
-       : SoundProcessor ("dolby_cp750", _("Dolby CP650 and CP750"))
+       : CinemaSoundProcessor ("dolby_cp750", _("Dolby CP650 and CP750"))
 {
 
 }
index b6c0e7df29915bec52085167bda2aa5cbab3cc4c..c545844fe584bca13cc425bc26a48452c4438387 100644 (file)
@@ -17,9 +17,9 @@
 
 */
 
-#include "sound_processor.h"
+#include "cinema_sound_processor.h"
 
-class DolbyCP750 : public SoundProcessor
+class DolbyCP750 : public CinemaSoundProcessor
 {
 public:
        DolbyCP750 ();
diff --git a/src/lib/encoded_data.cc b/src/lib/encoded_data.cc
new file mode 100644 (file)
index 0000000..61d2644
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+    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)
+{
+
+}
diff --git a/src/lib/encoded_data.h b/src/lib/encoded_data.h
new file mode 100644 (file)
index 0000000..232ed6e
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <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);
+};
index 693fd587e8bab1c8a6b2ba94253cfacc5caf6f96..0c9faa70d331bb722d70ffd17ba924456e344133 100644 (file)
 #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"
 
@@ -58,15 +58,14 @@ using boost::scoped_array;
 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 ()
@@ -88,18 +87,17 @@ Encoder::add_worker_threads (ServerDescription d)
 }
 
 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);
 
@@ -126,7 +124,7 @@ Encoder::process_end ()
             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 ());
@@ -135,9 +133,6 @@ Encoder::process_end ()
                        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,
@@ -181,8 +176,11 @@ Encoder::frame_done ()
        }
 }
 
+/** 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 ();
        
@@ -209,20 +207,24 @@ Encoder::process_video (shared_ptr<PlayerVideoFrame> pvf, bool same)
        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()
                                                  )
                                          ));
 
@@ -230,20 +232,13 @@ Encoder::process_video (shared_ptr<PlayerVideoFrame> pvf, bool same)
                   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 ()
 {
@@ -273,6 +268,8 @@ try
           encodings.
        */
        int remote_backoff = 0;
+       shared_ptr<DCPVideo> last_dcp_video;
+       shared_ptr<EncodedData> last_encoded;
        
        while (true) {
 
@@ -287,7 +284,7 @@ try
                }
 
                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 ();
                
@@ -295,38 +292,47 @@ try
 
                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 ();
index 8d5aa2c405a0c54843b49a30ae24d1bf42ea15aa..51df0176b575267a50ec75e5596059655811ec9e 100644 (file)
@@ -38,45 +38,42 @@ extern "C" {
 #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;
@@ -106,9 +103,8 @@ private:
        /** 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 */
index 8144f41b999690f526db309a4102547f4e69a05c..95d4c8cce3d3a7283763010af483957319b0242c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -56,8 +56,20 @@ MissingSettingError::MissingSettingError (string s)
 
 }
 
-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"))
+{
+
+}
index 3423a5754e340e3909b6b59ef617b5785d1a2809..52f257a8d3f558550ccba9724dfe612e3eca55c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#ifndef 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>
@@ -205,7 +205,7 @@ public:
        {}
 };
 
-/** @class NetworkError.
+/** @class NetworkError
  *  @brief Indicates some problem with communication on the network.
  */
 class NetworkError : public StringError
@@ -216,6 +216,9 @@ public:
        {}
 };
 
+/** @class KDMError
+ *  @brief A problem with a KDM.
+ */
 class KDMError : public StringError
 {
 public:
@@ -224,15 +227,44 @@ 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 {
@@ -269,6 +301,4 @@ private:
        mutable boost::mutex _mutex;
 };
 
-       
-
 #endif
index ebe62b51fbd412cb73956b2ae86db2c6058257ab..fa369dda429c9342c2b08eed7a4b74ee50a38c35 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -22,9 +22,11 @@ extern "C" {
 #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"
 
@@ -33,7 +35,7 @@ extern "C" {
 using std::string;
 using std::cout;
 using boost::shared_ptr;
-using libdcp::raw_convert;
+using dcp::raw_convert;
 
 boost::mutex FFmpeg::_mutex;
 
@@ -47,8 +49,7 @@ FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
        , _video_stream (-1)
 {
        setup_general ();
-       setup_video ();
-       setup_audio ();
+       setup_decoders ();
 }
 
 FFmpeg::~FFmpeg ()
@@ -56,14 +57,10 @@ 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);
 }
 
@@ -143,46 +140,24 @@ FFmpeg::setup_general ()
 }
 
 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
 {
@@ -192,9 +167,23 @@ 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)
 {
index 760918437d403dd0624984e31a07fb0bdddff583..8aaa54f84afab6973938e83eb138a60d6a2fa9c3 100644 (file)
@@ -56,6 +56,7 @@ public:
 protected:
        AVCodecContext* video_codec_context () const;
        AVCodecContext* audio_codec_context () const;
+       AVCodecContext* subtitle_codec_context () const;
        
        boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
 
@@ -79,8 +80,7 @@ protected:
 
 private:
        void setup_general ();
-       void setup_video ();
-       void setup_audio ();
+       void setup_decoders ();
 };
 
 #endif
diff --git a/src/lib/ffmpeg_audio_stream.cc b/src/lib/ffmpeg_audio_stream.cc
new file mode 100644 (file)
index 0000000..d8666e8
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    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"));
+}
diff --git a/src/lib/ffmpeg_audio_stream.h b/src/lib/ffmpeg_audio_stream.h
new file mode 100644 (file)
index 0000000..1587afc
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    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;
+};
index a4209f5b648306e734861c56ea96329b79a25a4a..bb4e022308dc9882dff57d5a0959d0a014cf9cab 100644 (file)
@@ -21,9 +21,11 @@ extern "C" {
 #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"
@@ -45,7 +47,7 @@ using std::cout;
 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;
@@ -62,7 +64,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, boost::filesystem::path
 
 }
 
-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)
@@ -108,7 +110,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, vector<boost::shared_ptr
 
        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."));
                }
 
@@ -156,7 +158,7 @@ FFmpegContent::as_xml (xmlpp::Node* node) const
        }
 
        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()));
        }
 }
 
@@ -167,20 +169,15 @@ FFmpegContent::examine (shared_ptr<Job> job)
 
        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 ();
@@ -194,9 +191,6 @@ FFmpegContent::examine (shared_ptr<Job> job)
                _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);
@@ -237,13 +231,13 @@ FFmpegContent::technical_summary () const
 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 ();
@@ -271,19 +265,14 @@ FFmpegContent::set_audio_stream (shared_ptr<FFmpegAudioStream> s)
        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
@@ -295,11 +284,11 @@ FFmpegContent::audio_channels () const
                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);
 
@@ -307,7 +296,7 @@ FFmpegContent::content_audio_frame_rate () const
                return 0;
        }
 
-       return _audio_stream->frame_rate;
+       return _audio_stream->frame_rate ();
 }
 
 bool
@@ -322,94 +311,12 @@ operator!= (FFmpegStream const & a, FFmpegStream const & b)
        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
@@ -421,7 +328,7 @@ FFmpegContent::audio_mapping () const
                return AudioMapping ();
        }
 
-       return _audio_stream->mapping;
+       return _audio_stream->mapping ();
 }
 
 void
@@ -438,8 +345,8 @@ FFmpegContent::set_filters (vector<Filter const *> const & filters)
 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
@@ -482,3 +389,29 @@ FFmpegContent::audio_analysis_path () const
        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 ();
+}
index c15e5c10ec0edc66d61db8238739d2d259f67ec1..da8152c0d92816de44a576f718a15f9f94c5db24 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 #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"
@@ -30,88 +31,9 @@ struct AVFormatContext;
 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
 {
@@ -127,7 +49,7 @@ class FFmpegContent : public VideoContent, public AudioContent, public SubtitleC
 {
 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 () {
@@ -139,18 +61,21 @@ public:
        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 {
@@ -181,19 +106,21 @@ public:
        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;
 };
index d40b798baafb77058f019e2bc6cf71817b5bd2f8..15443c346b3d302cce263165588044e641879160 100644 (file)
@@ -31,23 +31,26 @@ extern "C" {
 #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;
@@ -55,26 +58,19 @@ using std::vector;
 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:
 
@@ -84,13 +80,11 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
           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 */
 
@@ -104,24 +98,9 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC
 
        /* 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;
        }
 }
 
@@ -135,20 +114,15 @@ FFmpegDecoder::flush ()
        
        /* 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);
@@ -158,29 +132,25 @@ FFmpegDecoder::pass ()
                        /* 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.
@@ -313,82 +283,40 @@ FFmpegDecoder::bytes_per_audio_sample () const
 }
 
 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
@@ -404,42 +332,23 @@ FFmpegDecoder::decode_audio_packet ()
 
                int frame_finished;
                int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, &copy_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;
@@ -460,17 +369,13 @@ FFmpegDecoder::decode_video_packet ()
        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;
@@ -478,56 +383,16 @@ FFmpegDecoder::decode_video_packet ()
 
        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");
                }
@@ -535,47 +400,13 @@ FFmpegDecoder::decode_video_packet ()
 
        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;
        }
 
@@ -583,31 +414,29 @@ FFmpegDecoder::decode_subtitle_packet ()
           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];
@@ -629,20 +458,24 @@ FFmpegDecoder::decode_subtitle_packet ()
                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);
+}
index d4b4fa1c02984f8690c42bd264434b5266cff52e..9f85c2dca8e5e2d9fc66045cd694dab07c12fb90 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@ extern "C" {
 #include "subtitle_decoder.h"
 #include "ffmpeg.h"
 
-class Film;
+class Log;
 class FilterGraph;
 class ffmpeg_pts_offset_test;
 
@@ -47,22 +47,15 @@ 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;
 
@@ -73,15 +66,12 @@ private:
        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;
 };
index 5ccc8028b6ebae14dfbf6f1434b94e0c49a7e681..48d85da6f11113cb6cabc83215505c8df766110a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -23,6 +23,9 @@ extern "C" {
 }
 #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"
@@ -61,55 +64,93 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c)
                }
        }
 
-       /* 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;
@@ -118,27 +159,25 @@ FFmpegExaminer::frame_time (AVStream* s) const
 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
index 369dac29c992748b3b394b1a42ae1da3a4315235..8c31eb2c4aedadae419e7c5e41b83841f848c93b 100644 (file)
@@ -30,8 +30,8 @@ public:
        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;
@@ -41,17 +41,21 @@ public:
                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;
 };
diff --git a/src/lib/ffmpeg_stream.cc b/src/lib/ffmpeg_stream.cc
new file mode 100644 (file)
index 0000000..3fac333
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+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;
+}
diff --git a/src/lib/ffmpeg_stream.h b/src/lib/ffmpeg_stream.h
new file mode 100644 (file)
index 0000000..6bbcd0b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+    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
diff --git a/src/lib/ffmpeg_subtitle_stream.cc b/src/lib/ffmpeg_subtitle_stream.cc
new file mode 100644 (file)
index 0000000..3d8fd4e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+    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);
+}
diff --git a/src/lib/ffmpeg_subtitle_stream.h b/src/lib/ffmpeg_subtitle_stream.h
new file mode 100644 (file)
index 0000000..b16b825
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+    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;
+};
+
index 048f6923316e2cae13edfa9033daaa982f143db3..9c8d43204b1b830726c073de894e7b9f5c6e9e22 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/lib/file_group.cc
+ *  @brief FileGroup class.
+ */
+
 #include <cstdio>
 #include <sndfile.h>
 #include "file_group.h"
@@ -26,6 +30,7 @@
 using std::vector;
 using std::cout;
 
+/** Construct a FileGroup with no files */
 FileGroup::FileGroup ()
        : _current_path (0)
        , _current_file (0)
@@ -33,14 +38,17 @@ FileGroup::FileGroup ()
 
 }
 
+/** 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)
@@ -50,6 +58,7 @@ FileGroup::FileGroup (vector<boost::filesystem::path> const & p)
        seek (0, SEEK_SET);
 }
 
+/** Destroy a FileGroup, closing any open file */
 FileGroup::~FileGroup ()
 {
        if (_current_file) {
@@ -160,6 +169,7 @@ FileGroup::read (uint8_t* buffer, int amount) const
        return read;
 }
 
+/** @return Combined length of all the files */
 int64_t
 FileGroup::length () const
 {
index 65091c93646ac410c7f83b2c1a6d90dc1ef70e3a..5a65de96f7584e3afdab1ad71fd928f5df3b3005 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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:
index 54503ef72c6c7bf5b8c21184fd736d5ba7d949cf..26810992175d8da91065dfc4052d7da424afa0a2 100644 (file)
 #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"
@@ -77,9 +76,10 @@ using boost::ends_with;
 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);
@@ -91,11 +91,14 @@ using libdcp::raw_convert;
  * 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.
  *
@@ -109,7 +112,6 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _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 ())
@@ -119,6 +121,7 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _three_d (false)
        , _sequence_video (true)
        , _interop (false)
+       , _burn_subtitles (false)
        , _state_version (current_state_version)
        , _dirty (false)
 {
@@ -182,12 +185,12 @@ Film::video_identifier () const
                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 ();
@@ -227,6 +230,12 @@ Film::audio_mxf_filename () const
        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
 {
@@ -369,7 +378,6 @@ Film::metadata () 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));
@@ -378,6 +386,7 @@ Film::metadata () const
        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 ());
@@ -441,7 +450,6 @@ Film::read_metadata ()
 
        _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);
@@ -450,7 +458,10 @@ Film::read_metadata ()
        _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) */
@@ -585,9 +596,9 @@ Film::isdcf_name (bool if_created_now) const
        /* 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) {
@@ -689,7 +700,6 @@ Film::dcp_name (bool if_created_now) const
        return name();
 }
 
-
 void
 Film::set_directory (boost::filesystem::path d)
 {
@@ -739,13 +749,6 @@ Film::set_scaler (Scaler const * s)
        signal_changed (SCALER);
 }
 
-void
-Film::set_with_subtitles (bool w)
-{
-       _with_subtitles = w;
-       signal_changed (WITH_SUBTITLES);
-}
-
 void
 Film::set_j2k_bandwidth (int b)
 {
@@ -788,6 +791,13 @@ Film::set_interop (bool i)
        signal_changed (INTEROP);
 }
 
+void
+Film::set_burn_subtitles (bool b)
+{
+       _burn_subtitles = b;
+       signal_changed (BURN_SUBTITLES);
+}
+
 void
 Film::signal_changed (Property p)
 {
@@ -869,7 +879,7 @@ Film::j2c_path (int f, Eyes e, bool t) const
        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
 {
@@ -883,11 +893,14 @@ 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 (...) {
@@ -931,6 +944,13 @@ Film::content () const
        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)
 {
@@ -986,22 +1006,22 @@ Film::move_content_later (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
@@ -1022,31 +1042,7 @@ Film::playlist_changed ()
        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 */
@@ -1062,61 +1058,62 @@ Film::set_sequence_video (bool s)
 }
 
 /** @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;
@@ -1128,7 +1125,7 @@ Film::make_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
@@ -1146,10 +1143,3 @@ Film::should_be_enough_disk_space (double& required, double& available) const
        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 ());
-}
-
index b7d105688d06c2a1bf644544fb43aff9e9fd9c37..8a0823094e4c0ddf7b272ee05fae332ba631cc25 100644 (file)
@@ -31,8 +31,9 @@
 #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"
@@ -46,7 +47,7 @@ class Playlist;
 class AudioContent;
 class Scaler;
 class Screen;
-class isdcf_name_test;
+struct isdcf_name_test;
 
 /** @class Film
  *
@@ -69,6 +70,7 @@ public:
 
        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 ();
@@ -97,20 +99,15 @@ public:
                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;
@@ -118,29 +115,28 @@ public:
        /* 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;
        }
 
@@ -161,17 +157,18 @@ public:
                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,
        };
 
 
@@ -205,10 +202,6 @@ public:
                return _scaler;
        }
 
-       bool with_subtitles () const {
-               return _with_subtitles;
-       }
-
        /* signed is a reserved word */
        bool is_signed () const {
                return _signed;
@@ -246,6 +239,10 @@ public:
        bool interop () const {
                return _interop;
        }
+
+       bool burn_subtitles () const {
+               return _burn_subtitles;
+       }
        
 
        /* SET */
@@ -253,6 +250,7 @@ public:
        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>);
@@ -262,7 +260,6 @@ public:
        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);
@@ -273,6 +270,7 @@ public:
        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;
@@ -285,7 +283,7 @@ public:
 
 private:
 
-       friend class ::isdcf_name_test;
+       friend struct ::isdcf_name_test;
 
        void signal_changed (Property);
        std::string video_identifier () const;
@@ -315,8 +313,6 @@ private:
        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 */
@@ -335,15 +331,16 @@ private:
        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
index 639992d70747a37d1a5d89091c6cdb8bb0c566b4..2d8f83aa73da6d8b03c46005b5c45ff3780da6f7 100644 (file)
@@ -45,26 +45,29 @@ using std::make_pair;
 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."));
@@ -122,12 +125,15 @@ FilterGraph::FilterGraph (shared_ptr<const FFmpegContent> content, libdcp::Size
                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
@@ -138,19 +144,23 @@ FilterGraph::process (AVFrame* frame)
 {
        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;
 }
 
@@ -159,7 +169,7 @@ FilterGraph::process (AVFrame* frame)
  *  @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);
 }
index 9b403c2bc65734cf03a9b8458180a0b8695a26fc..5b43c5512fb9fa127bf5c364e292a22d9a7e1ce0 100644 (file)
@@ -36,16 +36,18 @@ class FFmpegContent;
 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;
 };
index 3b8c1a28ef48aa1d9b96638b708fb8a19a8f944b..bc64ba3b82373cb34bce1aff4e0f450d93ce5b29 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -30,6 +30,8 @@ extern "C" {
 #include "image.h"
 #include "exceptions.h"
 #include "scaler.h"
+#include "timer.h"
+#include "rect.h"
 #include "md5_digester.h"
 
 #include "i18n.h"
@@ -38,8 +40,9 @@ using std::string;
 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
@@ -83,7 +86,7 @@ Image::components () 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
@@ -99,13 +102,13 @@ Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_s
        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) {
@@ -139,7 +142,7 @@ Image::crop_scale_window (Crop crop, libdcp::Size inter_size, libdcp::Size out_s
 }
 
 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
@@ -170,7 +173,7 @@ Image::scale (libdcp::Size out_size, Scaler const * scaler, AVPixelFormat out_fo
 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) {
@@ -345,11 +348,34 @@ Image::make_black ()
        }
 }
 
+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;
@@ -368,15 +394,17 @@ Image::alpha_blend (shared_ptr<const Image> other, Position<int> position)
        }
 
        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;
                }
        }
 }
@@ -460,8 +488,8 @@ Image::bytes_per_pixel (int c) const
  *  @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)
 {
@@ -503,7 +531,7 @@ Image::allocate ()
 }
 
 Image::Image (Image const & other)
-       : libdcp::Image (other)
+       : dcp::Image (other)
        ,  _pixel_format (other._pixel_format)
        , _aligned (other._aligned)
 {
@@ -521,7 +549,7 @@ Image::Image (Image const & other)
 }
 
 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)
 {
@@ -540,7 +568,7 @@ Image::Image (AVFrame* frame)
 }
 
 Image::Image (shared_ptr<const Image> other, bool aligned)
-       : libdcp::Image (other)
+       : dcp::Image (other)
        , _pixel_format (other->_pixel_format)
        , _aligned (aligned)
 {
@@ -573,7 +601,7 @@ Image::operator= (Image const & other)
 void
 Image::swap (Image & other)
 {
-       libdcp::Image::swap (other);
+       dcp::Image::swap (other);
        
        std::swap (_pixel_format, other._pixel_format);
 
@@ -616,7 +644,7 @@ Image::stride () const
        return _stride;
 }
 
-libdcp::Size
+dcp::Size
 Image::size () const
 {
        return _size;
@@ -628,6 +656,31 @@ Image::aligned () const
        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
 {
@@ -639,4 +692,137 @@ 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);
+       }
+}
index f83bf6998111defbb49fcd49d67dea6703099992..172250eb1bb253ffd6a87e9998951ca6b534a7c3 100644 (file)
@@ -31,16 +31,17 @@ extern "C" {
 #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);
@@ -50,21 +51,23 @@ public:
        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;
@@ -76,7 +79,7 @@ public:
        std::string digest () const;
 
 private:
-       friend class pixel_formats_test;
+       friend struct pixel_formats_test;
        
        void allocate ();
        void swap (Image &);
@@ -91,4 +94,7 @@ private:
        bool _aligned;
 };
 
+extern PositionImage merge (std::list<PositionImage> images);
+extern bool operator== (Image const & a, Image const & b);
+
 #endif
index 915da7beba33b70687e0536d751d4e8e6e96a65f..84b0b75c9732a4cd47dc30e1698ee1a82335c675 100644 (file)
 #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"
@@ -55,7 +55,7 @@ ImageContent::ImageContent (shared_ptr<const Film> f, boost::filesystem::path p)
 }
 
 
-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)
 {
@@ -109,13 +109,11 @@ ImageContent::examine (shared_ptr<Job> job)
        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);
@@ -125,14 +123,12 @@ ImageContent::set_video_length (VideoContent::Frame len)
        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
@@ -140,7 +136,7 @@ ImageContent::identifier () const
 {
        SafeStringStream s;
        s << VideoContent::identifier ();
-       s << "_" << video_length();
+       s << "_" << video_length().get();
        return s.str ();
 }
 
index e56abce4a07a8218ab0ad4a7a1b26280d131adce..a1b1437c811fb391154b222466b7fa22a27b5258 100644 (file)
@@ -31,7 +31,7 @@ class ImageContent : public VideoContent
 {
 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 ());
@@ -41,11 +41,11 @@ public:
        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);
 };
index 7a9acd9e4960e611bda76e912e4837738e94dc59..8702c1a33b18692dbc43b89c9e60ab416ef9e50e 100644 (file)
@@ -23,7 +23,7 @@
 #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 ());
 }
index 5b82dd85c161eedd07ccfb0c09cdc90c6dc96708..242f69477826a499d915505cd0b9486808aba68d 100644 (file)
@@ -28,20 +28,19 @@ class ImageContent;
 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;
 };
 
index 7058ea3b282935c2a9265660500190ae770bd8d2..75ccb6a3e91fd3e21207ce26872f69f187b95170 100644 (file)
@@ -36,23 +36,22 @@ using boost::shared_ptr;
 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 ();
index 8887f0d3d15a2587f56faff713a62835b3af8a7b..6ae0422cbbd4477f8ac7878e05a2430b251711b0 100644 (file)
@@ -31,14 +31,14 @@ public:
        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;
 };
index 3851f88e78c79e873ee962110ff1a929747ba0e8..b6b387b76081947a554290c0846cf81333acd42c 100644 (file)
 
 */
 
-#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"
@@ -40,151 +42,6 @@ ImageProxy::ImageProxy (shared_ptr<Log> log)
 
 }
 
-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)
 {
@@ -192,6 +49,8 @@ image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shar
                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"));
index c0ccd912551782007bd83beaa85536e564e6f18d..7ff28e174ec092afdbc832ec6b07fb40c7f47c37 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#ifndef DCPOMATIC_IMAGE_PROXY_H
+#define DCPOMATIC_IMAGE_PROXY_H
+
 /** @file  src/lib/image_proxy.h
  *  @brief ImageProxy and subclasses.
  */
@@ -34,6 +37,11 @@ namespace cxml {
        class Node;
 }
 
+namespace dcp {
+       class MonoPictureFrame;
+       class StereoPictureFrame;
+}
+
 /** @class ImageProxy
  *  @brief A class which holds an Image, and can produce it on request.
  *
@@ -42,7 +50,7 @@ namespace cxml {
  *  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
@@ -55,38 +63,15 @@ public:
        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
diff --git a/src/lib/image_subtitle.h b/src/lib/image_subtitle.h
new file mode 100644 (file)
index 0000000..b25943a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    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
index dfba50a9a2262ecf02c9e95a026cc9d2abab77a7..7d960b6ac758538d0728b68501a803e0368dcc7d 100644 (file)
 
 #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");
index 0fb7e7baa0e1292468e0573d5d7424e7e7c165b2..e63f290e47de854ed9347433739eafc57564015d 100644 (file)
 
 #include <string>
 #include <libxml++/libxml++.h>
-
-namespace cxml {
-       class Node;
-}
+#include <libcxml/cxml.h>
 
 class ISDCFMetadata
 {
@@ -38,7 +35,7 @@ public:
                , 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);
diff --git a/src/lib/j2k_image_proxy.cc b/src/lib/j2k_image_proxy.cc
new file mode 100644 (file)
index 0000000..6924fad
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+    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()));
+               }
+       }
+}
diff --git a/src/lib/j2k_image_proxy.h b/src/lib/j2k_image_proxy.h
new file mode 100644 (file)
index 0000000..d7b5c83
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    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;
+};
index 52ec1426c0bd0300a6658293471ef795e2e995ae..1d3cb73b6fcf39f276e9aefc3fbdd91841397051 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <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"
 
@@ -66,8 +67,8 @@ Job::run_wrapper ()
 
                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 {
@@ -204,7 +205,7 @@ Job::set_state (State s)
        }       
 }
 
-/** @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
 {
@@ -279,6 +280,7 @@ Job::error_summary () 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;
index f5054b8ed0c11d4d0522b14ba420348da90284a0..108860594e2592144760965a02aeb1220f8cf02c 100644 (file)
@@ -21,7 +21,8 @@
 #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"
@@ -37,13 +38,13 @@ using boost::shared_ptr;
 
 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
@@ -104,17 +105,17 @@ make_screen_kdms (
        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;
@@ -129,9 +130,9 @@ make_cinema_kdms (
        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);
@@ -175,9 +176,9 @@ write_kdm_files (
        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
        )
 {
@@ -196,9 +197,9 @@ write_kdm_zip_files (
        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
        )
 {
@@ -216,9 +217,9 @@ email_kdms (
        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);
index 8fb4ec494d2e2a52866e8fe68d1cc5a2ff92766a..b80ef454f2ddc2b3092b19bb55783f5f63aa5c01 100644 (file)
@@ -27,9 +27,9 @@ extern void write_kdm_files (
        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
        );
 
@@ -37,9 +37,9 @@ extern void write_kdm_zip_files (
        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
        );
 
@@ -47,8 +47,8 @@ extern void email_kdms (
        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
        );
 
diff --git a/src/lib/magick_image_proxy.cc b/src/lib/magick_image_proxy.cc
new file mode 100644 (file)
index 0000000..e526518
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+    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;
+}
diff --git a/src/lib/magick_image_proxy.h b/src/lib/magick_image_proxy.h
new file mode 100644 (file)
index 0000000..8b43d0a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+    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;
+};
diff --git a/src/lib/mid_side_decoder.cc b/src/lib/mid_side_decoder.cc
new file mode 100644 (file)
index 0000000..be82f67
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "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;
+}
diff --git a/src/lib/mid_side_decoder.h b/src/lib/mid_side_decoder.h
new file mode 100644 (file)
index 0000000..dac6cb7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+    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>);
+};
+
+       
diff --git a/src/lib/piece.cc b/src/lib/piece.cc
deleted file mode 100644 (file)
index 494fb17..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-    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;
-}
-
index 17b87b884664e49ef3ef787f87f5ce5ffbbe3b03..976409381d3a8744c69f5f14e53f67a5ca688e08 100644 (file)
 
 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
index 8063d1212971afe2e9bf682a84d253361b902bac..db99cd2ad81626bbe10180bc3e28ea9ea878cf43 100644 (file)
 
 */
 
-#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);
 
@@ -44,23 +56,21 @@ using std::list;
 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));
@@ -69,579 +79,508 @@ Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
 }
 
 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;
 }
index 6e70ad707cf79052cd24a64cbc0c1344020a2e1c..a3b745c8e4ca69acd641cb106be7b733ca19e7da 100644 (file)
 #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;
@@ -38,42 +41,60 @@ class Playlist;
 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.
@@ -85,51 +106,58 @@ public:
 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;
diff --git a/src/lib/player_subtitles.h b/src/lib/player_subtitles.h
new file mode 100644 (file)
index 0000000..46994ea
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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
diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc
new file mode 100644 (file)
index 0000000..8e6fcd5
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+    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);
+}
diff --git a/src/lib/player_video.h b/src/lib/player_video.h
new file mode 100644 (file)
index 0000000..e9d2609
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+    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;
+};
diff --git a/src/lib/player_video_frame.h b/src/lib/player_video_frame.h
deleted file mode 100644 (file)
index 6a68682..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
-    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;
-};
index c3e430082ecacd2a51db12edb1e5ee536b577fc6..22412da4a3640b5313cd6d75cdb25422a4ee08f6 100644 (file)
@@ -79,20 +79,20 @@ Playlist::maybe_sequence_video ()
        _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 ();
                }
        }
 
@@ -120,7 +120,7 @@ Playlist::video_identifier () const
 
 /** @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) {
@@ -185,19 +185,6 @@ Playlist::remove (ContentList c)
        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:
@@ -261,12 +248,12 @@ Playlist::best_dcp_frame_rate () const
        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;
@@ -286,10 +273,10 @@ Playlist::reconnect ()
        }
 }
 
-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 ());
@@ -299,6 +286,23 @@ Playlist::video_end () const
        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)
 {
@@ -321,7 +325,7 @@ Playlist::content () const
 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 ());
@@ -329,7 +333,7 @@ Playlist::repeat (ContentList c, int n)
                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 ();
@@ -363,7 +367,7 @@ Playlist::move_earlier (shared_ptr<Content> c)
        }
 
        
-       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 ());
@@ -392,20 +396,3 @@ Playlist::move_later (shared_ptr<Content> c)
        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);
-}
index 12380696bdf49349debb1c334915c58493a9b187..9e3dbb6dfbf39ccf2f6f6864d1ee2920983ff447 100644 (file)
@@ -25,6 +25,7 @@
 #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;
@@ -38,18 +39,15 @@ class Job;
 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:
@@ -57,7 +55,7 @@ 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>);
@@ -65,17 +63,15 @@ public:
        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 ();
index 071f582ce8313595e3f906c2721ad2c11cd36e60..68418910cb9fc190ff35e6526c8a77a1b29e8472 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -18,23 +18,27 @@ msgstr ""
 "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)"
 
@@ -70,11 +74,11 @@ msgstr "Academy"
 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"
 
@@ -90,7 +94,7 @@ msgstr "Bi-Kubisch"
 msgid "Bilinear"
 msgstr "Bi-Linear"
 
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
 msgid "Cancelled"
 msgstr "Abgebrochen"
 
@@ -98,15 +102,15 @@ 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"
 
@@ -114,7 +118,7 @@ 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"
 
@@ -122,67 +126,77 @@ 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."
 
@@ -198,12 +212,12 @@ msgstr "Keine Verbindung zu Server %1 (%2)"
 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."
 
@@ -223,6 +237,10 @@ msgstr "SCP Session (%1) konnte nicht gestartet 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"
@@ -231,7 +249,7 @@ 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."
@@ -239,7 +257,7 @@ msgstr ""
 "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 ""
@@ -249,7 +267,7 @@ msgstr ""
 msgid "De-interlacing"
 msgstr "De-Interlacer"
 
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
 msgid ""
 "Dear Projectionist\n"
 "\n"
@@ -295,14 +313,18 @@ msgstr "Jedes Bild der Quelle wird %1 mal im DCP wiederholt.\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"
@@ -335,7 +357,7 @@ msgstr "Gauss Filter"
 msgid "Gradient debander"
 msgstr "Gradient Glätter"
 
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
 msgid "Hearing impaired"
 msgstr "HI"
 
@@ -343,7 +365,7 @@ 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)."
@@ -351,7 +373,7 @@ msgstr ""
 "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"
 
@@ -363,30 +385,50 @@ msgstr "Kernel De-Interlacer"
 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"
@@ -407,23 +449,23 @@ msgstr "Keine gültigen Bilddaten gefunden."
 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"
 
@@ -443,23 +485,23 @@ msgstr "Hinweis"
 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"
 
@@ -483,6 +525,14 @@ msgstr "Sinc"
 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"
@@ -495,7 +545,16 @@ msgstr "Telecine Filter"
 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."
@@ -503,11 +562,11 @@ msgstr ""
 "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!"
@@ -516,7 +575,7 @@ msgstr ""
 "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 "
@@ -530,7 +589,7 @@ msgstr ""
 msgid "Trailer"
 msgstr "Trailer"
 
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
 msgid "Transcode %1"
 msgstr "Wandle %1 um"
 
@@ -542,15 +601,15 @@ msgstr "Ãœberleitung"
 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."
 
@@ -562,7 +621,7 @@ msgstr "Unscharf Maskieren mit Gaußschem Unschärfefilter"
 msgid "Untitled"
 msgstr "Unbenannt"
 
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
 msgid "Visually impaired"
 msgstr "VI"
 
@@ -578,7 +637,7 @@ msgstr "X"
 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!"
 
@@ -590,11 +649,15 @@ msgstr "[Bewegte Bilder]"
 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"
 
@@ -602,11 +665,11 @@ 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"
 
@@ -618,23 +681,15 @@ msgstr "kopiere %1"
 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."
 
@@ -642,11 +697,11 @@ 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."
 
@@ -654,11 +709,11 @@ 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)"
 
@@ -674,39 +729,39 @@ msgstr "SSH Session konnte nicht gestartet werden"
 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"
 
@@ -722,38 +777,34 @@ msgstr "Benötigte Einstellung %1 fehlt"
 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"
 
@@ -761,10 +812,19 @@ 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"
 
index 3aa8c19fa596b42483cf02cc558af7744edae747..e874c64151ca619bc1663f6c60a01c9931e92e7a 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -17,23 +17,27 @@ msgstr ""
 "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)"
 
@@ -69,11 +73,11 @@ msgstr "Academy"
 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"
 
@@ -89,7 +93,7 @@ msgstr "Bicúbico"
 msgid "Bilinear"
 msgstr "Bilineal"
 
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
 msgid "Cancelled"
 msgstr "Cancelado"
 
@@ -97,15 +101,15 @@ 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"
 
@@ -113,7 +117,7 @@ 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"
 
@@ -121,63 +125,73 @@ 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."
 
@@ -193,12 +207,12 @@ msgstr "No se pudo conectar al servidor %1 (%2)"
 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"
 
@@ -218,6 +232,10 @@ msgstr "No se pudo iniciar la sesión SCP (%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"
@@ -226,7 +244,7 @@ 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."
@@ -234,7 +252,7 @@ msgstr ""
 "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."
@@ -243,7 +261,7 @@ 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"
@@ -287,14 +305,18 @@ msgstr "Cada imagen será repetida otras %1 veces en el DCP.\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"
@@ -327,7 +349,7 @@ msgstr "Gaussiano"
 msgid "Gradient debander"
 msgstr "Gradient debander"
 
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
 msgid "Hearing impaired"
 msgstr "Sordos"
 
@@ -335,7 +357,7 @@ 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 "
@@ -344,7 +366,7 @@ msgstr ""
 "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 ""
 
@@ -356,30 +378,50 @@ msgstr "Kernel deinterlacer"
 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"
@@ -400,20 +442,20 @@ msgstr ""
 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"
 
@@ -433,23 +475,23 @@ msgstr "Anuncio de servicio público"
 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"
 
@@ -473,6 +515,14 @@ msgstr "Sinc"
 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"
@@ -485,7 +535,16 @@ msgstr "Filtro telecine"
 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."
@@ -493,11 +552,11 @@ msgstr ""
 "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!"
@@ -506,7 +565,7 @@ msgstr ""
 "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 "
@@ -520,7 +579,7 @@ msgstr ""
 msgid "Trailer"
 msgstr "Trailer"
 
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
 msgid "Transcode %1"
 msgstr "Codificar %1"
 
@@ -532,15 +591,15 @@ msgstr "Transitional"
 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)"
 
@@ -552,7 +611,7 @@ msgstr "Máscara de desenfoque Gaussiano"
 msgid "Untitled"
 msgstr "Sin título"
 
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
 msgid "Visually impaired"
 msgstr "Ciegos"
 
@@ -568,7 +627,7 @@ msgstr "X"
 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."
 
@@ -580,11 +639,15 @@ msgstr "[imágenes en movimiento]"
 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"
 
@@ -592,11 +655,11 @@ 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"
 
@@ -608,23 +671,15 @@ msgstr "copiando %1"
 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"
 
@@ -632,11 +687,11 @@ 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"
 
@@ -644,11 +699,11 @@ 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"
 
@@ -664,39 +719,39 @@ msgstr "no se pudo abrir la sesión SSH"
 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"
 
@@ -712,38 +767,34 @@ msgstr "falta una configuración obligatoria %1"
 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"
 
@@ -751,10 +802,19 @@ 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"
 
index 0242c63e729ab726afb3b8e34ff823b98a456b1a..474da73f9a3a6fe24d33900b09c4c89e38716aa3 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -17,23 +17,27 @@ msgstr ""
 "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)"
 
@@ -69,11 +73,11 @@ msgstr "Academy"
 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"
 
@@ -89,7 +93,7 @@ msgstr "Bicubique"
 msgid "Bilinear"
 msgstr "Bilinéaire"
 
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
 msgid "Cancelled"
 msgstr "Annulé"
 
@@ -97,15 +101,15 @@ 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"
 
@@ -113,7 +117,7 @@ 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"
 
@@ -121,66 +125,77 @@ 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"
 
@@ -196,12 +211,12 @@ msgstr "Connexion au serveur %1 (%2) impossible"
 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"
 
@@ -221,6 +236,10 @@ msgstr "Démarrage de session SCP (%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"
@@ -229,7 +248,7 @@ 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."
@@ -237,7 +256,7 @@ msgstr ""
 "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é."
@@ -246,7 +265,7 @@ 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"
@@ -292,14 +311,18 @@ msgstr "Chaque image source sera répetée %1 fois dans le DCP.\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"
@@ -332,7 +355,7 @@ msgstr "Gaussien"
 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"
 
@@ -340,7 +363,7 @@ 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)."
@@ -348,7 +371,7 @@ msgstr ""
 "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 ""
 
@@ -360,30 +383,50 @@ msgstr "Désentrelaceur noyau"
 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"
@@ -404,19 +447,19 @@ msgstr "Aucun fichier image valide dans ce dossier."
 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"
 
@@ -436,23 +479,23 @@ msgstr "Public Service Announcement"
 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"
 
@@ -476,6 +519,14 @@ msgstr "Sinc"
 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"
@@ -488,7 +539,16 @@ msgstr "Filtre télécinéma"
 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."
@@ -496,11 +556,11 @@ msgstr ""
 "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!"
@@ -508,7 +568,7 @@ msgstr ""
 "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 "
@@ -522,7 +582,7 @@ msgstr ""
 msgid "Trailer"
 msgstr "Trailer"
 
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
 msgid "Transcode %1"
 msgstr "Transcodage %1"
 
@@ -534,15 +594,15 @@ msgstr "Transitional"
 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"
 
@@ -554,7 +614,7 @@ msgstr "Adoucissement et flou Gaussien"
 msgid "Untitled"
 msgstr "Sans titre"
 
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
 msgid "Visually impaired"
 msgstr "Déficients Visuels"
 
@@ -570,7 +630,7 @@ msgstr "X"
 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"
 
@@ -582,11 +642,15 @@ msgstr "[Déplacement d'images]"
 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é"
 
@@ -594,11 +658,11 @@ 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"
 
@@ -610,23 +674,15 @@ msgstr "copie de %1"
 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"
 
@@ -634,11 +690,11 @@ 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"
 
@@ -646,11 +702,11 @@ 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 "
@@ -668,39 +724,39 @@ msgstr "démarrage de session SSH impossible"
 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"
 
@@ -716,38 +772,34 @@ msgstr "paramètre %1 manquant"
 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"
 
@@ -755,10 +807,19 @@ 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"
 
index 0ddbc4a9adee748596e3baaa99b24b963cf2e559..a22ccee3ca5be3aedc9f104abaee92f071bef673 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -17,23 +17,27 @@ msgstr ""
 "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 ""
 
@@ -69,11 +73,11 @@ msgstr "Academy"
 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"
@@ -90,7 +94,7 @@ msgstr "Bicubica"
 msgid "Bilinear"
 msgstr "Bilineare"
 
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
 msgid "Cancelled"
 msgstr "Cancellato"
 
@@ -98,15 +102,15 @@ 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 ""
 
@@ -114,7 +118,7 @@ msgstr ""
 msgid "Computing digest"
 msgstr ""
 
-#: src/lib/writer.cc:471
+#: src/lib/writer.cc:486
 msgid "Computing image digest"
 msgstr ""
 
@@ -123,67 +127,79 @@ 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 ""
 
@@ -199,12 +215,12 @@ msgstr "Non posso connetermi al server %1 (%2)"
 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"
 
@@ -225,6 +241,10 @@ msgstr "Non posso avviare la sessione SCP (%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"
@@ -235,7 +255,7 @@ msgstr "Il DCP andrà al %1%% della velocità del sorgente.\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."
@@ -243,7 +263,7 @@ msgstr ""
 "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 ""
@@ -252,7 +272,7 @@ msgstr ""
 msgid "De-interlacing"
 msgstr "De-interlacciamento"
 
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
 #, fuzzy
 msgid ""
 "Dear Projectionist\n"
@@ -297,14 +317,18 @@ msgstr "Ogni fotogramma del sorgente sarà ripetuto %1 volte nel DCP.\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"
@@ -337,7 +361,7 @@ msgstr "Gaussiana"
 msgid "Gradient debander"
 msgstr "Gradiente debander"
 
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
 msgid "Hearing impaired"
 msgstr ""
 
@@ -345,7 +369,7 @@ 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 "
@@ -354,7 +378,7 @@ msgstr ""
 "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 ""
 
@@ -366,31 +390,51 @@ msgstr "Deinterlacciatore Kernel"
 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"
@@ -412,19 +456,19 @@ msgstr ""
 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 ""
 
@@ -444,25 +488,25 @@ msgstr "Annuncio di pubblico servizio"
 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"
 
@@ -486,6 +530,14 @@ msgstr "Sinc"
 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"
@@ -498,7 +550,16 @@ msgstr "Filtro telecinema"
 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."
@@ -506,11 +567,11 @@ msgstr ""
 "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 "
@@ -521,7 +582,7 @@ msgstr ""
 "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 "
@@ -536,7 +597,7 @@ msgstr ""
 msgid "Trailer"
 msgstr "Prossimamente"
 
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
 msgid "Transcode %1"
 msgstr "Transcodifica %1"
 
@@ -548,15 +609,15 @@ msgstr "Di transizione"
 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)"
 
@@ -568,7 +629,7 @@ msgstr "Maschera unsharp e sfocatura Gaussiana"
 msgid "Untitled"
 msgstr "Senza titolo"
 
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
 msgid "Visually impaired"
 msgstr ""
 
@@ -584,7 +645,7 @@ msgstr "X"
 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"
 
@@ -597,11 +658,15 @@ msgstr ""
 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"
 
@@ -609,11 +674,11 @@ 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"
 
@@ -625,23 +690,15 @@ msgstr "copia %1"
 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"
 
@@ -649,11 +706,11 @@ 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"
@@ -662,11 +719,11 @@ 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"
@@ -683,39 +740,39 @@ msgstr "non posso avviare la sessione SSH"
 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"
 
@@ -731,38 +788,34 @@ msgstr "persa la regolazione richiesta %1"
 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"
 
@@ -771,11 +824,20 @@ 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"
 
index 857aa12d48f05698e78326fcdf6adb10b6ae0d64..81a1e626bf2ff47a5e45ec7715dc2e1f649702bb 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -18,23 +18,27 @@ msgstr ""
 "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)"
 
@@ -70,11 +74,11 @@ msgstr "Academie"
 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"
 
@@ -90,7 +94,7 @@ msgstr "Bicubic"
 msgid "Bilinear"
 msgstr "Bilinear"
 
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
 msgid "Cancelled"
 msgstr "Afgebroken"
 
@@ -98,15 +102,15 @@ 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"
 
@@ -114,7 +118,7 @@ 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"
 
@@ -122,61 +126,71 @@ 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."
 
@@ -192,12 +206,12 @@ msgstr "Kan niet verbinden met server %1 (%2)"
 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"
 
@@ -217,6 +231,10 @@ msgstr "Kan SCP sessie niet starten (%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"
@@ -225,7 +243,7 @@ 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."
@@ -233,7 +251,7 @@ msgstr ""
 "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 ""
@@ -244,7 +262,7 @@ msgstr ""
 msgid "De-interlacing"
 msgstr "De-interlacing"
 
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
 msgid ""
 "Dear Projectionist\n"
 "\n"
@@ -289,14 +307,18 @@ msgstr "Elk content frame zal %1  herhaald worden in de DCP.\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"
@@ -329,7 +351,7 @@ msgstr "Gaussian"
 msgid "Gradient debander"
 msgstr "Gradient debander"
 
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
 msgid "Hearing impaired"
 msgstr "Slechthorenden"
 
@@ -337,7 +359,7 @@ 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)."
@@ -345,7 +367,7 @@ msgstr ""
 "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"
 
@@ -357,30 +379,50 @@ msgstr "Kernel deinterlacer"
 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"
@@ -401,23 +443,23 @@ msgstr "Geen geldige beeldbestanden gevonden in deze map."
 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"
 
@@ -437,23 +479,23 @@ msgstr "Publieke Service aankondiging"
 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"
 
@@ -477,6 +519,14 @@ msgstr "Sinc"
 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"
@@ -489,7 +539,16 @@ msgstr "Telecine filter"
 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."
@@ -497,11 +556,11 @@ msgstr ""
 "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!"
@@ -509,7 +568,7 @@ msgstr ""
 "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 "
@@ -523,7 +582,7 @@ msgstr ""
 msgid "Trailer"
 msgstr "Trailer"
 
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
 msgid "Transcode %1"
 msgstr "Omzetten %1"
 
@@ -535,15 +594,15 @@ msgstr "Bumper"
 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)"
 
@@ -555,7 +614,7 @@ msgstr "Unsharp mask and Gaussian blur"
 msgid "Untitled"
 msgstr "Niet benoemd"
 
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
 msgid "Visually impaired"
 msgstr "Slechtzienden"
 
@@ -571,7 +630,7 @@ msgstr "X"
 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"
 
@@ -583,11 +642,15 @@ msgstr "[bewegend beeld]"
 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"
 
@@ -595,11 +658,11 @@ 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"
 
@@ -611,23 +674,15 @@ msgstr "kopieeren %1"
 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"
 
@@ -635,11 +690,11 @@ 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"
 
@@ -647,11 +702,11 @@ 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)"
 
@@ -667,39 +722,39 @@ msgstr "kan SSH sessie niet starten"
 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"
 
@@ -715,37 +770,33 @@ msgstr "ontbrekende verplichte setting %1"
 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"
 
@@ -753,9 +804,18 @@ 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"
index d4b773c2ef1c4c4d4abb42f00ade2af8c0f8e0fd..29127294f9d2291671b9dd4deda2299120480214 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -17,23 +17,27 @@ msgstr ""
 "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)"
 
@@ -69,11 +73,11 @@ msgstr "Academy"
 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"
 
@@ -89,7 +93,7 @@ msgstr "Bikubisk"
 msgid "Bilinear"
 msgstr "Bilinjär"
 
-#: src/lib/job.cc:322
+#: src/lib/job.cc:324
 msgid "Cancelled"
 msgstr "Avbruten"
 
@@ -97,15 +101,15 @@ 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"
 
@@ -113,7 +117,7 @@ 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"
 
@@ -121,66 +125,76 @@ 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."
 
@@ -196,12 +210,12 @@ msgstr "Kunde inte ansluta till server %1 (%2)"
 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"
 
@@ -222,6 +236,10 @@ msgstr "Kunde inte starta SCP-session (%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"
@@ -230,7 +248,7 @@ 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."
@@ -238,7 +256,7 @@ msgstr ""
 "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 ""
@@ -248,7 +266,7 @@ msgid "De-interlacing"
 msgstr "Avflätning"
 
 # svÃ¥röversatt
-#: src/lib/config.cc:436
+#: src/lib/config.cc:427
 #, fuzzy
 msgid ""
 "Dear Projectionist\n"
@@ -291,14 +309,18 @@ msgstr ""
 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"
@@ -331,7 +353,7 @@ msgstr "Gaussisk"
 msgid "Gradient debander"
 msgstr "Gradientutjämnare"
 
-#: src/lib/util.cc:790
+#: src/lib/util.cc:779
 msgid "Hearing impaired"
 msgstr ""
 
@@ -339,7 +361,7 @@ 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 "
@@ -348,7 +370,7 @@ msgstr ""
 "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 ""
 
@@ -360,31 +382,51 @@ msgstr "Kernel-avflätare"
 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"
@@ -406,20 +448,20 @@ msgstr ""
 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 ""
 
@@ -439,24 +481,24 @@ msgstr "Public Service Announcement"
 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"
 
@@ -480,6 +522,14 @@ msgstr "Sinc"
 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"
@@ -492,7 +542,16 @@ msgstr "Telecine-filter"
 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."
@@ -500,11 +559,11 @@ msgstr ""
 "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 "
@@ -514,7 +573,7 @@ msgstr ""
 "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 "
@@ -528,7 +587,7 @@ msgstr ""
 msgid "Trailer"
 msgstr "Trailer"
 
-#: src/lib/transcode_job.cc:53
+#: src/lib/transcode_job.cc:54
 msgid "Transcode %1"
 msgstr "Konvertera %1"
 
@@ -540,16 +599,16 @@ msgstr "Transitional"
 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)"
 
@@ -561,7 +620,7 @@ msgstr "Oskärpemask och Gaussisk suddighet"
 msgid "Untitled"
 msgstr "Utan titel"
 
-#: src/lib/util.cc:791
+#: src/lib/util.cc:780
 msgid "Visually impaired"
 msgstr ""
 
@@ -578,7 +637,7 @@ msgstr "X"
 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"
 
@@ -590,12 +649,16 @@ msgstr "[rörliga bilder]"
 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"
 
@@ -603,11 +666,11 @@ 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"
 
@@ -619,23 +682,15 @@ msgstr "kopierar %1"
 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"
 
@@ -643,11 +698,11 @@ 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"
 
@@ -655,11 +710,11 @@ 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)"
@@ -676,39 +731,39 @@ msgstr "kunde inte starta SSH-session"
 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"
 
@@ -725,38 +780,34 @@ msgstr "saknad nödvändig inställning %1"
 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"
 
@@ -764,10 +815,19 @@ 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"
 
index f9bd0987cdb5ed3e2688ed63bf0d5cfdbbf1597d..3c561d85c2b14b909b2f7e1fd2445bed662ff189 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
 #define DCPOMATIC_POSITION_H
 
 /** @struct Position
- *  @brief A position.
+ *  @brief A position (x and y coordinates)
  */
 template <class T>
 class Position
@@ -50,4 +50,25 @@ operator+ (Position<T> const & a, Position<T> const & b)
        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
diff --git a/src/lib/position_image.cc b/src/lib/position_image.cc
new file mode 100644 (file)
index 0000000..44c1262
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+    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);
+}
diff --git a/src/lib/position_image.h b/src/lib/position_image.h
new file mode 100644 (file)
index 0000000..c0c65d1
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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
index 554d3c36c6c551f93e30b0d1305de5f94b9cdde4..fc36415c50638e161e73c8bd1df1ed19d3e01e99 100644 (file)
@@ -17,7 +17,7 @@
 
 */
 
-#include <libdcp/types.h>
+#include <dcp/types.h>
 #include "ratio.h"
 #include "util.h"
 
index ab157a9bcb4a65404d1531973d02fbd61d39ec76..69e3726c83f201e91cf48e5b955e215c6eea8d2c 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <vector>
 #include <boost/utility.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
 
 class Ratio : public boost::noncopyable
 {
diff --git a/src/lib/raw_image_proxy.cc b/src/lib/raw_image_proxy.cc
new file mode 100644 (file)
index 0000000..7e0688d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+    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);
+}
diff --git a/src/lib/raw_image_proxy.h b/src/lib/raw_image_proxy.h
new file mode 100644 (file)
index 0000000..6707f68
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+    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;
+};
index 6f4709c088ce137b2c589df64cb69304c1b62373..1feb8ad4fed460d19a9f83439e21bdd0abcc1ea0 100644 (file)
@@ -42,6 +42,13 @@ public:
                , 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_)
@@ -54,11 +61,13 @@ public:
        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);
        
@@ -69,7 +78,16 @@ public:
                        );
        }
 
-       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));
        }
 };
diff --git a/src/lib/render_subtitles.cc b/src/lib/render_subtitles.cc
new file mode 100644 (file)
index 0000000..6f10324
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+    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 ()));
+}
+
diff --git a/src/lib/render_subtitles.h b/src/lib/render_subtitles.h
new file mode 100644 (file)
index 0000000..d83dc11
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+    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);
index e7f50c4b7bf7fc2174248b2317d3d50d45813da9..e414436e8b39960c6c5f78ca16188c0781bc0407 100644 (file)
@@ -60,11 +60,9 @@ Resampler::~Resampler ()
        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));
@@ -80,7 +78,7 @@ Resampler::run (shared_ptr<const AudioBuffers> in, AudioContent::Frame frame)
        }
        
        resampled->set_frames (resampled_frames);
-       return make_pair (resampled, resamp_time);
+       return resampled;
 }      
 
 shared_ptr<const AudioBuffers>
index 69ec83ba93047f3493cd23005487b7845580944e..4ee11a7f0954958e69a99bf09be75196394838c3 100644 (file)
@@ -33,7 +33,7 @@ public:
        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:       
index e455de964b4acc5922b8cd01d79312072c43b16b..0ffcb62243b4635e23f1420ce86fcb06b751df95 100644 (file)
@@ -84,6 +84,11 @@ public:
                _stream.width (w);
        }
 
+       void fill (int f)
+       {
+               _stream.fill (f);
+       }
+       
        void precision (int p)
        {
                _stream.precision (p);
index 164dfe9875e4ec35a5578e8ff41f9132fe7eea7c..541307f5acc8ace90d682b5b396046ddcdcbab2e 100644 (file)
@@ -34,7 +34,7 @@ SendKDMEmailJob::SendKDMEmailJob (
        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)
index 778d3927ac01d7a741c88804065dab4f5df00c21..af84a13af2b959c034e342c3dd9d2b56448c8a12 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 #include <boost/filesystem.hpp>
-#include <libdcp/kdm.h>
+#include <dcp/types.h>
 #include "job.h"
 
 class Screen;
@@ -32,7 +32,7 @@ public:
                boost::filesystem::path,
                boost::posix_time::ptime,
                boost::posix_time::ptime,
-               libdcp::KDM::Formulation
+               dcp::Formulation
                );
 
        std::string name () const;
@@ -43,5 +43,5 @@ private:
        boost::filesystem::path _dcp;
        boost::posix_time::ptime _from;
        boost::posix_time::ptime _to;
-       libdcp::KDM::Formulation _formulation;
+       dcp::Formulation _formulation;
 };
index 9428ba611d70d2a96de10ef3793b348bce778aff..a699be57739699c2d8558cb1290295be7e57c406 100644 (file)
 #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"
@@ -61,16 +62,37 @@ using boost::thread;
 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.
  */
@@ -90,9 +112,9 @@ Server::process (shared_ptr<Socket> socket, struct timeval& after_read, struct t
                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);
        
@@ -116,10 +138,14 @@ Server::worker_thread ()
 {
        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 ();
                
@@ -186,39 +212,18 @@ Server::run (int num_threads)
 
        _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);
 
@@ -228,7 +233,7 @@ try
                boost::bind (&Server::broadcast_received, this)
                );
 
-       io_service.run ();
+       _broadcast.io_service.run ();
 }
 catch (...)
 {
@@ -265,3 +270,35 @@ Server::broadcast_received ()
                _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 ();
+}
+       
index b925031eb2df667cfff90f64a71eacdf3ecb6d7f..e2e1d46eca0f34f3748f708787b34437f1dc8402 100644 (file)
@@ -90,6 +90,7 @@ class Server : public ExceptionStore, public boost::noncopyable
 {
 public:
        Server (boost::shared_ptr<Log> log, bool verbose);
+       ~Server ();
 
        void run (int num_threads);
 
@@ -98,6 +99,10 @@ private:
        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;
@@ -107,6 +112,9 @@ private:
        boost::shared_ptr<Log> _log;
        bool _verbose;
 
+       boost::asio::io_service _io_service;
+       boost::asio::ip::tcp::acceptor _acceptor;
+
        struct Broadcast {
 
                Broadcast ()
@@ -118,6 +126,7 @@ private:
                boost::asio::ip::udp::socket* socket;
                char buffer[64];
                boost::asio::ip::udp::endpoint send_endpoint;
+               boost::asio::io_service io_service;
                
        } _broadcast;
 };
index a082f3babf939d6a0e240433d97db1bf43e0feb4..6371035910f6baba9e253790f356e164362f642a 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 #include <libcxml/cxml.h>
-#include <libdcp/raw_convert.h>
+#include <dcp/raw_convert.h>
 #include "server_finder.h"
 #include "exceptions.h"
 #include "util.h"
@@ -32,7 +32,7 @@ using std::vector;
 using std::cout;
 using boost::shared_ptr;
 using boost::scoped_array;
-using libdcp::raw_convert;
+using dcp::raw_convert;
 
 ServerFinder* ServerFinder::_instance = 0;
 
diff --git a/src/lib/single_stream_audio_content.cc b/src/lib/single_stream_audio_content.cc
new file mode 100644 (file)
index 0000000..5215976
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+    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);
+}
diff --git a/src/lib/single_stream_audio_content.h b/src/lib/single_stream_audio_content.h
new file mode 100644 (file)
index 0000000..fcfaf14
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    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
index ba3bd0a772d489714a01bebd7c90d56ab1b063e6..1a17976657c42759a10566aaec00559de94e1ba9 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 #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
 {
@@ -81,8 +84,8 @@ SndfileContent::information () 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 ();
@@ -102,69 +105,15 @@ SndfileContent::examine (shared_ptr<Job> job)
 {
        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);
-}
index a32043c5c63660caf3ec6bcf7aba59ac37158f66..75c723518ea61ebf48ed6e06163b2894e71fe53b 100644 (file)
 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
index f66a7c7dc9dd363f649f2c71632a9a5057d78949..602014d5883d2dba1381588bb44a7036debc9c96 100644 (file)
@@ -25,7 +25,6 @@
 #include <sndfile.h>
 #include "sndfile_content.h"
 #include "sndfile_decoder.h"
-#include "film.h"
 #include "exceptions.h"
 #include "audio_buffers.h"
 
@@ -37,9 +36,8 @@ using std::min;
 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)
 {
@@ -66,13 +64,17 @@ SndfileDecoder::~SndfileDecoder ()
        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 ();
@@ -101,9 +103,11 @@ SndfileDecoder::pass ()
        }
                
        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
@@ -112,10 +116,10 @@ SndfileDecoder::audio_channels () const
        return _info.channels;
 }
 
-AudioContent::Frame
+ContentTime
 SndfileDecoder::audio_length () const
 {
-       return _info.frames;
+       return ContentTime::from_frames (_info.frames, audio_frame_rate ());
 }
 
 int
@@ -124,8 +128,11 @@ SndfileDecoder::audio_frame_rate () const
        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;
 }
index 77fa6d17734da4096757dae504a759c9925e0e8b..41d5faf082ec29c75b2c2e1059f528c59a26e0ff 100644 (file)
 #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;
 };
diff --git a/src/lib/sound_processor.cc b/src/lib/sound_processor.cc
deleted file mode 100644 (file)
index 9be6621..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/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];
-}
diff --git a/src/lib/sound_processor.h b/src/lib/sound_processor.h
deleted file mode 100644 (file)
index 8f26522..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/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
diff --git a/src/lib/subrip.cc b/src/lib/subrip.cc
new file mode 100644 (file)
index 0000000..8a74236
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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 ());
+}
diff --git a/src/lib/subrip.h b/src/lib/subrip.h
new file mode 100644 (file)
index 0000000..14bc360
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    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
diff --git a/src/lib/subrip_content.cc b/src/lib/subrip_content.cc
new file mode 100644 (file)
index 0000000..14cb50b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+    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;
+}
diff --git a/src/lib/subrip_content.h b/src/lib/subrip_content.h
new file mode 100644 (file)
index 0000000..d2dcdee
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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;
+};
diff --git a/src/lib/subrip_decoder.cc b/src/lib/subrip_decoder.cc
new file mode 100644 (file)
index 0000000..411e754
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+    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;
+}
diff --git a/src/lib/subrip_decoder.h b/src/lib/subrip_decoder.h
new file mode 100644 (file)
index 0000000..ad9d04e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    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
diff --git a/src/lib/subrip_subtitle.h b/src/lib/subrip_subtitle.h
new file mode 100644 (file)
index 0000000..646fc1f
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#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
diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h
deleted file mode 100644 (file)
index c74f5c1..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-    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;
-};
index 3702eef4152c484ee3b044804ebdc86aabdaae91..5b370847ba08d2f2f156301f5864982f7820efae 100644 (file)
 */
 
 #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)
@@ -46,13 +61,20 @@ SubtitleContent::SubtitleContent (shared_ptr<const Film> f, boost::filesystem::p
 
 }
 
-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");
@@ -77,6 +99,10 @@ SubtitleContent::SubtitleContent (shared_ptr<const Film> f, vector<shared_ptr<Co
        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."));
                }
@@ -94,6 +120,7 @@ SubtitleContent::SubtitleContent (shared_ptr<const Film> f, vector<shared_ptr<Co
                }
        }
 
+       _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 ();
@@ -103,12 +130,23 @@ SubtitleContent::SubtitleContent (shared_ptr<const Film> f, vector<shared_ptr<Co
 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)
 {
@@ -148,3 +186,16 @@ SubtitleContent::set_subtitle_y_scale (double s)
        }
        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 ();
+}
index 329368e4432f92ddd698e4f294d8128474bbbbf2..c3c25232f4893ec59776d42c20f483ec5f72acc1 100644 (file)
@@ -29,22 +29,39 @@ public:
        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;
@@ -64,10 +81,11 @@ public:
                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.
         */
index c06f3d718a812e9403266e3ccf91f5c494fa69b8..e1485e221d70807109e3803b0493c9d8524fe974 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 #include <boost/shared_ptr.hpp>
 #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 ();
 }
index eeeadbd3f65fdda2f9ee46b90c9745f2ce713f8f..142cfa42b766b0813fb1570d530268d886beee2c 100644 (file)
 
 */
 
-#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
index 831c74b3b244d9a78b4369eb3d089852c74c9174..23a46d06dddcb7875a9247911d387028a0da802e 100644 (file)
@@ -37,6 +37,7 @@
 using std::string;
 using std::fixed;
 using std::setprecision;
+using std::cout;
 using boost::shared_ptr;
 
 /** @param s Film to use.
@@ -100,6 +101,7 @@ TranscodeJob::status () const
        return s.str ();
 }
 
+/** @return Approximate remaining time in seconds */
 int
 TranscodeJob::remaining_time () const
 {
@@ -117,6 +119,5 @@ 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;
 }
index b11ce8be599aaab0d8c3aa63f210199066fdd279..9d8eebe255d63d16dac547ab5851e8388c601c0c 100644 (file)
 #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
index d7736d4e8e56dd3a1cdf7440e6a5872bb7a59db1..ed0a6b1b561bb45a628080819d0866944e8a37f8 100644 (file)
@@ -42,7 +42,9 @@ public:
        }
 
 private:
+       boost::shared_ptr<const Film> _film;
        boost::shared_ptr<Player> _player;
+       boost::shared_ptr<Writer> _writer;
        boost::shared_ptr<Encoder> _encoder;
        bool _finishing;
 };
index 83bbf41e45c19125e779b4be18f6a48e9b2a5cd0..d052b2a9a71dd3c458bc9126785a15af263e8e6c 100644 (file)
 
 #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)
 {
index 4eb3d927e5090c8af08ef0c3cdc1167e9fe88929..9a6a30b861d45344325507186f5c08b2b200ea42 100644 (file)
@@ -23,7 +23,9 @@
 #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;
@@ -46,31 +48,29 @@ namespace xmlpp {
  */
 #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
@@ -120,7 +120,7 @@ struct Crop
        /** 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;
 
index 7bec061e9292e15d044c360e8c13d60dc6a0faab..c50022091fbe3ab6abb0169e86fa00e7444c4cb3 100644 (file)
@@ -21,7 +21,7 @@
 #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"
@@ -32,8 +32,9 @@
 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
@@ -42,6 +43,9 @@ write_callback_wrapper (void* data, size_t size, size_t nmemb, void* user)
        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)
@@ -73,6 +77,7 @@ UpdateChecker::~UpdateChecker ()
        delete[] _buffer;
 }
 
+/** Start running the update check */
 void
 UpdateChecker::run ()
 {
@@ -85,6 +90,7 @@ void
 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);
@@ -94,12 +100,16 @@ UpdateChecker::thread ()
                
                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);
index e96ccec3119903a76af0c26ee1a0a1af41ba49d7..c86adb8736adad074625f8b7a73dd47ae1eb666e 100644 (file)
 
 */
 
+/** @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:
@@ -32,28 +37,31 @@ 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;
diff --git a/src/lib/upmixer_a.cc b/src/lib/upmixer_a.cc
new file mode 100644 (file)
index 0000000..dce08fe
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+    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 ();
+}
diff --git a/src/lib/upmixer_a.h b/src/lib/upmixer_a.h
new file mode 100644 (file)
index 0000000..32e3f5f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+    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;
+};
index 2e9ca66b22ccf597004b6b89892406c0d4d5da3e..7a0f1a17aec65008bf2eea3585d0bebb5cf283c9 100644 (file)
@@ -43,6 +43,7 @@
 #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>
@@ -67,13 +67,15 @@ extern "C" {
 #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"
@@ -104,8 +106,8 @@ using std::set_terminate;
 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;
@@ -270,24 +272,6 @@ ffmpeg_version_to_string (int v)
        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)
 {
@@ -376,14 +360,16 @@ dcpomatic_setup ()
 
        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 ();
 }
@@ -406,7 +392,7 @@ mo_path ()
 boost::filesystem::path
 mo_path ()
 {
-       return "DCP-o-matic.app/Contents/Resources";
+       return "DCP-o-matic 2.app/Contents/Resources";
 }
 #endif
 
@@ -491,7 +477,10 @@ md5_digest (vector<boost::filesystem::path> files, shared_ptr<Job> job)
 
                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;
 
@@ -661,6 +650,17 @@ stride_round_up (int c, int const * stride, int 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
@@ -765,17 +765,6 @@ ensure_ui_thread ()
        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)
 {
@@ -826,59 +815,6 @@ tidy_for_filename (string f)
        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)
 {
@@ -925,14 +861,14 @@ 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 *
@@ -963,12 +899,34 @@ divide_with_round (int64_t a, int64_t b)
        }
 }
 
+/** 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 ();       
@@ -976,12 +934,16 @@ ScopedTemporary::~ScopedTemporary ()
        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)
 {
@@ -989,6 +951,7 @@ ScopedTemporary::open (char const * params)
        return _open;
 }
 
+/** Close the file */
 void
 ScopedTemporary::close ()
 {
@@ -997,3 +960,16 @@ 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;
+}
index 675c8d03e6304a42a56f4911e88eb4bdeeaa352d..724e8937ca3e4fb3b7b22c36bd2e6650efaf9b7b 100644 (file)
@@ -31,7 +31,7 @@
 #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>
@@ -47,11 +47,8 @@ extern "C" {
 #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);
@@ -69,12 +66,12 @@ extern bool valid_image_file (boost::filesystem::path);
 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);
@@ -83,6 +80,7 @@ extern int get_optional_int (std::multimap<std::string, std::string> const & kv,
 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
@@ -125,16 +123,20 @@ private:
 
 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 ();
index 13f2cf51676a9d10bb3b6acce245457d1dd2db66..ba656e4c2410330da76bfb4a193facc646137c8d 100644 (file)
@@ -19,8 +19,8 @@
 
 #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;
@@ -51,12 +56,11 @@ using std::max;
 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 ())
@@ -64,10 +68,9 @@ VideoContent::VideoContent (shared_ptr<const Film> f)
        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 ())
@@ -78,7 +81,6 @@ VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Fram
 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 ())
@@ -86,14 +88,20 @@ VideoContent::VideoContent (shared_ptr<const Film> f, boost::filesystem::path p)
        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");
@@ -110,6 +118,10 @@ VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Nod
        }
        
        _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)
@@ -146,55 +158,67 @@ VideoContent::VideoContent (shared_ptr<const Film> f, vector<shared_ptr<Content>
                        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);
 }
 
 
@@ -325,14 +349,17 @@ VideoContent::technical_summary () const
 {
        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:
@@ -340,9 +367,9 @@ VideoContent::video_size_after_3d_split () const
        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);
@@ -359,29 +386,44 @@ VideoContent::set_colour_conversion (ColourConversion c)
        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
@@ -425,3 +467,19 @@ VideoContent::set_video_frame_rate (float r)
        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> ();
+}
index 3a7b44306ca80890b10deca55c496127860ff352..e88fb022720d0c91e13d875b36ff6589c1eec0ce 100644 (file)
@@ -36,6 +36,8 @@ public:
        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
@@ -44,9 +46,9 @@ public:
        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;
@@ -54,21 +56,21 @@ public:
        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;
        }
@@ -78,11 +80,6 @@ public:
                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);
 
@@ -93,6 +90,9 @@ public:
 
        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);
@@ -135,10 +135,22 @@ public:
                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 ();
@@ -146,23 +158,24 @@ public:
 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
index e603582b89e7c5faef2d7690160786e8855b03c4..418c46eecd121fbf1d4019f27ae1f752758d5105 100644 (file)
@@ -123,24 +123,24 @@ VideoContentScale::from_id (string id)
 /** @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
                );
index 87dd2f1fafec05b49706c71f226d58cabadc495e..6b718d5741e09380d47abaff3e42268937178471 100644 (file)
@@ -22,7 +22,7 @@
 
 #include <vector>
 #include <boost/shared_ptr.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
 
 namespace cxml {
        class Node;
@@ -43,7 +43,7 @@ public:
        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;
index 5867ac9257aacc31f8c712039c093f531e636771..64c66ea55caf8bc64adb863aac01966e314820d2 100644 (file)
 
 #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 ();
 }
 
index 42add42aacc547c3be5e2bced87882dfdacecd0b..f5c3cd743ba43bf3df96d4a1706507a33a0e3a95 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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
index 039c494b52fe98f630798729207b65b26b4d46f3..87e9a04288a20fb13018391ad4c124a58b757e05 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <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;
 };
index 5af1aea1e1d8f15ea25c27017f60e9817cb55c9e..6262525c85d3a5777ead7a9dda416d7752622d52 100644 (file)
 
 #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"
@@ -57,6 +64,7 @@ using std::list;
 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;
 
@@ -71,7 +79,6 @@ Writer::Writer (shared_ptr<const Film> f, weak_ptr<Job> j)
        , _last_written_eyes (EYES_RIGHT)
        , _full_written (0)
        , _fake_written (0)
-       , _repeat_written (0)
        , _pushed_to_disk (0)
 {
        /* Remove any old DCP */
@@ -89,36 +96,39 @@ Writer::Writer (shared_ptr<const Film> f, weak_ptr<Job> j)
        */
 
        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));
@@ -175,7 +185,7 @@ Writer::fake_write (int frame, Eyes eyes)
        }
        
        FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
-       libdcp::FrameInfo info (ifi);
+       dcp::FrameInfo info (ifi);
        fclose (ifi);
        
        QueueItem qi;
@@ -200,8 +210,8 @@ Writer::fake_write (int frame, Eyes eyes)
 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());
        }
 }
 
@@ -277,7 +287,7 @@ try
                                        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;
@@ -285,39 +295,27 @@ try
                        }
                        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);
                        }
                }
 
@@ -392,15 +390,11 @@ Writer::finish ()
        
        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();
@@ -421,14 +415,11 @@ Writer::finish ()
                }
        }
 
-       /* 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 ();
@@ -439,80 +430,86 @@ Writer::finish ()
                                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
@@ -525,7 +522,7 @@ Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
                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);
@@ -610,6 +607,24 @@ Writer::can_fake_write (int frame) const
        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)
 {
index c0699ad4407158294a3c6d48eccdc8739efdca36..66fe98ec734c914d3de27eeb4b4ccde15e6309b7 100644 (file)
 
 */
 
+/** @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
@@ -51,8 +57,6 @@ public:
                    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 */
@@ -67,6 +71,17 @@ public:
 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:
@@ -78,6 +93,7 @@ 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 ();
 
@@ -123,15 +139,14 @@ private:
        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;
 };
index 6c1da1772e696276161de2d0f9d7385bd68bf50d..d62c22bae9738c3963559aa6796dca45a976f057 100644 (file)
@@ -7,26 +7,39 @@ sources = """
           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
@@ -37,16 +50,21 @@ sources = """
           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
@@ -54,10 +72,12 @@ sources = """
           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
@@ -66,6 +86,7 @@ sources = """
           types.cc
           ui_signaller.cc
           update.cc
+          upmixer_a.cc
           util.cc
           video_content.cc
           video_content_scale.cc
@@ -79,13 +100,13 @@ def build(bld):
     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:
@@ -99,9 +120,9 @@ def build(bld):
     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')
index fa89a4871e77d071940bacb8494b29dda26311d4..d08a11ea9edd84d5e1ae34f3dbdaaf779c69701a 100644 (file)
@@ -30,7 +30,7 @@
 #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"
@@ -45,6 +45,7 @@
 #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"
@@ -72,8 +73,6 @@ using std::exception;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 
-// #define DCPOMATIC_WINDOWS_CONSOLE 1
-
 class FilmChangedDialog
 {
 public:
@@ -145,20 +144,24 @@ 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;
@@ -188,12 +191,6 @@ public:
 
                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.
                */
@@ -219,6 +216,12 @@ public:
                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)
@@ -410,7 +413,7 @@ private:
                                        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 ());
@@ -423,7 +426,7 @@ private:
 
        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 ();
                }
@@ -431,7 +434,7 @@ private:
 
        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 ();
                }
@@ -542,7 +545,7 @@ private:
                }
                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) {
                        
@@ -707,6 +710,9 @@ static const wxCmdLineEntryDesc command_line_description[] = {
        { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 }
 };
 
+/** @class App
+ *  @brief The magic App class for wxWidgets.
+ */
 class App : public wxApp
 {
        bool OnInit ()
index 23ca938d4e49c7ec77d5bb0dd3b185908f891884..f55a425ce6a521d48719bee761f74a294023d878 100644 (file)
@@ -20,7 +20,7 @@
 #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"
index 26de1c71f8f2485bf273135f110bca61dc9a1bb6..304f4f697ab6e8a031c44dd2a74c3b896f2c448a 100644 (file)
@@ -190,7 +190,7 @@ main (int argc, char* argv[])
                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));
                        }
                }
 
index 758060a08e7aea7e49ce27594c03ece111926d64..6257d60af3f636e2a25eaf3464eebafa993c5ee3 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 #include <getopt.h>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
 #include "lib/film.h"
 #include "lib/cinema.h"
 #include "lib/kdm.h"
@@ -41,8 +41,8 @@ help ()
        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"
@@ -111,7 +111,7 @@ int main (int argc, char* argv[])
        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];
        
@@ -171,13 +171,13 @@ int main (int argc, char* argv[])
                        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));
                        }
                }
        }
@@ -248,8 +248,8 @@ int main (int argc, char* argv[])
                        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";
@@ -273,12 +273,18 @@ int main (int argc, char* argv[])
 
                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";
                                }
index f35797954ae907ff237146c2480ecc3289574bff..b816460a3376daadea18467caf49fb5e1a20ffd9 100644 (file)
@@ -32,7 +32,7 @@
 #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"
index ba09e24da2b8f68eedd334c1116f68943e8829d4..ab8f2fca70f20d5fc6efeaf3254a869f5cb6167b 100644 (file)
@@ -18,7 +18,7 @@ msgstr ""
 "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 "
@@ -28,70 +28,70 @@ msgstr ""
 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"
 
@@ -99,7 +99,7 @@ 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 "
@@ -108,7 +108,7 @@ msgstr ""
 "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)."
@@ -116,38 +116,38 @@ msgstr ""
 "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"
 
@@ -155,48 +155,48 @@ 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 "
@@ -205,11 +205,11 @@ msgstr ""
 "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?"
@@ -217,17 +217,17 @@ msgstr ""
 "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."
@@ -236,11 +236,11 @@ msgstr ""
 "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."
index e37d375e032a8258f610f9144625283c2a6225b9..c4074fb12b69434c066b67a35969dbea0aaa91ac 100644 (file)
@@ -17,7 +17,7 @@ msgstr ""
 "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."
 
@@ -25,70 +25,70 @@ 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"
 
@@ -96,50 +96,50 @@ 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"
 
@@ -147,51 +147,51 @@ 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 "
@@ -201,26 +201,26 @@ msgstr ""
 "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."
@@ -229,11 +229,11 @@ msgstr ""
 "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."
index 75eed562376a96d764bba969fe40d27cf3800bdf..6d426d4327ee16bfa39510aee03d0e41e77d250b 100644 (file)
@@ -17,7 +17,7 @@ msgstr ""
 "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."
@@ -26,70 +26,70 @@ msgstr ""
 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"
 
@@ -97,7 +97,7 @@ 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 "
@@ -106,7 +106,7 @@ msgstr ""
 "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)."
@@ -114,37 +114,37 @@ msgstr ""
 "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"
 
@@ -152,49 +152,49 @@ 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 "
@@ -203,26 +203,26 @@ msgstr ""
 "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."
@@ -231,11 +231,11 @@ msgstr ""
 "ê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."
index 07b348c13e228499a0391cfca6ea66c8ec3c7771..7f7baddaf08eabb73d1c594a5a7e523f41b2a8e8 100644 (file)
@@ -17,7 +17,7 @@ msgstr ""
 "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"
 
@@ -25,67 +25,67 @@ 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"
 
@@ -93,50 +93,50 @@ 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"
 
@@ -144,48 +144,48 @@ 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 "
@@ -194,35 +194,35 @@ msgstr ""
 "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."
index c77c54b950e79bdf2a61ab720079d1cc1c225b6a..02eafd9cc2aceccf4c85b53e4eadcfe7e8697340 100644 (file)
@@ -18,7 +18,7 @@ msgstr ""
 "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 "
@@ -28,27 +28,27 @@ msgstr ""
 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"
 
@@ -56,39 +56,39 @@ 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"
 
@@ -96,7 +96,7 @@ 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 "
@@ -105,7 +105,7 @@ msgstr ""
 "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)."
@@ -113,37 +113,37 @@ msgstr ""
 "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"
 
@@ -151,48 +151,48 @@ 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 "
@@ -201,26 +201,26 @@ msgstr ""
 "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."
@@ -228,11 +228,11 @@ msgstr ""
 "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."
index fbe64370fc87f77a821120c058740b26dd03b70a..9756a16c73e33dceb7d7ee597165fe188149e716 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -17,7 +17,7 @@ msgstr ""
 "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."
 
@@ -25,70 +25,70 @@ 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"
 
@@ -96,50 +96,50 @@ 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"
 
@@ -147,48 +147,48 @@ 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 "
@@ -197,11 +197,11 @@ msgstr ""
 "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?"
@@ -209,25 +209,25 @@ msgstr ""
 "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."
index a5d31fc086444e70c3bc07b11235df356ea04d3e..9223efb3eb6642592e1fa36af82a0a266bca774f 100644 (file)
 #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;
@@ -45,18 +46,18 @@ using boost::shared_ptr;
 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;
@@ -144,12 +145,10 @@ main (int argc, char* argv[])
                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";
index ac270af704026fe3763b1b08a2657e82ed704b30..3811bf7600ab19f4bc1bb1c5b2f45a15a633b996 100644 (file)
@@ -11,28 +11,28 @@ def configure(conf):
 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')
index 49e24531842564167377b85da5c48fd0055f7c85..1bbcaba799c65518a6bd65e19b714575c52d034d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/wx/about_dialog.cc
+ *  @brief The "about DCP-o-matic" dialogue box.
+ */
+
 #include <wx/notebook.h>
 #include <wx/hyperlink.h>
 #include "lib/version.h"
@@ -221,6 +225,10 @@ AboutDialog::AboutDialog (wxWindow* parent)
        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)
 {
index a78abb93e267fe1c1c7c60187a4a3edcef1b44af..4901cf9908331d5d46b26cb07e24cf711a03f4dd 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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:
@@ -29,6 +36,6 @@ public:
 private:
        void add_section (wxString, wxArrayString);
 
-       wxNotebook* _notebook;
+       wxNotebook* _notebook; ///< notebook used to keep each list of names for the credits
 };
 
index 6c1508aeebae25b197b4273ace9aec1b7c48395e..8e92400bdb956ffd7b58205dccf5626bf9015be7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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;
@@ -53,6 +58,9 @@ public:
        }
 };
 
+/** @class ValueRenderer
+ *  @brief wxGridCellRenderer for a gain value.
+ */
 class ValueRenderer : public wxGridCellRenderer
 {
 public:
@@ -155,7 +163,7 @@ AudioMappingView::left_click (wxGridEvent& ev)
                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);
@@ -181,28 +189,28 @@ AudioMappingView::right_click (wxGridEvent& ev)
 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) {
@@ -239,7 +247,7 @@ AudioMappingView::update_cells ()
                _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)))));
                }
        }
 
@@ -343,7 +351,7 @@ AudioMappingView::mouse_moved (wxMouseEvent& ev)
        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) {
index 98375eb9e3c071b6b83d23fe332e2a7988fb8dcf..7ed6994633c111a7813951ae92c78dcca3023410 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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>
index 118db7880cbeb42ca05bd4118f246eccd724ac1e..b7d6979d35f20e0988f0b3135c5f0e0acc6fac5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <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);
@@ -80,8 +83,13 @@ AudioPanel::AudioPanel (FilmEditor* e)
 
        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);
@@ -101,9 +109,10 @@ AudioPanel::AudioPanel (FilmEditor* e)
        _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));
 }
@@ -114,7 +123,7 @@ AudioPanel::film_changed (Film::Property property)
 {
        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:
@@ -128,7 +137,7 @@ AudioPanel::film_changed (Film::Property property)
 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) {
@@ -142,7 +151,6 @@ AudioPanel::film_content_changed (int property)
        } 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) {
@@ -155,9 +163,14 @@ AudioPanel::film_content_changed (int property)
                        
                        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"));
+               }
        }
 }
 
@@ -173,7 +186,7 @@ AudioPanel::gain_calculate_button_clicked ()
        }
        
        _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 ()
                        )
@@ -195,7 +208,7 @@ AudioPanel::show_clicked ()
                _audio_dialog = 0;
        }
 
-       AudioContentList ac = _editor->selected_audio_content ();
+       AudioContentList ac = _parent->selected_audio ();
        if (ac.size() != 1) {
                return;
        }
@@ -208,7 +221,7 @@ AudioPanel::show_clicked ()
 void
 AudioPanel::stream_changed ()
 {
-       FFmpegContentList fc = _editor->selected_ffmpeg_content ();
+       FFmpegContentList fc = _parent->selected_ffmpeg ();
        if (fc.size() != 1) {
                return;
        }
@@ -229,60 +242,48 @@ AudioPanel::stream_changed ()
        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);
        }
@@ -291,7 +292,7 @@ AudioPanel::mapping_changed (AudioMapping 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 ());
@@ -300,12 +301,38 @@ AudioPanel::content_selection_changed ()
        _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 ())));
+               }
+       }
+}
index fa18415bdb345fccc09e78a785077e2fa2ffe382..b0218575a7f0f12ca6c5a6b59f3107626e79dbe7 100644 (file)
@@ -18,7 +18,7 @@
 */
 
 #include "lib/audio_mapping.h"
-#include "film_editor_panel.h"
+#include "content_sub_panel.h"
 #include "content_widget.h"
 
 class wxSpinCtrlDouble;
@@ -28,10 +28,10 @@ class wxStaticText;
 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);
@@ -42,14 +42,16 @@ private:
        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;
index 816602355f8c26efed3bd0eb1631385bda4776b2..8d8f44b4ecc6fa05366c05b6366ef7a925a11e30 100644 (file)
 #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"
@@ -43,6 +49,7 @@
 #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;
@@ -112,7 +119,6 @@ public:
                _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);
@@ -530,6 +536,339 @@ private:
        }
 };
 
+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:
@@ -770,6 +1109,9 @@ private:
        wxButton* _reset_kdm_email;
 };
 
+/** @class AdvancedPage
+ *  @brief Advanced page of the preferences dialog.
+ */
 class AdvancedPage : public wxStockPreferencesPage, public Page
 {
 public:
@@ -821,6 +1163,12 @@ 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);
@@ -836,6 +1184,10 @@ public:
                _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;
        }
@@ -869,6 +1221,13 @@ private:
                }
                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;
@@ -876,6 +1235,9 @@ private:
        wxCheckBox* _log_warning;
        wxCheckBox* _log_error;
        wxCheckBox* _log_timing;
+#ifdef DCPOMATIC_WINDOWS       
+       wxCheckBox* _win32_console;
+#endif 
 };
        
 wxPreferencesEditor*
@@ -899,6 +1261,7 @@ create_config_dialog ()
        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));
index b91c82ab154c99e4bb2860bb16e7272888c50992..3e3c462b2e6981267283186ae2ecc0566db72210 100644 (file)
@@ -26,6 +26,7 @@
 #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"
@@ -40,6 +41,8 @@ enum {
        ID_repeat = 1,
        ID_join,
        ID_find_missing,
+       ID_re_examine,
+       ID_kdm,
        ID_remove
 };
 
@@ -50,12 +53,16 @@ ContentMenu::ContentMenu (wxWindow* p)
        _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);
 }
 
@@ -81,6 +88,15 @@ ContentMenu::popup (weak_ptr<Film> f, ContentList c, wxPoint p)
        _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);
 }
@@ -206,6 +222,19 @@ ContentMenu::find_missing ()
        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)
 {
@@ -226,3 +255,22 @@ ContentMenu::maybe_found_missing (weak_ptr<Job> j, weak_ptr<Content> oc, weak_pt
 
        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 ();
+}
index a9f9093c6ab5bb3c94d48244480bc1859495bbf2..77cf29a3057400d523ff3449a2ce96cb2f8d8679 100644 (file)
@@ -30,15 +30,17 @@ class Film;
 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>);
        
@@ -50,6 +52,8 @@ private:
        wxMenuItem* _repeat;
        wxMenuItem* _join;
        wxMenuItem* _find_missing;
+       wxMenuItem* _re_examine;
+       wxMenuItem* _kdm;
        wxMenuItem* _remove;
 };
 
diff --git a/src/wx/content_panel.cc b/src/wx/content_panel.cc
new file mode 100644 (file)
index 0000000..b4b9f13
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+    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])));
+       }
+}
diff --git a/src/wx/content_panel.h b/src/wx/content_panel.h
new file mode 100644 (file)
index 0000000..ab19841
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+    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;
+};
diff --git a/src/wx/content_sub_panel.cc b/src/wx/content_sub_panel.cc
new file mode 100644 (file)
index 0000000..7ea17c1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+    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);
+}
+
diff --git a/src/wx/content_sub_panel.h b/src/wx/content_sub_panel.h
new file mode 100644 (file)
index 0000000..5a1b739
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    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
index ca94850065bf4f1e9e75a865a4b463435789bd24..b4d06286e46972de23b863460ec8aebceba57653 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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)
@@ -97,11 +103,12 @@ public:
        }
 
        /** 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 */
@@ -145,7 +152,7 @@ private:
 
                _sizer->Detach (_button);
                _button->Hide ();
-               _sizer->Add (_wrapped, _position);
+               _sizer->Add (_wrapped, _position, _span);
                _wrapped->Show ();
                _sizer->Layout ();
        }
@@ -159,7 +166,7 @@ private:
                _wrapped->Hide ();
                _sizer->Detach (_wrapped);
                _button->Show ();
-               _sizer->Add (_button, _position);
+               _sizer->Add (_button, _position, _span);
                _sizer->Layout ();
        }
 
@@ -181,6 +188,7 @@ private:
        T* _wrapped;
        wxGridBagSizer* _sizer;
        wxGBPosition _position;
+       wxGBSpan _span;
        wxButton* _button;
        List _content;
        int _property;
diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc
new file mode 100644 (file)
index 0000000..f042e5e
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+    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;
+}
diff --git a/src/wx/dcp_panel.h b/src/wx/dcp_panel.h
new file mode 100644 (file)
index 0000000..88a9c4c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+    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;
+};
index 5772f6391e7cc94dc9d898799ecdab2ab4f66edf..481a147415e854aa4e6e2d03d77c8a604ef96702 100644 (file)
@@ -43,7 +43,7 @@ public:
 
                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);
 
index e73b272674ff802399817fd6dd7e803b68e53b53..7f9461d940fb4ba6f4035707fc1f930c214c62f9 100644 (file)
 #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;
@@ -72,343 +73,26 @@ using boost::lexical_cast;
 /** @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.
@@ -423,97 +107,8 @@ FilmEditor::film_changed (Film::Property p)
                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
@@ -528,69 +123,8 @@ FilmEditor::film_content_changed (int property)
                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 */
@@ -605,6 +139,9 @@ FilmEditor::set_film (shared_ptr<Film> f)
        
        _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));
@@ -616,121 +153,16 @@ FilmEditor::set_film (shared_ptr<Film> f)
                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
@@ -738,356 +170,3 @@ FilmEditor::active_jobs_changed (bool a)
 {
        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])));
-       }
-}
index ba9ff6fa0cad78924f0b062fb7ab258135f5a9e5..25749fffaf10ec8d3120ee1f02a442fa31ecde11 100644 (file)
  */
 
 #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.
@@ -49,121 +41,30 @@ public:
        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;
 };
diff --git a/src/wx/film_editor_panel.cc b/src/wx/film_editor_panel.cc
deleted file mode 100644 (file)
index a637df1..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <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);
-}
-
diff --git a/src/wx/film_editor_panel.h b/src/wx/film_editor_panel.h
deleted file mode 100644 (file)
index e0514ba..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef 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
index 54cd3e77dc30014816f35b90457d0014ea045f16..a46983a6f9972b0bf41efbc28ef1a4d03372988e 100644 (file)
@@ -24,6 +24,7 @@
 #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"
 
@@ -51,18 +53,19 @@ using std::make_pair;
 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);
@@ -75,6 +78,8 @@ FilmViewer::FilmViewer (wxWindow* p)
 
        _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);
@@ -95,6 +100,7 @@ FilmViewer::FilmViewer (wxWindow* p)
 
        _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));
@@ -108,6 +114,8 @@ FilmViewer::FilmViewer (wxWindow* p)
        JobManager::instance()->ActiveJobsChanged.connect (
                bind (&FilmViewer::active_jobs_changed, this, _1)
                );
+
+       setup_sensitivity ();
 }
 
 void
@@ -122,7 +130,7 @@ FilmViewer::set_film (shared_ptr<Film> f)
        _frame.reset ();
        
        _slider->SetValue (0);
-       set_position_text (0);
+       set_position_text ();
        
        if (!_film) {
                return;
@@ -136,47 +144,73 @@ FilmViewer::set_film (shared_ptr<Film> f)
                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);
                }
@@ -212,22 +246,29 @@ FilmViewer::paint_panel ()
                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
@@ -235,8 +276,9 @@ FilmViewer::panel_sized (wxSizeEvent& ev)
 {
        _panel_size.width = ev.GetSize().GetWidth();
        _panel_size.height = ev.GetSize().GetHeight();
+
        calculate_sizes ();
-       fetch_current_frame_again ();
+       get (_position, _last_get_accurate);
 }
 
 void
@@ -254,17 +296,24 @@ FilmViewer::calculate_sizes ()
        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);
 }
 
@@ -289,20 +338,7 @@ FilmViewer::check_play_state ()
 }
 
 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");
@@ -312,9 +348,9 @@ FilmViewer::set_position_text (Time t)
                
        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);
@@ -325,35 +361,6 @@ FilmViewer::set_position_text (Time t)
        _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)
 {
@@ -377,31 +384,18 @@ 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
@@ -412,5 +406,27 @@ FilmViewer::player_changed (bool frequent)
        }
 
        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 ();
+       }
 }
index 337b68446cf64bbd2b935ea6bcbda5cc11a12d77..e502c6f45cd2922e3d9fe90c32fe7553c67d83a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -28,23 +28,10 @@ class wxToggleButton;
 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
 {
@@ -59,22 +46,24 @@ private:
        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;
@@ -84,10 +73,20 @@ private:
        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;
 };
index ebecd234c871894320c657edc996046eeabc8701..4334fd446ce99ae7dad7665fc4dc471072f2903c 100644 (file)
@@ -161,10 +161,10 @@ KDMDialog::KDMDialog (wxWindow* parent, boost::shared_ptr<const Film> film)
 
        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);
@@ -490,10 +490,10 @@ KDMDialog::write_to () const
        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
index 13e9196ea055180a4ee29e6b664338b2d94b1a33..0fc95db8443dee447023e7170b3ee964c0daef85 100644 (file)
@@ -48,7 +48,7 @@ public:
        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>);
diff --git a/src/wx/make_signer_chain_dialog.cc b/src/wx/make_signer_chain_dialog.cc
new file mode 100644 (file)
index 0000000..8736f24
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+    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));
+}
diff --git a/src/wx/make_signer_chain_dialog.h b/src/wx/make_signer_chain_dialog.h
new file mode 100644 (file)
index 0000000..fc6391a
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+    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;
+};
+       
index c488cab4b12ee2a34d763fb7fe7334fee67b9b98..d7353e9144857dcf7a86933968607ae543bab77c 100644 (file)
@@ -18,26 +18,26 @@ msgstr ""
 "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"
 
@@ -45,11 +45,7 @@ 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"
 
@@ -57,35 +53,35 @@ 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"
 
@@ -93,7 +89,7 @@ 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"
 
@@ -101,19 +97,24 @@ 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..."
 
@@ -133,15 +134,15 @@ msgid ""
 "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"
 
@@ -149,18 +150,14 @@ 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 "
@@ -169,7 +166,7 @@ msgstr ""
 "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"
 
@@ -186,23 +183,27 @@ msgstr "Unten beschneiden"
 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"
 
@@ -218,7 +219,7 @@ msgstr "CPL ID"
 msgid "CPL annotation text"
 msgstr "CPL annotation text"
 
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
 msgid "Calculate..."
 msgstr "Berechne..."
 
@@ -230,11 +231,19 @@ msgstr "Abbrechen"
 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"
@@ -243,27 +252,27 @@ 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"
 
@@ -271,32 +280,32 @@ 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"
@@ -305,7 +314,7 @@ 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"
@@ -318,21 +327,26 @@ msgstr ""
 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"
@@ -341,20 +355,20 @@ 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"
 
@@ -362,7 +376,7 @@ 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"
 
@@ -371,51 +385,51 @@ 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..."
 
@@ -437,7 +451,7 @@ msgstr "Doremi"
 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"
 
@@ -462,8 +476,8 @@ msgstr "Kino bearbeiten..."
 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..."
@@ -476,18 +490,34 @@ msgstr "KDM Empfänger Email Adresse"
 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
@@ -502,15 +532,15 @@ msgstr "Projekt Eigenschaften"
 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"
 
@@ -522,7 +552,7 @@ msgstr "Bilder"
 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 "
@@ -532,11 +562,11 @@ msgstr ""
 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"
 
@@ -544,7 +574,7 @@ 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 (+/-)"
 
@@ -557,15 +587,15 @@ msgstr "Fader Rechner"
 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"
 
@@ -581,19 +611,15 @@ msgstr "Host"
 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"
 
@@ -605,19 +631,27 @@ msgstr "ISDCF 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"
 
@@ -629,23 +663,35 @@ msgstr ""
 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"
 
@@ -657,27 +703,33 @@ msgstr "Linearisiere Eingangs Gamma für niedrige Werte"
 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"
 
@@ -685,6 +737,11 @@ 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)"
@@ -693,16 +750,16 @@ 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"
 
@@ -710,8 +767,12 @@ 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"
@@ -724,27 +785,45 @@ msgstr "Neues Projekt"
 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"
@@ -757,12 +836,12 @@ msgstr "Ziel Gamma"
 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"
 
@@ -774,7 +853,7 @@ msgstr "Pause"
 msgid "Peak"
 msgstr "Spitze"
 
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
 msgid "Play"
 msgstr "Abspielen"
 
@@ -794,7 +873,19 @@ msgstr "Startposition"
 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"
 
@@ -806,16 +897,25 @@ msgstr "RMS"
 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"
 
@@ -835,15 +935,15 @@ msgstr "Wiederholen"
 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"
 
@@ -851,7 +951,7 @@ 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"
 
@@ -859,24 +959,32 @@ 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"
 
@@ -888,10 +996,20 @@ msgstr "Saal"
 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"
@@ -912,7 +1030,7 @@ msgstr "Server Hersteller"
 msgid "Server serial number"
 msgstr "Server Seriennummer"
 
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
 msgid "Servers"
 msgstr "Encoding Server"
 
@@ -920,15 +1038,15 @@ 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"
 
@@ -936,7 +1054,7 @@ 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"
 
@@ -944,11 +1062,15 @@ 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"
 
@@ -956,27 +1078,32 @@ 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"
 
@@ -996,7 +1123,7 @@ msgstr "Test Version"
 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 "
@@ -1010,7 +1137,7 @@ msgstr ""
 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."
 
@@ -1022,19 +1149,23 @@ msgstr "Dies ist keine gültige CPL Datei"
 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..."
 
@@ -1042,11 +1173,11 @@ 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"
 
@@ -1058,7 +1189,8 @@ msgstr "Schnitt vom Ende"
 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"
 
@@ -1074,7 +1206,7 @@ msgstr "Unbekannt"
 msgid "Until"
 msgstr "Bis"
 
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
 msgid "Up"
 msgstr "Nach oben"
 
@@ -1082,15 +1214,15 @@ 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"
 
@@ -1098,15 +1230,20 @@ 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"
 
@@ -1114,36 +1251,36 @@ 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"
@@ -1183,43 +1320,62 @@ msgstr ""
 "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)"
 
index 88754e0a65e8770e718bebe3eca05aae93885efc..0191fc503fc9a800403257eb0b16c79bfebfe695 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 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"
@@ -17,26 +17,26 @@ msgstr ""
 "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 ""
 
@@ -44,11 +44,7 @@ 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"
 
@@ -57,36 +53,36 @@ 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"
 
@@ -94,7 +90,7 @@ 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"
 
@@ -102,19 +98,24 @@ 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..."
 
@@ -134,16 +135,16 @@ msgid ""
 "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"
 
@@ -151,24 +152,20 @@ 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"
@@ -186,23 +183,27 @@ msgstr "Recortar abajo"
 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"
@@ -220,7 +221,7 @@ msgstr ""
 msgid "CPL annotation text"
 msgstr ""
 
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
 msgid "Calculate..."
 msgstr "Calcular..."
 
@@ -232,11 +233,19 @@ msgstr "Cancelar"
 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 ""
@@ -245,27 +254,27 @@ 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"
 
@@ -273,33 +282,33 @@ 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"
@@ -308,7 +317,7 @@ 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"
@@ -321,21 +330,26 @@ msgstr ""
 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"
@@ -344,20 +358,20 @@ 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"
 
@@ -365,7 +379,7 @@ 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"
 
@@ -374,53 +388,53 @@ 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..."
 
@@ -442,7 +456,7 @@ msgstr "Doremi"
 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"
 
@@ -467,8 +481,8 @@ msgstr "Editar cine..."
 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..."
@@ -481,18 +495,34 @@ msgstr "Remitente para los emails de KDM"
 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
@@ -507,15 +537,15 @@ msgstr "Propiedades de la película"
 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"
 
@@ -527,7 +557,7 @@ msgstr "Fotogramas"
 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."
@@ -536,12 +566,12 @@ msgstr ""
 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"
 
@@ -549,7 +579,7 @@ msgstr "Completo"
 msgid "Full length"
 msgstr "Duración completa"
 
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
 msgid "Gain"
 msgstr ""
 
@@ -562,15 +592,15 @@ msgstr "Calculadora de ganancia"
 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"
 
@@ -586,19 +616,15 @@ msgstr "Host"
 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"
 
@@ -611,19 +637,27 @@ msgstr "Nombre DCI"
 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"
 
@@ -636,23 +670,35 @@ msgstr ""
 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"
 
@@ -664,27 +710,33 @@ msgstr "Hacer lineal la curva de gamma de entrada para valores bajos"
 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"
 
@@ -692,6 +744,11 @@ 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 ""
@@ -700,16 +757,16 @@ 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"
 
@@ -717,8 +774,12 @@ 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"
@@ -731,27 +792,46 @@ msgstr "Nueva película"
 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"
@@ -765,12 +845,12 @@ msgstr "Gamma de salida"
 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"
 
@@ -782,7 +862,7 @@ msgstr "Pausa"
 msgid "Peak"
 msgstr "Pico"
 
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
 msgid "Play"
 msgstr "Reproducir"
 
@@ -802,7 +882,19 @@ msgstr "Posición"
 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"
 
@@ -814,16 +906,25 @@ msgstr "RMS"
 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"
 
@@ -843,15 +944,15 @@ msgstr "Repetir"
 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"
 
@@ -859,7 +960,7 @@ 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."
 
@@ -867,24 +968,32 @@ 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"
 
@@ -898,10 +1007,20 @@ msgstr "Añadir pantalla..."
 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"
@@ -923,7 +1042,7 @@ msgstr "Número de serie del servidor"
 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"
 
@@ -931,15 +1050,15 @@ 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"
 
@@ -947,7 +1066,7 @@ msgstr "Firmado"
 msgid "Smoothing"
 msgstr "Suavizado"
 
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
 msgid "Snap"
 msgstr "Acoplar"
 
@@ -955,11 +1074,16 @@ 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"
@@ -968,15 +1092,20 @@ 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"
 
@@ -984,11 +1113,11 @@ 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"
 
@@ -1009,7 +1138,7 @@ msgstr "Versión en prueba"
 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 "
@@ -1022,7 +1151,7 @@ msgstr ""
 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."
 
@@ -1034,19 +1163,23 @@ msgstr ""
 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..."
 
@@ -1055,11 +1188,11 @@ 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"
 
@@ -1071,7 +1204,8 @@ msgstr "Recortar del final"
 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"
 
@@ -1087,7 +1221,7 @@ msgstr "Desconocido"
 msgid "Until"
 msgstr "Hasta"
 
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
 msgid "Up"
 msgstr "Subir"
 
@@ -1095,16 +1229,16 @@ 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"
 
@@ -1112,15 +1246,20 @@ 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"
 
@@ -1128,38 +1267,38 @@ 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"
@@ -1199,43 +1338,62 @@ msgstr ""
 "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"
 
@@ -1293,19 +1451,12 @@ msgstr "vídeo"
 #~ 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"
 
@@ -1319,9 +1470,6 @@ msgstr "vídeo"
 #~ msgid "DVD-o-matic Preferences"
 #~ msgstr "Preferencias DVD-o-matic"
 
-#~ msgid "End"
-#~ msgstr "Fin"
-
 #~ msgid "Film"
 #~ msgstr "Película"
 
index 75fc5754aea23c16d19b06a2a25a7758de33076a..46c4822d62be04e774b03a59a0e69521685bae8f 100644 (file)
@@ -17,27 +17,27 @@ msgstr ""
 "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"
 
@@ -45,11 +45,7 @@ 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"
 
@@ -57,35 +53,35 @@ 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"
 
@@ -93,7 +89,7 @@ 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"
 
@@ -101,19 +97,24 @@ 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..."
 
@@ -133,15 +134,15 @@ msgid ""
 "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"
 
@@ -149,18 +150,14 @@ 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 "
@@ -169,7 +166,7 @@ msgstr ""
 "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"
@@ -187,23 +184,27 @@ msgstr "Rogner en bas"
 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"
 
@@ -219,7 +220,7 @@ msgstr "Id du CPL"
 msgid "CPL annotation text"
 msgstr "Commentaire CPL"
 
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
 msgid "Calculate..."
 msgstr "Calcul..."
 
@@ -231,11 +232,19 @@ msgstr "Annuler"
 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"
@@ -244,27 +253,27 @@ 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"
 
@@ -272,32 +281,32 @@ 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"
@@ -306,7 +315,7 @@ 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"
@@ -319,21 +328,26 @@ msgstr ""
 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"
@@ -342,20 +356,20 @@ 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"
 
@@ -363,7 +377,7 @@ 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"
 
@@ -372,51 +386,51 @@ 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..."
 
@@ -438,7 +452,7 @@ msgstr "Doremi"
 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"
 
@@ -463,8 +477,8 @@ msgstr "Éditer le cinéma"
 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..."
@@ -477,18 +491,34 @@ msgstr "Adresse email pour l'envoi de KDM"
 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
@@ -503,15 +533,15 @@ msgstr "Propriétés du film"
 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"
 
@@ -523,7 +553,7 @@ msgstr "Images"
 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."
 
@@ -531,11 +561,11 @@ 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"
 
@@ -543,7 +573,7 @@ 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"
 
@@ -556,15 +586,15 @@ msgstr "Calculateur de 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"
 
@@ -580,19 +610,15 @@ msgstr "Hôtes"
 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"
 
@@ -604,19 +630,27 @@ msgstr "Nom ISDCF"
 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"
 
@@ -628,23 +662,35 @@ msgstr ""
 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"
 
@@ -656,27 +702,33 @@ msgstr "Courbe gamma d'entrée linéaire pour les bas niveaux"
 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"
 
@@ -684,6 +736,11 @@ 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)"
@@ -692,16 +749,16 @@ 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"
 
@@ -709,8 +766,12 @@ 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"
@@ -723,27 +784,46 @@ msgstr "Nouveau Film"
 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"
@@ -756,12 +836,12 @@ msgstr "Gamma de 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"
 
@@ -773,7 +853,7 @@ msgstr "Pause"
 msgid "Peak"
 msgstr "Crête"
 
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
 msgid "Play"
 msgstr "Lecture"
 
@@ -793,7 +873,19 @@ msgstr "Position"
 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"
 
@@ -805,16 +897,25 @@ msgstr "RMS"
 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"
 
@@ -834,15 +935,15 @@ msgstr "Répéter"
 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"
 
@@ -850,7 +951,7 @@ 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."
 
@@ -858,24 +959,32 @@ 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"
 
@@ -887,10 +996,20 @@ msgstr "Ecrans"
 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"
@@ -911,7 +1030,7 @@ msgstr "Constructeur du serveur"
 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"
 
@@ -919,15 +1038,15 @@ 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é"
 
@@ -935,7 +1054,7 @@ msgstr "Signé"
 msgid "Smoothing"
 msgstr "Lissage"
 
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
 msgid "Snap"
 msgstr "Magnetisme"
 
@@ -943,11 +1062,16 @@ 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"
 
@@ -955,15 +1079,20 @@ 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"
 
@@ -971,11 +1100,11 @@ 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"
 
@@ -995,7 +1124,7 @@ msgstr "Version test"
 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 "
@@ -1009,7 +1138,7 @@ msgstr ""
 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."
 
@@ -1021,19 +1150,23 @@ msgstr "Ceci n'est pas un fichier CPL valide"
 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..."
 
@@ -1041,11 +1174,11 @@ 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"
 
@@ -1057,7 +1190,8 @@ msgstr "Rogner par la fin"
 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"
 
@@ -1073,7 +1207,7 @@ msgstr "inconnu."
 msgid "Until"
 msgstr "Jusqu'au"
 
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
 msgid "Up"
 msgstr "Monter"
 
@@ -1081,15 +1215,15 @@ 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"
 
@@ -1097,15 +1231,20 @@ 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"
 
@@ -1113,36 +1252,36 @@ 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"
@@ -1182,43 +1321,62 @@ msgstr ""
 "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"
 
@@ -1276,9 +1434,6 @@ msgstr "vidéo"
 #~ msgid "Add"
 #~ msgstr "Ajouter"
 
-#~ msgid "Duration"
-#~ msgstr "Durée"
-
 #~ msgid "Edit"
 #~ msgstr "Édition"
 
@@ -1291,9 +1446,6 @@ msgstr "vidéo"
 #~ msgid "Running"
 #~ msgstr "Progression"
 
-#~ msgid "Start time"
-#~ msgstr "Début"
-
 #~ msgid "A/B"
 #~ msgstr "A/B"
 
@@ -1313,9 +1465,6 @@ msgstr "vidéo"
 #~ msgid "DVD-o-matic Preferences"
 #~ msgstr "Préférences de DCP-o-matic"
 
-#~ msgid "End"
-#~ msgstr "Fin"
-
 #~ msgid "Film"
 #~ msgstr "Film"
 
index 320d15e0ce5bad94a35577accd51635921416509..35cb8db88a54ac8b805f07f29e2e98dfb5783df2 100644 (file)
@@ -17,25 +17,25 @@ msgstr ""
 "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 ""
 
@@ -43,11 +43,7 @@ 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 ""
 
@@ -56,35 +52,35 @@ 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 ""
 
@@ -92,7 +88,7 @@ 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"
 
@@ -100,19 +96,24 @@ 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..."
 
@@ -132,16 +133,16 @@ msgid ""
 "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"
 
@@ -149,18 +150,14 @@ 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 "
@@ -169,7 +166,7 @@ msgstr ""
 "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"
@@ -187,23 +184,27 @@ msgstr "Taglio in basso"
 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"
@@ -220,7 +221,7 @@ msgstr ""
 msgid "CPL annotation text"
 msgstr ""
 
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
 msgid "Calculate..."
 msgstr "Calcola..."
 
@@ -233,12 +234,20 @@ msgstr "Annulla"
 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 ""
@@ -247,27 +256,27 @@ 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"
 
@@ -276,33 +285,33 @@ 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"
@@ -311,7 +320,7 @@ 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"
@@ -324,21 +333,26 @@ msgstr ""
 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 ""
@@ -347,20 +361,20 @@ 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"
 
@@ -368,7 +382,7 @@ 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"
 
@@ -377,55 +391,55 @@ 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"
 
@@ -447,7 +461,7 @@ msgstr ""
 msgid "Doremi serial numbers must have 6 digits"
 msgstr ""
 
-#: src/wx/film_editor.cc:288
+#: src/wx/content_panel.cc:75
 msgid "Down"
 msgstr ""
 
@@ -472,8 +486,8 @@ msgstr "Modifica Cinema..."
 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..."
@@ -487,18 +501,34 @@ msgstr "Indirizzo email per consegna KDM"
 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
@@ -514,15 +544,15 @@ msgstr "Proprietà del film"
 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"
 
@@ -534,7 +564,7 @@ msgstr "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 ""
 
@@ -542,12 +572,12 @@ 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 ""
 
@@ -555,7 +585,7 @@ msgstr ""
 msgid "Full length"
 msgstr ""
 
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
 msgid "Gain"
 msgstr ""
 
@@ -568,15 +598,15 @@ msgstr "Calcolatore del guadagno audio"
 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"
 
@@ -592,19 +622,15 @@ msgstr ""
 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"
@@ -618,19 +644,27 @@ msgstr "Nome DCI"
 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 ""
 
@@ -642,23 +676,35 @@ 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"
 
@@ -670,28 +716,34 @@ msgstr "Linearizza la curva del gamma in ingresso per piccoli valori"
 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"
@@ -700,6 +752,11 @@ 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 ""
@@ -708,17 +765,17 @@ 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"
 
@@ -726,8 +783,12 @@ 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"
@@ -740,27 +801,46 @@ msgstr "Nuovo Film"
 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"
@@ -774,12 +854,12 @@ msgstr "Gamma in uscita"
 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"
@@ -792,7 +872,7 @@ msgstr "Pausa"
 msgid "Peak"
 msgstr "Picco"
 
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
 msgid "Play"
 msgstr "Riproduci"
 
@@ -812,7 +892,19 @@ msgstr "Posizione"
 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"
 
@@ -824,16 +916,24 @@ msgstr "RMS"
 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"
 
@@ -853,15 +953,15 @@ msgstr ""
 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"
 
@@ -869,7 +969,7 @@ 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."
 
@@ -877,24 +977,32 @@ 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"
 
@@ -908,10 +1016,20 @@ msgstr "Aggiungi Schermo"
 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"
@@ -932,7 +1050,7 @@ msgstr ""
 msgid "Server serial number"
 msgstr ""
 
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
 #, fuzzy
 msgid "Servers"
 msgstr "Server"
@@ -941,15 +1059,15 @@ 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 ""
 
@@ -957,7 +1075,7 @@ msgstr ""
 msgid "Smoothing"
 msgstr "Levigatura"
 
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
 msgid "Snap"
 msgstr ""
 
@@ -965,11 +1083,16 @@ 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"
@@ -978,15 +1101,20 @@ 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"
 
@@ -994,11 +1122,11 @@ 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"
@@ -1020,7 +1148,7 @@ msgstr "Versione di test"
 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 "
@@ -1033,7 +1161,7 @@ msgstr ""
 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 ""
 
@@ -1045,19 +1173,23 @@ 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 ""
 
@@ -1065,11 +1197,11 @@ 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 ""
 
@@ -1081,7 +1213,8 @@ msgstr "Taglia dalla fine"
 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"
 
@@ -1099,7 +1232,7 @@ msgstr "sconosciuto"
 msgid "Until"
 msgstr "Fino a"
 
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
 msgid "Up"
 msgstr "Su"
 
@@ -1107,16 +1240,16 @@ 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"
 
@@ -1124,15 +1257,20 @@ 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"
 
@@ -1140,36 +1278,36 @@ 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"
@@ -1209,45 +1347,64 @@ msgstr ""
 "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"
@@ -1290,19 +1447,12 @@ msgstr "Video"
 #~ 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"
 
@@ -1319,9 +1469,6 @@ msgstr "Video"
 #~ msgid "DVD-o-matic Preferences"
 #~ msgstr "Preferenze DVD-o-matic"
 
-#~ msgid "End"
-#~ msgstr "Fine"
-
 #~ msgid "Film"
 #~ msgstr "Film"
 
index a59f204afe1f05ee18785d4f6211b2d0c8fa19c3..e792f84be5cc6662db4909f5d11b9415486dafd9 100644 (file)
@@ -18,26 +18,26 @@ msgstr ""
 "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"
 
@@ -45,11 +45,7 @@ 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"
 
@@ -57,35 +53,35 @@ 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"
 
@@ -93,7 +89,7 @@ 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"
 
@@ -101,19 +97,24 @@ 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.."
 
@@ -141,15 +142,15 @@ msgstr ""
 "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"
 
@@ -157,11 +158,7 @@ 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."
@@ -169,7 +166,7 @@ msgstr ""
 "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 "
@@ -178,7 +175,7 @@ msgstr ""
 "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"
 
@@ -195,23 +192,27 @@ msgstr "Bottom crop"
 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"
 
@@ -227,7 +228,7 @@ msgstr "CPL ID"
 msgid "CPL annotation text"
 msgstr "CPL opmerkingen tekst"
 
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
 msgid "Calculate..."
 msgstr "Bereken..."
 
@@ -239,11 +240,19 @@ msgstr "Afbreken"
 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"
@@ -252,27 +261,27 @@ 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"
 
@@ -280,32 +289,32 @@ 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"
@@ -314,7 +323,7 @@ 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"
@@ -327,21 +336,26 @@ msgstr "Kopieer..."
 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"
@@ -350,20 +364,20 @@ 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"
 
@@ -371,7 +385,7 @@ 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"
 
@@ -380,51 +394,51 @@ 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..."
 
@@ -446,7 +460,7 @@ msgstr "Doremi"
 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"
 
@@ -471,8 +485,8 @@ msgstr "Edit Bioscoop"
 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..."
@@ -485,18 +499,34 @@ msgstr "Email adres voor KDM aflevering"
 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
@@ -511,15 +541,15 @@ msgstr "Film Instellingen"
 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"
 
@@ -531,7 +561,7 @@ msgstr "Frame"
 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."
 
@@ -539,11 +569,11 @@ 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"
 
@@ -551,7 +581,7 @@ msgstr "Volledig"
 msgid "Full length"
 msgstr "Volledige lengte"
 
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
 msgid "Gain"
 msgstr "Versterking"
 
@@ -564,15 +594,15 @@ msgstr "Gain Calculator"
 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"
 
@@ -588,19 +618,15 @@ msgstr "Host"
 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"
 
@@ -612,19 +638,27 @@ msgstr "ISDCF 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"
 
@@ -636,23 +670,35 @@ msgstr "soort KDM"
 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"
 
@@ -664,27 +710,33 @@ msgstr "Linearise input gamma curve for low values"
 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"
 
@@ -692,6 +744,11 @@ 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)"
@@ -700,16 +757,16 @@ 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"
 
@@ -717,8 +774,12 @@ 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"
@@ -731,29 +792,47 @@ msgstr "Nieuwe Film"
 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"
@@ -766,12 +845,12 @@ msgstr "Output gamma"
 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"
 
@@ -783,7 +862,7 @@ msgstr "Pauze"
 msgid "Peak"
 msgstr "Piek"
 
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
 msgid "Play"
 msgstr "Afspelen"
 
@@ -803,7 +882,19 @@ msgstr "Positie"
 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"
 
@@ -815,16 +906,25 @@ msgstr "RMS"
 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"
 
@@ -844,15 +944,15 @@ msgstr "Herhaal"
 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"
 
@@ -860,7 +960,7 @@ 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"
 
@@ -868,24 +968,32 @@ 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"
 
@@ -897,10 +1005,20 @@ msgstr "Schermen"
 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"
@@ -921,7 +1039,7 @@ msgstr "Server fabrikant"
 msgid "Server serial number"
 msgstr "Server serie nummer"
 
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
 msgid "Servers"
 msgstr "Servers"
 
@@ -929,15 +1047,15 @@ 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"
 
@@ -945,7 +1063,7 @@ msgstr "Signed"
 msgid "Smoothing"
 msgstr "Smoothing"
 
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
 msgid "Snap"
 msgstr "Snap"
 
@@ -953,11 +1071,15 @@ 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"
 
@@ -965,15 +1087,20 @@ 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"
 
@@ -981,11 +1108,11 @@ 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"
 
@@ -1005,7 +1132,7 @@ msgstr "Test Versie"
 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 "
@@ -1019,7 +1146,7 @@ msgstr ""
 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."
 
@@ -1031,19 +1158,23 @@ msgstr "Dit is geen geldig CPL bestand"
 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..."
 
@@ -1051,11 +1182,11 @@ 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"
 
@@ -1067,7 +1198,8 @@ msgstr "Trim vanaf einde"
 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"
 
@@ -1083,7 +1215,7 @@ msgstr "Onbekend"
 msgid "Until"
 msgstr "Totdat"
 
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
 msgid "Up"
 msgstr "Omhoog"
 
@@ -1091,15 +1223,15 @@ 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"
 
@@ -1107,15 +1239,20 @@ 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"
 
@@ -1123,36 +1260,36 @@ 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"
@@ -1191,43 +1328,62 @@ msgstr ""
 "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"
 
index 81fa8c4b330c582efa30447db3fabf346bb19483..441643ccef459df953af7a562da4336cb24e434a 100644 (file)
@@ -17,26 +17,26 @@ msgstr ""
 "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 ""
 
@@ -44,11 +44,7 @@ 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"
 
@@ -57,36 +53,36 @@ 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"
 
@@ -94,7 +90,7 @@ 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"
 
@@ -102,19 +98,24 @@ 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..."
 
@@ -134,16 +135,16 @@ msgid ""
 "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"
 
@@ -151,11 +152,7 @@ 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."
@@ -163,7 +160,7 @@ msgstr ""
 "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 "
@@ -172,7 +169,7 @@ msgstr ""
 "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"
@@ -190,23 +187,27 @@ msgstr "Nedre beskärning"
 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"
@@ -224,7 +225,7 @@ msgstr ""
 msgid "CPL annotation text"
 msgstr ""
 
-#: src/wx/audio_panel.cc:63
+#: src/wx/audio_panel.cc:66
 msgid "Calculate..."
 msgstr "Beräkna..."
 
@@ -237,12 +238,20 @@ msgstr "Avbryt"
 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 ""
@@ -251,27 +260,27 @@ 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"
 
@@ -280,35 +289,35 @@ 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"
@@ -317,7 +326,7 @@ 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"
@@ -330,21 +339,26 @@ msgstr ""
 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 ""
@@ -353,21 +367,21 @@ 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"
 
@@ -375,7 +389,7 @@ 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"
 
@@ -384,54 +398,54 @@ 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..."
 
@@ -453,7 +467,7 @@ msgstr ""
 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"
 
@@ -479,8 +493,8 @@ msgstr "Redigera Cinema..."
 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..."
@@ -493,18 +507,34 @@ msgstr "Mejladress för KDM-leverans"
 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
@@ -520,15 +550,15 @@ msgstr "Film Egenskaper"
 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"
 
@@ -540,7 +570,7 @@ msgstr "Bildrutor"
 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."
 
@@ -549,12 +579,12 @@ 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"
 
@@ -562,7 +592,7 @@ msgstr "Full"
 msgid "Full length"
 msgstr "Full längd"
 
-#: src/wx/audio_panel.cc:52
+#: src/wx/audio_panel.cc:55
 msgid "Gain"
 msgstr ""
 
@@ -575,15 +605,15 @@ msgstr "Volym Kalkylator"
 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"
 
@@ -599,19 +629,15 @@ msgstr "Dator"
 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"
 
@@ -624,19 +650,27 @@ msgstr "DCI namn"
 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"
@@ -652,23 +686,35 @@ msgid "KDM|Timing"
 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"
 
@@ -680,27 +726,33 @@ msgstr "Linjärisera indatas gammakurva för lÃ¥ga värden"
 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"
 
@@ -708,6 +760,11 @@ 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 ""
@@ -716,18 +773,18 @@ 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"
 
@@ -735,8 +792,12 @@ 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"
@@ -749,29 +810,49 @@ msgstr "Ny Film"
 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"
@@ -785,12 +866,12 @@ msgstr "Utdata gamma"
 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"
 
@@ -802,7 +883,7 @@ msgstr "Pausa"
 msgid "Peak"
 msgstr "Topp"
 
-#: src/wx/film_viewer.cc:64
+#: src/wx/film_viewer.cc:67
 msgid "Play"
 msgstr "Spela"
 
@@ -822,7 +903,19 @@ msgstr "Position"
 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"
 
@@ -834,16 +927,24 @@ msgstr "RMS"
 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"
 
@@ -864,15 +965,15 @@ msgstr "Upprepa"
 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"
 
@@ -880,7 +981,7 @@ 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."
 
@@ -888,24 +989,32 @@ 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"
 
@@ -919,10 +1028,20 @@ msgstr "Lägg till Skärm..."
 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"
@@ -943,7 +1062,7 @@ msgstr ""
 msgid "Server serial number"
 msgstr ""
 
-#: src/wx/config_dialog.cc:438
+#: src/wx/config_dialog.cc:444
 #, fuzzy
 msgid "Servers"
 msgstr "Server"
@@ -952,15 +1071,15 @@ 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"
 
@@ -969,7 +1088,7 @@ msgid "Smoothing"
 msgstr "Utjämning"
 
 # sammanhang?
-#: src/wx/timeline_dialog.cc:38
+#: src/wx/timeline_dialog.cc:39
 msgid "Snap"
 msgstr "Snap"
 
@@ -977,11 +1096,16 @@ 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"
@@ -990,15 +1114,20 @@ 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"
 
@@ -1006,11 +1135,11 @@ 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"
 
@@ -1032,7 +1161,7 @@ msgstr "Testversion"
 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 "
@@ -1045,7 +1174,7 @@ msgstr ""
 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 ""
 
@@ -1057,19 +1186,23 @@ 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..."
 
@@ -1079,11 +1212,11 @@ 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"
 
@@ -1095,7 +1228,8 @@ msgstr "Trimma frÃ¥n slut"
 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"
 
@@ -1114,7 +1248,7 @@ msgstr "okänt"
 msgid "Until"
 msgstr "Tills"
 
-#: src/wx/film_editor.cc:286
+#: src/wx/content_panel.cc:73
 msgid "Up"
 msgstr "Upp"
 
@@ -1122,16 +1256,16 @@ 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"
 
@@ -1139,15 +1273,20 @@ 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"
 
@@ -1155,38 +1294,38 @@ 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"
@@ -1225,44 +1364,63 @@ msgstr ""
 "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"
@@ -1322,19 +1480,12 @@ msgstr "video"
 #~ 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"
 
@@ -1351,9 +1502,6 @@ msgstr "video"
 #~ msgid "DVD-o-matic Preferences"
 #~ msgstr "DVD-o-matic Inställningar"
 
-#~ msgid "End"
-#~ msgstr "Slut"
-
 #~ msgid "Film"
 #~ msgstr "Film"
 
index 53ca237555af31672f2dd2517038dc829ce270bf..27fc75b1b39c649fd76b9890d1758e374f7af858 100644 (file)
@@ -45,8 +45,7 @@ PropertiesDialog::PropertiesDialog (wxWindow* parent, shared_ptr<Film> 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"));
@@ -64,10 +63,11 @@ PropertiesDialog::frames_already_encoded () const
        } 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 ();
 }
index c6991271675f3321e82886478fbad2a879bff1ea..503745683980f3b2a860ec09050c2b80bba01a84 100644 (file)
@@ -19,7 +19,7 @@
 
 #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"
@@ -29,9 +29,9 @@
 
 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)
 {
@@ -79,7 +79,7 @@ ScreenDialog::name () const
        return wx_to_std (_name->GetValue());
 }
 
-shared_ptr<libdcp::Certificate>
+optional<dcp::Certificate>
 ScreenDialog::certificate () const
 {
        return _certificate;
@@ -89,9 +89,9 @@ void
 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()));
        }
 }
index 3601a8f6c133e57a556ce22db289fa5bd732629a..3e110d230bb4c48bfa6e9fe16e8de939c12efb33 100644 (file)
@@ -19,7 +19,7 @@
 
 #include <wx/wx.h>
 #include <boost/shared_ptr.hpp>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
 #include "table_dialog.h"
 
 class Progress;
@@ -27,10 +27,10 @@ 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 ();
@@ -44,5 +44,5 @@ private:
        wxButton* _download_certificate;
        wxTextCtrl* _certificate_text;
 
-       boost::shared_ptr<libdcp::Certificate> _certificate;
+       boost::optional<dcp::Certificate> _certificate;
 };
index 7953682fca6fd25f857062b31fee4885a241fdfd..21d6f8e5ba6c46a48a57004fa625e3a584af4641 100644 (file)
 #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;
@@ -30,16 +37,17 @@ using boost::shared_ptr;
 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);
@@ -79,41 +87,37 @@ SubtitlePanel::SubtitlePanel (FilmEditor* e)
        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) {
@@ -124,7 +128,7 @@ SubtitlePanel::film_content_changed (int property)
        if (sc.size() == 1) {
                scs = sc.front ();
        }
-       
+
        if (property == FFmpegContentProperty::SUBTITLE_STREAMS) {
                _stream->Clear ();
                if (fcs) {
@@ -140,6 +144,9 @@ SubtitlePanel::film_content_changed (int property)
                        }
                }
                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) {
@@ -152,37 +159,53 @@ SubtitlePanel::film_content_changed (int property)
 }
 
 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;
        }
@@ -204,25 +227,25 @@ SubtitlePanel::stream_changed ()
 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);
        }
@@ -231,9 +254,9 @@ SubtitlePanel::x_scale_changed ()
 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);
        }
 }
 
@@ -241,8 +264,38 @@ void
 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 ();
+       }
+}
index 7f5d9239d1d477fbad194b2ab6a2a34618466633..bcff995a0029a5ef223d5381113648aaef893fc0 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include "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;
 };
diff --git a/src/wx/subtitle_view.cc b/src/wx/subtitle_view.cc
new file mode 100644 (file)
index 0000000..dc41db2
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+    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);
+}
+
diff --git a/src/wx/subtitle_view.h b/src/wx/subtitle_view.h
new file mode 100644 (file)
index 0000000..338742a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+    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;
+};
index 166446d8c76140eb1e5fa01c27731abdb003111a..bd0a182c2f4d6b1d3dd027875449c31863c25410 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <boost/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);
@@ -69,11 +69,11 @@ Timecode::Timecode (wxWindow* 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);
 
@@ -83,46 +83,7 @@ Timecode::Timecode (wxWindow* parent)
 }
 
 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, "");
@@ -132,20 +93,20 @@ Timecode::clear ()
 }
 
 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);
index d0e8176f2d12a5e1cd51fce3675850c7a661c666..7a34b80fec949eecf69eb06dec2bcab319404f80 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include <boost/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 ();
        
@@ -48,3 +51,46 @@ private:
        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
index eba12c47faff1421269b2f42067ec09ce90c8d6a..13baef6b7bb0c143021aeb67a97fe7dcc714127e 100644 (file)
 #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;
@@ -35,7 +37,9 @@ using boost::dynamic_pointer_cast;
 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:
@@ -64,9 +68,9 @@ 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;
@@ -76,7 +80,9 @@ private:
 };
 
 
-/** Parent class for views of pieces of content */
+/** @class ContentView
+ *  @brief Parent class for views of pieces of content.
+ */
 class ContentView : public View
 {
 public:
@@ -101,7 +107,7 @@ 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
                        );
        }
@@ -132,7 +138,8 @@ public:
        }
 
        virtual wxString type () const = 0;
-       virtual wxColour colour () const = 0;
+       virtual wxColour background_colour () const = 0;
+       virtual wxColour foreground_colour () const = 0;
        
 private:
 
@@ -146,18 +153,16 @@ 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 ();
@@ -169,14 +174,15 @@ private:
                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 ();
        }
@@ -195,7 +201,7 @@ private:
                }
 
                if (!frequent) {
-                       _timeline.setup_pixels_per_time_unit ();
+                       _timeline.setup_pixels_per_second ();
                        _timeline.Refresh ();
                }
        }
@@ -207,6 +213,9 @@ private:
        boost::signals2::scoped_connection _content_connection;
 };
 
+/** @class AudioContentView
+ *  @brief Timeline view for AudioContent.
+ */
 class AudioContentView : public ContentView
 {
 public:
@@ -220,12 +229,20 @@ private:
                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:
@@ -237,17 +254,62 @@ private:
 
        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
@@ -273,26 +335,26 @@ private:
 
        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) {
@@ -304,14 +366,17 @@ private:
                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;
@@ -325,12 +390,12 @@ private:
                        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);
                }
        }
 
@@ -339,9 +404,9 @@ private:
 };
 
 
-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)
@@ -380,8 +445,6 @@ Timeline::paint ()
                return;
        }
 
-       gc->SetFont (gc->CreateFont (*wxNORMAL_FONT));
-
        for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
                (*i)->paint (gc);
        }
@@ -411,10 +474,15 @@ Timeline::playlist_changed ()
                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 ();
 }
 
@@ -425,7 +493,7 @@ Timeline::playlist_content_changed (int property)
 
        if (property == ContentProperty::POSITION) {
                assign_tracks ();
-               setup_pixels_per_time_unit ();
+               setup_pixels_per_second ();
                Refresh ();
        }
 }
@@ -495,14 +563,14 @@ Timeline::tracks () const
 }
 
 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>
@@ -545,7 +613,7 @@ Timeline::left_down (wxMouseEvent& ev)
                }
                
                if (view == *i) {
-                       _film_editor->set_selection (cv->content ());
+                       _content_panel->set_selection (cv->content ());
                }
        }
 
@@ -604,11 +672,11 @@ Timeline::right_down (wxMouseEvent& ev)
 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();
 
@@ -627,13 +695,13 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
                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) {
@@ -644,7 +712,7 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
                        
                        {
                                /* 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();
@@ -653,7 +721,10 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
                        
                        {
                                /* 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 ();
@@ -665,14 +736,14 @@ Timeline::set_position_from_event (wxMouseEvent& ev)
                
                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);
@@ -697,7 +768,7 @@ Timeline::film () const
 void
 Timeline::resized ()
 {
-       setup_pixels_per_time_unit ();
+       setup_pixels_per_second ();
 }
 
 void
index fafb09c0e5aa0dc56b9c830131ae899a5c4616f7..82d10afde6cc8a8337c910b8b9506c1461c96585 100644 (file)
 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;
 
@@ -52,8 +52,8 @@ public:
                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 {
@@ -62,7 +62,7 @@ public:
 
        int tracks () const;
 
-       void setup_pixels_per_time_unit ();
+       void setup_pixels_per_second ();
 
        void set_snap (bool s) {
                _snap = s;
@@ -92,16 +92,16 @@ private:
        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;
index dbf7ae232be2cd5f72dd8a247e98a80fff968fa7..8ac90b8def6994d0e3f49e566d0f963c77c74de9 100644 (file)
 #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);
 
index 1e595500303b5b964b0ba54157c67d47f260625b..2f5fa5ec75b23a69eee666feeab0a25476005308 100644 (file)
@@ -27,7 +27,7 @@ class Playlist;
 class TimelineDialog : public wxDialog
 {
 public:
-       TimelineDialog (FilmEditor *, boost::shared_ptr<Film>);
+       TimelineDialog (ContentPanel *, boost::shared_ptr<Film>);
 
 private:
        void snap_toggled ();
index aa4f70a81b476c715c8e068a27764eaa30636abc..0f86a3f3f2b720f21a8df9f7b38577f91d6a1537 100644 (file)
 
 */
 
-#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);
 
        {
@@ -78,8 +78,8 @@ TimingPanel::TimingPanel (FilmEditor* e)
 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.
@@ -87,7 +87,7 @@ TimingPanel::film_content_changed (int property)
        
        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 ());
                }
@@ -104,7 +104,7 @@ TimingPanel::film_content_changed (int property)
                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 ());
                }
@@ -117,7 +117,7 @@ TimingPanel::film_content_changed (int property)
 
        } 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 ());
                }
@@ -130,7 +130,7 @@ TimingPanel::film_content_changed (int property)
                
        } 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 ());
                }
@@ -138,7 +138,7 @@ TimingPanel::film_content_changed (int property)
                if (check.size() == 1) {
                        _trim_end->set (cl.front()->trim_end (), film_video_frame_rate);
                } else {
-                       _trim_end->set (0, 24);
+                       _trim_end->clear ();
                }
        }
 
@@ -150,7 +150,7 @@ TimingPanel::film_content_changed (int property)
                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 ());
                }
@@ -197,20 +197,21 @@ TimingPanel::film_content_changed (int property)
 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)));
                }
        }
 }
@@ -218,9 +219,9 @@ TimingPanel::full_length_changed ()
 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 ()));
        }
 }
 
@@ -228,18 +229,18 @@ TimingPanel::trim_start_changed ()
 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());
        }
 }
 
@@ -252,7 +253,7 @@ TimingPanel::video_frame_rate_changed ()
 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) {
@@ -265,7 +266,7 @@ TimingPanel::set_video_frame_rate ()
 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);
index d9696a20135671a711fe27a14bc2bd5565df237a..00b7f84e75f63e09b13a38a8afa1cfc8bc2f1971 100644 (file)
 
 */
 
-#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 ();
@@ -38,11 +37,11 @@ private:
        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;
 };
index b33a97591c6617e45fe3a2da2a04bd1fca0fbe24..a8510cbbab38d4b3dd9776d268ffff24c5b13da0 100644 (file)
 #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;
@@ -64,8 +65,8 @@ scale_to_index (VideoContentScale scale)
        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);
@@ -82,7 +83,7 @@ VideoPanel::VideoPanel (FilmEditor* e)
                &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));
@@ -94,9 +95,8 @@ VideoPanel::VideoPanel (FilmEditor* e)
                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)),
@@ -104,7 +104,8 @@ VideoPanel::VideoPanel (FilmEditor* e)
                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));
@@ -115,10 +116,9 @@ VideoPanel::VideoPanel (FilmEditor* e)
                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)),
@@ -126,9 +126,20 @@ VideoPanel::VideoPanel (FilmEditor* e)
                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,
@@ -139,44 +150,29 @@ VideoPanel::VideoPanel (FilmEditor* e)
                &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);
@@ -201,6 +197,9 @@ VideoPanel::VideoPanel (FilmEditor* e)
        _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));
 }
@@ -222,7 +221,7 @@ VideoPanel::film_changed (Film::Property property)
 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 ()) {
@@ -251,6 +250,28 @@ VideoPanel::film_content_changed (int property)
                                _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 ();
+               }
        }
 }
 
@@ -258,7 +279,7 @@ VideoPanel::film_content_changed (int property)
 void
 VideoPanel::edit_filters_clicked ()
 {
-       FFmpegContentList c = _editor->selected_ffmpeg_content ();
+       FFmpegContentList c = _parent->selected_ffmpeg ();
        if (c.size() != 1) {
                return;
        }
@@ -272,7 +293,7 @@ VideoPanel::edit_filters_clicked ()
 void
 VideoPanel::setup_description ()
 {
-       VideoContentList vc = _editor->selected_video_content ();
+       VideoContentList vc = _parent->selected_video ();
        if (vc.empty ()) {
                _description->SetLabel ("");
                return;
@@ -298,8 +319,8 @@ VideoPanel::setup_description ()
        }
 
        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,
@@ -308,8 +329,8 @@ VideoPanel::setup_description ()
                ++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 (
@@ -331,7 +352,7 @@ VideoPanel::setup_description ()
 
        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;
 
@@ -346,7 +367,7 @@ VideoPanel::setup_description ()
 void
 VideoPanel::edit_colour_conversion_clicked ()
 {
-       VideoContentList vc = _editor->selected_video_content ();
+       VideoContentList vc = _parent->selected_video ();
        if (vc.size() != 1) {
                return;
        }
@@ -363,8 +384,8 @@ VideoPanel::edit_colour_conversion_clicked ()
 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;
 
@@ -381,5 +402,25 @@ VideoPanel::content_selection_changed ()
        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 ()));
+       }
+}
index 99633491d835ea9948538ad696459035e5f16876..aa0c6ed531af796a4fdc54df2c4f83e8b3957262 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
-#include "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);
@@ -38,6 +46,8 @@ public:
 private:
        void edit_filters_clicked ();
        void edit_colour_conversion_clicked ();
+       void fade_in_changed ();
+       void fade_out_changed ();
 
        void setup_description ();
 
@@ -46,6 +56,8 @@ private:
        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;
index 07105006904bfaa27cc0474bf5b095355e68ba1c..8801ccc75556c090b05876d91bd94743a1c88c02 100644 (file)
@@ -15,13 +15,15 @@ sources = """
           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
@@ -30,6 +32,7 @@ sources = """
           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
@@ -38,6 +41,7 @@ sources = """
           server_dialog.cc
           servers_list_dialog.cc
           subtitle_panel.cc
+          subtitle_view.cc
           table_dialog.cc
           timecode.cc
           timeline.cc
@@ -82,16 +86,16 @@ def build(bld):
     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')
index f306319608a0e194d5128b8f490d600179df4bfc..8fc6670d6befe69fd2558ae0e05081444c845acd 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
index f7df6fca48a9044f2df8e91f6b4582637cce5687..63f2049cd2ed4ca7ca118a7069002055520fa870 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 class wxEvtHandler;
 
+/** @class wxUISignaller
+ *  @brief UISignaller for the wxWidgets event loop
+ */
+
 class wxUISignaller : public UISignaller
 {
 public:
index 7f15217f14a65490345aacf3a4b81551de8f892c..c4a7fd5bca9f071b5da875d7a476d0c7d8ce2962 100644 (file)
@@ -123,7 +123,7 @@ int const ThreadedStaticText::_update_event_id = 10000;
  *  @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);
@@ -139,7 +139,7 @@ ThreadedStaticText::~ThreadedStaticText ()
 
 /** 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);
index e65804aa5e5ff2908eb2434a95c79ebdf1b16769..fa5b33bb921feb348e4a0d6ad83fda54c40411b7 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
diff --git a/test/README b/test/README
new file mode 100644 (file)
index 0000000..a129b66
--- /dev/null
@@ -0,0 +1,50 @@
+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
index 77b2aeaf6f9671a1685bf841d357685145a291a8..2799449191575571a55e3c2694a9032265608fee 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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 ()
@@ -26,8 +37,7 @@ 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;
@@ -44,13 +54,13 @@ BOOST_AUTO_TEST_CASE (audio_analysis_test)
                }
        }
 
-       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);
@@ -58,3 +68,25 @@ BOOST_AUTO_TEST_CASE (audio_analysis_test)
                }
        }
 }
+
+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 ();
+}
diff --git a/test/audio_buffers_test.cc b/test/audio_buffers_test.cc
new file mode 100644 (file)
index 0000000..15f9184
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+    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);
+                       }
+               }
+       }
+}
diff --git a/test/audio_decoder_test.cc b/test/audio_decoder_test.cc
new file mode 100644 (file)
index 0000000..a14e2f9
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+    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);
+               }
+       }
+}
index 8ac5f746c8fb46e3621d392f9267297e7e792804..68e14ff3ca353f470d9f09f8c14112f6713b34a4 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
@@ -53,10 +60,10 @@ void test_audio_delay (int delay_in_ms)
        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 */
@@ -66,11 +73,11 @@ void test_audio_delay (int delay_in_ms)
        /* 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);
@@ -86,7 +93,6 @@ void test_audio_delay (int delay_in_ms)
        }
 }
 
-
 /* Test audio delay when specified in a piece of audio content */
 BOOST_AUTO_TEST_CASE (audio_delay_test)
 {
diff --git a/test/audio_filter_test.cc b/test/audio_filter_test.cc
new file mode 100644 (file)
index 0000000..bcd16fd
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @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);
+               }
+       }
+}
index bfb53b0871193c681c333497b3343403d5d1e761..fc597b91df10e4ca9dadaf8303a4b7cb844de983 100644 (file)
 
 */
 
+/** @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;
@@ -35,10 +36,10 @@ BOOST_AUTO_TEST_CASE (audio_mapping_test)
 
        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);
 }
diff --git a/test/audio_merger_test.cc b/test/audio_merger_test.cc
deleted file mode 100644 (file)
index 31d055a..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include <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);
-       }
-}
index 5c594f68cc064f170d3290c83833422522012c52..148ec9738c74329c9c126506488fac020ff41037 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
 #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;
@@ -46,10 +46,10 @@ BOOST_AUTO_TEST_CASE (black_fill_test)
        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 ();
 
diff --git a/test/burnt_subtitle_test.cc b/test/burnt_subtitle_test.cc
new file mode 100644 (file)
index 0000000..d10d1ed
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+    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 ()));
+}
index 07af1255c486ea42380f570ba6a643d566aedde0..0154200ad054c5a5ff1a024c76cf7c6ec56e06c8 100644 (file)
 
 */
 
+/** @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) {
@@ -57,7 +67,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
                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;
@@ -72,12 +82,14 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
 
        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,
@@ -85,15 +97,16 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
                        )
                );
 
-       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
                        )
                );
@@ -122,11 +135,13 @@ BOOST_AUTO_TEST_CASE (client_server_test_rgb)
        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) {
@@ -136,7 +151,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
                }
        }
 
-       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;
@@ -151,12 +166,14 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
 
        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,
@@ -164,15 +181,16 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
                        )
                );
 
-       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
                        )
                );
@@ -201,5 +219,7 @@ BOOST_AUTO_TEST_CASE (client_server_test_yuv)
        for (list<thread*>::iterator i = threads.begin(); i != threads.end(); ++i) {
                delete *i;
        }
+
+       delete server;
 }
 
index f850847b837164264ad81bdb28284c43dbdf4c6a..7de169dd3e305ad23d76dbaf12b756ef80ccaebb 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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");
diff --git a/test/dcp_subtitle_test.cc b/test/dcp_subtitle_test.cc
new file mode 100644 (file)
index 0000000..95fa7d1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+    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));
+}
index 2e83d45c9101dbef0c064e3c0e26ef5446bcab85..98efe4dd0769ea62aae9e4550fdd41af43349311 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
@@ -55,56 +60,56 @@ BOOST_AUTO_TEST_CASE (ffmpeg_audio_test)
        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);
index 4922ec4d4d99f4c6d523ffc9175bf32ece02caee..234bf2c79b10bd8dafc5e74f71d2a36c98c22d3e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file 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");
diff --git a/test/ffmpeg_decoder_seek_test.cc b/test/ffmpeg_decoder_seek_test.cc
new file mode 100644 (file)
index 0000000..968c3bd
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+    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);
+}
+
diff --git a/test/ffmpeg_decoder_sequential_test.cc b/test/ffmpeg_decoder_sequential_test.cc
new file mode 100644 (file)
index 0000000..9a14c5a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+    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);
+}
+
index a3b9bb4f6506793186b1aff886f68f67c07d6662..26834aafc8d7512b304ae24488d3d013893c8d3b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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;
@@ -30,7 +36,7 @@ BOOST_AUTO_TEST_CASE (ffmpeg_examiner_test)
        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));
 }
diff --git a/test/ffmpeg_pts_offset.cc b/test/ffmpeg_pts_offset.cc
deleted file mode 100644 (file)
index 6caf0d0..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/test/unit_test.hpp>
-#include "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);
-       }
-}
diff --git a/test/ffmpeg_pts_offset_test.cc b/test/ffmpeg_pts_offset_test.cc
new file mode 100644 (file)
index 0000000..94e7223
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+    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);
+       }
+}
index 14c01a9763d32d00963bddf94b2e4b83dddd286e..888834511677c8d95f7cffa76a2c921b0dc3fff9 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  test/file_group_test.cc
+ *  @brief Check that FileGroup works.
+ */
+
 #include <stdint.h>
 #include <cstdio>
 #include <boost/test/unit_test.hpp>
index c9f4a2c38faaab5b4d82bbb0ece8a550b0fc3537..01cff5b06c46fbb993eb397f68bdefc9a1329a0e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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>
@@ -31,13 +35,9 @@ using boost::shared_ptr;
 
 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);
@@ -50,9 +50,9 @@ BOOST_AUTO_TEST_CASE (film_metadata_test)
 
        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");
@@ -60,5 +60,5 @@ BOOST_AUTO_TEST_CASE (film_metadata_test)
        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);
 }
index 7e197dc03bf12472c848579306d0be8d45360ea8..e8ebcea3b959833ccb1009c561c7b99f4394bbf5 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
 
@@ -53,6 +59,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -61,6 +68,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -69,6 +77,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -77,6 +86,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -85,6 +95,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -93,6 +104,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -101,6 +113,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -109,6 +122,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -117,6 +131,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -125,6 +140,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -133,6 +149,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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.
@@ -150,6 +167,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -158,6 +176,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 ();
@@ -166,6 +185,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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) */
        
@@ -173,6 +193,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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 */
 
@@ -187,6 +208,7 @@ BOOST_AUTO_TEST_CASE (best_dcp_frame_rate_test_single)
        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
@@ -233,43 +255,43 @@ BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
        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));
 }
 
index 5662c729b5bd354dad0ec43cd1efc0eca769b642..44d15a8cb2ab18427f79e0d72ab1991548b7e4ca 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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);
@@ -50,12 +57,12 @@ BOOST_AUTO_TEST_CASE (aligned_image_test)
        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);
@@ -67,9 +74,9 @@ BOOST_AUTO_TEST_CASE (aligned_image_test)
        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;
@@ -78,7 +85,7 @@ BOOST_AUTO_TEST_CASE (aligned_image_test)
 
 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);
@@ -99,12 +106,12 @@ BOOST_AUTO_TEST_CASE (compact_image_test)
        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);
@@ -116,9 +123,9 @@ BOOST_AUTO_TEST_CASE (compact_image_test)
        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;
@@ -128,7 +135,7 @@ BOOST_AUTO_TEST_CASE (compact_image_test)
 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;
@@ -141,7 +148,7 @@ BOOST_AUTO_TEST_CASE (crop_image_test)
 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 */
@@ -170,7 +177,7 @@ boost::shared_ptr<Image>
 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));
 
@@ -228,7 +235,7 @@ write_file (shared_ptr<Image> image, string file)
 
 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));
@@ -276,12 +283,134 @@ crop_scale_window_single (AVPixelFormat in_format, libdcp::Size in_size, Crop cr
 /** 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);
+       }
 }
diff --git a/test/import_dcp_test.cc b/test/import_dcp_test.cc
new file mode 100644 (file)
index 0000000..80cd9c3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    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");
+}
index 7d2911c4e756259f917a2fcf1e6f368c0fca67ce..97a23b946a765eb7c758c41678960d3f0d69ea81 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
index dd0208b1d1dd7379a82ee945c53ff5b573dd54c2..f6c3a4bb2bbc87616ffc3cfbeb84a3ffacc07704 100644 (file)
 
 */
 
+/** @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>
 }
@@ -27,13 +34,10 @@ extern "C" {
 
 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
index 1b720d9bf5d3b88fe2ecf1f2399c1c9209674bdc..68d225e6efa36dc6b69a5b30f8319b7e6da74eaa 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  src/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" {
@@ -28,6 +35,9 @@ 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)
diff --git a/test/player_test.cc b/test/player_test.cc
new file mode 100644 (file)
index 0000000..b6f864f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+    Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @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);
+               }
+       }
+}
+
index f3cbb504f0b874b9d5ef97b65d681b2e91ff33f3..eab30ceee02ab3d098b8935d516551fbbc7d8060 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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));
 }
 
index 284895e0a75d3bcee73ae157e078fc1ad6b86925..92eae27eb9e8cf4ce734cea3878f0f0de8464327 100644 (file)
 
 */
 
+/** @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"
@@ -30,12 +34,13 @@ using std::string;
 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");
@@ -52,20 +57,22 @@ BOOST_AUTO_TEST_CASE (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 (&note, _1, _2)));
 }
diff --git a/test/repeat_frame_test.cc b/test/repeat_frame_test.cc
new file mode 100644 (file)
index 0000000..4f66420
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    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 ()));
+}
+
index 9247159a7065b1a46ecf06d49f74f8cfdbab6695..ffd636ac8ffb6371503d6f1a90743fd945f55c0a 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
@@ -34,19 +39,16 @@ resampler_test_one (int from, int to)
 
        /* 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);
index cdf1653cdf84a7c78b40f1518a3d81516a600063..441af6bf30514ecb2eaaafa58ae7deea3ffdaa16 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file 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;
 
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE (scaling_test)
 
        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");
diff --git a/test/seek_zero_test.cc b/test/seek_zero_test.cc
new file mode 100644 (file)
index 0000000..682fa93
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+    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);
+}
index f1136a3f1a91c159fa3a32d3158d8b139822de88..d876a0228d7acafa9cd7ab0bb8319097c127869d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
@@ -33,7 +38,8 @@ using std::string;
 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);
@@ -52,56 +58,56 @@ static void test_silence_padding (int channels)
        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);
diff --git a/test/skip_frame_test.cc b/test/skip_frame_test.cc
new file mode 100644 (file)
index 0000000..a77d845
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+    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 ()));
+}
+
index fed3ecabeb99283bcad12a4e0f542c5bef664b2c..de2108066bbc5c3147903bacdb17cbe759dafc61 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @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;
@@ -65,19 +70,19 @@ BOOST_AUTO_TEST_CASE (stream_test)
                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);
 }
 
diff --git a/test/subrip_test.cc b/test/subrip_test.cc
new file mode 100644 (file)
index 0000000..e18ee2e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+    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");
+}
index 0b87b8062a67adc503bf076ba32624cf7678a2ff..71cd50ac928b9b9cab897abc445532c7c918a55e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
@@ -29,6 +35,7 @@
 #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>
@@ -41,6 +48,8 @@ using std::cerr;
 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:
@@ -53,20 +62,27 @@ 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);
@@ -94,11 +110,50 @@ new_test_film (string name)
        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");
@@ -108,6 +163,9 @@ check_file (boost::filesystem::path ref, boost::filesystem::path check)
        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);
@@ -115,7 +173,11 @@ check_file (boost::filesystem::path ref, boost::filesystem::path check)
                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;
        }
 
@@ -127,27 +189,28 @@ check_file (boost::filesystem::path ref, boost::filesystem::path check)
 }
 
 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)));
 }
@@ -168,7 +231,7 @@ check_xml (xmlpp::Element* ref, xmlpp::Element* test, list<string> ignore)
 
        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 */
 
@@ -197,6 +260,9 @@ check_xml (xmlpp::Element* ref, xmlpp::Element* test, list<string> ignore)
                ++k;
                ++l;
        }
+
+       BOOST_CHECK (k == ref_children.end ());
+       BOOST_CHECK (l == test_children.end ());
 }
 
 void
@@ -218,10 +284,19 @@ wait_for_jobs ()
                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";
                        }
                }
        }
@@ -230,3 +305,12 @@ wait_for_jobs ()
 
        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 ());
+}
index dd007e8c9a15033e0db8112b4f3856bb5582c3e4..c9e477e02b53fffbce03b3f5fe2d7e0522bb3d11 100644 (file)
 #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);
index 8da9b46a891fa8daeed4cd406eb784d23b8c7cc6..5ad78103fb98d05c40cdf4e2477c29556cc17757 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  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"
diff --git a/test/upmixer_a_test.cc b/test/upmixer_a_test.cc
new file mode 100644 (file)
index 0000000..b115db2
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+    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");
+}
index 40a2835f1515f68a47ba5b9981a812778ad10295..f5bf94c011464265754e59aeddde65dc44e09dc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 
 */
 
+/** @file  test/util_test.cc
+ *  @brief Test various utility methods.
+ */
+
 #include <boost/test/unit_test.hpp>
 #include "lib/util.h"
 #include "lib/exceptions.h"
@@ -54,6 +58,24 @@ BOOST_AUTO_TEST_CASE (md5_digest_test)
        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);
@@ -67,6 +89,12 @@ BOOST_AUTO_TEST_CASE (divide_with_round_test)
        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");
index 09df146737a95c3cff4b1e28e5eef0d655f84218..371ea7a539cbed4c43214409ed31b76f14920002 100644 (file)
@@ -10,42 +10,66 @@ def configure(conf):
                               """, 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 = ''
diff --git a/test/xml_subtitle_test.cc b/test/xml_subtitle_test.cc
new file mode 100644 (file)
index 0000000..561b102
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+    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 ()));
+}
diff --git a/wscript b/wscript
index 62b47d9a4a9427c15f9ae5a1d78633d800a89555..6b55dbdad09f5aabde6ef3dfaf8f23fcf2145175 100644 (file)
--- a/wscript
+++ b/wscript
@@ -5,7 +5,7 @@ import distutils
 import distutils.spawn
 
 APPNAME = 'dcpomatic'
-VERSION = '1.76.2devel'
+VERSION = '2.0.14devel'
 
 def options(opt):
     opt.load('compiler_cxx')
@@ -60,10 +60,15 @@ def dynamic_openjpeg(conf):
     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:
@@ -87,9 +92,13 @@ def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh):
         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
@@ -226,7 +235,7 @@ def configure(conf):
     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')
@@ -239,6 +248,10 @@ def configure(conf):
     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)
@@ -261,6 +274,7 @@ def configure(conf):
         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)
 
@@ -275,6 +289,7 @@ def configure(conf):
         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)
 
@@ -289,6 +304,7 @@ def configure(conf):
         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)
 
@@ -307,6 +323,7 @@ def configure(conf):
         dynamic_ffmpeg(conf)
         dynamic_openjpeg(conf)
         dynamic_dcp(conf)
+        dynamic_sub(conf)
         dynamic_ssh(conf)
 
     # Not packaging; just a straight build
@@ -318,6 +335,7 @@ def configure(conf):
         dynamic_boost(conf, boost_lib_suffix, boost_thread)
         dynamic_ffmpeg(conf)
         dynamic_dcp(conf)
+        dynamic_sub(conf)
         dynamic_openjpeg(conf)
         dynamic_ssh(conf)
 
@@ -332,6 +350,8 @@ def configure(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>
@@ -368,10 +388,10 @@ def build(bld):
         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)
 
@@ -437,4 +457,4 @@ def pot_merge(bld):
     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')