From: Carl Hetherington Date: Tue, 9 Jul 2013 19:35:39 +0000 (+0100) Subject: Merge master. X-Git-Tag: v2.0.48~1337^2~302 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=89115db77729a2c99f1a09ff6a461720e16f889e;hp=-c Merge master. --- 89115db77729a2c99f1a09ff6a461720e16f889e diff --combined cscript index c51f3a033,6a9b48a89..e7ef219d4 --- a/cscript +++ b/cscript @@@ -6,33 -6,32 +6,32 @@@ def dependencies(target) if target.platform == 'windows': return () else: - return (('openjpeg-cdist', None), - ('libcxml', None), - ('ffmpeg-cdist', '7a23ec9c771184ab563cfe24ad9b427f38368961'), - ('libdcp', None)) + # XXX: should be some versions in here + return (('ffmpeg-cdist', 'e797834288eaf05a2f406524ae04aaa0f114cb08'), + ('libdcp', 'v0.54')) - def build(env, target): - cmd = './waf configure --prefix=%s' % env.work_dir_cscript() + def build(target): + cmd = './waf configure --prefix=%s' % target.work_dir_cscript() if target.platform == 'windows': cmd += ' --target-windows' elif target.platform == 'linux': cmd += ' --static' - env.command(cmd) + target.command(cmd) - env.command('./waf') + target.command('./waf') if target.platform == 'linux' or target.platform == 'osx': - env.command('./waf install') + target.command('./waf install') - def package(env, target, version): + def package(target, version): if target.platform == 'windows': shutil.copyfile('build/platform/windows/installer.%s.nsi' % target.bits, 'build/platform/windows/installer2.%s.nsi' % target.bits) - env.command('sed -i "s~%%resources%%~%s/platform/windows~g" build/platform/windows/installer2.%s.nsi' % (os.getcwd(), target.bits)) - env.command('sed -i "s~%%deps%%~%s~g" build/platform/windows/installer2.%s.nsi' % (env.windows_prefix, target.bits)) - env.command('sed -i "s~%%binaries%%~%s/build~g" build/platform/windows/installer2.%s.nsi' % (os.getcwd(), target.bits)) - env.command('sed -i "s~%%bits%%~32~g" build/platform/windows/installer2.%s.nsi' % target.bits) - env.command('makensis build/platform/windows/installer2.%s.nsi' % target.bits) + target.command('sed -i "s~%%resources%%~%s/platform/windows~g" build/platform/windows/installer2.%s.nsi' % (os.getcwd(), target.bits)) + target.command('sed -i "s~%%deps%%~%s~g" build/platform/windows/installer2.%s.nsi' % (target.windows_prefix, target.bits)) + target.command('sed -i "s~%%binaries%%~%s/build~g" build/platform/windows/installer2.%s.nsi' % (os.getcwd(), target.bits)) + target.command('sed -i "s~%%bits%%~32~g" build/platform/windows/installer2.%s.nsi' % target.bits) + target.command('makensis build/platform/windows/installer2.%s.nsi' % target.bits) return os.path.abspath(glob.glob('build/platform/windows/*%s*.exe' % target.bits)[0]) elif target.platform == 'linux': if target.bits == 32: @@@ -41,21 -40,21 +40,21 @@@ cpu = 'amd64' shutil.copyfile('platform/linux/control-%s-%d' % (target.version, target.bits), 'debian/control') - env.command('./waf dist') + target.command('./waf dist') f = open('debian/files', 'w') - print >>f,'dvdomatic_%s-1_%s.deb video extra' % (version, cpu) + print >>f,'dcpomatic_%s-1_%s.deb video extra' % (version, cpu) shutil.rmtree('build/deb', ignore_errors=True) os.makedirs('build/deb') os.chdir('build/deb') - shutil.move('../../dvdomatic-%s.tar.bz2' % version, 'dvdomatic_%s.orig.tar.bz2' % version) - target.command('tar xjf dvdomatic_%s.orig.tar.bz2' % version) - os.chdir('dvdomatic-%s' % version) + shutil.move('../../dcpomatic-%s.tar.bz2' % version, 'dcpomatic_%s.orig.tar.bz2' % version) - env.command('tar xjf dcpomatic_%s.orig.tar.bz2' % version) ++ target.command('tar xjf dcpomatic_%s.orig.tar.bz2' % version) + os.chdir('dcpomatic-%s' % version) - env.command('dch -b -v %s-1 "New upstream release."' % version) - env.set('CDIST_LINKFLAGS', env.get('LINKFLAGS')) - env.set('CDIST_CXXFLAGS', env.get('CXXFLAGS')) - env.set('CDIST_PKG_CONFIG_PATH', env.get('PKG_CONFIG_PATH')) - env.command('dpkg-buildpackage') + target.command('dch -b -v %s-1 "New upstream release."' % version) + target.set('CDIST_LINKFLAGS', target.get('LINKFLAGS')) + target.set('CDIST_CXXFLAGS', target.get('CXXFLAGS')) + target.set('CDIST_PKG_CONFIG_PATH', target.get('PKG_CONFIG_PATH')) + target.command('dpkg-buildpackage') debs = [] for p in glob.glob('../*.deb'): @@@ -63,16 -62,16 +62,16 @@@ return debs elif target.platform == 'osx': - env.command('bash platform/osx/make_dmg.sh') + target.command('bash platform/osx/make_dmg.sh') return os.path.abspath(glob.glob('build/platform/osx/DVD-o-matic*.dmg')[0]) - def make_pot(env): - env.command('./waf pot') + def make_pot(target): + target.command('./waf pot') - return [os.path.abspath('build/src/lib/libdvdomatic.pot'), - os.path.abspath('build/src/wx/libdvdomatic-wx.pot'), - os.path.abspath('build/src/tools/dvdomatic.pot')] + return [os.path.abspath('build/src/lib/libdcpomatic.pot'), + os.path.abspath('build/src/wx/libdcpomatic-wx.pot'), + os.path.abspath('build/src/tools/dcpomatic.pot')] - def make_manual(env): + def make_manual(target): os.chdir('doc/manual') - env.command('make') + target.command('make') return [os.path.abspath('pdf'), os.path.abspath('html')] diff --combined platform/linux/control-12.04-32 index dc104958a,753df4931..a337944c3 --- a/platform/linux/control-12.04-32 +++ b/platform/linux/control-12.04-32 @@@ -1,24 -1,24 +1,24 @@@ -Source: dvdomatic +Source: dcpomatic Section: video Priority: extra Maintainer: Carl Hetherington - Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libwxgtk2.8-dev (>= 2.8.12.1), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7) + Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7), libgtk2.0-dev (>= 2.24.10) Standards-Version: 3.9.3 -Homepage: http://carlh.net/software/dvdomatic +Homepage: http://carlh.net/software/dcpomatic -Package: dvdomatic +Package: dcpomatic Architecture: i386 - Depends: libc6 (>= 2.15), libwxgtk2.8-0 (>= 2.8.12.1), libssh-4 (>= 0.5.2), libboost-filesystem1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libsndfile1 (>= 1.0.25), libmagick++4 (>= 8:6.6.9.7), libxml++2.6-2 (>= 2.34.1) + Depends: libc6 (>= 2.15), libssh-4 (>= 0.5.2), libboost-filesystem1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libsndfile1 (>= 1.0.25), libmagick++4 (>= 8:6.6.9.7), libxml++2.6-2 (>= 2.34.1), libgtk2.0-0 (>= 2.24.10) Description: Generator of Digital Cinema Packages (DCPs) - DVD-o-matic generates Digital Cinema Packages (DCPs) from video and audio + DCP-o-matic generates Digital Cinema Packages (DCPs) from video and audio files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant digital projectors. -Package: dvdomatic-dbg +Package: dcpomatic-dbg Architecture: i386 Section: debug Priority: extra -Depends: ${dvdomatic:Depends}, ${misc:Depends} -Description: debugging symbols for dvdomatic - This package contains the debugging symbols for dvdomatic. +Depends: ${dcpomatic:Depends}, ${misc:Depends} +Description: debugging symbols for dcpomatic + This package contains the debugging symbols for dcpomatic. diff --combined platform/linux/control-12.04-64 index 09c636e4a,bb7672f53..c2cfdf5d7 --- a/platform/linux/control-12.04-64 +++ b/platform/linux/control-12.04-64 @@@ -1,24 -1,24 +1,24 @@@ -Source: dvdomatic +Source: dcpomatic Section: video Priority: extra Maintainer: Carl Hetherington - Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libwxgtk2.8-dev (>= 2.8.12.1), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7) + Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7), libgtk2.0-dev (>= 2.4.10) Standards-Version: 3.9.3 -Homepage: http://carlh.net/software/dvdomatic +Homepage: http://carlh.net/software/dcpomatic -Package: dvdomatic +Package: dcpomatic Architecture: amd64 Depends: libc6 (>= 2.15), libwxgtk2.8-0 (>= 2.8.12.1), libssh-4 (>= 0.5.2), libboost-filesystem1.46.1 (>= 1.46.1), libboost-thread1.46.1 (>= 1.46.1), libsndfile1 (>= 1.0.25), libmagick++4 (>= 8:6.6.9.7), libxml++2.6-2 (>= 2.34.1) Description: Generator of Digital Cinema Packages (DCPs) - DVD-o-matic generates Digital Cinema Packages (DCPs) from video and audio + DCP-o-matic generates Digital Cinema Packages (DCPs) from video and audio files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant digital projectors. -Package: dvdomatic-dbg +Package: dcpomatic-dbg Architecture: amd64 Section: debug Priority: extra -Depends: ${dvdomatic:Depends}, ${misc:Depends} -Description: debugging symbols for dvdomatic - This package contains the debugging symbols for dvdomatic. +Depends: ${dcpomatic:Depends}, ${misc:Depends} +Description: debugging symbols for dcpomatic + This package contains the debugging symbols for dcpomatic. diff --combined platform/linux/control-12.10-32 index 1330b3e5f,116e26b75..14dc5a0dc --- a/platform/linux/control-12.10-32 +++ b/platform/linux/control-12.10-32 @@@ -1,23 -1,23 +1,23 @@@ -Source: dvdomatic +Source: dcpomatic Section: video Priority: extra Maintainer: Carl Hetherington - Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libwxgtk2.8-dev (>= 2.8.12.1), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7) + Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7), libgtk2.0-dev (>= 2.24.13) Standards-Version: 3.9.3 -Homepage: http://carlh.net/software/dvdomatic +Homepage: http://carlh.net/software/dcpomatic -Package: dvdomatic +Package: dcpomatic Architecture: i386 - Depends: libc6 (>= 2.15), libwxgtk2.8-0 (>= 2.8.12.1), libssh-4 (>= 0.5.2), libboost-filesystem1.49.0 (>= 1.49.0), libboost-thread1.49.0 (>= 1.49.0), libsndfile1 (>= 1.0.25), libmagick++5 (>= 8:6.7.7.10), libxml++2.6-2 (>= 2.34.2) + Depends: libc6 (>= 2.15), libssh-4 (>= 0.5.2), libboost-filesystem1.49.0 (>= 1.49.0), libboost-thread1.49.0 (>= 1.49.0), libsndfile1 (>= 1.0.25), libmagick++5 (>= 8:6.7.7.10), libxml++2.6-2 (>= 2.34.2), libgtk2.0-0 (>= 2.24.13) Description: Generator of Digital Cinema Packages (DCPs) - DVD-o-matic generates Digital Cinema Packages (DCPs) from video and audio + DCP-o-matic generates Digital Cinema Packages (DCPs) from video and audio files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant digital projectors. -Package: dvdomatic-dbg +Package: dcpomatic-dbg Architecture: i386 Section: debug Priority: extra -Depends: ${dvdomatic:Depends}, ${misc:Depends} -Description: debugging symbols for dvdomatic - This package contains the debugging symbols for dvdomatic. +Depends: ${dcpomatic:Depends}, ${misc:Depends} +Description: debugging symbols for dcpomatic + This package contains the debugging symbols for dcpomatic. diff --combined platform/linux/control-12.10-64 index ea1c491ed,486d1f225..8a8019f01 --- a/platform/linux/control-12.10-64 +++ b/platform/linux/control-12.10-64 @@@ -1,24 -1,24 +1,24 @@@ -Source: dvdomatic +Source: dcpomatic Section: video Priority: extra Maintainer: Carl Hetherington - Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libwxgtk2.8-dev (>= 2.8.12.1), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7) + Build-Depends: debhelper (>= 8.0.0), python (>= 2.7.3), g++ (>= 4:4.6.3), pkg-config (>= 0.26), libssh-dev (>= 0.5.2), libboost-filesystem-dev (>= 1.46.0), libboost-thread-dev (>= 1.46.0), libsndfile1-dev (>= 1.0.25), libmagick++-dev (>= 8:6.6.9.7), libgtk2.0-dev (>= 2.24.13) Standards-Version: 3.9.3 -Homepage: http://carlh.net/software/dvdomatic +Homepage: http://carlh.net/software/dcpomatic -Package: dvdomatic +Package: dcpomatic Architecture: amd64 - Depends: libc6 (>= 2.15), libwxgtk2.8-0 (>= 2.8.12.1), libssh-4 (>= 0.5.2), libboost-filesystem1.49.0 (>= 1.49.0), libboost-thread1.49.0 (>= 1.49.0), libsndfile1 (>= 1.0.25), libmagick++5 (>= 8:6.7.7.10), libxml++2.6-2 (>= 2.34.2) + Depends: libc6 (>= 2.15), libssh-4 (>= 0.5.2), libboost-filesystem1.49.0 (>= 1.49.0), libboost-thread1.49.0 (>= 1.49.0), libsndfile1 (>= 1.0.25), libmagick++5 (>= 8:6.7.7.10), libxml++2.6-2 (>= 2.34.2), libgtk2.0-0 (>= 2.24.13) Description: Generator of Digital Cinema Packages (DCPs) - DVD-o-matic generates Digital Cinema Packages (DCPs) from video and audio + DCP-o-matic generates Digital Cinema Packages (DCPs) from video and audio files (such as those from DVDs or Blu-Rays) for presentation on DCI-compliant digital projectors. -Package: dvdomatic-dbg +Package: dcpomatic-dbg Architecture: amd64 Section: debug Priority: extra -Depends: ${dvdomatic:Depends}, ${misc:Depends} -Description: debugging symbols for dvdomatic - This package contains the debugging symbols for dvdomatic. +Depends: ${dcpomatic:Depends}, ${misc:Depends} +Description: debugging symbols for dcpomatic + This package contains the debugging symbols for dcpomatic. diff --combined platform/osx/make_dmg.sh index 1fcdc6d06,a409d82fe..debd4aaac --- a/platform/osx/make_dmg.sh +++ b/platform/osx/make_dmg.sh @@@ -5,8 -5,8 +5,8 @@@ version=`cat wscript | egrep ^VERSION # DMG size in megabytes DMG_SIZE=256 WORK=build/platform/osx - ENV=/Users/carl/Environments/osx/10.8 - DEPS=/Users/carl/cdist + ENV=/Users/carl/Environments/osx + ROOT=/Users/carl/cdist appdir="DVD-o-matic.app" approot=$appdir/Contents @@@ -19,46 -19,58 +19,58 @@@ mkdir -p $WORK/$maco mkdir -p $WORK/$libs mkdir -p $WORK/$resources - cp build/src/tools/dcpomatic $WORK/$macos/ - cp build/src/lib/libdcpomatic.dylib $WORK/$libs/ - cp build/src/wx/libdcpomatic-wx.dylib $WORK/$libs/ - cp $DEPS/lib/libdcp.dylib $WORK/$libs/ - cp $DEPS/lib/libasdcp-libdcp.dylib $WORK/$libs/ - cp $DEPS/lib/libkumu-libdcp.dylib $WORK/$libs/ - cp $DEPS/lib/libopenjpeg*.dylib $WORK/$libs/ - cp $DEPS/lib/libavformat*.dylib $WORK/$libs/ - cp $DEPS/lib/libavfilter*.dylib $WORK/$libs/ - cp $DEPS/lib/libavutil*.dylib $WORK/$libs/ - cp $DEPS/lib/libavcodec*.dylib $WORK/$libs/ - cp $DEPS/lib/libswscale*.dylib $WORK/$libs/ - cp $DEPS/lib/libpostproc*.dylib $WORK/$libs/ - cp $DEPS/lib/libswresample*.dylib $WORK/$libs/ - cp $ENV/lib/libboost_system.dylib $WORK/$libs/ - cp $ENV/lib/libboost_filesystem.dylib $WORK/$libs/ - cp $ENV/lib/libboost_thread.dylib $WORK/$libs/ - cp $ENV/lib/libboost_date_time.dylib $WORK/$libs/ - cp $ENV/lib/libssl*.dylib $WORK/$libs/ - cp $ENV/lib/libcrypto*.dylib $WORK/$libs/ - cp $ENV/lib/libxml++-2.6*.dylib $WORK/$libs/ - cp $ENV/lib/libxml2*.dylib $WORK/$libs/ - cp $ENV/lib/libglibmm-2.4*.dylib $WORK/$libs/ - cp $ENV/lib/libgobject*.dylib $WORK/$libs/ - cp $ENV/lib/libgthread*.dylib $WORK/$libs/ - cp $ENV/lib/libgmodule*.dylib $WORK/$libs/ - cp $ENV/lib/libsigc*.dylib $WORK/$libs/ - cp $ENV/lib/libglib-2*.dylib $WORK/$libs/ - cp $ENV/lib/libintl*.dylib $WORK/$libs/ - cp $ENV/lib/libsndfile*.dylib $WORK/$libs/ - cp $ENV/lib/libMagick++*.dylib $WORK/$libs/ - cp $ENV/lib/libMagickCore*.dylib $WORK/$libs/ - cp $ENV/lib/libMagickWand*.dylib $WORK/$libs/ - cp $ENV/lib/libssh*.dylib $WORK/$libs/ - cp $ENV/lib/libwx*.dylib $WORK/$libs/ - cp $ENV/lib/libfontconfig*.dylib $WORK/$libs/ - cp $ENV/lib/libfreetype*.dylib $WORK/$libs/ - cp $ENV/lib/libexpat*.dylib $WORK/$libs/ + function universal_copy { + echo $2 + for f in $1/32/$2; do + if [ -h $f ]; then + ln -s $(readlink $f) $3/`basename $f` + else + g=`echo $f | sed -e "s/\/32\//\/64\//g"` + mkdir -p $3 + lipo -create $f $g -output $3/`basename $f` + fi + done + } + + universal_copy $ROOT src/dvdomatic/build/src/tools/dvdomatic $WORK/$macos + universal_copy $ROOT src/dvdomatic/build/src/lib/libdvdomatic.dylib $WORK/$libs + universal_copy $ROOT src/dvdomatic/build/src/wx/libdvdomatic-wx.dylib $WORK/$libs + universal_copy $ROOT lib/libcxml.dylib $WORK/$libs + universal_copy $ROOT lib/libdcp.dylib $WORK/$libs + universal_copy $ROOT lib/libasdcp-libdcp.dylib $WORK/$libs + universal_copy $ROOT lib/libkumu-libdcp.dylib $WORK/$libs + universal_copy $ROOT lib/libopenjpeg*.dylib $WORK/$libs + universal_copy $ROOT lib/libavformat*.dylib $WORK/$libs + universal_copy $ROOT lib/libavfilter*.dylib $WORK/$libs + universal_copy $ROOT lib/libavutil*.dylib $WORK/$libs + universal_copy $ROOT lib/libavcodec*.dylib $WORK/$libs + universal_copy $ROOT lib/libswscale*.dylib $WORK/$libs + universal_copy $ROOT lib/libpostproc*.dylib $WORK/$libs + universal_copy $ROOT lib/libswresample*.dylib $WORK/$libs + universal_copy $ENV lib/libboost_system.dylib $WORK/$libs + universal_copy $ENV lib/libboost_filesystem.dylib $WORK/$libs + universal_copy $ENV lib/libboost_thread.dylib $WORK/$libs + universal_copy $ENV lib/libboost_date_time.dylib $WORK/$libs + universal_copy $ENV lib/libxml++-2.6*.dylib $WORK/$libs + universal_copy $ENV lib/libxml2*.dylib $WORK/$libs + universal_copy $ENV lib/libglibmm-2.4*.dylib $WORK/$libs + universal_copy $ENV lib/libgobject*.dylib $WORK/$libs + universal_copy $ENV lib/libgthread*.dylib $WORK/$libs + universal_copy $ENV lib/libgmodule*.dylib $WORK/$libs + universal_copy $ENV lib/libsigc*.dylib $WORK/$libs + universal_copy $ENV lib/libglib-2*.dylib $WORK/$libs + universal_copy $ENV lib/libintl*.dylib $WORK/$libs + universal_copy $ENV lib/libsndfile*.dylib $WORK/$libs + universal_copy $ENV lib/libMagick++*.dylib $WORK/$libs + universal_copy $ENV lib/libMagickCore*.dylib $WORK/$libs + universal_copy $ENV lib/libMagickWand*.dylib $WORK/$libs + universal_copy $ENV lib/libssh*.dylib $WORK/$libs + universal_copy $ENV lib/libwx*.dylib $WORK/$libs + universal_copy $ENV lib/libfontconfig*.dylib $WORK/$libs + universal_copy $ENV lib/libfreetype*.dylib $WORK/$libs + universal_copy $ENV lib/libexpat*.dylib $WORK/$libs -for obj in $WORK/$macos/dvdomatic $WORK/$libs/*.dylib; do +for obj in $WORK/$macos/dcpomatic $WORK/$libs/*.dylib; do deps=`otool -L $obj | awk '{print $1}' | egrep "(/Users/carl|libboost|libssh)"` changes="" for dep in $deps; do @@@ -70,13 -82,12 +82,12 @@@ fi done - pwd cp build/platform/osx/Info.plist $WORK/$approot -cp icons/dvdomatic.icns $WORK/$resources/DVD-o-matic.icns +cp icons/dcpomatic.icns $WORK/$resources/DVD-o-matic.icns -tmp_dmg=$WORK/dvdomatic_tmp.dmg -dmg="$WORK/DVD-o-matic $version.dmg" -vol_name=DVD-o-matic-$version +tmp_dmg=$WORK/dcpomatic_tmp.dmg +dmg="$WORK/DCP-o-matic $version.dmg" +vol_name=DCP-o-matic-$version mkdir -p $WORK/$vol_name @@@ -100,7 -111,7 +111,7 @@@ echo set arrangement of theViewOptions to not arranged set icon size of theViewOptions to 64 make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"} - set position of item "DVD-o-matic.app" of container window to {90, 80} + set position of item "DCP-o-matic.app" of container window to {90, 80} set position of item "Applications" of container window to {310, 80} close open @@@ -117,8 -128,8 +128,8 @@@ syn umount $device hdiutil eject $device hdiutil convert -format UDZO $tmp_dmg -imagekey zlib-level=9 -o "$dmg" -sips -i $WORK/$resources/DVD-o-matic.icns -DeRez -only icns $WORK/$resources/DVD-o-matic.icns > $WORK/$resources/DVD-o-matic.rsrc -Rez -append $WORK/$resources/DVD-o-matic.rsrc -o "$dmg" +sips -i $WORK/$resources/DCP-o-matic.icns +DeRez -only icns $WORK/$resources/DCP-o-matic.icns > $WORK/$resources/DCP-o-matic.rsrc +Rez -append $WORK/$resources/DCP-o-matic.rsrc -o "$dmg" SetFile -a C "$dmg" diff --combined platform/windows/installer.nsi.32.in index 3a2cdb9e8,6dd1de2d9..314fe176f --- a/platform/windows/installer.nsi.32.in +++ b/platform/windows/installer.nsi.32.in @@@ -1,14 -1,14 +1,14 @@@ !include "MUI2.nsh" -Name "DVD-o-matic" +Name "DCP-o-matic" RequestExecutionLevel admin -outFile "DVD-o-matic @version@ 32-bit Installer.exe" -!define MUI_ICON "%resources%/dvdomatic.ico" -!define MUI_UNICON "%resources%/dvdomatic.ico" -!define MUI_SPECIALBITMAP "%resources%/dvdomatic.bmp" +outFile "DCP-o-matic @version@ 32-bit Installer.exe" +!define MUI_ICON "%resources%/dcpomatic.ico" +!define MUI_UNICON "%resources%/dcpomatic.ico" +!define MUI_SPECIALBITMAP "%resources%/dcpomatic.bmp" -InstallDir "$PROGRAMFILES\DVD-o-matic" +InstallDir "$PROGRAMFILES\DCP-o-matic" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "../../../COPYING" @@@ -32,6 -32,7 +32,7 @@@ File "%deps%/bin/avcodec-55.dll File "%deps%/bin/avfilter-3.dll" File "%deps%/bin/avformat-55.dll" File "%deps%/bin/avutil-52.dll" + File "%deps%/bin/avdevice-55.dll" File "%deps%/bin/dcp.dll" File "%deps%/bin/libintl-8.dll" File "%deps%/bin/kumu-libdcp.dll" @@@ -79,15 -80,15 +80,16 @@@ File "%deps%/bin/libpixman-1-0.dll File "%deps%/bin/libfontconfig-1.dll" File "%deps%/bin/libexpat-1.dll" File "%deps%/bin/libbz2.dll" +File "%deps%/bin/cxml.dll" + File "%deps%/bin/ffprobe.exe" -File "%binaries%/src/wx/dvdomatic-wx.dll" -File "%binaries%/src/lib/dvdomatic.dll" -File "%binaries%/src/tools/dvdomatic.exe" -File "%binaries%/src/tools/dvdomatic_batch.exe" -File "%binaries%/src/tools/makedcp.exe" -File "%binaries%/src/tools/servomatic_cli.exe" -File "%binaries%/src/tools/servomatic_gui.exe" +File "%binaries%/src/wx/dcpomatic-wx.dll" +File "%binaries%/src/lib/dcpomatic.dll" +File "%binaries%/src/tools/dcpomatic.exe" +File "%binaries%/src/tools/dcpomatic_batch.exe" +File "%binaries%/src/tools/dcpomatic_cli.exe" +File "%binaries%/src/tools/dcpomatic_server_cli.exe" +File "%binaries%/src/tools/dcpomatic_server.exe" # I don't know why, but sometimes it seems that # delegates.xml must be in with the binaries, and @@@ -97,34 -98,34 +99,34 @@@ SetOutPath "$PROFILE\.magick File "%deps%/etc/ImageMagick/delegates.xml" SetOutPath "$INSTDIR\locale\fr\LC_MESSAGES" -File "%binaries%/src/lib/mo/fr_FR/libdvdomatic.mo" -File "%binaries%/src/wx/mo/fr_FR/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/fr_FR/dvdomatic.mo" +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" SetOutPath "$INSTDIR\locale\it\LC_MESSAGES" -File "%binaries%/src/lib/mo/it_IT/libdvdomatic.mo" -File "%binaries%/src/wx/mo/it_IT/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/it_IT/dvdomatic.mo" +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" SetOutPath "$INSTDIR\locale\es\LC_MESSAGES" -File "%binaries%/src/lib/mo/es_ES/libdvdomatic.mo" -File "%binaries%/src/wx/mo/es_ES/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/es_ES/dvdomatic.mo" +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" SetOutPath "$INSTDIR\locale\sv\LC_MESSAGES" -File "%binaries%/src/lib/mo/sv_SE/libdvdomatic.mo" -File "%binaries%/src/wx/mo/sv_SE/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/sv_SE/dvdomatic.mo" +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" -CreateShortCut "$DESKTOP\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic_batch.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic batch converter.lnk" "$INSTDIR\bin\dcpomatic_batch.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" -CreateDirectory "$SMPROGRAMS\DVD-o-matic" -CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall DVD-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic_batch.exe" "" "$INSTDIR\bin\dvdomatic_batch.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" "$INSTDIR\bin\servomatic_gui.exe" 0 +CreateDirectory "$SMPROGRAMS\DCP-o-matic" +CreateShortCut "$SMPROGRAMS\DCP-o-matic\Uninstall DCP-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" "$INSTDIR\bin\dcpomatic.exe" 0 +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\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" "$INSTDIR\bin\dcpomatic_server.exe" 0 -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "DisplayName" "DVD-o-matic (remove only)" -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "UninstallString" "$INSTDIR\Uninstall.exe" +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" WriteUninstaller "$INSTDIR\Uninstall.exe" @@@ -135,22 -136,12 +137,12 @@@ Section "Uninstall RMDir /r "$INSTDIR\*.*" RMDir "$INSTDIR" - <<<<<<< HEAD -Delete "$DESKTOP\DVD-o-matic.lnk" -Delete "$DESKTOP\DVD-o-matic batch converter.lnk" -Delete "$DESKTOP\DVD-o-matic encode server.lnk" -Delete "$SMPROGRAMS\DVD-o-matic\*.*" -RmDir "$SMPROGRAMS\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" +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\DVD-o-matic.lnk" - Delete "$DESKTOP\DVD-o-matic batch converter.lnk" - Delete "$DESKTOP\DVD-o-matic encode server.lnk" - Delete "$SMPROGRAMS\DVD-o-matic\*.*" - RmDir "$SMPROGRAMS\DVD-o-matic" - DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DVD-o-matic" - DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" - >>>>>>> master SectionEnd diff --combined platform/windows/installer.nsi.64.in index f4f1e9068,e98a3a6d8..4bf959119 --- a/platform/windows/installer.nsi.64.in +++ b/platform/windows/installer.nsi.64.in @@@ -1,16 -1,16 +1,16 @@@ !include "MUI2.nsh" !include "x64.nsh" -Name "DVD-o-matic" +Name "DCP-o-matic" RequestExecutionLevel admin -outFile "DVD-o-matic @version@ 64-bit Installer.exe" -!define MUI_ICON "%resources%/dvdomatic.ico" -!define MUI_UNICON "%resources%/dvdomatic.ico" -!define MUI_SPECIALBITMAP "%resources%/dvdomatic.bmp" +outFile "DCP-o-matic @version@ 64-bit Installer.exe" +!define MUI_ICON "%resources%/dcpomatic.ico" +!define MUI_UNICON "%resources%/dcpomatic.ico" +!define MUI_SPECIALBITMAP "%resources%/dcpomatic.bmp" -InstallDir "$PROGRAMFILES\DVD-o-matic" +InstallDir "$PROGRAMFILES\DCP-o-matic" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "../../../COPYING" @@@ -32,7 -32,7 +32,7 @@@ ${If} ${RunningX64 ; disable registry redirection (enable access to 64-bit portion of registry) SetRegView 64 ; change install dir - StrCpy $INSTDIR "$PROGRAMFILES64\DVD-o-matic" + StrCpy $INSTDIR "$PROGRAMFILES64\DCP-o-matic" ${EndIf} SetOutPath "$INSTDIR\bin" @@@ -42,6 -42,7 +42,7 @@@ File "%deps%/bin/avcodec-55.dll File "%deps%/bin/avfilter-3.dll" File "%deps%/bin/avformat-55.dll" File "%deps%/bin/avutil-52.dll" + File "%deps%/bin/avdevice-55.dll" File "%deps%/bin/dcp.dll" File "%deps%/bin/libintl-8.dll" File "%deps%/bin/kumu-libdcp.dll" @@@ -89,15 -90,15 +90,16 @@@ File "%deps%/bin/libpixman-1-0.dll File "%deps%/bin/libfontconfig-1.dll" File "%deps%/bin/libexpat-1.dll" File "%deps%/bin/libbz2.dll" +File "%deps%/bin/cxml.dll" + File "%deps%/bin/ffprobe.exe" -File "%binaries%/src/wx/dvdomatic-wx.dll" -File "%binaries%/src/lib/dvdomatic.dll" -File "%binaries%/src/tools/dvdomatic.exe" -File "%binaries%/src/tools/dvdomatic_batch.exe" -File "%binaries%/src/tools/makedcp.exe" -File "%binaries%/src/tools/servomatic_cli.exe" -File "%binaries%/src/tools/servomatic_gui.exe" +File "%binaries%/src/wx/dcpomatic-wx.dll" +File "%binaries%/src/lib/dcpomatic.dll" +File "%binaries%/src/tools/dcpomatic.exe" +File "%binaries%/src/tools/dcpomatic_batch.exe" +File "%binaries%/src/tools/dcpomatic_cli.exe" +File "%binaries%/src/tools/dcpomatic_server_cli.exe" +File "%binaries%/src/tools/dcpomatic_server.exe" # I don't know why, but sometimes it seems that # delegates.xml must be in with the binaries, and @@@ -107,34 -108,34 +109,34 @@@ SetOutPath "$PROFILE\.magick File "%deps%/etc/ImageMagick/delegates.xml" SetOutPath "$INSTDIR\locale\fr\LC_MESSAGES" -File "%binaries%/src/lib/mo/fr_FR/libdvdomatic.mo" -File "%binaries%/src/wx/mo/fr_FR/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/fr_FR/dvdomatic.mo" +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" SetOutPath "$INSTDIR\locale\it\LC_MESSAGES" -File "%binaries%/src/lib/mo/it_IT/libdvdomatic.mo" -File "%binaries%/src/wx/mo/it_IT/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/it_IT/dvdomatic.mo" +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" SetOutPath "$INSTDIR\locale\es\LC_MESSAGES" -File "%binaries%/src/lib/mo/es_ES/libdvdomatic.mo" -File "%binaries%/src/wx/mo/es_ES/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/es_ES/dvdomatic.mo" +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" SetOutPath "$INSTDIR\locale\sv\LC_MESSAGES" -File "%binaries%/src/lib/mo/sv_SE/libdvdomatic.mo" -File "%binaries%/src/wx/mo/sv_SE/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/sv_SE/dvdomatic.mo" +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" -CreateShortCut "$DESKTOP\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic_batch.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic batch converter.lnk" "$INSTDIR\bin\dcpomatic_batch.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" -CreateDirectory "$SMPROGRAMS\DVD-o-matic" -CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall DVD-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic_batch.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" "$INSTDIR\bin\servomatic_gui.exe" 0 +CreateDirectory "$SMPROGRAMS\DCP-o-matic" +CreateShortCut "$SMPROGRAMS\DCP-o-matic\Uninstall DCP-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" "$INSTDIR\bin\dcpomatic.exe" 0 +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\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" "$INSTDIR\bin\dcpomatic_server.exe" 0 -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "DisplayName" "DVD-o-matic (remove only)" -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "UninstallString" "$INSTDIR\Uninstall.exe" +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" WriteUninstaller "$INSTDIR\Uninstall.exe" @@@ -145,12 -146,12 +147,12 @@@ Section "Uninstall RMDir /r "$INSTDIR\*.*" RMDir "$INSTDIR" -Delete "$DESKTOP\DVD-o-matic.lnk" -Delete "$DESKTOP\DVD-o-matic batch converter.lnk" -Delete "$DESKTOP\DVD-o-matic encode server.lnk" -Delete "$SMPROGRAMS\DVD-o-matic\*.*" -RmDir "$SMPROGRAMS\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" +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" SectionEnd diff --combined src/lib/config.cc index c9ec730f2,d2d7fa2fd..e0fbcc703 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@@ -22,12 -22,11 +22,12 @@@ #include #include #include +#include #include "config.h" #include "server.h" #include "scaler.h" #include "filter.h" -#include "format.h" +#include "ratio.h" #include "dcp_content_type.h" #include "sound_processor.h" @@@ -37,21 -36,19 +37,22 @@@ using std::vector using std::ifstream; using std::string; using std::ofstream; +using std::list; + using std::max; using boost::shared_ptr; +using boost::lexical_cast; +using boost::optional; Config* Config::_instance = 0; /** Construct default configuration */ Config::Config () - : _num_local_encoding_threads (2) + : _num_local_encoding_threads (max (2U, boost::thread::hardware_concurrency())) , _server_port (6192) - , _reference_scaler (Scaler::from_id (N_("bicubic"))) , _tms_path (N_(".")) , _sound_processor (SoundProcessor::from_id (N_("dolby_cp750"))) - , _default_format (0) + , _default_still_length (10) + , _default_container (Ratio::from_id ("185")) , _default_dcp_content_type (0) { _allowed_dcp_frame_rates.push_back (24); @@@ -60,61 -57,8 +61,61 @@@ _allowed_dcp_frame_rates.push_back (48); _allowed_dcp_frame_rates.push_back (50); _allowed_dcp_frame_rates.push_back (60); +} + +void +Config::read () +{ + if (!boost::filesystem::exists (file (false))) { + read_old_metadata (); + return; + } + + cxml::File f (file (false), "Config"); + optional c; + + _num_local_encoding_threads = f.number_child ("NumLocalEncodingThreads"); + _default_directory = f.string_child ("DefaultDirectory"); + _server_port = f.number_child ("ServerPort"); - ifstream f (file().c_str ()); + list > servers = f.node_children ("Server"); + for (list >::iterator i = servers.begin(); i != servers.end(); ++i) { + _servers.push_back (new ServerDescription (*i)); + } + + _tms_ip = f.string_child ("TMSIP"); + _tms_path = f.string_child ("TMSPath"); + _tms_user = f.string_child ("TMSUser"); + _tms_password = f.string_child ("TMSPassword"); + + c = f.optional_string_child ("SoundProcessor"); + if (c) { + _sound_processor = SoundProcessor::from_id (c.get ()); + } + + _language = f.optional_string_child ("Language"); + + c = f.optional_string_child ("DefaultContainer"); + if (c) { + _default_container = Ratio::from_id (c.get ()); + } + + c = f.optional_string_child ("DefaultDCPContentType"); + if (c) { + _default_dcp_content_type = DCPContentType::from_dci_name (c.get ()); + } + + _dcp_metadata.issuer = f.optional_string_child ("DCPMetadataIssuer").get_value_or (""); + _dcp_metadata.creator = f.optional_string_child ("DCPMetadataCreator").get_value_or (""); + + _default_dci_metadata = DCIMetadata (f.node_child ("DCIMetadata")); + _default_still_length = f.optional_number_child("DefaultStillLength").get_value_or (10); +} + +void +Config::read_old_metadata () +{ + ifstream f (file(true).c_str ()); string line; while (getline (f, line)) { if (line.empty ()) { @@@ -139,6 -83,10 +140,6 @@@ _default_directory = v; } else if (k == N_("server_port")) { _server_port = atoi (v.c_str ()); - } else if (k == N_("reference_scaler")) { - _reference_scaler = Scaler::from_id (v); - } else if (k == N_("reference_filter")) { - _reference_filters.push_back (Filter::from_id (v)); } else if (k == N_("server")) { _servers.push_back (ServerDescription::create_from_metadata (v)); } else if (k == N_("tms_ip")) { @@@ -153,8 -101,8 +154,8 @@@ _sound_processor = SoundProcessor::from_id (v); } else if (k == "language") { _language = v; - } else if (k == "default_format") { - _default_format = Format::from_metadata (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_dci_name (v); } else if (k == "dcp_metadata_issuer") { @@@ -165,23 -113,19 +166,23 @@@ _dcp_metadata.issue_date = v; } - _default_dci_metadata.read (k, v); + _default_dci_metadata.read_old_metadata (k, v); } } /** @return Filename to write configuration to */ string -Config::file () const +Config::file (bool old) const { boost::filesystem::path p; p /= g_get_user_config_dir (); boost::system::error_code ec; boost::filesystem::create_directory (p, ec); - p /= N_(".dvdomatic"); + if (old) { + p /= ".dvdomatic"; + } else { + p /= ".dcpomatic.xml"; + } return p.string (); } @@@ -191,13 -135,6 +192,13 @@@ Config::instance ( { if (_instance == 0) { _instance = new Config; + try { + _instance->read (); + } catch (...) { + /* configuration load failed; never mind, just + stick with the default. + */ + } } return _instance; @@@ -207,41 -144,44 +208,41 @@@ void Config::write () const { - ofstream f (file().c_str ()); - f << "num_local_encoding_threads " << _num_local_encoding_threads << "\n" - << "default_directory " << _default_directory << "\n" - << "server_port " << _server_port << "\n"; - - if (_reference_scaler) { - f << "reference_scaler " << _reference_scaler->id () << "\n"; - } + xmlpp::Document doc; + xmlpp::Element* root = doc.create_root_node ("Config"); - for (vector::const_iterator i = _reference_filters.begin(); i != _reference_filters.end(); ++i) { - f << "reference_filter " << (*i)->id () << "\n"; - } + root->add_child("NumLocalEncodingThreads")->add_child_text (lexical_cast (_num_local_encoding_threads)); + root->add_child("DefaultDirectory")->add_child_text (_default_directory); + root->add_child("ServerPort")->add_child_text (lexical_cast (_server_port)); for (vector::const_iterator i = _servers.begin(); i != _servers.end(); ++i) { - f << "server " << (*i)->as_metadata () << "\n"; + (*i)->as_xml (root->add_child ("Server")); } - f << "tms_ip " << _tms_ip << "\n"; - f << "tms_path " << _tms_path << "\n"; - f << "tms_user " << _tms_user << "\n"; - f << "tms_password " << _tms_password << "\n"; + root->add_child("TMSIP")->add_child_text (_tms_ip); + 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) { - f << "sound_processor " << _sound_processor->id () << "\n"; + root->add_child("SoundProcessor")->add_child_text (_sound_processor->id ()); } if (_language) { - f << "language " << _language.get() << "\n"; + root->add_child("Language")->add_child_text (_language.get()); } - if (_default_format) { - f << "default_format " << _default_format->as_metadata() << "\n"; + if (_default_container) { + root->add_child("DefaultContainer")->add_child_text (_default_container->id ()); } if (_default_dcp_content_type) { - f << "default_dcp_content_type " << _default_dcp_content_type->dci_name() << "\n"; + root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->dci_name ()); } - f << "dcp_metadata_issuer " << _dcp_metadata.issuer << "\n"; - f << "dcp_metadata_creator " << _dcp_metadata.creator << "\n"; - f << "dcp_metadata_issue_date " << _dcp_metadata.issue_date << "\n"; + root->add_child("DCPMetadataIssuer")->add_child_text (_dcp_metadata.issuer); + root->add_child("DCPMetadataCreator")->add_child_text (_dcp_metadata.creator); + + _default_dci_metadata.as_xml (root->add_child ("DCIMetadata")); + + root->add_child("DefaultStillLength")->add_child_text (lexical_cast (_default_still_length)); - _default_dci_metadata.write (f); + doc.write_to_file_formatted (file (false)); } string diff --combined src/lib/cross.cc index ffd44eb02,124697fb4..ee0ef89b2 --- a/src/lib/cross.cc +++ b/src/lib/cross.cc @@@ -20,27 -20,35 +20,35 @@@ #include #include #include "cross.h" - #ifdef DCPOMATIC_POSIX + #include "compose.hpp" + #include "log.h" -#ifdef DVDOMATIC_LINUX ++#ifdef DCPOMATIC_LINUX #include + #include #endif -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS - #include "windows.h" + #include + #undef DATADIR + #include #endif -#ifdef DVDOMATIC_OSX +#ifdef DCPOMATIC_OSX #include #endif using std::pair; + using std::list; using std::ifstream; using std::string; + using std::make_pair; + using boost::shared_ptr; void -dvdomatic_sleep (int s) +dcpomatic_sleep (int s) { -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX sleep (s); #endif -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS Sleep (s * 1000); #endif } @@@ -52,7 -60,7 +60,7 @@@ cpu_info ( pair info; info.second = 0; -#ifdef DVDOMATIC_LINUX +#ifdef DCPOMATIC_LINUX ifstream f ("/proc/cpuinfo"); while (f.good ()) { string l; @@@ -68,7 -76,7 +76,7 @@@ } #endif -#ifdef DVDOMATIC_OSX +#ifdef DCPOMATIC_OSX size_t N = sizeof (info.second); sysctlbyname ("hw.ncpu", &info.second, &N, 0, 0); char buffer[64]; @@@ -81,3 -89,101 +89,101 @@@ return info; } + void + run_ffprobe (boost::filesystem::path content, boost::filesystem::path out, shared_ptr log) + { -#ifdef DVDOMATIC_WINDOWS ++#ifdef DCPOMATIC_WINDOWS + SECURITY_ATTRIBUTES security; + security.nLength = sizeof (security); + security.bInheritHandle = TRUE; + security.lpSecurityDescriptor = 0; + + HANDLE child_stderr_read; + HANDLE child_stderr_write; + if (!CreatePipe (&child_stderr_read, &child_stderr_write, &security, 0)) { + log->log ("ffprobe call failed (could not CreatePipe)"); + return; + } + + wchar_t dir[512]; + GetModuleFileName (GetModuleHandle (0), dir, sizeof (dir)); + PathRemoveFileSpec (dir); + SetCurrentDirectory (dir); + + STARTUPINFO startup_info; + ZeroMemory (&startup_info, sizeof (startup_info)); + startup_info.cb = sizeof (startup_info); + startup_info.hStdError = child_stderr_write; + startup_info.dwFlags |= STARTF_USESTDHANDLES; + + wchar_t command[512]; + wcscpy (command, L"ffprobe.exe \""); + + wchar_t file[512]; + MultiByteToWideChar (CP_UTF8, 0, content.string().c_str(), -1, file, sizeof(file)); + wcscat (command, file); + + wcscat (command, L"\""); + + PROCESS_INFORMATION process_info; + ZeroMemory (&process_info, sizeof (process_info)); + if (!CreateProcess (0, command, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) { + log->log ("ffprobe call failed (could not CreateProcess)"); + return; + } + + FILE* o = fopen (out.string().c_str(), "w"); + if (!o) { + log->log ("ffprobe call failed (could not create output file)"); + return; + } + + CloseHandle (child_stderr_write); + + while (1) { + char buffer[512]; + DWORD read; + if (!ReadFile(child_stderr_read, buffer, sizeof(buffer), &read, 0) || read == 0) { + break; + } + fwrite (buffer, read, 1, o); + } + + fclose (o); + + WaitForSingleObject (process_info.hProcess, INFINITE); + CloseHandle (process_info.hProcess); + CloseHandle (process_info.hThread); + CloseHandle (child_stderr_read); + #else + string ffprobe = "ffprobe \"" + content.string() + "\" 2> \"" + out.string() + "\""; + log->log (String::compose ("Probing with %1", ffprobe)); + system (ffprobe.c_str ()); + #endif + } + + list > + mount_info () + { + list > m; + -#ifdef DVDOMATIC_LINUX ++#ifdef DCPOMATIC_LINUX + FILE* f = setmntent ("/etc/mtab", "r"); + if (!f) { + return m; + } + + while (1) { + struct mntent* mnt = getmntent (f); + if (!mnt) { + break; + } + + m.push_back (make_pair (mnt->mnt_dir, mnt->mnt_type)); + } + + endmntent (f); + #endif + + return m; + } diff --combined src/lib/cross.h index d185286b1,d9cc2d12f..a00fee679 --- a/src/lib/cross.h +++ b/src/lib/cross.h @@@ -17,9 -17,16 +17,15 @@@ */ -#include + #include + -class Log; - -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS #define WEXITSTATUS(w) (w) #endif -extern void dvdomatic_sleep (int); ++class Log; ++ +void dcpomatic_sleep (int); extern std::pair cpu_info (); + extern void run_ffprobe (boost::filesystem::path, boost::filesystem::path, boost::shared_ptr); + extern std::list > mount_info (); diff --combined src/lib/encoder.cc index c3865d2c1,0ac32d3bf..d3181acd9 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@@ -22,13 -22,20 +22,13 @@@ */ #include -#include -#include -#include #include "encoder.h" #include "util.h" -#include "options.h" #include "film.h" #include "log.h" -#include "exceptions.h" -#include "filter.h" #include "config.h" #include "dcp_video_frame.h" #include "server.h" -#include "format.h" #include "cross.h" #include "writer.h" @@@ -40,17 -47,20 +40,18 @@@ using std::stringstream using std::vector; using std::list; using std::cout; + using std::min; using std::make_pair; -using namespace boost; +using boost::shared_ptr; +using boost::optional; int const Encoder::_history_size = 25; /** @param f Film that we are encoding */ -Encoder::Encoder (shared_ptr f) +Encoder::Encoder (shared_ptr f, shared_ptr j) : _film (f) - , _video_frames_in (0) + , _job (j) , _video_frames_out (0) -#ifdef HAVE_SWRESAMPLE - , _swr_context (0) -#endif , _have_a_real_frame (false) , _terminate (false) { @@@ -68,6 -78,35 +69,6 @@@ Encoder::~Encoder ( void Encoder::process_begin () { - if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate()) { -#ifdef HAVE_SWRESAMPLE - - stringstream s; - s << String::compose (N_("Will resample audio from %1 to %2"), _film->audio_stream()->sample_rate(), _film->target_audio_sample_rate()); - _film->log()->log (s.str ()); - - /* We will be using planar float data when we call the resampler */ - _swr_context = swr_alloc_set_opts ( - 0, - _film->audio_stream()->channel_layout(), - AV_SAMPLE_FMT_FLTP, - _film->target_audio_sample_rate(), - _film->audio_stream()->channel_layout(), - AV_SAMPLE_FMT_FLTP, - _film->audio_stream()->sample_rate(), - 0, 0 - ); - - swr_init (_swr_context); -#else - throw EncodeError (_("Cannot resample audio as libswresample is not present")); -#endif - } else { -#ifdef HAVE_SWRESAMPLE - _swr_context = 0; -#endif - } - for (int i = 0; i < Config::instance()->num_local_encoding_threads (); ++i) { _threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, (ServerDescription *) 0))); } @@@ -80,13 -119,51 +81,13 @@@ } } - _writer.reset (new Writer (_film)); + _writer.reset (new Writer (_film, _job)); } void Encoder::process_end () { -#if HAVE_SWRESAMPLE - if (_film->audio_stream() && _film->audio_stream()->channels() && _swr_context) { - - shared_ptr out (new AudioBuffers (_film->audio_stream()->channels(), 256)); - - while (1) { - int const frames = swr_convert (_swr_context, (uint8_t **) out->data(), 256, 0, 0); - - if (frames < 0) { - throw EncodeError (_("could not run sample-rate converter")); - } - - if (frames == 0) { - break; - } - - out->set_frames (frames); - write_audio (out); - } - - swr_free (&_swr_context); - } -#endif - - if (_film->audio_channels() == 0 && _film->minimum_audio_channels() > 0) { - /* Put audio in where there is none at all */ - int64_t af = video_frames_to_audio_frames (_video_frames_out, 48000, _film->dcp_frame_rate ()); - while (af) { - int64_t const this_time = min (af, static_cast (24000)); - shared_ptr out (new AudioBuffers (_film->minimum_audio_channels(), this_time)); - out->make_silent (); - out->set_frames (this_time); - write_audio (out); - - af -= this_time; - } - } - boost::mutex::scoped_lock lock (_mutex); _film->log()->log (String::compose (N_("Clearing queue of %1"), _queue.size ())); @@@ -131,7 -208,7 +132,7 @@@ * or 0 if not known. */ float -Encoder::current_frames_per_second () const +Encoder::current_encoding_rate () const { boost::mutex::scoped_lock lock (_history_mutex); if (int (_time_history.size()) < _history_size) { @@@ -169,8 -246,15 +170,8 @@@ Encoder::frame_done ( } void -Encoder::process_video (shared_ptr image, bool same, boost::shared_ptr sub) +Encoder::process_video (shared_ptr image, bool same) { - FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate()); - - if (frc.skip && (_video_frames_in % 2)) { - ++_video_frames_in; - return; - } - boost::mutex::scoped_lock lock (_mutex); /* Wait until the queue has gone down a bit */ @@@ -198,12 -282,15 +199,12 @@@ frame_done (); } else { /* Queue this new frame for encoding */ - pair const s = Filter::ffmpeg_strings (_film->filters()); TIMING ("adding to queue of %1", _queue.size ()); - _queue.push_back (boost::shared_ptr ( + /* XXX: padding */ + _queue.push_back (shared_ptr ( new DCPVideoFrame ( - image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_film), - _film->subtitle_offset(), _film->subtitle_scale(), - _film->scaler(), _video_frames_out, _film->dcp_frame_rate(), s.second, - _film->colour_lut(), _film->j2k_bandwidth(), - _film->log() + image, _video_frames_out, _film->dcp_video_frame_rate(), + _film->colour_lut(), _film->j2k_bandwidth(), _film->log() ) )); @@@ -211,13 -298,49 +212,13 @@@ _have_a_real_frame = true; } - ++_video_frames_in; ++_video_frames_out; - - if (frc.repeat) { - _writer->repeat (_video_frames_out); - ++_video_frames_out; - frame_done (); - } } void Encoder::process_audio (shared_ptr data) { - if (!data->frames ()) { - return; - } - -#if HAVE_SWRESAMPLE - /* Maybe sample-rate convert */ - if (_swr_context) { - - /* Compute the resampled frames count and add 32 for luck */ - int const max_resampled_frames = ceil ((int64_t) data->frames() * _film->target_audio_sample_rate() / _film->audio_stream()->sample_rate()) + 32; - - shared_ptr resampled (new AudioBuffers (_film->audio_stream()->channels(), max_resampled_frames)); - - /* Resample audio */ - int const resampled_frames = swr_convert ( - _swr_context, (uint8_t **) resampled->data(), max_resampled_frames, (uint8_t const **) data->data(), data->frames() - ); - - if (resampled_frames < 0) { - throw EncodeError (_("could not run sample-rate converter")); - } - - resampled->set_frames (resampled_frames); - - /* And point our variables at the resampled audio */ - data = resampled; - } -#endif - - write_audio (data); + _writer->write (data); } void @@@ -260,7 -383,7 +261,7 @@@ Encoder::encoder_thread (ServerDescript } TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _queue.size()); - boost::shared_ptr vf = _queue.front (); + shared_ptr vf = _queue.front (); _film->log()->log (String::compose (N_("Encoder thread %1 pops frame %2 from queue"), boost::this_thread::get_id(), vf->frame()), Log::VERBOSE); _queue.pop_front (); @@@ -314,10 -437,34 +315,10 @@@ } if (remote_backoff > 0) { - dvdomatic_sleep (remote_backoff); + dcpomatic_sleep (remote_backoff); } lock.lock (); _condition.notify_all (); } } - -void -Encoder::write_audio (shared_ptr data) -{ - AudioMapping m (_film); - if (m.dcp_channels() != _film->audio_channels()) { - - /* Remap and pad with silence */ - - shared_ptr b (new AudioBuffers (m.dcp_channels(), data->frames ())); - for (int i = 0; i < m.dcp_channels(); ++i) { - optional s = m.dcp_to_source (static_cast (i)); - if (!s) { - b->make_silent (i); - } else { - memcpy (b->data()[i], data->data()[s.get()], data->frames() * sizeof(float)); - } - } - - data = b; - } - - _writer->write (data); -} diff --combined src/lib/ffmpeg_decoder.cc index a3fdaf9b1,c2143b949..bf0949130 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@@ -28,13 -28,19 +28,13 @@@ #include #include #include +#include extern "C" { #include #include -#include -#include } -#include #include "film.h" -#include "format.h" -#include "transcoder.h" -#include "job.h" #include "filter.h" -#include "options.h" #include "exceptions.h" #include "image.h" #include "util.h" @@@ -42,7 -48,6 +42,7 @@@ #include "ffmpeg_decoder.h" #include "filter_graph.h" #include "subtitle.h" +#include "audio_buffers.h" #include "i18n.h" @@@ -51,67 -56,182 +51,66 @@@ using std::string using std::vector; using std::stringstream; using std::list; +using std::min; using boost::shared_ptr; using boost::optional; using boost::dynamic_pointer_cast; using libdcp::Size; -boost::mutex FFmpegDecoder::_mutex; - -FFmpegDecoder::FFmpegDecoder (shared_ptr f, DecodeOptions o) - : Decoder (f, o) - , VideoDecoder (f, o) - , AudioDecoder (f, o) - , _format_context (0) - , _video_stream (-1) - , _frame (0) - , _video_codec_context (0) - , _video_codec (0) - , _audio_codec_context (0) - , _audio_codec (0) +FFmpegDecoder::FFmpegDecoder (shared_ptr f, shared_ptr c, bool video, bool audio) + : Decoder (f) + , VideoDecoder (f) + , AudioDecoder (f) + , FFmpeg (c) , _subtitle_codec_context (0) , _subtitle_codec (0) + , _decode_video (video) + , _decode_audio (audio) + , _pts_offset (0) { - setup_general (); - setup_video (); - setup_audio (); setup_subtitle (); -} - -FFmpegDecoder::~FFmpegDecoder () -{ - boost::mutex::scoped_lock lm (_mutex); - - if (_audio_codec_context) { - avcodec_close (_audio_codec_context); - } - - if (_video_codec_context) { - avcodec_close (_video_codec_context); - } - - if (_subtitle_codec_context) { - avcodec_close (_subtitle_codec_context); - } - - av_free (_frame); - - avformat_close_input (&_format_context); -} - -void -FFmpegDecoder::setup_general () -{ - av_register_all (); - - if (avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0) < 0) { - throw OpenFileError (_film->content_path ()); - } - - if (avformat_find_stream_info (_format_context, 0) < 0) { - throw DecodeError (_("could not find stream information")); - } - - /* Find video, audio and subtitle streams and choose the first of each */ - - for (uint32_t i = 0; i < _format_context->nb_streams; ++i) { - AVStream* s = _format_context->streams[i]; - if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) { - _video_stream = i; - } else if (s->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - - /* This is a hack; sometimes it seems that _audio_codec_context->channel_layout isn't set up, - so bodge it here. No idea why we should have to do this. - */ - - if (s->codec->channel_layout == 0) { - s->codec->channel_layout = av_get_default_channel_layout (s->codec->channels); - } - - _audio_streams.push_back ( - shared_ptr ( - new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout) - ) - ); - - } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) { - _subtitle_streams.push_back ( - shared_ptr ( - new SubtitleStream (stream_name (s), i) - ) - ); - } - } - - if (_video_stream < 0) { - throw DecodeError (N_("could not find video stream")); - } - _frame = avcodec_alloc_frame (); - if (_frame == 0) { - throw DecodeError (N_("could not allocate frame")); + if (video && audio && c->audio_stream() && c->first_video() && c->audio_stream()->first_audio) { + _pts_offset = compute_pts_offset (c->first_video().get(), c->audio_stream()->first_audio.get(), c->video_frame_rate()); } } -void -FFmpegDecoder::setup_video () +double +FFmpegDecoder::compute_pts_offset (double first_video, double first_audio, float video_frame_rate) { - assert (first_video >= 0); - assert (first_audio >= 0); - - boost::mutex::scoped_lock lm (_mutex); + double const old_first_video = first_video; - _video_codec_context = _format_context->streams[_video_stream]->codec; - _video_codec = avcodec_find_decoder (_video_codec_context->codec_id); - - if (_video_codec == 0) { - throw DecodeError (_("could not find video decoder")); + /* Round the first video to a frame boundary */ + if (fabs (rint (first_video * video_frame_rate) - first_video * video_frame_rate) > 1e-6) { + first_video = ceil (first_video * video_frame_rate) / video_frame_rate; } - if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) { - throw DecodeError (N_("could not open video decoder")); - } + /* Compute the required offset (also removing any common start delay) */ + return first_video - old_first_video - min (first_video, first_audio); } -void -FFmpegDecoder::setup_audio () +FFmpegDecoder::~FFmpegDecoder () { + boost::mutex::scoped_lock lm (_mutex); - - if (!_audio_stream) { - return; - } - - shared_ptr ffa = dynamic_pointer_cast (_audio_stream); - assert (ffa); - - _audio_codec_context = _format_context->streams[ffa->id()]->codec; - _audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id); + - if (_audio_codec == 0) { - throw DecodeError (_("could not find audio decoder")); - } - - if (avcodec_open2 (_audio_codec_context, _audio_codec, 0) < 0) { - throw DecodeError (N_("could not open audio decoder")); + if (_subtitle_codec_context) { + avcodec_close (_subtitle_codec_context); } -} +} void -FFmpegDecoder::setup_subtitle () -{ - boost::mutex::scoped_lock lm (_mutex); - - if (!_subtitle_stream || _subtitle_stream->id() >= int (_format_context->nb_streams)) { - return; - } - - _subtitle_codec_context = _format_context->streams[_subtitle_stream->id()]->codec; - _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id); - - if (_subtitle_codec == 0) { - throw DecodeError (_("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::pass () { int r = av_read_frame (_format_context, &_packet); - + if (r < 0) { if (r != AVERROR_EOF) { /* Maybe we should fail here, but for now we'll just finish off instead */ char buf[256]; av_strerror (r, buf, sizeof(buf)); - _film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r)); + shared_ptr film = _film.lock (); + assert (film); + film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r)); } /* Get any remaining frames */ @@@ -121,28 -241,41 +120,28 @@@ /* XXX: should we reset _packet.data and size after each *_decode_* call? */ - int frame_finished; - - if (_opt.decode_video) { - while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - filter_and_emit_video (); - } + if (_decode_video) { + while (decode_video_packet ()); } - - if (_audio_stream && _opt.decode_audio) { + + if (_ffmpeg_content->audio_stream() && _decode_audio) { decode_audio_packet (); } - - return true; + + /* Stop us being asked for any more data */ + _video_position = _ffmpeg_content->video_length (); + _audio_position = _ffmpeg_content->audio_length (); + return; } avcodec_get_frame_defaults (_frame); - shared_ptr ffa = dynamic_pointer_cast (_audio_stream); - - if (_packet.stream_index == _video_stream && _opt.decode_video) { - - int frame_finished; - int const r = avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet); - if (r >= 0 && frame_finished) { - - if (r != _packet.size) { - _film->log()->log (String::compose (N_("Used only %1 bytes of %2 in packet"), r, _packet.size)); - } - - filter_and_emit_video (); - } - - } else if (ffa && _packet.stream_index == ffa->id() && _opt.decode_audio) { + if (_packet.stream_index == _video_stream && _decode_video) { + decode_video_packet (); + } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->id && _decode_audio) { decode_audio_packet (); - } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt.decode_subtitles) { + } else if (_ffmpeg_content->subtitle_stream() && _packet.stream_index == _ffmpeg_content->subtitle_stream()->id) { +#if 0 int got_subtitle; AVSubtitle sub; @@@ -153,19 -286,19 +152,19 @@@ if (sub.num_rects > 0) { shared_ptr ts; try { - emit_subtitle (shared_ptr (new TimedSubtitle (sub))); + subtitle (shared_ptr (new TimedSubtitle (sub))); } catch (...) { /* some problem with the subtitle; we probably didn't understand it */ } } else { - emit_subtitle (shared_ptr ()); + subtitle (shared_ptr ()); } avsubtitle_free (&sub); } +#endif } - + av_free_packet (&_packet); - return false; } /** @param data pointer to array of pointers to buffers. @@@ -174,16 -307,19 +173,16 @@@ shared_ptr FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) { - assert (_film->audio_channels()); + assert (_ffmpeg_content->audio_channels()); assert (bytes_per_audio_sample()); - shared_ptr ffa = dynamic_pointer_cast (_audio_stream); - assert (ffa); - /* Deinterleave and convert to float */ - assert ((size % (bytes_per_audio_sample() * ffa->channels())) == 0); + assert ((size % (bytes_per_audio_sample() * _ffmpeg_content->audio_channels())) == 0); int const total_samples = size / bytes_per_audio_sample(); - int const frames = total_samples / _film->audio_channels(); - shared_ptr audio (new AudioBuffers (ffa->channels(), frames)); + int const frames = total_samples / _ffmpeg_content->audio_channels(); + shared_ptr audio (new AudioBuffers (_ffmpeg_content->audio_channels(), frames)); switch (audio_sample_format()) { case AV_SAMPLE_FMT_S16: @@@ -195,7 -331,7 +194,7 @@@ audio->data(channel)[sample] = float(*p++) / (1 << 15); ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@@ -206,7 -342,7 +205,7 @@@ case AV_SAMPLE_FMT_S16P: { int16_t** p = reinterpret_cast (data); - for (int i = 0; i < _film->audio_channels(); ++i) { + for (int i = 0; i < _ffmpeg_content->audio_channels(); ++i) { for (int j = 0; j < frames; ++j) { audio->data(i)[j] = static_cast(p[i][j]) / (1 << 15); } @@@ -223,7 -359,7 +222,7 @@@ audio->data(channel)[sample] = static_cast(*p++) / (1 << 31); ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@@ -240,7 -376,7 +239,7 @@@ audio->data(channel)[sample] = *p++; ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@@ -251,7 -387,7 +250,7 @@@ case AV_SAMPLE_FMT_FLTP: { float** p = reinterpret_cast (data); - for (int i = 0; i < _film->audio_channels(); ++i) { + for (int i = 0; i < _ffmpeg_content->audio_channels(); ++i) { memcpy (audio->data(i), p[i], frames * sizeof(float)); } } @@@ -264,14 -400,89 +263,14 @@@ return audio; } -float -FFmpegDecoder::frames_per_second () 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); -} - AVSampleFormat FFmpegDecoder::audio_sample_format () const { - if (_audio_codec_context == 0) { + if (!_ffmpeg_content->audio_stream()) { return (AVSampleFormat) 0; } - return _audio_codec_context->sample_fmt; -} - -libdcp::Size -FFmpegDecoder::native_size () const -{ - return libdcp::Size (_video_codec_context->width, _video_codec_context->height); -} - -PixelFormat -FFmpegDecoder::pixel_format () const -{ - return _video_codec_context->pix_fmt; -} - -int -FFmpegDecoder::time_base_numerator () const -{ - return _video_codec_context->time_base.num; -} - -int -FFmpegDecoder::time_base_denominator () const -{ - return _video_codec_context->time_base.den; -} - -int -FFmpegDecoder::sample_aspect_ratio_numerator () const -{ - return _video_codec_context->sample_aspect_ratio.num; -} - -int -FFmpegDecoder::sample_aspect_ratio_denominator () const -{ - return _video_codec_context->sample_aspect_ratio.den; -} - -string -FFmpegDecoder::stream_name (AVStream* s) const -{ - stringstream n; - - if (s->metadata) { - AVDictionaryEntry const * lang = av_dict_get (s->metadata, N_("language"), 0, 0); - if (lang) { - n << lang->value; - } - - AVDictionaryEntry const * title = av_dict_get (s->metadata, N_("title"), 0, 0); - if (title) { - if (!n.str().empty()) { - n << N_(" "); - } - n << title->value; - } - } - - if (n.str().empty()) { - n << N_("unknown"); - } - - return n.str (); + return audio_codec_context()->sample_fmt; } int @@@ -281,29 -492,91 +280,29 @@@ FFmpegDecoder::bytes_per_audio_sample ( } void -FFmpegDecoder::set_audio_stream (shared_ptr s) +FFmpegDecoder::seek (VideoContent::Frame frame) { - AudioDecoder::set_audio_stream (s); - setup_audio (); + do_seek (frame, false, false); } void -FFmpegDecoder::set_subtitle_stream (shared_ptr s) -{ - VideoDecoder::set_subtitle_stream (s); - setup_subtitle (); - OutputChanged (); -} - -void -FFmpegDecoder::filter_and_emit_video () +FFmpegDecoder::seek_back () { - int64_t const bet = av_frame_get_best_effort_timestamp (_frame); - if (bet == AV_NOPTS_VALUE) { - _film->log()->log ("Dropping frame without PTS"); + if (_video_position == 0) { return; } - shared_ptr graph; - - { - boost::mutex::scoped_lock lm (_filter_graphs_mutex); - - list >::iterator i = _filter_graphs.begin(); - while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) { - ++i; - } - - if (i == _filter_graphs.end ()) { - graph = filter_graph_factory (_film, this, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format); - _filter_graphs.push_back (graph); - _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format)); - } else { - graph = *i; - } - } - - list > images = graph->process (_frame); - - for (list >::iterator i = images.begin(); i != images.end(); ++i) { - emit_video (*i, false, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); - } -} - -bool -FFmpegDecoder::seek (double p) -{ - return do_seek (p, false, false); -} - -bool -FFmpegDecoder::seek_to_last () -{ - /* This AVSEEK_FLAG_BACKWARD in do_seek is a bit of a hack; without it, if we ask for a seek to the same place as last time - (used when we change decoder parameters and want to re-fetch the frame) we end up going forwards rather than - staying in the same place. - */ - return do_seek (last_source_time(), true, false); -} - -void -FFmpegDecoder::seek_back () -{ - do_seek (last_source_time() - 2.5 / frames_per_second (), true, true); + do_seek (_video_position - 1, true, true); } void -FFmpegDecoder::seek_forward () -{ - do_seek (last_source_time() - 0.5 / frames_per_second(), true, true); -} - -bool -FFmpegDecoder::do_seek (double p, bool backwards, bool accurate) +FFmpegDecoder::do_seek (VideoContent::Frame frame, bool backwards, bool accurate) { - int64_t const vt = p / av_q2d (_format_context->streams[_video_stream]->time_base); + int64_t const vt = frame * _ffmpeg_content->video_frame_rate() / av_q2d (_format_context->streams[_video_stream]->time_base); + av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0); + _video_position = frame; - int const r = av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0); - - avcodec_flush_buffers (_video_codec_context); + avcodec_flush_buffers (video_codec_context()); if (_subtitle_codec_context) { avcodec_flush_buffers (_subtitle_codec_context); } @@@ -312,19 -585,17 +311,19 @@@ while (1) { int r = av_read_frame (_format_context, &_packet); if (r < 0) { - return true; + return; } avcodec_get_frame_defaults (_frame); if (_packet.stream_index == _video_stream) { int finished = 0; - int const r = avcodec_decode_video2 (_video_codec_context, _frame, &finished, &_packet); + int const r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet); if (r >= 0 && finished) { int64_t const bet = av_frame_get_best_effort_timestamp (_frame); if (bet > vt) { + _video_position = (bet * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset) + * _ffmpeg_content->video_frame_rate(); break; } } @@@ -333,164 -604,123 +332,156 @@@ av_free_packet (&_packet); } } - - return r < 0; } -shared_ptr -FFmpegAudioStream::create (string t, optional v) +void +FFmpegDecoder::decode_audio_packet () { - if (!v) { - /* version < 1; no type in the string, and there's only FFmpeg streams anyway */ - return shared_ptr (new FFmpegAudioStream (t, v)); - } + /* Audio packets can contain multiple frames, so we may have to call avcodec_decode_audio4 + several times. + */ + + AVPacket copy_packet = _packet; - stringstream s (t); - string type; - s >> type; - if (type != N_("ffmpeg")) { - return shared_ptr (); - } + while (copy_packet.size > 0) { - return shared_ptr (new FFmpegAudioStream (t, v)); + int frame_finished; + int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet); + if (decode_result >= 0) { + 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 */ + shared_ptr silence ( + new AudioBuffers ( + _ffmpeg_content->audio_channels(), + pts * _ffmpeg_content->content_audio_frame_rate() + ) + ); + + silence->make_silent (); + audio (silence, _audio_position); + } + } + - - int const data_size = av_samples_get_buffer_size ( - 0, audio_codec_context()->channels, _frame->nb_samples, audio_sample_format (), 1 - ); - - assert (audio_codec_context()->channels == _ffmpeg_content->audio_channels()); - audio (deinterleave_audio (_frame->data, data_size), _audio_position); ++ copy_packet.data += decode_result; ++ copy_packet.size -= decode_result; + } - - copy_packet.data += decode_result; - copy_packet.size -= decode_result; + } + } } -FFmpegAudioStream::FFmpegAudioStream (string t, optional version) +bool +FFmpegDecoder::decode_video_packet () { - stringstream n (t); - - int name_index = 4; - if (!version) { - name_index = 2; - int channels; - n >> _id >> channels; - _channel_layout = av_get_default_channel_layout (channels); - _sample_rate = 0; - } else { - string type; - /* Current (marked version 1) */ - n >> type >> _id >> _sample_rate >> _channel_layout; - assert (type == N_("ffmpeg")); + int frame_finished; + if (avcodec_decode_video2 (video_codec_context(), _frame, &frame_finished, &_packet) < 0 || !frame_finished) { + return false; } + + boost::mutex::scoped_lock lm (_filter_graphs_mutex); - for (int i = 0; i < name_index; ++i) { - size_t const s = t.find (' '); - if (s != string::npos) { - t = t.substr (s + 1); - } + shared_ptr graph; + + list >::iterator i = _filter_graphs.begin(); + while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) { + ++i; } - _name = t; -} + if (i == _filter_graphs.end ()) { + shared_ptr film = _film.lock (); + assert (film); -string -FFmpegAudioStream::to_string () const -{ - return String::compose (N_("ffmpeg %1 %2 %3 %4"), _id, _sample_rate, _channel_layout, _name); -} + graph.reset (new FilterGraph (_ffmpeg_content, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)); + _filter_graphs.push_back (graph); -void -FFmpegDecoder::film_changed (Film::Property p) -{ - switch (p) { - case Film::CROP: - case Film::FILTERS: - { - boost::mutex::scoped_lock lm (_filter_graphs_mutex); - _filter_graphs.clear (); + film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format)); + } else { + graph = *i; } - OutputChanged (); - break; - default: - break; + list > images = graph->process (_frame); + + string post_process = Filter::ffmpeg_strings (_ffmpeg_content->filters()).second; + + for (list >::iterator i = images.begin(); i != images.end(); ++i) { + + shared_ptr image = *i; + if (!post_process.empty ()) { + image = image->post_process (post_process, true); + } + + int64_t const bet = av_frame_get_best_effort_timestamp (_frame); + if (bet != AV_NOPTS_VALUE) { + + double const pts = bet * av_q2d (_format_context->streams[_video_stream]->time_base) - _pts_offset; + double const next = _video_position / _ffmpeg_content->video_frame_rate(); + double const one_frame = 1 / _ffmpeg_content->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. + */ + boost::shared_ptr black ( + new SimpleImage ( + static_cast (_frame->format), + libdcp::Size (video_codec_context()->width, video_codec_context()->height), + true + ) + ); + + black->make_black (); + video (image, 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 (image, false, _video_position); + } + } else { + shared_ptr film = _film.lock (); + assert (film); + film->log()->log ("Dropping frame without PTS"); + } } -} -/** @return Length (in video frames) according to our content's header */ -SourceFrame -FFmpegDecoder::length () const -{ - return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second(); + return true; } + void -FFmpegDecoder::decode_audio_packet () +FFmpegDecoder::setup_subtitle () { - shared_ptr ffa = dynamic_pointer_cast (_audio_stream); - assert (ffa); - - /* Audio packets can contain multiple frames, so we may have to call avcodec_decode_audio4 - several times. - */ + boost::mutex::scoped_lock lm (_mutex); - AVPacket copy_packet = _packet; + if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) { + return; + } - while (copy_packet.size > 0) { + _subtitle_codec_context = _format_context->streams[_ffmpeg_content->subtitle_stream()->id]->codec; + _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id); - int frame_finished; - int const decode_result = avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, ©_packet); - if (decode_result < 0) { - /* error */ - break; - } - - if (frame_finished) { - - /* Where we are in the source, in seconds */ - double const source_pts_seconds = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base) - * av_frame_get_best_effort_timestamp(_frame); - - int const data_size = av_samples_get_buffer_size ( - 0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1 - ); - - assert (_audio_codec_context->channels == _film->audio_channels()); - Audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds); - } - - copy_packet.data += decode_result; - copy_packet.size -= decode_result; + if (_subtitle_codec == 0) { + throw DecodeError (_("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; +} + diff --combined src/lib/film.cc index ad565aca0,ce555ac8b..fa75ab1f1 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@@ -29,29 -29,29 +29,29 @@@ #include #include #include +#include +#include #include "film.h" -#include "format.h" #include "job.h" #include "filter.h" -#include "transcoder.h" #include "util.h" #include "job_manager.h" -#include "ab_transcode_job.h" #include "transcode_job.h" #include "scp_dcp_job.h" #include "log.h" -#include "options.h" #include "exceptions.h" #include "examine_content_job.h" #include "scaler.h" -#include "decoder_factory.h" #include "config.h" #include "version.h" #include "ui_signaller.h" -#include "video_decoder.h" -#include "audio_decoder.h" -#include "sndfile_decoder.h" -#include "analyse_audio_job.h" +#include "playlist.h" +#include "player.h" +#include "ffmpeg_content.h" +#include "imagemagick_content.h" +#include "sndfile_content.h" +#include "dcp_content_type.h" +#include "ratio.h" #include "cross.h" #include "i18n.h" @@@ -68,12 -68,9 +68,12 @@@ using std::setfill using std::min; using std::make_pair; using std::endl; +using std::cout; using std::list; using boost::shared_ptr; +using boost::weak_ptr; using boost::lexical_cast; +using boost::dynamic_pointer_cast; using boost::to_upper_copy; using boost::ends_with; using boost::starts_with; @@@ -82,31 -79,40 +82,32 @@@ using libdcp::Size int const Film::state_version = 4; -/** Construct a Film object in a given directory, reading any metadata - * file that exists in that directory. An exception will be thrown if - * must_exist is true and the specified directory does not exist. +/** Construct a Film object in a given directory. * * @param d Film directory. - * @param must_exist true to throw an exception if does not exist. */ -Film::Film (string d, bool must_exist) - : _use_dci_name (true) - , _trust_content_header (true) +Film::Film (string d) + : _playlist (new Playlist) + , _use_dci_name (true) , _dcp_content_type (Config::instance()->default_dcp_content_type ()) - , _format (Config::instance()->default_format ()) + , _container (Config::instance()->default_container ()) , _scaler (Scaler::from_id ("bicubic")) - , _trim_start (0) - , _trim_end (0) - , _trim_type (CPL) - , _dcp_ab (false) - , _use_content_audio (true) - , _audio_gain (0) - , _audio_delay (0) - , _still_duration (10) , _with_subtitles (false) , _subtitle_offset (0) , _subtitle_scale (1) , _colour_lut (0) , _j2k_bandwidth (200000000) , _dci_metadata (Config::instance()->default_dci_metadata ()) - , _dcp_frame_rate (0) + , _dcp_video_frame_rate (24) + , _dcp_audio_channels (MAX_AUDIO_CHANNELS) + , _minimum_audio_channels (0) - , _source_frame_rate (0) , _dirty (false) { set_dci_date_today (); + + _playlist->Changed.connect (bind (&Film::playlist_changed, this)); + _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2)); /* Make state.directory a complete path without ..s (where possible) (Code swiped from Adam Bowen on stackoverflow) @@@ -127,46 -133,103 +128,46 @@@ } set_directory (result.string ()); - - if (!boost::filesystem::exists (directory())) { - if (must_exist) { - throw OpenFileError (directory()); - } else { - boost::filesystem::create_directory (directory()); - } - } - - _sndfile_stream = SndfileStream::create (); - -- _log.reset (new FileLog (file ("log"))); - - if (must_exist) { - read_metadata (); - } else { - write_metadata (); - } } Film::Film (Film const & o) : boost::enable_shared_from_this (o) /* note: the copied film shares the original's log */ , _log (o._log) + , _playlist (new Playlist (o._playlist)) , _directory (o._directory) , _name (o._name) , _use_dci_name (o._use_dci_name) - , _content (o._content) - , _trust_content_header (o._trust_content_header) , _dcp_content_type (o._dcp_content_type) - , _format (o._format) - , _crop (o._crop) - , _filters (o._filters) + , _container (o._container) , _scaler (o._scaler) - , _trim_start (o._trim_start) - , _trim_end (o._trim_end) - , _trim_type (o._trim_type) - , _dcp_ab (o._dcp_ab) - , _content_audio_stream (o._content_audio_stream) - , _external_audio (o._external_audio) - , _use_content_audio (o._use_content_audio) - , _audio_gain (o._audio_gain) - , _audio_delay (o._audio_delay) - , _still_duration (o._still_duration) - , _subtitle_stream (o._subtitle_stream) , _with_subtitles (o._with_subtitles) , _subtitle_offset (o._subtitle_offset) , _subtitle_scale (o._subtitle_scale) , _colour_lut (o._colour_lut) , _j2k_bandwidth (o._j2k_bandwidth) , _dci_metadata (o._dci_metadata) + , _dcp_video_frame_rate (o._dcp_video_frame_rate) , _dci_date (o._dci_date) - , _dcp_frame_rate (o._dcp_frame_rate) + , _minimum_audio_channels (o._minimum_audio_channels) - , _size (o._size) - , _length (o._length) - , _content_digest (o._content_digest) - , _content_audio_streams (o._content_audio_streams) - , _sndfile_stream (o._sndfile_stream) - , _subtitle_streams (o._subtitle_streams) - , _source_frame_rate (o._source_frame_rate) , _dirty (o._dirty) { - -} - -Film::~Film () -{ - + _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2)); } string Film::video_state_identifier () const { - assert (format ()); + assert (container ()); LocaleGuard lg; - pair f = Filter::ffmpeg_strings (filters()); - stringstream s; - s << format()->id() - << "_" << content_digest() - << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom - << "_" << _dcp_frame_rate - << "_" << f.first << "_" << f.second + s << container()->id() + << "_" << _playlist->video_digest() + << "_" << _dcp_video_frame_rate << "_" << scaler()->id() << "_" << j2k_bandwidth() - << "_" << boost::lexical_cast (colour_lut()); - - if (trim_type() == ENCODE) { - s << "_" << trim_start() << "_" << trim_end(); - } - - if (dcp_ab()) { - pair fa = Filter::ffmpeg_strings (Config::instance()->reference_filters()); - s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second; - } + << "_" << lexical_cast (colour_lut()); return s.str (); } @@@ -184,7 -247,6 +185,6 @@@ Film::info_dir () cons string Film::internal_video_mxf_dir () const { - boost::filesystem::path p; return dir ("video"); } @@@ -222,12 -284,13 +222,12 @@@ Film::filename_safe_name () cons return o; } -string -Film::audio_analysis_path () const +boost::filesystem::path +Film::audio_analysis_path (shared_ptr c) const { - boost::filesystem::path p; - p /= "analysis"; - p /= content_digest(); - return file (p.string ()); + boost::filesystem::path p = dir ("analysis"); + p /= c->digest(); + return p; } /** Add suitable Jobs to the JobManager to create a DCP for this Film */ @@@ -240,7 -303,7 +240,7 @@@ Film::make_dcp ( throw BadSettingError (_("name"), _("cannot contain slashes")); } - log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary())); + log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary())); { char buffer[128]; @@@ -248,18 -311,23 +248,18 @@@ log()->log (String::compose ("Starting to make DCP on %1", buffer)); } - log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video")))); - if (length()) { - log()->log (String::compose ("Content length %1", length().get())); - } - log()->log (String::compose ("Content digest %1", content_digest())); - log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate())); +// log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video")))); +// if (length()) { +// log()->log (String::compose ("Content length %1", length().get())); +// } +// log()->log (String::compose ("Content digest %1", content_digest())); +// log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate())); log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads())); log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth())); - if (use_content_audio()) { - log()->log ("Using content's audio"); - } else { - log()->log (String::compose ("Using external audio (%1 files)", external_audio().size())); - } -#ifdef DVDOMATIC_DEBUG - log()->log ("DVD-o-matic built in debug mode."); +#ifdef DCPOMATIC_DEBUG + log()->log ("DCP-o-matic built in debug mode."); #else - log()->log ("DVD-o-matic built in optimised mode."); + log()->log ("DCP-o-matic built in optimised mode."); #endif #ifdef LIBDCP_DEBUG log()->log ("libdcp built in debug mode."); @@@ -268,13 -336,17 +268,17 @@@ #endif pair const c = cpu_info (); log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second)); + list > const m = mount_info (); + for (list >::const_iterator i = m.begin(); i != m.end(); ++i) { + log()->log (String::compose ("Mount: %1 %2", i->first, i->second)); + } - if (format() == 0) { - throw MissingSettingError (_("format")); + if (container() == 0) { + throw MissingSettingError (_("container")); } - if (content().empty ()) { - throw MissingSettingError (_("content")); + if (_playlist->content().empty ()) { + throw StringError (_("You must add some content to the DCP before creating it")); } if (dcp_content_type() == 0) { @@@ -285,7 -357,60 +289,7 @@@ throw MissingSettingError (_("name")); } - DecodeOptions od; - od.decode_subtitles = with_subtitles (); - - shared_ptr r; - - if (dcp_ab()) { - r = JobManager::instance()->add (shared_ptr (new ABTranscodeJob (shared_from_this(), od))); - } else { - r = JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this(), od))); - } -} - -/** Start a job to analyse the audio of our content file */ -void -Film::analyse_audio () -{ - if (_analyse_audio_job) { - return; - } - - _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this())); - _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this)); - JobManager::instance()->add (_analyse_audio_job); -} - -/** Start a job to examine our content file */ -void -Film::examine_content () -{ - if (_examine_content_job) { - return; - } - - _examine_content_job.reset (new ExamineContentJob (shared_from_this())); - _examine_content_job->Finished.connect (bind (&Film::examine_content_finished, this)); - JobManager::instance()->add (_examine_content_job); -} - -void -Film::analyse_audio_finished () -{ - ensure_ui_thread (); - - if (_analyse_audio_job->finished_ok ()) { - AudioAnalysisSucceeded (); - } - - _analyse_audio_job.reset (); -} - -void -Film::examine_content_finished () -{ - _examine_content_job.reset (); + JobManager::instance()->add (shared_ptr (new TranscodeJob (shared_from_this()))); } /** Start a job to send our DCP to the configured TMS */ @@@ -302,7 -427,7 +306,7 @@@ Film::send_dcp_to_tms ( int Film::encoded_frames () const { - if (format() == 0) { + if (container() == 0) { return 0; } @@@ -319,43 -444,87 +323,44 @@@ void Film::write_metadata () const { + if (!boost::filesystem::exists (directory())) { + boost::filesystem::create_directory (directory()); + } + boost::mutex::scoped_lock lm (_state_mutex); LocaleGuard lg; boost::filesystem::create_directories (directory()); - string const m = file ("metadata"); - ofstream f (m.c_str ()); - if (!f.good ()) { - throw CreateFileError (m); - } + xmlpp::Document doc; + xmlpp::Element* root = doc.create_root_node ("Metadata"); - f << "version " << state_version << endl; + root->add_child("Version")->add_child_text (lexical_cast (state_version)); + root->add_child("Name")->add_child_text (_name); + root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0"); - /* User stuff */ - f << "name " << _name << endl; - f << "use_dci_name " << _use_dci_name << endl; - f << "content " << _content << endl; - f << "trust_content_header " << (_trust_content_header ? "1" : "0") << endl; if (_dcp_content_type) { - f << "dcp_content_type " << _dcp_content_type->dci_name () << endl; - } - if (_format) { - f << "format " << _format->as_metadata () << endl; - } - f << "left_crop " << _crop.left << endl; - f << "right_crop " << _crop.right << endl; - f << "top_crop " << _crop.top << endl; - f << "bottom_crop " << _crop.bottom << endl; - for (vector::const_iterator i = _filters.begin(); i != _filters.end(); ++i) { - f << "filter " << (*i)->id () << endl; - } - f << "scaler " << _scaler->id () << endl; - f << "trim_start " << _trim_start << endl; - f << "trim_end " << _trim_end << endl; - switch (_trim_type) { - case CPL: - f << "trim_type cpl\n"; - break; - case ENCODE: - f << "trim_type encode\n"; - break; - } - f << "dcp_ab " << (_dcp_ab ? "1" : "0") << endl; - if (_content_audio_stream) { - f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl; + root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ()); } - for (vector::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) { - f << "external_audio " << *i << endl; - } - f << "use_content_audio " << (_use_content_audio ? "1" : "0") << endl; - f << "audio_gain " << _audio_gain << endl; - f << "audio_delay " << _audio_delay << endl; - f << "still_duration " << _still_duration << endl; - if (_subtitle_stream) { - f << "selected_subtitle_stream " << _subtitle_stream->to_string() << endl; - } - f << "with_subtitles " << _with_subtitles << endl; - f << "subtitle_offset " << _subtitle_offset << endl; - f << "subtitle_scale " << _subtitle_scale << endl; - f << "colour_lut " << _colour_lut << endl; - f << "j2k_bandwidth " << _j2k_bandwidth << endl; - _dci_metadata.write (f); - f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << endl; - f << "dcp_frame_rate " << _dcp_frame_rate << endl; - f << "minimum_audio_channels " << _minimum_audio_channels << endl; - f << "width " << _size.width << endl; - f << "height " << _size.height << endl; - f << "length " << _length.get_value_or(0) << endl; - f << "content_digest " << _content_digest << endl; - - for (vector >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) { - f << "content_audio_stream " << (*i)->to_string () << endl; - } - - f << "external_audio_stream " << _sndfile_stream->to_string() << endl; - for (vector >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) { - f << "subtitle_stream " << (*i)->to_string () << endl; + if (_container) { + root->add_child("Container")->add_child_text (_container->id ()); } - f << "source_frame_rate " << _source_frame_rate << endl; + root->add_child("Scaler")->add_child_text (_scaler->id ()); + root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0"); + root->add_child("SubtitleOffset")->add_child_text (lexical_cast (_subtitle_offset)); + root->add_child("SubtitleScale")->add_child_text (lexical_cast (_subtitle_scale)); + root->add_child("ColourLUT")->add_child_text (lexical_cast (_colour_lut)); + root->add_child("J2KBandwidth")->add_child_text (lexical_cast (_j2k_bandwidth)); + _dci_metadata.as_xml (root->add_child ("DCIMetadata")); + root->add_child("DCPVideoFrameRate")->add_child_text (lexical_cast (_dcp_video_frame_rate)); + root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date)); + root->add_child("DCPAudioChannels")->add_child_text (lexical_cast (_dcp_audio_channels)); ++ root->add_child("MinimumAudioChannels")->add_child_text (lexical_cast (_minimum_audio_channels)); + _playlist->as_xml (root->add_child ("Playlist")); + + doc.write_to_file_formatted (file ("metadata.xml")); _dirty = false; } @@@ -367,45 -536,179 +372,46 @@@ Film::read_metadata ( boost::mutex::scoped_lock lm (_state_mutex); LocaleGuard lg; - _external_audio.clear (); - _content_audio_streams.clear (); - _subtitle_streams.clear (); - - boost::optional version; - - /* Backward compatibility things */ - boost::optional audio_sample_rate; - boost::optional audio_stream_index; - boost::optional subtitle_stream_index; - - ifstream f (file ("metadata").c_str()); - if (!f.good()) { - throw OpenFileError (file ("metadata")); + if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) { + throw StringError (_("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 create a new Film, re-add your content and set it up again. Sorry!")); } - - multimap kv = read_key_value (f); - /* We need version before anything else */ - multimap::iterator v = kv.find ("version"); - if (v != kv.end ()) { - version = atoi (v->second.c_str()); - } + cxml::File f (file ("metadata.xml"), "Metadata"); - for (multimap::const_iterator i = kv.begin(); i != kv.end(); ++i) { - string const k = i->first; - string const v = i->second; + _name = f.string_child ("Name"); + _use_dci_name = f.bool_child ("UseDCIName"); - if (k == "audio_sample_rate") { - audio_sample_rate = atoi (v.c_str()); - } - - /* User-specified stuff */ - if (k == "name") { - _name = v; - } else if (k == "use_dci_name") { - _use_dci_name = (v == "1"); - } else if (k == "content") { - _content = v; - } else if (k == "trust_content_header") { - _trust_content_header = (v == "1"); - } else if (k == "dcp_content_type") { - if (version < 3) { - _dcp_content_type = DCPContentType::from_pretty_name (v); - } else { - _dcp_content_type = DCPContentType::from_dci_name (v); - } - } else if (k == "format") { - _format = Format::from_metadata (v); - } else if (k == "left_crop") { - _crop.left = atoi (v.c_str ()); - } else if (k == "right_crop") { - _crop.right = atoi (v.c_str ()); - } else if (k == "top_crop") { - _crop.top = atoi (v.c_str ()); - } else if (k == "bottom_crop") { - _crop.bottom = atoi (v.c_str ()); - } else if (k == "filter") { - _filters.push_back (Filter::from_id (v)); - } else if (k == "scaler") { - _scaler = Scaler::from_id (v); - } else if ( ((!version || version < 2) && k == "dcp_trim_start") || k == "trim_start") { - _trim_start = atoi (v.c_str ()); - } else if ( ((!version || version < 2) && k == "dcp_trim_end") || k == "trim_end") { - _trim_end = atoi (v.c_str ()); - } else if (k == "trim_type") { - if (v == "cpl") { - _trim_type = CPL; - } else if (v == "encode") { - _trim_type = ENCODE; - } - } else if (k == "dcp_ab") { - _dcp_ab = (v == "1"); - } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) { - if (!version) { - audio_stream_index = atoi (v.c_str ()); - } else { - _content_audio_stream = audio_stream_factory (v, version); - } - } else if (k == "external_audio") { - _external_audio.push_back (v); - } else if (k == "use_content_audio") { - _use_content_audio = (v == "1"); - } else if (k == "audio_gain") { - _audio_gain = atof (v.c_str ()); - } else if (k == "audio_delay") { - _audio_delay = atoi (v.c_str ()); - } else if (k == "still_duration") { - _still_duration = atoi (v.c_str ()); - } else if (k == "selected_subtitle_stream") { - if (!version) { - subtitle_stream_index = atoi (v.c_str ()); - } else { - _subtitle_stream = subtitle_stream_factory (v, version); - } - } else if (k == "with_subtitles") { - _with_subtitles = (v == "1"); - } else if (k == "subtitle_offset") { - _subtitle_offset = atoi (v.c_str ()); - } else if (k == "subtitle_scale") { - _subtitle_scale = atof (v.c_str ()); - } else if (k == "colour_lut") { - _colour_lut = atoi (v.c_str ()); - } else if (k == "j2k_bandwidth") { - _j2k_bandwidth = atoi (v.c_str ()); - } else if (k == "dci_date") { - _dci_date = boost::gregorian::from_undelimited_string (v); - } else if (k == "dcp_frame_rate") { - _dcp_frame_rate = atoi (v.c_str ()); - } else if (k == "minimum_audio_channels") { - _minimum_audio_channels = atoi (v.c_str ()); + { + optional c = f.optional_string_child ("DCPContentType"); + if (c) { + _dcp_content_type = DCPContentType::from_dci_name (c.get ()); } + } - _dci_metadata.read (k, v); - - /* Cached stuff */ - if (k == "width") { - _size.width = atoi (v.c_str ()); - } else if (k == "height") { - _size.height = atoi (v.c_str ()); - } else if (k == "length") { - int const vv = atoi (v.c_str ()); - if (vv) { - _length = vv; - } - } else if (k == "content_digest") { - _content_digest = v; - } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) { - _content_audio_streams.push_back (audio_stream_factory (v, version)); - } else if (k == "external_audio_stream") { - _sndfile_stream = audio_stream_factory (v, version); - } else if (k == "subtitle_stream") { - _subtitle_streams.push_back (subtitle_stream_factory (v, version)); - } else if (k == "source_frame_rate") { - _source_frame_rate = atof (v.c_str ()); - } else if (version < 4 && k == "frames_per_second") { - _source_frame_rate = atof (v.c_str ()); - /* Fill in what would have been used for DCP frame rate by the older version */ - _dcp_frame_rate = best_dcp_frame_rate (_source_frame_rate); + { + optional c = f.optional_string_child ("Container"); + if (c) { + _container = Ratio::from_id (c.get ()); } } - if (!version) { - if (audio_sample_rate) { - /* version < 1 didn't specify sample rate in the audio streams, so fill it in here */ - for (vector >::iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) { - (*i)->set_sample_rate (audio_sample_rate.get()); - } - } + _scaler = Scaler::from_id (f.string_child ("Scaler")); + _with_subtitles = f.bool_child ("WithSubtitles"); + _subtitle_offset = f.number_child ("SubtitleOffset"); + _subtitle_scale = f.number_child ("SubtitleScale"); + _colour_lut = f.number_child ("ColourLUT"); + _j2k_bandwidth = f.number_child ("J2KBandwidth"); + _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata")); + _dcp_video_frame_rate = f.number_child ("DCPVideoFrameRate"); + _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate")); + _dcp_audio_channels = f.number_child ("DCPAudioChannels"); ++ _minimum_audio_channels = f.number_child ("MinimumAudioChannels"); - /* also the selected stream was specified as an index */ - if (audio_stream_index && audio_stream_index.get() >= 0 && audio_stream_index.get() < (int) _content_audio_streams.size()) { - _content_audio_stream = _content_audio_streams[audio_stream_index.get()]; - } + _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist")); - /* similarly the subtitle */ - if (subtitle_stream_index && subtitle_stream_index.get() >= 0 && subtitle_stream_index.get() < (int) _subtitle_streams.size()) { - _subtitle_stream = _subtitle_streams[subtitle_stream_index.get()]; - } - } - _dirty = false; } -libdcp::Size -Film::cropped_size (libdcp::Size s) const -{ - boost::mutex::scoped_lock lm (_state_mutex); - s.width -= _crop.left + _crop.right; - s.height -= _crop.top + _crop.bottom; - return s; -} - /** Given a directory name, return its full path within the Film's directory. * The directory (and its parents) will be created if they do not exist. */ @@@ -441,6 -744,67 +447,6 @@@ Film::file (string f) cons return p.string (); } -/** @return full path of the content (actual video) file - * of the Film. - */ -string -Film::content_path () const -{ - boost::mutex::scoped_lock lm (_state_mutex); - if (boost::filesystem::path(_content).has_root_directory ()) { - return _content; - } - - return file (_content); -} - -ContentType -Film::content_type () const -{ - if (boost::filesystem::is_directory (_content)) { - /* Directory of images, we assume */ - return VIDEO; - } - - if (still_image_file (_content)) { - return STILL; - } - - return VIDEO; -} - -/** @return The sampling rate that we will resample the audio to */ -int -Film::target_audio_sample_rate () const -{ - if (!audio_stream()) { - return 0; - } - - /* Resample to a DCI-approved sample rate */ - double t = dcp_audio_sample_rate (audio_stream()->sample_rate()); - - FrameRateConversion frc (source_frame_rate(), dcp_frame_rate()); - - /* 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) { - t *= source_frame_rate() * frc.factor() / dcp_frame_rate(); - } - - return rint (t); -} - -int -Film::still_duration_in_frames () const -{ - return still_duration() * source_frame_rate(); -} - /** @return a DCI-compliant name for a DCP of this film */ string Film::dci_name (bool if_created_now) const @@@ -465,8 -829,8 +471,8 @@@ d << "_" << dcp_content_type()->dci_name(); } - if (format()) { - d << "_" << format()->dci_name(); + if (container()) { + d << "_" << container()->dci_name(); } DCIMetadata const dm = dci_metadata (); @@@ -487,7 -851,22 +493,7 @@@ } } - switch (audio_channels()) { - case 1: - d << "_10"; - break; - case 2: - d << "_20"; - break; - case 6: - d << "_51"; - break; - case 8: - d << "_71"; - break; - } - - d << "_2K"; + d << "_51_2K"; if (!dm.studio.empty ()) { d << "_" << dm.studio; @@@ -550,6 -929,110 +556,6 @@@ Film::set_use_dci_name (bool u signal_changed (USE_DCI_NAME); } -void -Film::set_content (string c) -{ - string check = directory (); - - boost::filesystem::path slash ("/"); - string platform_slash = slash.make_preferred().string (); - - if (!ends_with (check, platform_slash)) { - check += platform_slash; - } - - if (boost::filesystem::path(c).has_root_directory () && starts_with (c, check)) { - c = c.substr (_directory.length() + 1); - } - - string old_content; - - { - boost::mutex::scoped_lock lm (_state_mutex); - if (c == _content) { - return; - } - - old_content = _content; - _content = c; - } - - /* Do this before we start using FFmpeg ourselves */ - run_ffprobe (c, file ("ffprobe.log"), _log); - - /* Reset streams here in case the new content doesn't have one or the other */ - _content_audio_stream = shared_ptr (); - _subtitle_stream = shared_ptr (); - - /* Start off using content audio */ - set_use_content_audio (true); - - /* Create a temporary decoder so that we can get information - about the content. - */ - - try { - Decoders d = decoder_factory (shared_from_this(), DecodeOptions()); - - set_size (d.video->native_size ()); - set_source_frame_rate (d.video->frames_per_second ()); - set_dcp_frame_rate (best_dcp_frame_rate (source_frame_rate ())); - set_subtitle_streams (d.video->subtitle_streams ()); - if (d.audio) { - set_content_audio_streams (d.audio->audio_streams ()); - } - - { - boost::mutex::scoped_lock lm (_state_mutex); - _content = c; - } - - signal_changed (CONTENT); - - /* Start off with the first audio and subtitle streams */ - if (d.audio && !d.audio->audio_streams().empty()) { - set_content_audio_stream (d.audio->audio_streams().front()); - } - - if (!d.video->subtitle_streams().empty()) { - set_subtitle_stream (d.video->subtitle_streams().front()); - } - - examine_content (); - - } catch (...) { - - boost::mutex::scoped_lock lm (_state_mutex); - _content = old_content; - throw; - - } - - /* Default format */ - set_format (Config::instance()->default_format ()); - - /* Still image DCPs must use external audio */ - if (content_type() == STILL) { - set_use_content_audio (false); - } -} - -void -Film::set_trust_content_header (bool t) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _trust_content_header = t; - } - - signal_changed (TRUST_CONTENT_HEADER); - - if (!_trust_content_header && !content().empty()) { - /* We just said that we don't trust the content's header */ - examine_content (); - } -} - void Film::set_dcp_content_type (DCPContentType const * t) { @@@ -561,13 -1044,90 +567,13 @@@ } void -Film::set_format (Format const * f) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _format = f; - } - signal_changed (FORMAT); -} - -void -Film::set_crop (Crop c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _crop = c; - } - signal_changed (CROP); -} - -void -Film::set_left_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - - if (_crop.left == c) { - return; - } - - _crop.left = c; - } - signal_changed (CROP); -} - -void -Film::set_right_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - if (_crop.right == c) { - return; - } - - _crop.right = c; - } - signal_changed (CROP); -} - -void -Film::set_top_crop (int c) +Film::set_container (Ratio const * c) { { boost::mutex::scoped_lock lm (_state_mutex); - if (_crop.top == c) { - return; - } - - _crop.top = c; - } - signal_changed (CROP); -} - -void -Film::set_bottom_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - if (_crop.bottom == c) { - return; - } - - _crop.bottom = c; + _container = c; } - signal_changed (CROP); -} - -void -Film::set_filters (vector f) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _filters = f; - } - signal_changed (FILTERS); + signal_changed (CONTAINER); } void @@@ -580,6 -1140,123 +586,6 @@@ Film::set_scaler (Scaler const * s signal_changed (SCALER); } -void -Film::set_trim_start (int t) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _trim_start = t; - } - signal_changed (TRIM_START); -} - -void -Film::set_trim_end (int t) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _trim_end = t; - } - signal_changed (TRIM_END); -} - -void -Film::set_trim_type (TrimType t) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _trim_type = t; - } - signal_changed (TRIM_TYPE); -} - -void -Film::set_dcp_ab (bool a) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _dcp_ab = a; - } - signal_changed (DCP_AB); -} - -void -Film::set_content_audio_stream (shared_ptr s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _content_audio_stream = s; - } - signal_changed (CONTENT_AUDIO_STREAM); -} - -void -Film::set_external_audio (vector a) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _external_audio = a; - } - - shared_ptr decoder (new SndfileDecoder (shared_from_this(), DecodeOptions())); - if (decoder->audio_stream()) { - _sndfile_stream = decoder->audio_stream (); - } - - signal_changed (EXTERNAL_AUDIO); -} - -void -Film::set_use_content_audio (bool e) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _use_content_audio = e; - } - - signal_changed (USE_CONTENT_AUDIO); -} - -void -Film::set_audio_gain (float g) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _audio_gain = g; - } - signal_changed (AUDIO_GAIN); -} - -void -Film::set_audio_delay (int d) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _audio_delay = d; - } - signal_changed (AUDIO_DELAY); -} - -void -Film::set_still_duration (int d) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _still_duration = d; - } - signal_changed (STILL_DURATION); -} - -void -Film::set_subtitle_stream (shared_ptr s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_stream = s; - } - signal_changed (SUBTITLE_STREAM); -} - void Film::set_with_subtitles (bool w) { @@@ -641,16 -1318,96 +647,26 @@@ Film::set_dci_metadata (DCIMetadata m } -void -Film::set_dcp_frame_rate (int f) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _dcp_frame_rate = f; - } - signal_changed (DCP_FRAME_RATE); -} - + void + Film::set_minimum_audio_channels (int c) + { + { + boost::mutex::scoped_lock lm (_state_mutex); + _minimum_audio_channels = c; + } + signal_changed (MINIMUM_AUDIO_CHANNELS); + } + void -Film::set_size (libdcp::Size s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _size = s; - } - signal_changed (SIZE); -} - -void -Film::set_length (SourceFrame l) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _length = l; - } - signal_changed (LENGTH); -} - -void -Film::unset_length () -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _length = boost::none; - } - signal_changed (LENGTH); -} - -void -Film::set_content_digest (string d) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _content_digest = d; - } - _dirty = true; -} - -void -Film::set_content_audio_streams (vector > s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _content_audio_streams = s; - } - signal_changed (CONTENT_AUDIO_STREAMS); -} - -void -Film::set_subtitle_streams (vector > s) +Film::set_dcp_video_frame_rate (int f) { { boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_streams = s; + _dcp_video_frame_rate = f; } - signal_changed (SUBTITLE_STREAMS); + signal_changed (DCP_VIDEO_FRAME_RATE); } -void -Film::set_source_frame_rate (float f) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _source_frame_rate = f; - } - signal_changed (SOURCE_FRAME_RATE); -} - void Film::signal_changed (Property p) { @@@ -659,17 -1416,20 +675,17 @@@ _dirty = true; } - if (ui_signaller) { - ui_signaller->emit (boost::bind (boost::ref (Changed), p)); + switch (p) { + case Film::CONTENT: + set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ()); + break; + default: + break; } -} -int -Film::audio_channels () const -{ - shared_ptr s = audio_stream (); - if (!s) { - return 0; + if (ui_signaller) { + ui_signaller->emit (boost::bind (boost::ref (Changed), p)); } - - return s->channels (); } void @@@ -678,6 -1438,16 +694,6 @@@ Film::set_dci_date_today ( _dci_date = boost::gregorian::day_clock::local_day (); } -boost::shared_ptr -Film::audio_stream () const -{ - if (use_content_audio()) { - return _content_audio_stream; - } - - return _sndfile_stream; -} - string Film::info_path (int f) const { @@@ -733,146 -1503,20 +749,146 @@@ Film::have_dcp () cons return true; } -bool -Film::has_audio () const +shared_ptr +Film::player () const +{ + return shared_ptr (new Player (shared_from_this (), _playlist)); +} + +shared_ptr +Film::playlist () const +{ + boost::mutex::scoped_lock lm (_state_mutex); + return _playlist; +} + +Playlist::ContentList +Film::content () const +{ + return _playlist->content (); +} + +void +Film::examine_and_add_content (shared_ptr c) { - if (use_content_audio()) { - return audio_stream(); + shared_ptr j (new ExamineContentJob (shared_from_this(), c)); + j->Finished.connect (bind (&Film::add_content_weak, this, boost::weak_ptr (c))); + JobManager::instance()->add (j); +} + +void +Film::add_content_weak (weak_ptr c) +{ + shared_ptr content = c.lock (); + if (content) { + add_content (content); } +} - vector const e = external_audio (); - for (vector::const_iterator i = e.begin(); i != e.end(); ++i) { - if (!i->empty ()) { - return true; - } +void +Film::add_content (shared_ptr c) +{ + /* Add video content after any existing content */ + if (dynamic_pointer_cast (c)) { + c->set_start (_playlist->video_end ()); + } + + _playlist->add (c); +} + +void +Film::remove_content (shared_ptr c) +{ + _playlist->remove (c); +} + +Time +Film::length () const +{ + return _playlist->length (); +} + +bool +Film::has_subtitles () const +{ + return _playlist->has_subtitles (); +} + +OutputVideoFrame +Film::best_dcp_video_frame_rate () const +{ + return _playlist->best_dcp_frame_rate (); +} + +void +Film::playlist_content_changed (boost::weak_ptr c, int p) +{ + if (p == VideoContentProperty::VIDEO_FRAME_RATE) { + set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ()); + } + + if (ui_signaller) { + ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p)); } +} + +void +Film::playlist_changed () +{ + signal_changed (CONTENT); +} - return false; +int +Film::loop () const +{ + return _playlist->loop (); +} + +void +Film::set_loop (int c) +{ + _playlist->set_loop (c); +} + +OutputAudioFrame +Film::time_to_audio_frames (Time t) const +{ + return t * dcp_audio_frame_rate () / TIME_HZ; +} + +OutputVideoFrame +Film::time_to_video_frames (Time t) const +{ + return t * dcp_video_frame_rate () / TIME_HZ; +} + +Time +Film::audio_frames_to_time (OutputAudioFrame f) const +{ + return f * TIME_HZ / dcp_audio_frame_rate (); +} + +Time +Film::video_frames_to_time (OutputVideoFrame f) const +{ + return f * TIME_HZ / dcp_video_frame_rate (); +} + +OutputAudioFrame +Film::dcp_audio_frame_rate () const +{ + /* XXX */ + return 48000; } +void +Film::set_sequence_video (bool s) +{ + _playlist->set_sequence_video (s); +} + +libdcp::Size +Film::full_frame () const +{ + return libdcp::Size (2048, 1080); +} diff --combined src/lib/film.h index 5bb9acf29,ca9bd57f4..f5a7c1246 --- a/src/lib/film.h +++ b/src/lib/film.h @@@ -18,12 -18,12 +18,12 @@@ */ /** @file src/film.h - * @brief A representation of a piece of video (with sound), including naming, - * the source content file, and how it should be presented in a DCP. + * @brief A representation of some audio and video content, and details of + * how they should be presented in a DCP. */ -#ifndef DVDOMATIC_FILM_H -#define DVDOMATIC_FILM_H +#ifndef DCPOMATIC_FILM_H +#define DCPOMATIC_FILM_H #include #include @@@ -32,43 -32,49 +32,43 @@@ #include #include #include -extern "C" { -#include -} -#include "dcp_content_type.h" #include "util.h" -#include "stream.h" #include "dci_metadata.h" +#include "types.h" +#include "ffmpeg_content.h" +#include "playlist.h" -class Format; +class DCPContentType; class Job; class Filter; class Log; class ExamineContentJob; class AnalyseAudioJob; class ExternalAudioStream; +class Content; +class Player; /** @class Film - * @brief A representation of a video, maybe with sound. - * - * A representation of a piece of video (maybe with sound), including naming, - * the source content file, and how it should be presented in a DCP. + * @brief A representation of some audio and video content, and details of + * how they should be presented in a DCP. */ class Film : public boost::enable_shared_from_this { public: - Film (std::string d, bool must_exist = true); + Film (std::string d); Film (Film const &); - ~Film (); std::string info_dir () const; std::string j2c_path (int f, bool t) const; std::string info_path (int f) const; std::string internal_video_mxf_dir () const; std::string internal_video_mxf_filename () const; - std::string audio_analysis_path () const; + boost::filesystem::path audio_analysis_path (boost::shared_ptr) const; std::string dcp_video_mxf_filename () const; std::string dcp_audio_mxf_filename () const; - void examine_content (); - void analyse_audio (); void send_dcp_to_tms (); - void make_dcp (); /** @return Logger. @@@ -83,9 -89,15 +83,9 @@@ std::string file (std::string f) const; std::string dir (std::string d) const; - std::string content_path () const; - ContentType content_type () const; - - int target_audio_sample_rate () const; - - void write_metadata () const; void read_metadata (); + void write_metadata () const; - libdcp::Size cropped_size (libdcp::Size) const; std::string dci_name (bool if_created_now) const; std::string dcp_name (bool if_created_now = false) const; @@@ -94,32 -106,16 +94,32 @@@ return _dirty; } - int audio_channels () const; - - void set_dci_date_today (); + libdcp::Size full_frame () const; bool have_dcp () const; - enum TrimType { - CPL, - ENCODE - }; + boost::shared_ptr player () const; + boost::shared_ptr playlist () const; + + OutputAudioFrame dcp_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; + + /* Proxies for some Playlist methods */ + + Playlist::ContentList content () const; + + Time length () const; + bool has_subtitles () const; + OutputVideoFrame best_dcp_video_frame_rate () const; + + void set_loop (int); + int loop () const; + + void set_sequence_video (bool); /** Identifiers for the parts of our state; used for signalling changes. @@@ -128,19 -124,37 +128,20 @@@ NONE, NAME, USE_DCI_NAME, + /** The playlist's content list has changed (i.e. content has been added, moved around or removed) */ CONTENT, - TRUST_CONTENT_HEADER, + LOOP, DCP_CONTENT_TYPE, - FORMAT, - CROP, - FILTERS, + CONTAINER, SCALER, - TRIM_START, - TRIM_END, - TRIM_TYPE, - DCP_AB, - CONTENT_AUDIO_STREAM, - EXTERNAL_AUDIO, - USE_CONTENT_AUDIO, - AUDIO_GAIN, - AUDIO_DELAY, - STILL_DURATION, - SUBTITLE_STREAM, WITH_SUBTITLES, SUBTITLE_OFFSET, SUBTITLE_SCALE, COLOUR_LUT, J2K_BANDWIDTH, DCI_METADATA, - SIZE, - LENGTH, - CONTENT_AUDIO_STREAMS, - SUBTITLE_STREAMS, - SOURCE_FRAME_RATE, - DCP_FRAME_RATE, + DCP_VIDEO_FRAME_RATE, + MINIMUM_AUDIO_CHANNELS }; @@@ -161,14 -175,34 +162,14 @@@ return _use_dci_name; } - std::string content () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _content; - } - - bool trust_content_header () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _trust_content_header; - } - DCPContentType const * dcp_content_type () const { boost::mutex::scoped_lock lm (_state_mutex); return _dcp_content_type; } - Format const * format () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _format; - } - - Crop crop () const { + Ratio const * container () const { boost::mutex::scoped_lock lm (_state_mutex); - return _crop; - } - - std::vector filters () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _filters; + return _container; } Scaler const * scaler () const { @@@ -176,6 -210,63 +177,6 @@@ return _scaler; } - int trim_start () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _trim_start; - } - - int trim_end () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _trim_end; - } - - TrimType trim_type () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _trim_type; - } - - bool dcp_ab () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _dcp_ab; - } - - boost::shared_ptr content_audio_stream () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _content_audio_stream; - } - - std::vector external_audio () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _external_audio; - } - - bool use_content_audio () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _use_content_audio; - } - - float audio_gain () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _audio_gain; - } - - int audio_delay () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _audio_delay; - } - - int still_duration () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _still_duration; - } - - int still_duration_in_frames () const; - - boost::shared_ptr subtitle_stream () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _subtitle_stream; - } - bool with_subtitles () const { boost::mutex::scoped_lock lm (_state_mutex); return _with_subtitles; @@@ -206,58 -297,119 +207,64 @@@ return _dci_metadata; } - int dcp_frame_rate () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _dcp_frame_rate; - } - - libdcp::Size size () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _size; - } - - boost::optional length () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _length; - } - - std::string content_digest () const { + /* XXX: -> "video_frame_rate" */ + int dcp_video_frame_rate () const { boost::mutex::scoped_lock lm (_state_mutex); - return _content_digest; - } - - std::vector > content_audio_streams () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _content_audio_streams; + return _dcp_video_frame_rate; } - std::vector > subtitle_streams () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _subtitle_streams; - } - - float source_frame_rate () const { + int dcp_audio_channels () const { boost::mutex::scoped_lock lm (_state_mutex); - if (content_type() == STILL) { - return 24; - } - - return _source_frame_rate; + return _dcp_audio_channels; } + int minimum_audio_channels () const { + boost::mutex::scoped_lock lm (_state_mutex); + return _minimum_audio_channels; + } + - boost::shared_ptr audio_stream () const; - bool has_audio () const; - /* SET */ void set_directory (std::string); void set_name (std::string); void set_use_dci_name (bool); - void set_content (std::string); - void set_trust_content_header (bool); + void examine_and_add_content (boost::shared_ptr); + void add_content (boost::shared_ptr); + void remove_content (boost::shared_ptr); void set_dcp_content_type (DCPContentType const *); - void set_format (Format const *); - void set_crop (Crop); - void set_left_crop (int); - void set_right_crop (int); - void set_top_crop (int); - void set_bottom_crop (int); - void set_filters (std::vector); + void set_container (Ratio const *); void set_scaler (Scaler const *); - void set_trim_start (int); - void set_trim_end (int); - void set_trim_type (TrimType); - void set_dcp_ab (bool); - void set_content_audio_stream (boost::shared_ptr); - void set_external_audio (std::vector); - void set_use_content_audio (bool); - void set_audio_gain (float); - void set_audio_delay (int); - void set_still_duration (int); - void set_subtitle_stream (boost::shared_ptr); void set_with_subtitles (bool); void set_subtitle_offset (int); void set_subtitle_scale (float); void set_colour_lut (int); void set_j2k_bandwidth (int); void set_dci_metadata (DCIMetadata); - void set_dcp_frame_rate (int); - void set_size (libdcp::Size); - void set_length (SourceFrame); - void unset_length (); - void set_content_digest (std::string); - void set_content_audio_streams (std::vector >); - void set_subtitle_streams (std::vector >); - void set_source_frame_rate (float); + void set_dcp_video_frame_rate (int); + void set_dci_date_today (); + void set_minimum_audio_channels (int); - /** Emitted when some property has changed */ + /** Emitted when some property has of the Film has changed */ mutable boost::signals2::signal Changed; - boost::signals2::signal AudioAnalysisSucceeded; + /** Emitted when some property of our content has changed */ + mutable boost::signals2::signal, int)> ContentChanged; /** Current version number of the state file */ static int const state_version; private: - /** Log to write to */ - boost::shared_ptr _log; - - /** Any running ExamineContentJob, or 0 */ - boost::shared_ptr _examine_content_job; - /** Any running AnalyseAudioJob, or 0 */ - boost::shared_ptr _analyse_audio_job; - void signal_changed (Property); - void examine_content_finished (); - void analyse_audio_finished (); std::string video_state_identifier () const; + void playlist_changed (); + void playlist_content_changed (boost::weak_ptr, int); std::string filename_safe_name () const; + void add_content_weak (boost::weak_ptr); + + /** Log to write to */ + boost::shared_ptr _log; + boost::shared_ptr _playlist; /** Complete path to directory containing the film metadata; * must not be relative. @@@ -266,16 -418,54 +273,16 @@@ /** Mutex for _directory */ mutable boost::mutex _directory_mutex; - /** Name for DVD-o-matic */ + /** Name for DCP-o-matic */ std::string _name; /** True if a auto-generated DCI-compliant name should be used for our DCP */ bool _use_dci_name; - /** File or directory containing content; may be relative to our directory - * or an absolute path. - */ - std::string _content; - /** If this is true, we will believe the length specified by the content - * file's header; if false, we will run through the whole content file - * the first time we see it in order to obtain the length. - */ - bool _trust_content_header; /** The type of content that this Film represents (feature, trailer etc.) */ DCPContentType const * _dcp_content_type; - /** The format to present this Film in (flat, scope, etc.) */ - Format const * _format; - /** The crop to apply to the source */ - Crop _crop; - /** Video filters that should be used when generating DCPs */ - std::vector _filters; + /** The container to put this Film in (flat, scope, etc.) */ + Ratio const * _container; /** Scaler algorithm to use */ Scaler const * _scaler; - /** Frames to trim off the start of the DCP */ - int _trim_start; - /** Frames to trim off the end of the DCP */ - int _trim_end; - TrimType _trim_type; - /** true to create an A/B comparison DCP, where the left half of the image - is the video without any filters or post-processing, and the right half - has the specified filters and post-processing. - */ - bool _dcp_ab; - /** The audio stream to use from our content */ - boost::shared_ptr _content_audio_stream; - /** List of filenames of external audio files, in channel order - (L, R, C, Lfe, Ls, Rs) - */ - std::vector _external_audio; - /** true to use audio from our content file; false to use external audio */ - bool _use_content_audio; - /** Gain to apply to audio in dB */ - float _audio_gain; - /** Delay to apply to audio (positive moves audio later) in milliseconds */ - int _audio_delay; - /** Duration to make still-sourced films (in seconds) */ - int _still_duration; - boost::shared_ptr _subtitle_stream; /** True if subtitles should be shown for this film */ bool _with_subtitles; /** y offset for placing subtitles, in source pixels; +ve is further down @@@ -291,14 -481,32 +298,15 @@@ int _colour_lut; /** bandwidth for J2K files in bits per second */ int _j2k_bandwidth; - /** DCI naming stuff */ DCIMetadata _dci_metadata; + /** Frames per second to run our DCP at */ + int _dcp_video_frame_rate; /** The date that we should use in a DCI name */ boost::gregorian::date _dci_date; - /** Frames per second to run our DCP at */ - int _dcp_frame_rate; + int _dcp_audio_channels; + int _minimum_audio_channels; - /* Data which are cached to speed things up */ - - /** Size, in pixels, of the source (ignoring cropping) */ - libdcp::Size _size; - /** The length of the source, in video frames (as far as we know) */ - boost::optional _length; - /** MD5 digest of our content file */ - std::string _content_digest; - /** The audio streams in our content */ - std::vector > _content_audio_streams; - /** A stream to represent possible external audio (will always exist) */ - boost::shared_ptr _sndfile_stream; - /** the subtitle streams that we can use */ - std::vector > _subtitle_streams; - /** Frames per second of the source */ - float _source_frame_rate; - /** true if our state has changed since we last saved it */ mutable bool _dirty; diff --combined src/lib/ratio.h index 6916a7491,000000000..5480eee12 mode 100644,000000..100644 --- a/src/lib/ratio.h +++ b/src/lib/ratio.h @@@ -1,66 -1,0 +1,71 @@@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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_RATIO_H ++#define DCPOMATIC_RATIO_H ++ +#include +#include + +class Ratio +{ +public: + Ratio (float ratio, std::string id, std::string n, std::string d) + : _ratio (ratio) + , _id (id) + , _nickname (n) + , _dci_name (d) + {} + + libdcp::Size size (libdcp::Size) const; + + std::string id () const { + return _id; + } + + std::string nickname () const { + return _nickname; + } + + std::string dci_name () const { + return _dci_name; + } + + float ratio () const { + return _ratio; + } + + static void setup_ratios (); + static Ratio const * from_id (std::string i); + static std::vector all () { + return _ratios; + } + +private: + float _ratio; + /** id for use in metadata */ + std::string _id; + /** nickname (e.g. Flat, Scope) */ + std::string _nickname; + std::string _dci_name; + + static std::vector _ratios; +}; ++ ++#endif diff --combined src/lib/util.h index c68bb4f16,c9e5bef16..7af8ffedf --- a/src/lib/util.h +++ b/src/lib/util.h @@@ -22,8 -22,8 +22,8 @@@ * @brief Some utility functions and classes. */ -#ifndef DVDOMATIC_UTIL_H -#define DVDOMATIC_UTIL_H +#ifndef DCPOMATIC_UTIL_H +#define DCPOMATIC_UTIL_H #include #include @@@ -37,10 -37,8 +37,10 @@@ extern "C" #include } #include "compose.hpp" +#include "types.h" +#include "video_content.h" -#ifdef DVDOMATIC_DEBUG +#ifdef DCPOMATIC_DEBUG #define TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TIMING); #else #define TIMING(...) @@@ -52,24 -50,26 +52,25 @@@ #define MAX_AUDIO_CHANNELS 6 class Scaler; + class Film; extern std::string seconds_to_hms (int); +extern std::string time_to_hms (Time); extern std::string seconds_to_approximate_hms (int); extern void stacktrace (std::ostream &, int); extern std::string dependency_version_summary (); extern double seconds (struct timeval); -extern void dvdomatic_setup (); -extern void dvdomatic_setup_gettext_i18n (std::string); +extern void dcpomatic_setup (); +extern void dcpomatic_setup_gettext_i18n (std::string); extern std::vector split_at_spaces_considering_quotes (std::string); -extern std::string md5_digest (std::string); +extern std::string md5_digest (boost::filesystem::path); extern std::string md5_digest (void const *, int); extern void ensure_ui_thread (); extern std::string audio_channel_name (int); -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS extern boost::filesystem::path mo_path (); #endif -typedef int SourceFrame; - struct FrameRateConversion { FrameRateConversion (float, int); @@@ -105,8 -105,96 +106,8 @@@ std::string description; }; -int best_dcp_frame_rate (float); - -enum ContentType { - STILL, ///< content is still images - VIDEO ///< content is a video -}; - -/** @struct Crop - * @brief A description of the crop of an image or video. - */ -struct Crop -{ - Crop () : left (0), right (0), top (0), bottom (0) {} - - /** Number of pixels to remove from the left-hand side */ - int left; - /** Number of pixels to remove from the right-hand side */ - int right; - /** Number of pixels to remove from the top */ - int top; - /** Number of pixels to remove from the bottom */ - int bottom; -}; - -extern bool operator== (Crop const & a, Crop const & b); -extern bool operator!= (Crop const & a, Crop const & b); - -/** @struct Position - * @brief A position. - */ -struct Position -{ - Position () - : x (0) - , y (0) - {} - - Position (int x_, int y_) - : x (x_) - , y (y_) - {} - - /** x coordinate */ - int x; - /** y coordinate */ - int y; -}; - -namespace dvdomatic -{ - -/** @struct Rect - * @brief A rectangle. - */ -struct Rect -{ - Rect () - : x (0) - , y (0) - , width (0) - , height (0) - {} - - Rect (int x_, int y_, int w_, int h_) - : x (x_) - , y (y_) - , width (w_) - , height (h_) - {} - - int x; - int y; - int width; - int height; - - Position position () const { - return Position (x, y); - } - - libdcp::Size size () const { - return libdcp::Size (width, height); - } - - Rect intersection (Rect const & other) const; -}; - -} - extern std::string crop_string (Position, libdcp::Size); -extern int dcp_audio_sample_rate (int); +extern int dcp_audio_frame_rate (int); extern std::string colour_lut_index_to_name (int index); extern int stride_round_up (int, int const *, int); extern int stride_lookup (int c, int const * stride); @@@ -119,7 -207,7 +120,7 @@@ extern std::string get_optional_string /** @class Socket * @brief A class to wrap a boost::asio::ip::tcp::socket with some things - * that are useful for DVD-o-matic. + * that are useful for DCP-o-matic. * * This class wraps some things that I could not work out how to do with boost; * most notably, sync read/write calls with timeouts. @@@ -153,7 -241,68 +154,7 @@@ private int _timeout; }; -/** @class AudioBuffers - * @brief A class to hold multi-channel audio data in float format. - */ -class AudioBuffers -{ -public: - AudioBuffers (int channels, int frames); - AudioBuffers (AudioBuffers const &); - AudioBuffers (boost::shared_ptr); - ~AudioBuffers (); - - float** data () const { - return _data; - } - - float* data (int) const; - - int channels () const { - return _channels; - } - - int frames () const { - return _frames; - } - - void set_frames (int f); - - void make_silent (); - void make_silent (int c); - - void copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset); - void move (int from, int to, int frames); - -private: - /** Number of channels */ - int _channels; - /** Number of frames (where a frame is one sample across all channels) */ - int _frames; - /** Number of frames that _data can hold */ - int _allocated_frames; - /** Audio data (so that, e.g. _data[2][6] is channel 2, sample 6) */ - float** _data; -}; - -class AudioMapping -{ -public: - AudioMapping (boost::shared_ptr); - - boost::optional source_to_dcp (int c) const; - boost::optional dcp_to_source (libdcp::Channel c) const; - - int minimum_dcp_channels () const; - int dcp_channels () const; - -private: - int _source_channels; - int _minimum_channels; -}; - -extern int64_t video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float frames_per_second); -extern bool still_image_file (std::string); +extern int64_t video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second); class LocaleGuard { diff --combined src/lib/version.h index 518862fc4,e1ec9067c..b70be8343 --- a/src/lib/version.h +++ b/src/lib/version.h @@@ -1,3 -1,4 +1,4 @@@ -extern char const * dvdomatic_version; -extern char const * dvdomatic_git_commit; -extern char const * dvdomatic_cxx_flags; +extern char const * dcpomatic_version; +extern char const * dcpomatic_git_commit; ++extern char const * dcpomatic_cxx_flags; diff --combined src/lib/writer.cc index cf81d5b70,cff0b5be2..cbb84a940 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@@ -18,23 -18,19 +18,24 @@@ */ #include + #include #include #include #include #include +#include #include #include "writer.h" #include "compose.hpp" #include "film.h" -#include "format.h" +#include "ratio.h" #include "log.h" #include "dcp_video_frame.h" +#include "dcp_content_type.h" +#include "player.h" +#include "audio_mapping.h" #include "config.h" +#include "job.h" #include "i18n.h" @@@ -48,9 -44,8 +49,9 @@@ using boost::shared_ptr int const Writer::_maximum_frames_in_memory = 8; -Writer::Writer (shared_ptr f) +Writer::Writer (shared_ptr f, shared_ptr j) : _film (f) + , _job (j) , _first_nonexistant_frame (0) , _thread (0) , _finish (false) @@@ -75,24 -70,28 +76,24 @@@ new libdcp::MonoPictureAsset ( _film->internal_video_mxf_dir (), _film->internal_video_mxf_filename (), - _film->dcp_frame_rate (), - _film->format()->dcp_size () + _film->dcp_video_frame_rate (), + _film->container()->size (_film->full_frame ()) ) ); _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0); - AudioMapping m (_film); + _sound_asset.reset ( + new libdcp::SoundAsset ( + _film->dir (_film->dcp_name()), + _film->dcp_audio_mxf_filename (), + _film->dcp_video_frame_rate (), + _film->dcp_audio_channels (), + _film->dcp_audio_frame_rate() + ) + ); - if (m.dcp_channels() > 0) { - _sound_asset.reset ( - new libdcp::SoundAsset ( - _film->dir (_film->dcp_name()), - _film->dcp_audio_mxf_filename (), - _film->dcp_frame_rate (), - m.dcp_channels (), - dcp_audio_sample_rate (_film->audio_stream()->sample_rate()) - ) - ); - - _sound_asset_writer = _sound_asset->start_write (); - } + _sound_asset_writer = _sound_asset->start_write (); _thread = new boost::thread (boost::bind (&Writer::thread, this)); } @@@ -202,10 -201,6 +203,10 @@@ tr } } lock.lock (); + + if (_film->length ()) { + _job->set_progress (float(_full_written + _fake_written + _repeat_written) / _film->time_to_video_frames (_film->length())); + } ++_last_written_frame; } @@@ -261,11 -256,21 +262,11 @@@ Writer::finish ( _thread = 0; _picture_asset_writer->finalize (); - - if (_sound_asset_writer) { - _sound_asset_writer->finalize (); - } - + _sound_asset_writer->finalize (); + int const frames = _last_written_frame + 1; - int duration = 0; - if (_film->trim_type() == Film::CPL) { - duration = frames - _film->trim_start() - _film->trim_end(); - _picture_asset->set_entry_point (_film->trim_start ()); - } else { - duration = frames; - } - _picture_asset->set_duration (duration); + _picture_asset->set_duration (frames); /* Hard-link the video MXF into the DCP */ @@@ -289,7 -294,13 +290,7 @@@ _picture_asset->set_directory (_film->dir (_film->dcp_name ())); _picture_asset->set_file_name (_film->dcp_video_mxf_filename ()); - - if (_sound_asset) { - if (_film->trim_type() == Film::CPL) { - _sound_asset->set_entry_point (_film->trim_start ()); - } - _sound_asset->set_duration (duration); - } + _sound_asset->set_duration (frames); libdcp::DCP dcp (_film->dir (_film->dcp_name())); @@@ -299,7 -310,7 +300,7 @@@ _film->dcp_name(), _film->dcp_content_type()->libdcp_kind (), frames, - _film->dcp_frame_rate () + _film->dcp_video_frame_rate () ) ); @@@ -342,8 -353,9 +343,9 @@@ Writer::check_existing_picture_mxf ( boost::filesystem::path p; p /= _film->internal_video_mxf_dir (); p /= _film->internal_video_mxf_filename (); - FILE* mxf = fopen (p.string().c_str(), N_("rb")); + FILE* mxf = fopen (p.string().c_str(), "rb"); if (!mxf) { + _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno)); return; } diff --combined src/lib/wscript index 3e7f2e33b,7c7a64d58..2f8653984 --- a/src/lib/wscript +++ b/src/lib/wscript @@@ -2,57 -2,53 +2,57 @@@ import o import i18n sources = """ - ab_transcode_job.cc - ab_transcoder.cc analyse_audio_job.cc audio_analysis.cc + audio_buffers.cc + audio_content.cc audio_decoder.cc - audio_source.cc + audio_mapping.cc config.cc - combiner.cc + content.cc cross.cc dci_metadata.cc dcp_content_type.cc dcp_video_frame.cc decoder.cc - decoder_factory.cc - delay_line.cc dolby_cp750.cc encoder.cc examine_content_job.cc exceptions.cc filter_graph.cc + ffmpeg.cc + ffmpeg_content.cc ffmpeg_decoder.cc + ffmpeg_examiner.cc film.cc filter.cc - format.cc - gain.cc image.cc + imagemagick_content.cc imagemagick_decoder.cc + imagemagick_examiner.cc job.cc job_manager.cc log.cc lut.cc - matcher.cc + player.cc + playlist.cc + ratio.cc + resampler.cc scp_dcp_job.cc scaler.cc server.cc + sndfile_content.cc sndfile_decoder.cc sound_processor.cc - stream.cc subtitle.cc timer.cc transcode_job.cc transcoder.cc - trimmer.cc + types.cc ui_signaller.cc util.cc + video_content.cc video_decoder.cc - video_source.cc writer.cc """ @@@ -62,28 -58,26 +62,28 @@@ def build(bld) else: obj = bld(features = 'cxx cxxshlib') - obj.name = 'libdvdomatic' + obj.name = 'libdcpomatic' 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 GLIB LZMA + SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA """ obj.source = sources + ' version.cc' if bld.env.TARGET_WINDOWS: - obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY' + obj.uselib += ' WINSOCK2 BFD DBGHELP IBERTY SHLWAPI' obj.source += ' stack.cpp' + if bld.env.STATIC: + obj.uselib += ' XML++' + obj.source = sources + " version.cc" + obj.target = 'dcpomatic' - obj.target = 'dvdomatic' - - i18n.po_to_mo(os.path.join('src', 'lib'), 'libdvdomatic', bld) + i18n.po_to_mo(os.path.join('src', 'lib'), 'libdcpomatic', bld) def pot(bld): - i18n.pot(os.path.join('src', 'lib'), sources, 'libdvdomatic') + i18n.pot(os.path.join('src', 'lib'), sources, 'libdcpomatic') def pot_merge(bld): - i18n.pot_merge(os.path.join('src', 'lib'), 'libdvdomatic') + i18n.pot_merge(os.path.join('src', 'lib'), 'libdcpomatic') diff --combined src/tools/dcpomatic.cc index 683e60ceb,000000000..ac39d4fed mode 100644,000000..100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@@ -1,518 -1,0 +1,518 @@@ +/* + Copyright (C) 2012 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 +#include +#include +#ifdef __WXMSW__ +#include +#endif +#ifdef __WXOSX__ +#include +#endif +#include +#include +#include +#include "wx/film_viewer.h" +#include "wx/film_editor.h" +#include "wx/job_manager_view.h" +#include "wx/config_dialog.h" +#include "wx/job_wrapper.h" +#include "wx/wx_util.h" +#include "wx/new_film_dialog.h" +#include "wx/properties_dialog.h" +#include "wx/wx_ui_signaller.h" +#include "wx/about_dialog.h" +#include "lib/film.h" +#include "lib/config.h" +#include "lib/util.h" +#include "lib/version.h" +#include "lib/ui_signaller.h" +#include "lib/log.h" + +using std::cout; +using std::string; +using std::wstring; +using std::stringstream; +using std::map; +using std::make_pair; +using std::exception; +using std::ofstream; +using boost::shared_ptr; + +static FilmEditor* film_editor = 0; +static FilmViewer* film_viewer = 0; +static shared_ptr film; +static std::string log_level; +static std::string film_to_load; +static std::string film_to_create; +static wxMenu* jobs_menu = 0; + +static void set_menu_sensitivity (); + +class FilmChangedDialog +{ +public: + FilmChangedDialog () + { + _dialog = new wxMessageDialog ( + 0, + wxString::Format (_("Save changes to film \"%s\" before closing?"), std_to_wx (film->name ()).data()), + _("Film changed"), + wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION + ); + } + + ~FilmChangedDialog () + { + _dialog->Destroy (); + } + + int run () + { + return _dialog->ShowModal (); + } + +private: + wxMessageDialog* _dialog; +}; + + +void +maybe_save_then_delete_film () +{ + if (!film) { + return; + } + + if (film->dirty ()) { + FilmChangedDialog d; + switch (d.run ()) { + case wxID_NO: + break; + case wxID_YES: + film->write_metadata (); + break; + } + } + + film.reset (); +} + +enum Sensitivity { + ALWAYS, + NEEDS_FILM +}; + +map menu_items; + +void +add_item (wxMenu* menu, wxString text, int id, Sensitivity sens) +{ + wxMenuItem* item = menu->Append (id, text); + menu_items.insert (make_pair (item, sens)); +} + +void +set_menu_sensitivity () +{ + for (map::iterator i = menu_items.begin(); i != menu_items.end(); ++i) { + if (i->second == NEEDS_FILM) { + i->first->Enable (film != 0); + } else { + i->first->Enable (true); + } + } +} + +enum { + ID_file_new = 1, + ID_file_open, + ID_file_save, + ID_file_properties, + ID_jobs_make_dcp, + ID_jobs_send_dcp_to_tms, + ID_jobs_show_dcp, +}; + +void +setup_menu (wxMenuBar* m) +{ + wxMenu* file = new wxMenu; + add_item (file, _("New..."), ID_file_new, ALWAYS); + add_item (file, _("&Open..."), ID_file_open, ALWAYS); + file->AppendSeparator (); + add_item (file, _("&Save"), ID_file_save, NEEDS_FILM); + file->AppendSeparator (); + add_item (file, _("&Properties..."), ID_file_properties, NEEDS_FILM); +#ifndef __WXOSX__ + file->AppendSeparator (); - #endif ++#endif + add_item (file, _("&Exit"), wxID_EXIT, ALWAYS); + +#ifdef __WXOSX__ + add_item (file, _("&Preferences..."), wxID_PREFERENCES, ALWAYS); +#else + wxMenu* edit = new wxMenu; + add_item (edit, _("&Preferences..."), wxID_PREFERENCES, ALWAYS); +#endif + + jobs_menu = new wxMenu; + add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM); + add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM); + add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM); + + wxMenu* help = new wxMenu; +#ifdef __WXOSX__ + add_item (help, _("About DCP-o-matic"), wxID_ABOUT, ALWAYS); +#else + add_item (help, _("About"), wxID_ABOUT, ALWAYS); +#endif + + m->Append (file, _("&File")); +#ifndef __WXOSX__ + m->Append (edit, _("&Edit")); +#endif + m->Append (jobs_menu, _("&Jobs")); + m->Append (help, _("&Help")); +} + +bool +window_closed (wxCommandEvent &) +{ + maybe_save_then_delete_film (); + return false; +} + +class Frame : public wxFrame +{ +public: + Frame (wxString const & title) + : wxFrame (NULL, -1, title) + { + wxMenuBar* bar = new wxMenuBar; + setup_menu (bar); + SetMenuBar (bar); + + Connect (ID_file_new, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_new)); + Connect (ID_file_open, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_open)); + Connect (ID_file_save, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_save)); + Connect (ID_file_properties, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_properties)); + Connect (wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_exit)); + Connect (wxID_PREFERENCES, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::edit_preferences)); + Connect (ID_jobs_make_dcp, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_make_dcp)); + Connect (ID_jobs_send_dcp_to_tms, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_send_dcp_to_tms)); + Connect (ID_jobs_show_dcp, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_show_dcp)); + Connect (wxID_ABOUT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about)); + + Connect (wxID_ANY, wxEVT_MENU_OPEN, wxMenuEventHandler (Frame::menu_opened)); + + film_editor = new FilmEditor (film, this); + film_viewer = new FilmViewer (film, this); + JobManagerView* job_manager_view = new JobManagerView (this, static_cast (0)); + + wxBoxSizer* right_sizer = new wxBoxSizer (wxVERTICAL); + right_sizer->Add (film_viewer, 2, wxEXPAND | wxALL, 6); + right_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6); + + wxBoxSizer* main_sizer = new wxBoxSizer (wxHORIZONTAL); + main_sizer->Add (film_editor, 1, wxEXPAND | wxALL, 6); + main_sizer->Add (right_sizer, 2, wxEXPAND | wxALL, 6); + + set_menu_sensitivity (); + + film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1)); + if (film) { + file_changed (film->directory ()); + } else { + file_changed (""); + } + + set_film (); + SetSizer (main_sizer); + } + +private: + + void menu_opened (wxMenuEvent& ev) + { + if (ev.GetMenu() != jobs_menu) { + return; + } + + bool const have_dcp = film && film->have_dcp(); + jobs_menu->Enable (ID_jobs_send_dcp_to_tms, have_dcp); + jobs_menu->Enable (ID_jobs_show_dcp, have_dcp); + } + + void set_film () + { + film_viewer->set_film (film); + film_editor->set_film (film); + set_menu_sensitivity (); + } + + void file_changed (string f) + { + stringstream s; + s << wx_to_std (_("DCP-o-matic")); + if (!f.empty ()) { + s << " - " << f; + } + + SetTitle (std_to_wx (s.str())); + } + + void file_new (wxCommandEvent &) + { + NewFilmDialog* d = new NewFilmDialog (this); + int const r = d->ShowModal (); + + if (r == wxID_OK) { + + if (boost::filesystem::exists (d->get_path()) && !boost::filesystem::is_empty(d->get_path())) { + if (!confirm_dialog ( + this, + std_to_wx ( + String::compose (wx_to_std (_("The directory %1 already exists and is not empty. " + "Are you sure you want to use it?")), + d->get_path().c_str()) + ) + )) { + return; + } + } + + maybe_save_then_delete_film (); + film.reset (new Film (d->get_path ())); + film->write_metadata (); + film->log()->set_level (log_level); + film->set_name (boost::filesystem::path (d->get_path()).filename().generic_string()); + set_film (); + } + + d->Destroy (); + } + + void file_open (wxCommandEvent &) + { + wxDirDialog* c = new wxDirDialog (this, _("Select film to open"), wxStandardPaths::Get().GetDocumentsDir(), wxDEFAULT_DIALOG_STYLE | wxDD_DIR_MUST_EXIST); + int r; + while (1) { + r = c->ShowModal (); + if (r == wxID_OK && c->GetPath() == wxStandardPaths::Get().GetDocumentsDir()) { + error_dialog (this, _("You did not select a folder. Make sure that you select a folder before clicking Open.")); + } else { + break; + } + } + + if (r == wxID_OK) { + maybe_save_then_delete_film (); + try { + film.reset (new Film (wx_to_std (c->GetPath ()))); + film->log()->set_level (log_level); + set_film (); + } catch (std::exception& e) { + wxString p = c->GetPath (); + wxCharBuffer b = p.ToUTF8 (); + error_dialog (this, wxString::Format (_("Could not open film at %s (%s)"), p.data(), std_to_wx (e.what()).data())); + } + } + + c->Destroy (); + } + + void file_save (wxCommandEvent &) + { + film->write_metadata (); + } + + void file_properties (wxCommandEvent &) + { + PropertiesDialog* d = new PropertiesDialog (this, film); + d->ShowModal (); + d->Destroy (); + } + + void file_exit (wxCommandEvent &) + { + maybe_save_then_delete_film (); + Close (true); + } + + void edit_preferences (wxCommandEvent &) + { + ConfigDialog* d = new ConfigDialog (this); + d->ShowModal (); + d->Destroy (); + Config::instance()->write (); + } + + void jobs_make_dcp (wxCommandEvent &) + { + JobWrapper::make_dcp (this, film); + } + + void jobs_send_dcp_to_tms (wxCommandEvent &) + { + film->send_dcp_to_tms (); + } + + void jobs_show_dcp (wxCommandEvent &) + { +#ifdef __WXMSW__ + string d = film->directory(); + wstring w; + w.assign (d.begin(), d.end()); + ShellExecute (0, L"open", w.c_str(), 0, 0, SW_SHOWDEFAULT); +#else + int r = system ("which nautilus"); + if (WEXITSTATUS (r) == 0) { + system (string ("nautilus " + film->directory()).c_str ()); + } else { + int r = system ("which konqueror"); + if (WEXITSTATUS (r) == 0) { + system (string ("konqueror " + film->directory()).c_str ()); + } + } +#endif + } + + void help_about (wxCommandEvent &) + { + AboutDialog* d = new AboutDialog (this); + d->ShowModal (); + d->Destroy (); + } +}; + +#if wxMINOR_VERSION == 9 +static const wxCmdLineEntryDesc command_line_description[] = { + { wxCMD_LINE_OPTION, "l", "log", "set log level (silent, verbose or timing)", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_SWITCH, "n", "new", "create new film", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_NONE, "", "", "", wxCmdLineParamType (0), 0 } +}; +#else +static const wxCmdLineEntryDesc command_line_description[] = { + { wxCMD_LINE_OPTION, wxT("l"), wxT("log"), wxT("set log level (silent, verbose or timing)"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_SWITCH, wxT("n"), wxT("new"), wxT("create new film"), wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_PARAM, 0, 0, wxT("film to load or create"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL }, + { wxCMD_LINE_NONE, wxT(""), wxT(""), wxT(""), wxCmdLineParamType (0), 0 } +}; +#endif + +class App : public wxApp +{ + bool OnInit () + { + if (!wxApp::OnInit()) { + return false; + } + +#ifdef DCPOMATIC_LINUX + unsetenv ("UBUNTU_MENUPROXY"); +#endif + +#ifdef __WXOSX__ + ProcessSerialNumber serial; + GetCurrentProcess (&serial); + TransformProcessType (&serial, kProcessTransformToForegroundApplication); +#endif + + wxInitAllImageHandlers (); + + /* Enable i18n; this will create a Config object + to look for a force-configured language. This Config + object will be wrong, however, because dcpomatic_setup + hasn't yet been called and there aren't any scalers, filters etc. + set up yet. + */ + dcpomatic_setup_i18n (); + + /* Set things up, including scalers / filters etc. + which will now be internationalised correctly. + */ + dcpomatic_setup (); + + /* Force the configuration to be re-loaded correctly next + time it is needed. + */ + Config::drop (); + + if (!film_to_load.empty() && boost::filesystem::is_directory (film_to_load)) { + try { + film.reset (new Film (film_to_load)); + film->read_metadata (); + film->log()->set_level (log_level); + } catch (exception& e) { + error_dialog (0, std_to_wx (String::compose (wx_to_std (_("Could not load film %1 (%2)")), film_to_load, e.what()))); + } + } + + if (!film_to_create.empty ()) { + film.reset (new Film (film_to_create)); + film->write_metadata (); + film->log()->set_level (log_level); + film->set_name (boost::filesystem::path (film_to_create).filename().generic_string ()); + } + + Frame* f = new Frame (_("DCP-o-matic")); + SetTopWindow (f); + f->Maximize (); + f->Show (); + + ui_signaller = new wxUISignaller (this); + this->Connect (-1, wxEVT_IDLE, wxIdleEventHandler (App::idle)); + + return true; + } + + void OnInitCmdLine (wxCmdLineParser& parser) + { + parser.SetDesc (command_line_description); + parser.SetSwitchChars (wxT ("-")); + } + + bool OnCmdLineParsed (wxCmdLineParser& parser) + { + if (parser.GetParamCount() > 0) { + if (parser.Found (wxT ("new"))) { + film_to_create = wx_to_std (parser.GetParam (0)); + } else { + film_to_load = wx_to_std (parser.GetParam(0)); + } + } + + wxString log; + if (parser.Found (wxT ("log"), &log)) { + log_level = wx_to_std (log); + } + + return true; + } + + void idle (wxIdleEvent &) + { + ui_signaller->ui_idle (); + } +}; + +IMPLEMENT_APP (App) diff --combined src/tools/dcpomatic_cli.cc index 8623d9194,000000000..ee9e2cdc0 mode 100644,000000..100644 --- a/src/tools/dcpomatic_cli.cc +++ b/src/tools/dcpomatic_cli.cc @@@ -1,199 -1,0 +1,204 @@@ +/* + Copyright (C) 2012 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 +#include +#include +#include +#include "film.h" +#include "filter.h" +#include "transcode_job.h" +#include "job_manager.h" +#include "util.h" +#include "scaler.h" +#include "version.h" +#include "cross.h" +#include "config.h" +#include "log.h" + +using std::string; +using std::cerr; +using std::cout; +using std::vector; +using std::pair; +using std::list; +using boost::shared_ptr; + +static void +help (string n) +{ + cerr << "Syntax: " << n << " [OPTION] \n" + << " -v, --version show DCP-o-matic version\n" + << " -h, --help show this help\n" + << " -d, --deps list DCP-o-matic dependency details and quit\n" ++ << " -f, --flags show flags passed to C++ compiler on build\n" + << " -n, --no-progress do not print progress to stdout\n" + << " -r, --no-remote do not use any remote servers\n" + << "\n" + << " is the film directory.\n"; +} + +int +main (int argc, char* argv[]) +{ + string film_dir; + bool progress = true; + bool no_remote = false; + int log_level = 0; + + int option_index = 0; + while (1) { + static struct option long_options[] = { + { "version", no_argument, 0, 'v'}, + { "help", no_argument, 0, 'h'}, + { "deps", no_argument, 0, 'd'}, ++ { "flags", no_argument, 0, 'f'}, + { "no-progress", no_argument, 0, 'n'}, + { "no-remote", no_argument, 0, 'r'}, + { "log-level", required_argument, 0, 'l' }, + { 0, 0, 0, 0 } + }; + - int c = getopt_long (argc, argv, "vhdnrl:", long_options, &option_index); ++ int c = getopt_long (argc, argv, "vhdfnrl:", long_options, &option_index); + + if (c == -1) { + break; + } + + switch (c) { + case 'v': + cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; + exit (EXIT_SUCCESS); + case 'h': + help (argv[0]); + exit (EXIT_SUCCESS); + case 'd': + cout << dependency_version_summary () << "\n"; + exit (EXIT_SUCCESS); ++ case 'f': ++ cout << dcpomatic_cxx_flags << "\n"; ++ exit (EXIT_SUCCESS); + case 'n': + progress = false; + break; + case 'r': + no_remote = true; + break; + case 'l': + log_level = atoi (optarg); + break; + } + } + + if (optind >= argc) { + help (argv[0]); + exit (EXIT_FAILURE); + } + + film_dir = argv[optind]; + + dcpomatic_setup (); + + if (no_remote) { + Config::instance()->set_servers (vector ()); + } + + cout << "DCP-o-matic " << dcpomatic_version << " git " << dcpomatic_git_commit; + char buf[256]; + if (gethostname (buf, 256) == 0) { + cout << " on " << buf; + } + cout << "\n"; + + shared_ptr film; + try { + film.reset (new Film (film_dir)); + film->read_metadata (); + } catch (std::exception& e) { + cerr << argv[0] << ": error reading film `" << film_dir << "' (" << e.what() << ")\n"; + exit (EXIT_FAILURE); + } + + film->log()->set_level ((Log::Level) log_level); + + cout << "\nMaking DCP for " << film->name() << "\n"; +// cout << "Content: " << film->content() << "\n"; +// pair const f = Filter::ffmpeg_strings (film->filters ()); +// cout << "Filters: " << f.first << " " << f.second << "\n"; + + film->make_dcp (); + + bool should_stop = false; + bool first = true; + bool error = false; + while (!should_stop) { + + dcpomatic_sleep (5); + + list > jobs = JobManager::instance()->get (); + + if (!first && progress) { + cout << "\033[" << jobs.size() << "A"; + cout.flush (); + } + + first = false; + + int unfinished = 0; + int finished_in_error = 0; + + for (list >::iterator i = jobs.begin(); i != jobs.end(); ++i) { + if (progress) { + cout << (*i)->name() << ": "; + + float const p = (*i)->overall_progress (); + + if (p >= 0) { + cout << (*i)->status() << " \n"; + } else { + cout << ": Running \n"; + } + } + + if (!(*i)->finished ()) { + ++unfinished; + } + + if ((*i)->finished_in_error ()) { + ++finished_in_error; + error = true; + } + + if (!progress && (*i)->finished_in_error ()) { + /* We won't see this error if we haven't been showing progress, + so show it now. + */ + cout << (*i)->status() << "\n"; + } + } + + if (unfinished == 0 || finished_in_error != 0) { + should_stop = true; + } + } + + return error ? EXIT_FAILURE : EXIT_SUCCESS; +} + + diff --combined src/tools/wscript index 38d986f25,20a92cad2..c7ab44604 --- a/src/tools/wscript +++ b/src/tools/wscript @@@ -4,29 -4,31 +4,31 @@@ from waflib import Log import i18n def build(bld): - for t in ['makedcp', 'servomatic_cli', 'servomatictest']: + for t in ['dcpomatic_cli', 'dcpomatic_server_cli']: obj = bld(features = 'cxx cxxprogram') - obj.uselib = 'BOOST_THREAD OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC' - obj.uselib = 'BOOST_THREAD OPENJPEG DCP AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS' ++ obj.uselib = 'BOOST_THREAD OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC WXWIDGETS' obj.includes = ['..'] - obj.use = ['libdvdomatic'] + obj.use = ['libdcpomatic'] obj.source = '%s.cc' % t obj.target = t if not bld.env.DISABLE_GUI: - for t in ['dvdomatic', 'dvdomatic_batch', 'servomatic_gui']: + for t in ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server']: obj = bld(features = 'cxx cxxprogram') - obj.uselib = 'DCP CXML OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC' + obj.uselib = 'DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML WXWIDGETS' + if bld.env.STATIC: + obj.uselib += ' GTK' obj.includes = ['..'] - obj.use = ['libdvdomatic', 'libdvdomatic-wx'] + obj.use = ['libdcpomatic', 'libdcpomatic-wx'] obj.source = '%s.cc' % t if bld.env.TARGET_WINDOWS: - obj.source += ' ../../platform/windows/dvdomatic.rc' + obj.source += ' ../../platform/windows/dcpomatic.rc' obj.target = t - i18n.po_to_mo(os.path.join('src', 'tools'), 'dvdomatic', bld) + i18n.po_to_mo(os.path.join('src', 'tools'), 'dcpomatic', bld) def pot(bld): - i18n.pot(os.path.join('src', 'tools'), 'dvdomatic.cc', 'dvdomatic') + i18n.pot(os.path.join('src', 'tools'), 'dcpomatic.cc', 'dcpomatic') def pot_merge(bld): - i18n.pot_merge(os.path.join('src', 'tools'), 'dvdomatic') + i18n.pot_merge(os.path.join('src', 'tools'), 'dcpomatic') diff --combined src/wx/about_dialog.cc index 0c56cf1be,cbac3ed43..ca19326a1 --- a/src/wx/about_dialog.cc +++ b/src/wx/about_dialog.cc @@@ -27,7 -27,7 +27,7 @@@ using std::vector; AboutDialog::AboutDialog (wxWindow* parent) - : wxDialog (parent, wxID_ANY, _("About DVD-o-matic")) + : wxDialog (parent, wxID_ANY, _("About DCP-o-matic")) { wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL); @@@ -38,15 -38,15 +38,15 @@@ wxFont version_font (*wxNORMAL_FONT); version_font.SetWeight (wxFONTWEIGHT_BOLD); - wxStaticText* t = new wxStaticText (this, wxID_ANY, _("DVD-o-matic")); + wxStaticText* t = new wxStaticText (this, wxID_ANY, _("DCP-o-matic")); t->SetFont (title_font); sizer->Add (t, wxSizerFlags().Centre().Border()); wxString s; - if (strcmp (dvdomatic_git_commit, "release") == 0) { - t = new wxStaticText (this, wxID_ANY, std_to_wx (String::compose ("Version %1", dvdomatic_version))); + if (strcmp (dcpomatic_git_commit, "release") == 0) { + t = new wxStaticText (this, wxID_ANY, std_to_wx (String::compose ("Version %1", dcpomatic_version))); } else { - t = new wxStaticText (this, wxID_ANY, std_to_wx (String::compose ("Version %1 git %2", dvdomatic_version, dvdomatic_git_commit))); + t = new wxStaticText (this, wxID_ANY, std_to_wx (String::compose ("Version %1 git %2", dcpomatic_version, dcpomatic_git_commit))); } t->SetFont (version_font); sizer->Add (t, wxSizerFlags().Centre().Border()); @@@ -62,8 -62,8 +62,8 @@@ wxHyperlinkCtrl* h = new wxHyperlinkCtrl ( this, wxID_ANY, - wxT ("www.carlh.net/software/dvdomatic"), - wxT ("http://www.carlh.net/software/dvdomatic") + wxT ("dcpomatic.com"), + wxT ("http://dcpomatic.com") ); sizer->Add (h, wxSizerFlags().Centre().Border()); @@@ -117,6 -117,10 +117,6 @@@ sizer->Add (_notebook, wxSizerFlags().Centre().Border().Expand()); -#if 0 - info.SetWebSite (wxT ("http://carlh.net/software/dvdomatic")); -#endif - SetSizerAndFit (sizer); } @@@ -138,7 -142,7 +138,7 @@@ AboutDialog::add_section (wxString name int c = 0; for (size_t i = 0; i < credits.Count(); ++i) { - add_label_to_sizer (sizers[c], panel, credits[i]); + add_label_to_sizer (sizers[c], panel, credits[i], false); ++c; if (c == N) { c = 0; diff --combined src/wx/audio_dialog.cc index ea7cc60dd,21e4e5940..22e09cc7a --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@@ -18,10 -18,10 +18,10 @@@ */ #include +#include "lib/audio_analysis.h" +#include "lib/film.h" #include "audio_dialog.h" #include "audio_plot.h" -#include "audio_analysis.h" -#include "film.h" #include "wx_util.h" using boost::shared_ptr; @@@ -43,6 -43,7 +43,6 @@@ AudioDialog::AudioDialog (wxWindow* par wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Channels")); side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16); } - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { _channel_checkbox[i] = new wxCheckBox (this, wxID_ANY, std_to_wx (audio_channel_name (i))); @@@ -83,40 -84,62 +83,39 @@@ } void -AudioDialog::set_film (boost::shared_ptr f) +AudioDialog::set_content (shared_ptr c) { - _film_changed_connection.disconnect (); - _film_audio_analysis_succeeded_connection.disconnect (); + _content_changed_connection.disconnect (); - _film = f; + _content = c; try_to_load_analysis (); - setup_channels (); - _plot->set_gain (_film->audio_gain ()); + _plot->set_gain (_content->audio_gain ()); - _film_changed_connection = _film->Changed.connect (bind (&AudioDialog::film_changed, this, _1)); - _film_audio_analysis_succeeded_connection = _film->AudioAnalysisSucceeded.connect (bind (&AudioDialog::try_to_load_analysis, this)); + _content_changed_connection = _content->Changed.connect (bind (&AudioDialog::content_changed, this, _2)); - SetTitle (wxString::Format (_("DVD-o-matic audio - %s"), std_to_wx(_film->name()).data())); + SetTitle (wxString::Format (_("DCP-o-matic audio - %s"), std_to_wx(_content->file().filename().string()).data())); } -void -AudioDialog::setup_channels () -{ - if (!_film->audio_stream()) { - return; - } - - AudioMapping m (_film); - - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - if (m.dcp_to_source(static_cast(i))) { - _channel_checkbox[i]->Show (); - } else { - _channel_checkbox[i]->Hide (); - } - } -} -- void AudioDialog::try_to_load_analysis () { shared_ptr a; - if (boost::filesystem::exists (_film->audio_analysis_path())) { - a.reset (new AudioAnalysis (_film->audio_analysis_path ())); + if (boost::filesystem::exists (_content->audio_analysis_path())) { + a.reset (new AudioAnalysis (_content->audio_analysis_path ())); } else { if (IsShown ()) { - _film->analyse_audio (); + _content->analyse_audio (bind (&AudioDialog::try_to_load_analysis, this)); } } _plot->set_analysis (a); - AudioMapping m (_film); - optional c = m.source_to_dcp (0); - if (c) { - _channel_checkbox[c.get()]->SetValue (true); - _plot->set_channel_visible (0, true); + if (_channel_checkbox[0]) { + _channel_checkbox[0]->SetValue (true); } + _plot->set_channel_visible (0, true); for (int i = 0; i < AudioPoint::COUNT; ++i) { _type_checkbox[i]->SetValue (true); @@@ -134,14 -157,27 +133,14 @@@ AudioDialog::channel_clicked (wxCommand assert (c < MAX_AUDIO_CHANNELS); - AudioMapping m (_film); - optional s = m.dcp_to_source (static_cast (c)); - if (s) { - _plot->set_channel_visible (s.get(), _channel_checkbox[c]->GetValue ()); - } + _plot->set_channel_visible (c, _channel_checkbox[c]->GetValue ()); } void -AudioDialog::film_changed (Film::Property p) +AudioDialog::content_changed (int p) { - switch (p) { - case Film::AUDIO_GAIN: - _plot->set_gain (_film->audio_gain ()); - break; - case Film::CONTENT_AUDIO_STREAM: - case Film::EXTERNAL_AUDIO: - case Film::USE_CONTENT_AUDIO: - setup_channels (); - break; - default: - break; + if (p == AudioContentProperty::AUDIO_GAIN) { + _plot->set_gain (_content->audio_gain ()); } } diff --combined src/wx/config_dialog.cc index 844b03ad7,e622c331b..e66be174d --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@@ -28,7 -28,7 +28,7 @@@ #include #include "lib/config.h" #include "lib/server.h" -#include "lib/format.h" +#include "lib/ratio.h" #include "lib/scaler.h" #include "lib/filter.h" #include "lib/dcp_content_type.h" @@@ -57,6 -57,8 +57,6 @@@ ConfigDialog::ConfigDialog (wxWindow* p _notebook->AddPage (_metadata_panel, _("Metadata"), false); make_tms_panel (); _notebook->AddPage (_tms_panel, _("TMS"), false); - make_ab_panel (); - _notebook->AddPage (_ab_panel, _("A/B mode"), false); wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); overall_sizer->Add (s, 1, wxEXPAND | wxALL, 6); @@@ -78,7 -80,7 +78,7 @@@ ConfigDialog::make_misc_panel ( wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); _misc_panel->SetSizer (s); - wxFlexGridSizer* table = new wxFlexGridSizer (3, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (3, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (3, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); s->Add (table, 1, wxALL | wxEXPAND, 8); @@@ -93,7 -95,7 +93,7 @@@ table->Add (_language, 1, wxEXPAND); table->AddSpacer (0); - wxStaticText* restart = add_label_to_sizer (table, _misc_panel, _("(restart DVD-o-matic to see language changes)")); + wxStaticText* restart = add_label_to_sizer (table, _misc_panel, _("(restart DVD-o-matic to see language changes)"), false); wxFont font = restart->GetFont(); font.SetStyle (wxFONTSTYLE_ITALIC); font.SetPointSize (font.GetPointSize() - 1); @@@ -101,18 -103,13 +101,18 @@@ table->AddSpacer (0); table->AddSpacer (0); - add_label_to_sizer (table, _misc_panel, _("Threads to use for encoding on this host")); + add_label_to_sizer (table, _misc_panel, _("Threads to use for encoding on this host"), true); _num_local_encoding_threads = new wxSpinCtrl (_misc_panel); - table->Add (_num_local_encoding_threads, 1, wxEXPAND); + table->Add (_num_local_encoding_threads, 1); table->AddSpacer (0); - add_label_to_sizer (table, _misc_panel, _("Default duration of still images")); ++ add_label_to_sizer (table, _misc_panel, _("Default duration of still images"), true); + _default_still_length = new wxSpinCtrl (_misc_panel); + table->Add (_default_still_length, 1, wxEXPAND); - add_label_to_sizer (table, _misc_panel, _("s")); ++ add_label_to_sizer (table, _misc_panel, _("s"), false); + - add_label_to_sizer (table, _misc_panel, _("Default directory for new films")); - #ifdef __WXMSW__ + add_label_to_sizer (table, _misc_panel, _("Default directory for new films"), true); -#ifdef DVDOMATIC_USE_OWN_DIR_PICKER ++#ifdef DCPOMATIC_USE_OWN_DIR_PICKER _default_directory = new DirPickerCtrl (_misc_panel); #else _default_directory = new wxDirPickerCtrl (_misc_panel, wxDD_DIR_MUST_EXIST); @@@ -120,17 -117,17 +120,17 @@@ table->Add (_default_directory, 1, wxEXPAND); table->AddSpacer (0); - add_label_to_sizer (table, _misc_panel, _("Default DCI name details")); + add_label_to_sizer (table, _misc_panel, _("Default DCI name details"), true); _default_dci_metadata_button = new wxButton (_misc_panel, wxID_ANY, _("Edit...")); table->Add (_default_dci_metadata_button); table->AddSpacer (1); - add_label_to_sizer (table, _misc_panel, _("Default container")); - add_label_to_sizer (table, _misc_panel, _("Default format"), true); - _default_format = new wxChoice (_misc_panel, wxID_ANY); - table->Add (_default_format); ++ add_label_to_sizer (table, _misc_panel, _("Default container"), true); + _default_container = new wxChoice (_misc_panel, wxID_ANY); + table->Add (_default_container); table->AddSpacer (1); - add_label_to_sizer (table, _misc_panel, _("Default content type")); + add_label_to_sizer (table, _misc_panel, _("Default content type"), true); _default_dcp_content_type = new wxChoice (_misc_panel, wxID_ANY); table->Add (_default_dcp_content_type); table->AddSpacer (1); @@@ -160,26 -157,22 +160,26 @@@ _num_local_encoding_threads->SetValue (config->num_local_encoding_threads ()); _num_local_encoding_threads->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (ConfigDialog::num_local_encoding_threads_changed), 0, this); + _default_still_length->SetRange (1, 3600); + _default_still_length->SetValue (config->default_still_length ()); + _default_still_length->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (ConfigDialog::default_still_length_changed), 0, this); + _default_directory->SetPath (std_to_wx (config->default_directory_or (wx_to_std (wxStandardPaths::Get().GetDocumentsDir())))); _default_directory->Connect (wxID_ANY, wxEVT_COMMAND_DIRPICKER_CHANGED, wxCommandEventHandler (ConfigDialog::default_directory_changed), 0, this); _default_dci_metadata_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (ConfigDialog::edit_default_dci_metadata_clicked), 0, this); - vector fmt = Format::all (); + vector ratio = Ratio::all (); int n = 0; - for (vector::iterator i = fmt.begin(); i != fmt.end(); ++i) { - _default_format->Append (std_to_wx ((*i)->name ())); - if (*i == config->default_format ()) { - _default_format->SetSelection (n); + for (vector::iterator i = ratio.begin(); i != ratio.end(); ++i) { + _default_container->Append (std_to_wx ((*i)->nickname ())); + if (*i == config->default_container ()) { + _default_container->SetSelection (n); } ++n; } - _default_format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (ConfigDialog::default_format_changed), 0, this); + _default_container->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (ConfigDialog::default_container_changed), 0, this); vector const ct = DCPContentType::all (); n = 0; @@@ -201,23 -194,23 +201,23 @@@ ConfigDialog::make_tms_panel ( wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); _tms_panel->SetSizer (s); - wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); s->Add (table, 1, wxALL | wxEXPAND, 8); - add_label_to_sizer (table, _tms_panel, _("IP address")); + add_label_to_sizer (table, _tms_panel, _("IP address"), true); _tms_ip = new wxTextCtrl (_tms_panel, wxID_ANY); table->Add (_tms_ip, 1, wxEXPAND); - add_label_to_sizer (table, _tms_panel, _("Target path")); + add_label_to_sizer (table, _tms_panel, _("Target path"), true); _tms_path = new wxTextCtrl (_tms_panel, wxID_ANY); table->Add (_tms_path, 1, wxEXPAND); - add_label_to_sizer (table, _tms_panel, _("User name")); + add_label_to_sizer (table, _tms_panel, _("User name"), true); _tms_user = new wxTextCtrl (_tms_panel, wxID_ANY); table->Add (_tms_user, 1, wxEXPAND); - add_label_to_sizer (table, _tms_panel, _("Password")); + add_label_to_sizer (table, _tms_panel, _("Password"), true); _tms_password = new wxTextCtrl (_tms_panel, wxID_ANY); table->Add (_tms_password, 1, wxEXPAND); @@@ -240,15 -233,15 +240,15 @@@ ConfigDialog::make_metadata_panel ( wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); _metadata_panel->SetSizer (s); - wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); s->Add (table, 1, wxALL | wxEXPAND, 8); - add_label_to_sizer (table, _metadata_panel, _("Issuer")); + add_label_to_sizer (table, _metadata_panel, _("Issuer"), true); _issuer = new wxTextCtrl (_metadata_panel, wxID_ANY); table->Add (_issuer, 1, wxEXPAND); - add_label_to_sizer (table, _metadata_panel, _("Creator")); + add_label_to_sizer (table, _metadata_panel, _("Creator"), true); _creator = new wxTextCtrl (_metadata_panel, wxID_ANY); table->Add (_creator, 1, wxEXPAND); @@@ -260,6 -253,48 +260,6 @@@ _creator->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ConfigDialog::creator_changed), 0, this); } -void -ConfigDialog::make_ab_panel () -{ - _ab_panel = new wxPanel (_notebook); - wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); - _ab_panel->SetSizer (s); - - wxFlexGridSizer* table = new wxFlexGridSizer (3, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); - table->AddGrowableCol (1, 1); - s->Add (table, 1, wxALL, 8); - - add_label_to_sizer (table, _ab_panel, _("Reference scaler"), true); - _reference_scaler = new wxChoice (_ab_panel, wxID_ANY); - vector const sc = Scaler::all (); - for (vector::const_iterator i = sc.begin(); i != sc.end(); ++i) { - _reference_scaler->Append (std_to_wx ((*i)->name ())); - } - - table->Add (_reference_scaler, 1, wxEXPAND); - table->AddSpacer (0); - - { - add_label_to_sizer (table, _ab_panel, _("Reference filters"), true); - wxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _reference_filters = new wxStaticText (_ab_panel, wxID_ANY, wxT ("")); - s->Add (_reference_filters, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, 6); - _reference_filters_button = new wxButton (_ab_panel, wxID_ANY, _("Edit...")); - s->Add (_reference_filters_button, 0); - table->Add (s, 1, wxEXPAND); - table->AddSpacer (0); - } - - Config* config = Config::instance (); - - _reference_scaler->SetSelection (Scaler::as_index (config->reference_scaler ())); - _reference_scaler->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (ConfigDialog::reference_scaler_changed), 0, this); - - pair p = Filter::ffmpeg_strings (config->reference_filters ()); - _reference_filters->SetLabel (std_to_wx (p.first) + N_(" ") + std_to_wx (p.second)); - _reference_filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (ConfigDialog::edit_reference_filters_clicked), 0, this); -} - void ConfigDialog::make_servers_panel () { @@@ -267,7 -302,7 +267,7 @@@ wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); _servers_panel->SetSizer (s); - wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (0, 1); s->Add (table, 1, wxALL | wxEXPAND, 8); @@@ -288,11 -323,11 +288,11 @@@ { wxSizer* s = new wxBoxSizer (wxVERTICAL); _add_server = new wxButton (_servers_panel, wxID_ANY, _("Add")); - s->Add (_add_server); + s->Add (_add_server, 0, wxTOP | wxBOTTOM, 2); _edit_server = new wxButton (_servers_panel, wxID_ANY, _("Edit")); - s->Add (_edit_server); + s->Add (_edit_server, 0, wxTOP | wxBOTTOM, 2); _remove_server = new wxButton (_servers_panel, wxID_ANY, _("Remove")); - s->Add (_remove_server); + s->Add (_remove_server, 0, wxTOP | wxBOTTOM, 2); table->Add (s, 0); } @@@ -439,6 -474,32 +439,6 @@@ ConfigDialog::server_selection_changed _remove_server->Enable (i >= 0); } -void -ConfigDialog::reference_scaler_changed (wxCommandEvent &) -{ - int const n = _reference_scaler->GetSelection (); - if (n >= 0) { - Config::instance()->set_reference_scaler (Scaler::from_index (n)); - } -} - -void -ConfigDialog::edit_reference_filters_clicked (wxCommandEvent &) -{ - FilterDialog* d = new FilterDialog (this, Config::instance()->reference_filters ()); - d->ActiveChanged.connect (boost::bind (&ConfigDialog::reference_filters_changed, this, _1)); - d->ShowModal (); - d->Destroy (); -} - -void -ConfigDialog::reference_filters_changed (vector f) -{ - Config::instance()->set_reference_filters (f); - pair p = Filter::ffmpeg_strings (Config::instance()->reference_filters ()); - _reference_filters->SetLabel (std_to_wx (p.first) + N_(" ") + std_to_wx (p.second)); -} - void ConfigDialog::edit_default_dci_metadata_clicked (wxCommandEvent &) { @@@ -466,16 -527,10 +466,16 @@@ ConfigDialog::setup_language_sensitivit } void -ConfigDialog::default_format_changed (wxCommandEvent &) +ConfigDialog::default_still_length_changed (wxCommandEvent &) +{ + Config::instance()->set_default_still_length (_default_still_length->GetValue ()); +} + +void +ConfigDialog::default_container_changed (wxCommandEvent &) { - vector fmt = Format::all (); - Config::instance()->set_default_format (fmt[_default_format->GetSelection()]); + vector ratio = Ratio::all (); + Config::instance()->set_default_container (ratio[_default_container->GetSelection()]); } void diff --combined src/wx/config_dialog.h index 3da48fd08,a97788942..459d64dd7 --- a/src/wx/config_dialog.h +++ b/src/wx/config_dialog.h @@@ -25,6 -25,7 +25,7 @@@ #include #include #include + #include "wx_util.h" class DirPickerCtrl; class wxNotebook; @@@ -47,14 -48,16 +48,14 @@@ private void tms_user_changed (wxCommandEvent &); void tms_password_changed (wxCommandEvent &); void num_local_encoding_threads_changed (wxCommandEvent &); + void default_still_length_changed (wxCommandEvent &); void default_directory_changed (wxCommandEvent &); void edit_default_dci_metadata_clicked (wxCommandEvent &); - void reference_scaler_changed (wxCommandEvent &); - void edit_reference_filters_clicked (wxCommandEvent &); - void reference_filters_changed (std::vector); void add_server_clicked (wxCommandEvent &); void edit_server_clicked (wxCommandEvent &); void remove_server_clicked (wxCommandEvent &); void server_selection_changed (wxListEvent &); - void default_format_changed (wxCommandEvent &); + void default_container_changed (wxCommandEvent &); void default_dcp_content_type_changed (wxCommandEvent &); void issuer_changed (wxCommandEvent &); void creator_changed (wxCommandEvent &); @@@ -65,29 -68,33 +66,29 @@@ void make_misc_panel (); void make_tms_panel (); void make_metadata_panel (); - void make_ab_panel (); void make_servers_panel (); wxNotebook* _notebook; wxPanel* _misc_panel; wxPanel* _tms_panel; - wxPanel* _ab_panel; wxPanel* _servers_panel; wxPanel* _metadata_panel; wxCheckBox* _set_language; wxChoice* _language; - wxChoice* _default_format; + wxChoice* _default_container; wxChoice* _default_dcp_content_type; wxTextCtrl* _tms_ip; wxTextCtrl* _tms_path; wxTextCtrl* _tms_user; wxTextCtrl* _tms_password; wxSpinCtrl* _num_local_encoding_threads; -#ifdef DVDOMATIC_USE_OWN_DIR_PICKER + wxSpinCtrl* _default_still_length; - #ifdef __WXMSW__ ++#ifdef DCPOMATIC_USE_OWN_DIR_PICKER DirPickerCtrl* _default_directory; #else wxDirPickerCtrl* _default_directory; #endif wxButton* _default_dci_metadata_button; - wxChoice* _reference_scaler; - wxStaticText* _reference_filters; - wxButton* _reference_filters_button; wxListCtrl* _servers; wxButton* _add_server; wxButton* _edit_server; diff --combined src/wx/dci_metadata_dialog.cc index c08c58ed4,9c124ed74..df3a24f62 --- a/src/wx/dci_metadata_dialog.cc +++ b/src/wx/dci_metadata_dialog.cc @@@ -27,34 -27,34 +27,34 @@@ using boost::shared_ptr DCIMetadataDialog::DCIMetadataDialog (wxWindow* parent, DCIMetadata dm) : wxDialog (parent, wxID_ANY, _("DCI name"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) { - wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); - add_label_to_sizer (table, this, _("Audio Language (e.g. EN)")); + add_label_to_sizer (table, this, _("Audio Language (e.g. EN)"), true); _audio_language = new wxTextCtrl (this, wxID_ANY); table->Add (_audio_language, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Subtitle Language (e.g. FR)")); + add_label_to_sizer (table, this, _("Subtitle Language (e.g. FR)"), true); _subtitle_language = new wxTextCtrl (this, wxID_ANY); table->Add (_subtitle_language, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Territory (e.g. UK)")); + add_label_to_sizer (table, this, _("Territory (e.g. UK)"), true); _territory = new wxTextCtrl (this, wxID_ANY); table->Add (_territory, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Rating (e.g. 15)")); + add_label_to_sizer (table, this, _("Rating (e.g. 15)"), true); _rating = new wxTextCtrl (this, wxID_ANY); table->Add (_rating, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Studio (e.g. TCF)")); + add_label_to_sizer (table, this, _("Studio (e.g. TCF)"), true); _studio = new wxTextCtrl (this, wxID_ANY); table->Add (_studio, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Facility (e.g. DLA)")); + add_label_to_sizer (table, this, _("Facility (e.g. DLA)"), true); _facility = new wxTextCtrl (this, wxID_ANY); table->Add (_facility, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Package Type (e.g. OV)")); + add_label_to_sizer (table, this, _("Package Type (e.g. OV)"), true); _package_type = new wxTextCtrl (this, wxID_ANY); table->Add (_package_type, 1, wxEXPAND); diff --combined src/wx/film_editor.cc index 27803fb9a,a54b412b3..2d8c752a1 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@@ -25,20 -25,19 +25,20 @@@ #include #include #include +#include #include #include #include -#include "lib/format.h" #include "lib/film.h" #include "lib/transcode_job.h" #include "lib/exceptions.h" -#include "lib/ab_transcode_job.h" #include "lib/job_manager.h" #include "lib/filter.h" +#include "lib/ratio.h" #include "lib/config.h" -#include "lib/ffmpeg_decoder.h" -#include "lib/log.h" +#include "lib/imagemagick_content.h" +#include "lib/sndfile_content.h" +#include "lib/dcp_content_type.h" #include "filter_dialog.h" #include "wx_util.h" #include "film_editor.h" @@@ -47,10 -46,6 +47,10 @@@ #include "dci_metadata_dialog.h" #include "scaler.h" #include "audio_dialog.h" +#include "imagemagick_content_dialog.h" +#include "timeline_dialog.h" +#include "audio_mapping_view.h" +#include "timecode.h" using std::string; using std::cout; @@@ -60,114 -55,155 +60,119 @@@ using std::fixed using std::setprecision; using std::list; using std::vector; +using std::max; using boost::shared_ptr; +using boost::weak_ptr; using boost::dynamic_pointer_cast; +using boost::lexical_cast; /** @param f Film to edit */ FilmEditor::FilmEditor (shared_ptr f, wxWindow* parent) : wxPanel (parent) - , _film (f) , _generally_sensitive (true) , _audio_dialog (0) + , _timeline_dialog (0) { wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); - SetSizer (s); - _notebook = new wxNotebook (this, wxID_ANY); - s->Add (_notebook, 1); - make_film_panel (); - _notebook->AddPage (_film_panel, _("Film"), true); - make_video_panel (); - _notebook->AddPage (_video_panel, _("Video"), false); - make_audio_panel (); - _notebook->AddPage (_audio_panel, _("Audio"), false); - make_subtitle_panel (); - _notebook->AddPage (_subtitle_panel, _("Subtitles"), false); + _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); + + setup_ratios (); - set_film (_film); + set_film (f); connect_to_widgets (); JobManager::instance()->ActiveJobsChanged.connect ( bind (&FilmEditor::active_jobs_changed, this, _1) ); - setup_visibility (); - setup_formats (); + SetSizerAndFit (s); } void -FilmEditor::make_film_panel () +FilmEditor::make_dcp_panel () { - _film_panel = new wxPanel (_notebook); - _film_sizer = new wxBoxSizer (wxVERTICAL); - _film_panel->SetSizer (_film_sizer); + _dcp_panel = new wxPanel (_main_notebook); + _dcp_sizer = new wxBoxSizer (wxVERTICAL); + _dcp_panel->SetSizer (_dcp_sizer); - wxGridBagSizer* grid = new wxGridBagSizer (4, 4); - wxGridBagSizer* grid = new wxGridBagSizer (DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); - _film_sizer->Add (grid, 0, wxALL, 8); ++ 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"), wxGBPosition (r, 0)); - add_label_to_grid_bag_sizer (grid, _film_panel, _("Name"), true, wxGBPosition (r, 0)); - _name = new wxTextCtrl (_film_panel, wxID_ANY); ++ 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); + grid->Add (_name, wxGBPosition(r, 1), wxDefaultSpan, wxEXPAND | wxLEFT | wxRIGHT); ++r; - add_label_to_grid_bag_sizer (grid, _dcp_panel, _("DCP Name"), wxGBPosition (r, 0)); - add_label_to_grid_bag_sizer (grid, _film_panel, _("DCP Name"), true, wxGBPosition (r, 0)); - _dcp_name = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); ++ 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; + int flags = wxALIGN_CENTER_VERTICAL; + #ifdef __WXOSX__ + flags |= wxALIGN_RIGHT; + #endif - - _use_dci_name = new wxCheckBox (_film_panel, wxID_ANY, _("Use DCI name")); ++ + _use_dci_name = new wxCheckBox (_dcp_panel, wxID_ANY, _("Use DCI name")); - grid->Add (_use_dci_name, wxGBPosition (r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); + grid->Add (_use_dci_name, wxGBPosition (r, 0), wxDefaultSpan, flags); - _edit_dci_button = new wxButton (_film_panel, wxID_ANY, _("Details...")); + _edit_dci_button = new wxButton (_dcp_panel, wxID_ANY, _("Details...")); grid->Add (_edit_dci_button, wxGBPosition (r, 1), wxDefaultSpan); ++r; - add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Container"), wxGBPosition (r, 0)); - add_label_to_grid_bag_sizer (grid, _film_panel, _("Content"), true, wxGBPosition (r, 0)); - _content = new wxFilePickerCtrl (_film_panel, wxID_ANY, wxT (""), _("Select Content File"), wxT("*.*")); - grid->Add (_content, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND); - ++r; - - _trust_content_header = new wxCheckBox (_film_panel, wxID_ANY, _("Trust content's header")); - video_control (_trust_content_header); - grid->Add (_trust_content_header, wxGBPosition (r, 0), wxGBSpan(1, 2)); ++ 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)); ++ grid->Add (_container, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND); ++r; - add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Content Type"), wxGBPosition (r, 0)); - add_label_to_grid_bag_sizer (grid, _film_panel, _("Content Type"), true, wxGBPosition (r, 0)); - _dcp_content_type = new wxChoice (_film_panel, wxID_ANY); ++ 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; - video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Original Frame Rate"), true, wxGBPosition (r, 0))); - _source_frame_rate = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); - grid->Add (video_control (_source_frame_rate), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - ++r; - { - add_label_to_grid_bag_sizer (grid, _dcp_panel, _("DCP Frame Rate"), wxGBPosition (r, 0)); - add_label_to_grid_bag_sizer (grid, _film_panel, _("DCP Frame Rate"), true, wxGBPosition (r, 0)); ++ add_label_to_grid_bag_sizer (grid, _dcp_panel, _("DCP Frame Rate"), true, wxGBPosition (r, 0)); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _dcp_frame_rate = new wxChoice (_film_panel, wxID_ANY); + _dcp_frame_rate = new wxChoice (_dcp_panel, wxID_ANY); s->Add (_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL); - _best_dcp_frame_rate = new wxButton (_film_panel, wxID_ANY, _("Use best")); - s->Add (_best_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 6); + _best_dcp_frame_rate = new wxButton (_dcp_panel, wxID_ANY, _("Use best")); + s->Add (_best_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); grid->Add (s, wxGBPosition (r, 1)); } ++r; - _frame_rate_description = new wxStaticText (_film_panel, wxID_ANY, wxT ("\n \n "), wxDefaultPosition, wxDefaultSize); - grid->Add (video_control (_frame_rate_description), wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6); - wxFont font = _frame_rate_description->GetFont(); - font.SetStyle(wxFONTSTYLE_ITALIC); - font.SetPointSize(font.GetPointSize() - 1); - _frame_rate_description->SetFont(font); - ++r; - - video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Length"), true, wxGBPosition (r, 0))); - _length = new wxStaticText (_film_panel, wxID_ANY, wxT ("")); - grid->Add (video_control (_length), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - ++r; - - { - add_label_to_grid_bag_sizer (grid, _dcp_panel, _("JPEG2000 bandwidth"), wxGBPosition (r, 0)); - video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim frames"), true, wxGBPosition (r, 0))); - wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); - video_control (add_label_to_sizer (s, _film_panel, _("Start"), true)); - _trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); - s->Add (video_control (_trim_start)); - video_control (add_label_to_sizer (s, _film_panel, _("End"), true)); - _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); - s->Add (video_control (_trim_end)); - ++ 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, _("MBps")); ++ add_label_to_sizer (s, _dcp_panel, _("MBps"), false); grid->Add (s, wxGBPosition (r, 1)); } ++r; - add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Scaler"), wxGBPosition (r, 0)); - video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), true, wxGBPosition (r, 0))); - _trim_type = new wxChoice (_film_panel, wxID_ANY); - grid->Add (video_control (_trim_type), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); ++ 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)); ++ grid->Add (_scaler, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); ++r; - _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B")); - video_control (_dcp_ab); - grid->Add (_dcp_ab, wxGBPosition (r, 0)); - ++r; + vector const sc = Scaler::all (); + for (vector::const_iterator i = sc.begin(); i != sc.end(); ++i) { + _scaler->Append (std_to_wx ((*i)->name())); + } - /* STILL-only stuff */ - { - still_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Duration"), true, wxGBPosition (r, 0))); - wxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _still_duration = new wxSpinCtrl (_film_panel); - still_control (_still_duration); - s->Add (_still_duration, 1, wxEXPAND); - /// TRANSLATORS: `s' here is an abbreviation for seconds, the unit of time - still_control (add_label_to_sizer (s, _film_panel, _("s"), false)); - grid->Add (s, wxGBPosition (r, 1)); + vector const ratio = Ratio::all (); + for (vector::const_iterator i = ratio.begin(); i != ratio.end(); ++i) { + _container->Append (std_to_wx ((*i)->nickname ())); } - ++r; vector const ct = DCPContentType::all (); for (vector::const_iterator i = ct.begin(); i != ct.end(); ++i) { @@@ -179,87 -215,93 +184,89 @@@ _dcp_frame_rate->Append (std_to_wx (boost::lexical_cast (*i))); } - _trim_type->Append (_("encode all frames and play the subset")); - _trim_type->Append (_("encode only the subset")); + _j2k_bandwidth->SetRange (50, 250); } void FilmEditor::connect_to_widgets () { - _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this); - _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this); - _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this); - _format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this); - _content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this); - _trust_content_header->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_header_changed), 0, this); - _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this); - _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this); - _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this); - _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this); - _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this); - _scaler->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this); - _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this); - _dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this); - _best_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this); - _pad_with_silence->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::pad_with_silence_toggled), 0, this); - _minimum_audio_channels->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::minimum_audio_channels_changed), 0, this); - _dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this); - _still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this); - _trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this); - _trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this); - _trim_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::trim_type_changed), 0, this); - _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this); - _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this); - _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this); - _colour_lut->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this); - _j2k_bandwidth->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this); - _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this); - _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this); - _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this); + _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this); + _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this); + _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this); + _container->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::container_changed), 0, this); + _ratio->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::ratio_changed), 0, this); + _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this); + _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this); + _content_add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_add_clicked), 0, this); + _content_remove->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_remove_clicked), 0, this); + _content_timeline->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_timeline_clicked), 0, this); + _loop_content->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::loop_content_toggled), 0, this); + _loop_count->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::loop_count_changed), 0, this); + _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this); + _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this); + _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this); + _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this); + _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this); + _scaler->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this); + _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this); + _dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this); + _best_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this); ++// _pad_with_silence->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::pad_with_silence_toggled), 0, this); ++// _minimum_audio_channels->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::minimum_audio_channels_changed), 0, this); + _with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this); + _subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this); + _subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this); + _colour_lut->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this); + _j2k_bandwidth->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this); + _audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this); _audio_gain_calculate_button->Connect ( wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this ); - _show_audio->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::show_audio_clicked), 0, this); - _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this); - _use_content_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this); - _use_external_audio->Connect (wxID_ANY, wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler (FilmEditor::use_audio_changed), 0, this); - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - _external_audio[i]->Connect ( - wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::external_audio_changed), 0, this - ); - } + _show_audio->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::show_audio_clicked), 0, this); + _audio_delay->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this); + _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this); + _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this); + _audio_mapping->Changed.connect (boost::bind (&FilmEditor::audio_mapping_changed, this, _1)); + _start->Changed.connect (boost::bind (&FilmEditor::start_changed, this)); + _length->Changed.connect (boost::bind (&FilmEditor::length_changed, this)); } void FilmEditor::make_video_panel () { - _video_panel = new wxPanel (_notebook); - _video_sizer = new wxBoxSizer (wxVERTICAL); - _video_panel->SetSizer (_video_sizer); + _video_panel = new wxPanel (_content_notebook); + wxBoxSizer* video_sizer = new wxBoxSizer (wxVERTICAL); + _video_panel->SetSizer (video_sizer); - wxGridBagSizer* grid = new wxGridBagSizer (4, 4); - wxGridBagSizer* grid = new wxGridBagSizer (DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); - _video_sizer->Add (grid, 0, wxALL, 8); ++ wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + video_sizer->Add (grid, 0, wxALL, 8); int r = 0; - add_label_to_grid_bag_sizer (grid, _video_panel, _("Left crop"), wxGBPosition (r, 0)); - add_label_to_grid_bag_sizer (grid, _video_panel, _("Format"), true, wxGBPosition (r, 0)); - _format = new wxChoice (_video_panel, wxID_ANY); - grid->Add (_format, wxGBPosition (r, 1)); - ++r; - + add_label_to_grid_bag_sizer (grid, _video_panel, _("Left crop"), true, wxGBPosition (r, 0)); _left_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); grid->Add (_left_crop, wxGBPosition (r, 1)); ++r; - add_label_to_grid_bag_sizer (grid, _video_panel, _("Right crop"), wxGBPosition (r, 0)); + add_label_to_grid_bag_sizer (grid, _video_panel, _("Right crop"), true, wxGBPosition (r, 0)); _right_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); grid->Add (_right_crop, wxGBPosition (r, 1)); ++r; - add_label_to_grid_bag_sizer (grid, _video_panel, _("Top crop"), wxGBPosition (r, 0)); + add_label_to_grid_bag_sizer (grid, _video_panel, _("Top crop"), true, wxGBPosition (r, 0)); _top_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); grid->Add (_top_crop, wxGBPosition (r, 1)); ++r; - add_label_to_grid_bag_sizer (grid, _video_panel, _("Bottom crop"), wxGBPosition (r, 0)); + add_label_to_grid_bag_sizer (grid, _video_panel, _("Bottom crop"), true, wxGBPosition (r, 0)); _bottom_crop = new wxSpinCtrl (_video_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1)); grid->Add (_bottom_crop, wxGBPosition (r, 1)); ++r; - add_label_to_grid_bag_sizer (grid, _video_panel, _("Scale to"), wxGBPosition (r, 0)); ++ add_label_to_grid_bag_sizer (grid, _video_panel, _("Scale to"), true, wxGBPosition (r, 0)); + _ratio = new wxChoice (_video_panel, wxID_ANY); + grid->Add (_ratio, wxGBPosition (r, 1)); + ++r; + _scaling_description = new wxStaticText (_video_panel, wxID_ANY, wxT ("\n \n \n \n"), wxDefaultPosition, wxDefaultSize); grid->Add (_scaling_description, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND | wxALIGN_CENTER_VERTICAL | wxALL, 6); wxFont font = _scaling_description->GetFont(); @@@ -270,17 -312,29 +277,17 @@@ /* VIDEO-only stuff */ { - add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), wxGBPosition (r, 0)); - video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), true, wxGBPosition (r, 0))); ++ add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), true, wxGBPosition (r, 0)); wxSizer* s = new wxBoxSizer (wxHORIZONTAL); _filters = new wxStaticText (_video_panel, wxID_ANY, _("None")); - video_control (_filters); s->Add (_filters, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM | wxRIGHT, 6); _filters_button = new wxButton (_video_panel, wxID_ANY, _("Edit...")); - s->Add (_filters_button, 0); - video_control (_filters_button); + s->Add (_filters_button, 0, wxALIGN_CENTER_VERTICAL); grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); } ++r; - add_label_to_grid_bag_sizer (grid, _video_panel, _("Colour look-up table"), wxGBPosition (r, 0)); - video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Scaler"), true, wxGBPosition (r, 0))); - _scaler = new wxChoice (_video_panel, wxID_ANY); - grid->Add (video_control (_scaler), wxGBPosition (r, 1)); - ++r; - - vector const sc = Scaler::all (); - for (vector::const_iterator i = sc.begin(); i != sc.end(); ++i) { - _scaler->Append (std_to_wx ((*i)->name())); - } - + add_label_to_grid_bag_sizer (grid, _video_panel, _("Colour look-up table"), true, wxGBPosition (r, 0)); _colour_lut = new wxChoice (_video_panel, wxID_ANY); for (int i = 0; i < 2; ++i) { _colour_lut->Append (std_to_wx (colour_lut_index_to_name (i))); @@@ -289,224 -343,215 +296,248 @@@ grid->Add (_colour_lut, wxGBPosition (r, 1), wxDefaultSpan, wxEXPAND); ++r; - { - add_label_to_grid_bag_sizer (grid, _video_panel, _("JPEG2000 bandwidth"), true, wxGBPosition (r, 0)); - wxSizer* s = new wxBoxSizer (wxHORIZONTAL); - _j2k_bandwidth = new wxSpinCtrl (_video_panel, wxID_ANY); - s->Add (_j2k_bandwidth, 1); - add_label_to_sizer (s, _video_panel, _("MBps"), false); - grid->Add (s, wxGBPosition (r, 1)); - } - ++r; - _left_crop->SetRange (0, 1024); _top_crop->SetRange (0, 1024); _right_crop->SetRange (0, 1024); _bottom_crop->SetRange (0, 1024); - _still_duration->SetRange (1, 60 * 60); - _trim_start->SetRange (0, 24 * 60 * 60); - _trim_end->SetRange (0, 24 * 60 * 60); - _j2k_bandwidth->SetRange (50, 250); +} + +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 | wxLC_SINGLE_SEL); + s->Add (_content, 1, wxEXPAND | wxTOP | wxBOTTOM, 6); + + _content->InsertColumn (0, wxT("")); + _content->SetColumnWidth (0, 512); + + wxBoxSizer* b = new wxBoxSizer (wxVERTICAL); + _content_add = new wxButton (_content_panel, wxID_ANY, _("Add...")); + b->Add (_content_add, 1, wxEXPAND | wxLEFT | wxRIGHT); + _content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove")); + b->Add (_content_remove, 1, wxEXPAND | wxLEFT | wxRIGHT); + _content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline...")); + b->Add (_content_timeline, 1, wxEXPAND | wxLEFT | wxRIGHT); + + s->Add (b, 0, wxALL, 4); + + _content_sizer->Add (s, 0.75, wxEXPAND | wxALL, 6); + } + + wxBoxSizer* h = new wxBoxSizer (wxHORIZONTAL); + _loop_content = new wxCheckBox (_content_panel, wxID_ANY, _("Loop everything")); + h->Add (_loop_content, 0, wxALL, 6); + _loop_count = new wxSpinCtrl (_content_panel, wxID_ANY); + h->Add (_loop_count, 0, wxALL, 6); - add_label_to_sizer (h, _content_panel, _("times")); ++ add_label_to_sizer (h, _content_panel, _("times"), false); + _content_sizer->Add (h, 0, wxALL, 6); + + _content_notebook = new wxNotebook (_content_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_LEFT); + _content_sizer->Add (_content_notebook, 1, wxEXPAND | wxTOP, 6); + + make_video_panel (); + _content_notebook->AddPage (_video_panel, _("Video"), false); + make_audio_panel (); + _content_notebook->AddPage (_audio_panel, _("Audio"), false); + make_subtitle_panel (); + _content_notebook->AddPage (_subtitle_panel, _("Subtitles"), false); + make_timing_panel (); + _content_notebook->AddPage (_timing_panel, _("Timing"), false); + + _loop_count->SetRange (2, 1024); } void FilmEditor::make_audio_panel () { - _audio_panel = new wxPanel (_notebook); - _audio_sizer = new wxBoxSizer (wxVERTICAL); - _audio_panel->SetSizer (_audio_sizer); + _audio_panel = new wxPanel (_content_notebook); + wxBoxSizer* audio_sizer = new wxBoxSizer (wxVERTICAL); + _audio_panel->SetSizer (audio_sizer); - wxFlexGridSizer* grid = new wxFlexGridSizer (3, 4, 4); - wxFlexGridSizer* grid = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); - _audio_sizer->Add (grid, 0, wxALL, 8); ++ wxFlexGridSizer* grid = new wxFlexGridSizer (3, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + audio_sizer->Add (grid, 0, wxALL, 8); _show_audio = new wxButton (_audio_panel, wxID_ANY, _("Show Audio...")); grid->Add (_show_audio, 1); grid->AddSpacer (0); + grid->AddSpacer (0); - add_label_to_sizer (grid, _audio_panel, _("Audio Gain")); ++ add_label_to_sizer (grid, _audio_panel, _("Audio Gain"), true); { - video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Gain"), true)); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); _audio_gain = new wxSpinCtrl (_audio_panel); - s->Add (video_control (_audio_gain), 1); - video_control (add_label_to_sizer (s, _audio_panel, _("dB"), false)); - _audio_gain_calculate_button = new wxButton (_audio_panel, wxID_ANY, _("Calculate...")); - video_control (_audio_gain_calculate_button); - s->Add (_audio_gain_calculate_button, 1, wxEXPAND); - grid->Add (s); + s->Add (_audio_gain, 1); - add_label_to_sizer (s, _audio_panel, _("dB")); ++ add_label_to_sizer (s, _audio_panel, _("dB"), false); + grid->Add (s, 1); } + + _audio_gain_calculate_button = new wxButton (_audio_panel, wxID_ANY, _("Calculate...")); + grid->Add (_audio_gain_calculate_button); - add_label_to_sizer (grid, _audio_panel, _("Audio Delay")); ++ add_label_to_sizer (grid, _audio_panel, _("Audio Delay"), false); { - video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Delay"), true)); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); _audio_delay = new wxSpinCtrl (_audio_panel); - s->Add (video_control (_audio_delay), 1); + s->Add (_audio_delay, 1); /// TRANSLATORS: this is an abbreviation for milliseconds, the unit of time - add_label_to_sizer (s, _audio_panel, _("ms")); - video_control (add_label_to_sizer (s, _audio_panel, _("ms"), false)); ++ add_label_to_sizer (s, _audio_panel, _("ms"), false); grid->Add (s); } + grid->AddSpacer (0); + - add_label_to_sizer (grid, _audio_panel, _("Audio Stream")); ++ add_label_to_sizer (grid, _audio_panel, _("Audio Stream"), true); + _audio_stream = new wxChoice (_audio_panel, wxID_ANY); + grid->Add (_audio_stream, 1); + _audio_description = new wxStaticText (_audio_panel, wxID_ANY, wxT ("")); + grid->AddSpacer (0); + + grid->Add (_audio_description, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8); + grid->AddSpacer (0); + grid->AddSpacer (0); + + _audio_mapping = new AudioMappingView (_audio_panel); + audio_sizer->Add (_audio_mapping, 1, wxEXPAND | wxALL, 6); + ++#if 0 + { + _pad_with_silence = new wxCheckBox (_audio_panel, wxID_ANY, _("Pad with silence to")); + grid->Add (_pad_with_silence); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _minimum_audio_channels = new wxSpinCtrl (_audio_panel); + s->Add (_minimum_audio_channels, 1); + add_label_to_sizer (s, _audio_panel, _("channels"), false); + grid->Add (s); + } + + { + _use_content_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use content's audio"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP); + grid->Add (video_control (_use_content_audio)); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _audio_stream = new wxChoice (_audio_panel, wxID_ANY); + s->Add (video_control (_audio_stream), 1); + _audio = new wxStaticText (_audio_panel, wxID_ANY, wxT ("")); + s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8); + grid->Add (s); + } - - _use_external_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use external audio")); - grid->Add (_use_external_audio); - grid->AddSpacer (0); - - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - add_label_to_sizer (grid, _audio_panel, std_to_wx (audio_channel_name (i)), true); - _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), _("Select Audio File"), wxT ("*.wav")); - grid->Add (_external_audio[i], 1, wxEXPAND); - } ++#endif + _audio_gain->SetRange (-60, 60); _audio_delay->SetRange (-1000, 1000); - _minimum_audio_channels->SetRange (0, MAX_AUDIO_CHANNELS); ++// _minimum_audio_channels->SetRange (0, MAX_AUDIO_CHANNELS); } void FilmEditor::make_subtitle_panel () { - _subtitle_panel = new wxPanel (_notebook); - _subtitle_sizer = new wxBoxSizer (wxVERTICAL); - _subtitle_panel->SetSizer (_subtitle_sizer); - wxFlexGridSizer* grid = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); - _subtitle_sizer->Add (grid, 0, wxALL, 8); + _subtitle_panel = new wxPanel (_content_notebook); + wxBoxSizer* subtitle_sizer = new wxBoxSizer (wxVERTICAL); + _subtitle_panel->SetSizer (subtitle_sizer); - wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4); ++ wxFlexGridSizer* grid = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + subtitle_sizer->Add (grid, 0, wxALL, 8); _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, _("With Subtitles")); - video_control (_with_subtitles); grid->Add (_with_subtitles, 1); + grid->AddSpacer (0); - _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY); - grid->Add (video_control (_subtitle_stream)); - { - add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset")); - video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset"), true)); ++ add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset"), true); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); _subtitle_offset = new wxSpinCtrl (_subtitle_panel); s->Add (_subtitle_offset); - add_label_to_sizer (s, _subtitle_panel, _("pixels")); - video_control (add_label_to_sizer (s, _subtitle_panel, _("pixels"), false)); ++ add_label_to_sizer (s, _subtitle_panel, _("pixels"), false); grid->Add (s); } { - add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale")); - video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale"), true)); ++ add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale"), true); wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); _subtitle_scale = new wxSpinCtrl (_subtitle_panel); - s->Add (video_control (_subtitle_scale)); - video_control (add_label_to_sizer (s, _subtitle_panel, _("%"), false)); + s->Add (_subtitle_scale); - add_label_to_sizer (s, _subtitle_panel, _("%")); ++ add_label_to_sizer (s, _subtitle_panel, _("%"), false); grid->Add (s); } - add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Stream")); ++ add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Stream"), true); + _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY); + grid->Add (_subtitle_stream, 1, wxEXPAND | wxALL, 6); + grid->AddSpacer (0); + _subtitle_offset->SetRange (-1024, 1024); _subtitle_scale->SetRange (1, 1000); } +void +FilmEditor::make_timing_panel () +{ + _timing_panel = new wxPanel (_content_notebook); + wxBoxSizer* timing_sizer = new wxBoxSizer (wxVERTICAL); + _timing_panel->SetSizer (timing_sizer); + wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4); + timing_sizer->Add (grid, 0, wxALL, 8); + - add_label_to_sizer (grid, _timing_panel, _("Start time")); ++ add_label_to_sizer (grid, _timing_panel, _("Start time"), true); + _start = new Timecode (_timing_panel); + grid->Add (_start); - add_label_to_sizer (grid, _timing_panel, _("Length")); ++ add_label_to_sizer (grid, _timing_panel, _("Length"), true); + _length = new Timecode (_timing_panel); + grid->Add (_length); +} + + /** Called when the left crop widget has been changed */ void FilmEditor::left_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_left_crop (_left_crop->GetValue ()); + c->set_left_crop (_left_crop->GetValue ()); } /** Called when the right crop widget has been changed */ void FilmEditor::right_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_right_crop (_right_crop->GetValue ()); + c->set_right_crop (_right_crop->GetValue ()); } /** Called when the top crop widget has been changed */ void FilmEditor::top_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_top_crop (_top_crop->GetValue ()); + c->set_top_crop (_top_crop->GetValue ()); } /** Called when the bottom crop value has been changed */ void FilmEditor::bottom_crop_changed (wxCommandEvent &) { - if (!_film) { - return; - } - - _film->set_bottom_crop (_bottom_crop->GetValue ()); -} - -/** Called when the content filename has been changed */ -void -FilmEditor::content_changed (wxCommandEvent &) -{ - if (!_film) { - return; - } - - try { - _film->set_content (wx_to_std (_content->GetPath ())); - } catch (std::exception& e) { - _content->SetPath (std_to_wx (_film->directory ())); - error_dialog (this, wxString::Format (_("Could not set content: %s"), std_to_wx (e.what()).data())); - } -} - -void -FilmEditor::trust_content_header_changed (wxCommandEvent &) -{ - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_trust_content_header (_trust_content_header->GetValue ()); -} - -/** Called when the DCP A/B switch has been toggled */ -void -FilmEditor::dcp_ab_toggled (wxCommandEvent &) -{ - if (!_film) { - return; - } - - _film->set_dcp_ab (_dcp_ab->GetValue ()); + c->set_bottom_crop (_bottom_crop->GetValue ()); } /** Called when the name widget has been changed */ @@@ -567,7 -612,7 +598,7 @@@ FilmEditor::dcp_frame_rate_changed (wxC return; } - _film->set_dcp_frame_rate ( + _film->set_dcp_video_frame_rate ( boost::lexical_cast ( wx_to_std (_dcp_frame_rate->GetString (_dcp_frame_rate->GetSelection ())) ) @@@ -594,29 -639,118 +625,30 @@@ FilmEditor::film_changed (Film::Propert case Film::NONE: break; case Film::CONTENT: - checked_set (_content, _film->content ()); - setup_visibility (); - setup_formats (); - setup_subtitle_control_sensitivity (); - setup_streams (); - setup_show_audio_sensitivity (); - setup_frame_rate_description (); - setup_minimum_audio_channels (); - break; - case Film::TRUST_CONTENT_HEADER: - checked_set (_trust_content_header, _film->trust_content_header ()); - break; - case Film::SUBTITLE_STREAMS: + setup_content (); setup_subtitle_control_sensitivity (); - setup_streams (); - break; - case Film::CONTENT_AUDIO_STREAMS: - setup_streams (); setup_show_audio_sensitivity (); - setup_frame_rate_description (); + setup_minimum_audio_channels (); break; - case Film::FORMAT: - { - int n = 0; - vector::iterator i = _formats.begin (); - while (i != _formats.end() && *i != _film->format ()) { - ++i; - ++n; - } - if (i == _formats.end()) { - checked_set (_format, -1); - } else { - checked_set (_format, n); - } - setup_dcp_name (); - setup_scaling_description (); - break; - } - case Film::CROP: - checked_set (_left_crop, _film->crop().left); - checked_set (_right_crop, _film->crop().right); - checked_set (_top_crop, _film->crop().top); - checked_set (_bottom_crop, _film->crop().bottom); - setup_scaling_description (); + case Film::LOOP: + checked_set (_loop_content, _film->loop() > 1); + checked_set (_loop_count, _film->loop()); + setup_loop_sensitivity (); break; - case Film::FILTERS: - { - pair p = Filter::ffmpeg_strings (_film->filters ()); - if (p.first.empty () && p.second.empty ()) { - _filters->SetLabel (_("None")); - } else { - string const b = p.first + " " + p.second; - _filters->SetLabel (std_to_wx (b)); - } - _film_sizer->Layout (); + case Film::CONTAINER: + setup_container (); break; - } case Film::NAME: checked_set (_name, _film->name()); setup_dcp_name (); break; - case Film::SOURCE_FRAME_RATE: - s << fixed << setprecision(2) << _film->source_frame_rate(); - _source_frame_rate->SetLabel (std_to_wx (s.str ())); - setup_frame_rate_description (); - break; - case Film::SIZE: - setup_scaling_description (); - break; - case Film::LENGTH: - if (_film->source_frame_rate() > 0 && _film->length()) { - s << _film->length().get() << " " - << wx_to_std (_("frames")) << "; " << seconds_to_hms (_film->length().get() / _film->source_frame_rate()); - } else if (_film->length()) { - s << _film->length().get() << " " - << wx_to_std (_("frames")); - } - _length->SetLabel (std_to_wx (s.str ())); - if (_film->length()) { - _trim_start->SetRange (0, _film->length().get()); - _trim_end->SetRange (0, _film->length().get()); - } - break; case Film::DCP_CONTENT_TYPE: checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ())); setup_dcp_name (); break; - case Film::DCP_AB: - checked_set (_dcp_ab, _film->dcp_ab ()); - break; case Film::SCALER: checked_set (_scaler, Scaler::as_index (_film->scaler ())); break; - case Film::TRIM_START: - checked_set (_trim_start, _film->trim_start()); - break; - case Film::TRIM_END: - checked_set (_trim_end, _film->trim_end()); - break; - case Film::TRIM_TYPE: - checked_set (_trim_type, _film->trim_type() == Film::CPL ? 0 : 1); - break; - case Film::AUDIO_GAIN: - checked_set (_audio_gain, _film->audio_gain ()); - break; - case Film::AUDIO_DELAY: - checked_set (_audio_delay, _film->audio_delay ()); - break; - case Film::STILL_DURATION: - checked_set (_still_duration, _film->still_duration ()); - break; case Film::WITH_SUBTITLES: checked_set (_with_subtitles, _film->with_subtitles ()); setup_subtitle_control_sensitivity (); @@@ -641,173 -775,109 +673,177 @@@ case Film::DCI_METADATA: setup_dcp_name (); break; - case Film::CONTENT_AUDIO_STREAM: - if (_film->content_audio_stream()) { - checked_set (_audio_stream, _film->content_audio_stream()->to_string()); - } - setup_dcp_name (); - setup_audio_details (); - setup_audio_control_sensitivity (); - setup_show_audio_sensitivity (); - setup_frame_rate_description (); - setup_minimum_audio_channels (); - break; - case Film::USE_CONTENT_AUDIO: - _film->log()->log (String::compose ("Film::USE_CONTENT_AUDIO changed; setting GUI using %1", _film->use_content_audio ())); - checked_set (_use_content_audio, _film->use_content_audio()); - checked_set (_use_external_audio, !_film->use_content_audio()); - setup_dcp_name (); - setup_audio_details (); - setup_audio_control_sensitivity (); - setup_show_audio_sensitivity (); - setup_frame_rate_description (); - setup_minimum_audio_channels (); - break; - case Film::SUBTITLE_STREAM: - if (_film->subtitle_stream()) { - checked_set (_subtitle_stream, _film->subtitle_stream()->to_string()); - } - break; - case Film::EXTERNAL_AUDIO: + case Film::DCP_VIDEO_FRAME_RATE: { - vector a = _film->external_audio (); - for (size_t i = 0; i < a.size() && i < MAX_AUDIO_CHANNELS; ++i) { - checked_set (_external_audio[i], a[i]); - } - setup_audio_details (); - setup_show_audio_sensitivity (); - setup_frame_rate_description (); - setup_minimum_audio_channels (); - break; - } - case Film::DCP_FRAME_RATE: + bool done = false; for (unsigned int i = 0; i < _dcp_frame_rate->GetCount(); ++i) { - if (wx_to_std (_dcp_frame_rate->GetString(i)) == boost::lexical_cast (_film->dcp_frame_rate())) { - if (_dcp_frame_rate->GetSelection() != int(i)) { - _dcp_frame_rate->SetSelection (i); - break; - } + if (wx_to_std (_dcp_frame_rate->GetString(i)) == boost::lexical_cast (_film->dcp_video_frame_rate())) { + checked_set (_dcp_frame_rate, i); + done = true; + break; } } - if (_film->source_frame_rate()) { - _best_dcp_frame_rate->Enable (best_dcp_frame_rate (_film->source_frame_rate ()) != _film->dcp_frame_rate ()); - } else { - _best_dcp_frame_rate->Disable (); + if (!done) { + checked_set (_dcp_frame_rate, -1); } - setup_frame_rate_description (); + _best_dcp_frame_rate->Enable (_film->best_dcp_video_frame_rate () != _film->dcp_video_frame_rate ()); + break; + } + case Film::MINIMUM_AUDIO_CHANNELS: - checked_set (_minimum_audio_channels, _film->minimum_audio_channels ()); ++// checked_set (_minimum_audio_channels, _film->minimum_audio_channels ()); + setup_minimum_audio_channels (); + break; } } void -FilmEditor::setup_frame_rate_description () +FilmEditor::film_content_changed (weak_ptr weak_content, int property) { - wxString d; - int lines = 0; - - if (_film->source_frame_rate()) { - d << std_to_wx (FrameRateConversion (_film->source_frame_rate(), _film->dcp_frame_rate()).description); - ++lines; -#ifdef HAVE_SWRESAMPLE - if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate ()) { - d << wxString::Format ( - _("Audio will be resampled from %dHz to %dHz\n"), - _film->audio_stream()->sample_rate(), - _film->target_audio_sample_rate() - ); - ++lines; - } -#endif + if (!_film) { + /* We call this method ourselves (as well as using it as a signal handler) + so _film can be 0. + */ + return; } - for (int i = lines; i < 2; ++i) { - d << wxT ("\n "); + shared_ptr content = weak_content.lock (); + shared_ptr video_content; + shared_ptr audio_content; + shared_ptr ffmpeg_content; + if (content) { + video_content = dynamic_pointer_cast (content); + audio_content = dynamic_pointer_cast (content); + ffmpeg_content = dynamic_pointer_cast (content); } - _frame_rate_description->SetLabel (d); + /* We can't use case {} here */ + + if (property == ContentProperty::START) { + if (content) { + _start->set (content->start (), _film->dcp_video_frame_rate ()); + } else { + _start->set (0, 24); + } + } else if (property == ContentProperty::LENGTH) { + if (content) { + _length->set (content->length (), _film->dcp_video_frame_rate ()); + } else { + _length->set (0, 24); + } + } else if (property == VideoContentProperty::VIDEO_CROP) { + checked_set (_left_crop, video_content ? video_content->crop().left : 0); + checked_set (_right_crop, video_content ? video_content->crop().right : 0); + checked_set (_top_crop, video_content ? video_content->crop().top : 0); + checked_set (_bottom_crop, video_content ? video_content->crop().bottom : 0); + setup_scaling_description (); + } else if (property == VideoContentProperty::VIDEO_RATIO) { + if (video_content) { + int n = 0; + vector ratios = Ratio::all (); + vector::iterator i = ratios.begin (); + while (i != ratios.end() && *i != video_content->ratio()) { + ++i; + ++n; + } + + if (i == ratios.end()) { + checked_set (_ratio, -1); + } else { + checked_set (_ratio, n); + } + } else { + checked_set (_ratio, -1); + } + } else if (property == AudioContentProperty::AUDIO_GAIN) { + checked_set (_audio_gain, audio_content ? audio_content->audio_gain() : 0); + } else if (property == AudioContentProperty::AUDIO_DELAY) { + checked_set (_audio_delay, audio_content ? audio_content->audio_delay() : 0); + } else if (property == AudioContentProperty::AUDIO_MAPPING) { + _audio_mapping->set (audio_content ? audio_content->audio_mapping () : AudioMapping ()); + } else if (property == FFmpegContentProperty::SUBTITLE_STREAMS) { + _subtitle_stream->Clear (); + if (ffmpeg_content) { + vector > s = ffmpeg_content->subtitle_streams (); + if (s.empty ()) { + _subtitle_stream->Enable (false); + } + for (vector >::iterator i = s.begin(); i != s.end(); ++i) { + _subtitle_stream->Append (std_to_wx ((*i)->name), new wxStringClientData (std_to_wx (lexical_cast ((*i)->id)))); + } + + if (ffmpeg_content->subtitle_stream()) { + checked_set (_subtitle_stream, lexical_cast (ffmpeg_content->subtitle_stream()->id)); + } else { + _subtitle_stream->SetSelection (wxNOT_FOUND); + } + } + setup_subtitle_control_sensitivity (); + } else if (property == FFmpegContentProperty::AUDIO_STREAMS) { + _audio_stream->Clear (); + if (ffmpeg_content) { + vector > a = ffmpeg_content->audio_streams (); + for (vector >::iterator i = a.begin(); i != a.end(); ++i) { + _audio_stream->Append (std_to_wx ((*i)->name), new wxStringClientData (std_to_wx (lexical_cast ((*i)->id)))); + } + + if (ffmpeg_content->audio_stream()) { + checked_set (_audio_stream, lexical_cast (ffmpeg_content->audio_stream()->id)); + } + } + setup_show_audio_sensitivity (); + } else if (property == FFmpegContentProperty::AUDIO_STREAM) { + setup_dcp_name (); + setup_show_audio_sensitivity (); + } else if (property == FFmpegContentProperty::FILTERS) { + if (ffmpeg_content) { + pair p = Filter::ffmpeg_strings (ffmpeg_content->filters ()); + if (p.first.empty () && p.second.empty ()) { + _filters->SetLabel (_("None")); + } else { + string const b = p.first + " " + p.second; + _filters->SetLabel (std_to_wx (b)); + } + _dcp_sizer->Layout (); + } + } } -/** Called when the format widget has been changed */ void -FilmEditor::format_changed (wxCommandEvent &) +FilmEditor::setup_container () +{ + int n = 0; + vector ratios = Ratio::all (); + vector::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 (); + setup_scaling_description (); +} + +/** Called when the container widget has been changed */ +void +FilmEditor::container_changed (wxCommandEvent &) { if (!_film) { return; } - int const n = _format->GetSelection (); + int const n = _container->GetSelection (); if (n >= 0) { - assert (n < int (_formats.size())); - _film->set_format (_formats[n]); + vector ratios = Ratio::all (); + assert (n < int (ratios.size())); + _film->set_container (ratios[n]); } } @@@ -829,17 -899,12 +865,17 @@@ FilmEditor::dcp_content_type_changed (w void FilmEditor::set_film (shared_ptr f) { - _film = f; + set_things_sensitive (f != 0); - set_things_sensitive (_film != 0); + if (_film == f) { + return; + } + + _film = f; if (_film) { _film->Changed.connect (bind (&FilmEditor::film_changed, this, _1)); + _film->ContentChanged.connect (bind (&FilmEditor::film_content_changed, this, _1, _2)); } if (_film) { @@@ -848,23 -913,41 +884,23 @@@ FileChanged (""); } - if (_audio_dialog) { - _audio_dialog->set_film (_film); - } - film_changed (Film::NAME); film_changed (Film::USE_DCI_NAME); film_changed (Film::CONTENT); - film_changed (Film::TRUST_CONTENT_HEADER); + film_changed (Film::LOOP); film_changed (Film::DCP_CONTENT_TYPE); - film_changed (Film::FORMAT); - film_changed (Film::CROP); - film_changed (Film::FILTERS); + film_changed (Film::CONTAINER); film_changed (Film::SCALER); - film_changed (Film::TRIM_START); - film_changed (Film::TRIM_END); - film_changed (Film::TRIM_TYPE); - film_changed (Film::DCP_AB); - film_changed (Film::CONTENT_AUDIO_STREAM); - film_changed (Film::EXTERNAL_AUDIO); - film_changed (Film::USE_CONTENT_AUDIO); - film_changed (Film::AUDIO_GAIN); - film_changed (Film::AUDIO_DELAY); - film_changed (Film::STILL_DURATION); film_changed (Film::WITH_SUBTITLES); film_changed (Film::SUBTITLE_OFFSET); film_changed (Film::SUBTITLE_SCALE); film_changed (Film::COLOUR_LUT); film_changed (Film::J2K_BANDWIDTH); film_changed (Film::DCI_METADATA); - film_changed (Film::SIZE); - film_changed (Film::LENGTH); - film_changed (Film::CONTENT_AUDIO_STREAMS); - film_changed (Film::SUBTITLE_STREAMS); - film_changed (Film::SOURCE_FRAME_RATE); - film_changed (Film::DCP_FRAME_RATE); + film_changed (Film::DCP_VIDEO_FRAME_RATE); + + wxListEvent ev; + content_selection_changed (ev); } /** Updates the sensitivity of lots of widgets to a given value. @@@ -878,48 -961,41 +914,48 @@@ FilmEditor::set_things_sensitive (bool _name->Enable (s); _use_dci_name->Enable (s); _edit_dci_button->Enable (s); - _format->Enable (s); + _ratio->Enable (s); _content->Enable (s); - _trust_content_header->Enable (s); _left_crop->Enable (s); _right_crop->Enable (s); _top_crop->Enable (s); _bottom_crop->Enable (s); _filters_button->Enable (s); _scaler->Enable (s); - _audio_stream->Enable (s); _dcp_content_type->Enable (s); + _best_dcp_frame_rate->Enable (s); _dcp_frame_rate->Enable (s); - _trim_start->Enable (s); - _trim_end->Enable (s); - _trim_type->Enable (s); - _dcp_ab->Enable (s); _colour_lut->Enable (s); _j2k_bandwidth->Enable (s); _audio_gain->Enable (s); _audio_gain_calculate_button->Enable (s); _show_audio->Enable (s); _audio_delay->Enable (s); - _still_duration->Enable (s); + _container->Enable (s); + _loop_content->Enable (s); + _loop_count->Enable (s); setup_subtitle_control_sensitivity (); - setup_audio_control_sensitivity (); setup_show_audio_sensitivity (); + setup_content_sensitivity (); } /** Called when the `Edit filters' button has been clicked */ void FilmEditor::edit_filters_clicked (wxCommandEvent &) { - FilterDialog* d = new FilterDialog (this, _film->filters()); - d->ActiveChanged.connect (bind (&Film::set_filters, _film, _1)); + shared_ptr c = selected_content (); + if (!c) { + return; + } + + shared_ptr fc = dynamic_pointer_cast (c); + if (!fc) { + return; + } + + FilterDialog* d = new FilterDialog (this, fc->filters()); + d->ActiveChanged.connect (bind (&FFmpegContent::set_filters, fc, _1)); d->ShowModal (); d->Destroy (); } @@@ -941,39 -1017,105 +977,39 @@@ FilmEditor::scaler_changed (wxCommandEv void FilmEditor::audio_gain_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr ac = selected_audio_content (); + if (!ac) { return; } - _film->set_audio_gain (_audio_gain->GetValue ()); + ac->set_audio_gain (_audio_gain->GetValue ()); } void FilmEditor::audio_delay_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr ac = selected_audio_content (); + if (!ac) { return; } - _film->set_audio_delay (_audio_delay->GetValue ()); -} - -wxControl * -FilmEditor::video_control (wxControl* c) -{ - _video_controls.push_back (c); - return c; -} - -wxControl * -FilmEditor::still_control (wxControl* c) -{ - _still_controls.push_back (c); - return c; + ac->set_audio_delay (_audio_delay->GetValue ()); } void -FilmEditor::setup_visibility () +FilmEditor::setup_main_notebook_size () { - ContentType c = VIDEO; - - if (_film) { - c = _film->content_type (); - } - - for (list::iterator i = _video_controls.begin(); i != _video_controls.end(); ++i) { - (*i)->Show (c == VIDEO); - } - - for (list::iterator i = _still_controls.begin(); i != _still_controls.end(); ++i) { - (*i)->Show (c == STILL); - } + _main_notebook->InvalidateBestSize (); - setup_notebook_size (); -} + _content_sizer->Layout (); + _content_sizer->SetSizeHints (_content_panel); + _dcp_sizer->Layout (); + _dcp_sizer->SetSizeHints (_dcp_panel); -void -FilmEditor::setup_notebook_size () -{ - _notebook->InvalidateBestSize (); - - _film_sizer->Layout (); - _film_sizer->SetSizeHints (_film_panel); - _video_sizer->Layout (); - _video_sizer->SetSizeHints (_video_panel); - _audio_sizer->Layout (); - _audio_sizer->SetSizeHints (_audio_panel); - _subtitle_sizer->Layout (); - _subtitle_sizer->SetSizeHints (_subtitle_panel); - - _notebook->Fit (); + _main_notebook->Fit (); Fit (); } -void -FilmEditor::still_duration_changed (wxCommandEvent &) -{ - if (!_film) { - return; - } - - _film->set_still_duration (_still_duration->GetValue ()); -} - -void -FilmEditor::trim_start_changed (wxCommandEvent &) -{ - if (!_film) { - return; - } - - _film->set_trim_start (_trim_start->GetValue ()); -} - -void -FilmEditor::trim_end_changed (wxCommandEvent &) -{ - if (!_film) { - return; - } - - _film->set_trim_end (_trim_end->GetValue ()); -} - void FilmEditor::audio_gain_calculate_button_clicked (wxCommandEvent &) { @@@ -1002,16 -1144,29 +1038,16 @@@ } void -FilmEditor::setup_formats () +FilmEditor::setup_ratios () { - ContentType c = VIDEO; + _ratios = Ratio::all (); - if (_film) { - c = _film->content_type (); + _ratio->Clear (); + for (vector::iterator i = _ratios.begin(); i != _ratios.end(); ++i) { + _ratio->Append (std_to_wx ((*i)->nickname ())); } - - _formats.clear (); - vector fmt = Format::all (); - for (vector::iterator i = fmt.begin(); i != fmt.end(); ++i) { - if (c == VIDEO || (c == STILL && dynamic_cast (*i))) { - _formats.push_back (*i); - } - } - - _format->Clear (); - for (vector::iterator i = _formats.begin(); i != _formats.end(); ++i) { - _format->Append (std_to_wx ((*i)->name ())); - } - - _film_sizer->Layout (); + _dcp_sizer->Layout (); } void @@@ -1029,7 -1184,7 +1065,7 @@@ FilmEditor::setup_subtitle_control_sens { bool h = false; if (_generally_sensitive && _film) { - h = !_film->subtitle_streams().empty(); + h = _film->has_subtitles (); } _with_subtitles->Enable (h); @@@ -1039,10 -1194,29 +1075,10 @@@ j = _film->with_subtitles (); } - _subtitle_stream->Enable (j); _subtitle_offset->Enable (j); _subtitle_scale->Enable (j); } -void -FilmEditor::setup_audio_control_sensitivity () -{ - _use_content_audio->Enable (_generally_sensitive && _film && !_film->content_audio_streams().empty()); - _use_external_audio->Enable (_generally_sensitive); - - bool const source = _generally_sensitive && _use_content_audio->GetValue(); - bool const external = _generally_sensitive && _use_external_audio->GetValue(); - - _audio_stream->Enable (source); - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - _external_audio[i]->Enable (external); - } - - _pad_with_silence->Enable (_generally_sensitive && _film && _film->audio_stream() && _film->audio_stream()->channels() < MAX_AUDIO_CHANNELS); - _minimum_audio_channels->Enable (_generally_sensitive && _pad_with_silence->GetValue ()); -} - void FilmEditor::use_dci_name_toggled (wxCommandEvent &) { @@@ -1067,202 -1241,143 +1103,202 @@@ FilmEditor::edit_dci_button_clicked (wx } void -FilmEditor::setup_streams () +FilmEditor::active_jobs_changed (bool a) { - _audio_stream->Clear (); - vector > a = _film->content_audio_streams (); - for (vector >::iterator i = a.begin(); i != a.end(); ++i) { - shared_ptr ffa = dynamic_pointer_cast (*i); - assert (ffa); - _audio_stream->Append (std_to_wx (ffa->name()), new wxStringClientData (std_to_wx (ffa->to_string ()))); - } - - if (_film->use_content_audio() && _film->audio_stream()) { - checked_set (_audio_stream, _film->audio_stream()->to_string()); - } + set_things_sensitive (!a); +} - _subtitle_stream->Clear (); - vector > s = _film->subtitle_streams (); - for (vector >::iterator i = s.begin(); i != s.end(); ++i) { - _subtitle_stream->Append (std_to_wx ((*i)->name()), new wxStringClientData (std_to_wx ((*i)->to_string ()))); - } - if (_film->subtitle_stream()) { - checked_set (_subtitle_stream, _film->subtitle_stream()->to_string()); +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 { - _subtitle_stream->SetSelection (wxNOT_FOUND); + _dcp_name->SetLabel (std_to_wx (s)); } } void -FilmEditor::audio_stream_changed (wxCommandEvent &) +FilmEditor::show_audio_clicked (wxCommandEvent &) { - if (!_film) { + if (_audio_dialog) { + _audio_dialog->Destroy (); + _audio_dialog = 0; + } + + shared_ptr c = selected_content (); + if (!c) { return; } - _film->set_content_audio_stream ( - audio_stream_factory ( - string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())), - Film::state_version - ) - ); + shared_ptr ac = dynamic_pointer_cast (c); + if (!ac) { + return; + } + + _audio_dialog = new AudioDialog (this); + _audio_dialog->Show (); + _audio_dialog->set_content (ac); } void -FilmEditor::subtitle_stream_changed (wxCommandEvent &) +FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &) { if (!_film) { return; } + + _film->set_dcp_video_frame_rate (_film->best_dcp_video_frame_rate ()); +} - _film->set_subtitle_stream ( - subtitle_stream_factory ( - string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())), - Film::state_version - ) - ); +void +FilmEditor::setup_show_audio_sensitivity () +{ + _show_audio->Enable (_film); } void -FilmEditor::setup_audio_details () +FilmEditor::setup_content () { - if (!_film->content_audio_stream()) { - _audio->SetLabel (wxT ("")); - } else { - wxString s; - if (_film->audio_stream()->channels() == 1) { - s << _("1 channel"); - } else { - s << _film->audio_stream()->channels () << wxT (" ") << _("channels"); + 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 (); + + Playlist::ContentList content = _film->content (); + for (Playlist::ContentList::iterator i = content.begin(); i != content.end(); ++i) { + int const t = _content->GetItemCount (); + _content->InsertItem (t, std_to_wx ((*i)->summary ())); + if ((*i)->summary() == selected_summary) { + _content->SetItemState (t, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } - s << wxT (", ") << _film->audio_stream()->sample_rate() << _("Hz"); - _audio->SetLabel (s); } - setup_notebook_size (); + 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::active_jobs_changed (bool a) +FilmEditor::content_add_clicked (wxCommandEvent &) { - set_things_sensitive (!a); + wxFileDialog* d = new wxFileDialog (this, _("Choose a file or files"), wxT (""), wxT (""), wxT ("*.*"), wxFD_MULTIPLE); + int const r = d->ShowModal (); + d->Destroy (); + + if (r != wxID_OK) { + return; + } + + wxArrayString paths; + d->GetPaths (paths); + + for (unsigned int i = 0; i < paths.GetCount(); ++i) { + boost::filesystem::path p (wx_to_std (paths[i])); + + shared_ptr c; + + if (ImageMagickContent::valid_file (p)) { + c.reset (new ImageMagickContent (_film, p)); + } else if (SndfileContent::valid_file (p)) { + c.reset (new SndfileContent (_film, p)); + } else { + c.reset (new FFmpegContent (_film, p)); + } + + _film->examine_and_add_content (c); + } } void -FilmEditor::use_audio_changed (wxCommandEvent &) +FilmEditor::content_remove_clicked (wxCommandEvent &) { - _film->set_use_content_audio (_use_content_audio->GetValue()); + shared_ptr c = selected_content (); + if (c) { + _film->remove_content (c); + } } void -FilmEditor::external_audio_changed (wxCommandEvent &) +FilmEditor::content_selection_changed (wxListEvent &) { - vector a; - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - a.push_back (wx_to_std (_external_audio[i]->GetPath())); + setup_content_sensitivity (); + shared_ptr s = selected_content (); + + if (_audio_dialog && s && dynamic_pointer_cast (s)) { + _audio_dialog->set_content (dynamic_pointer_cast (s)); } - - _film->set_external_audio (a); + + film_content_changed (s, ContentProperty::START); + film_content_changed (s, ContentProperty::LENGTH); + film_content_changed (s, VideoContentProperty::VIDEO_CROP); + film_content_changed (s, VideoContentProperty::VIDEO_RATIO); + film_content_changed (s, AudioContentProperty::AUDIO_GAIN); + film_content_changed (s, AudioContentProperty::AUDIO_DELAY); + film_content_changed (s, AudioContentProperty::AUDIO_MAPPING); + film_content_changed (s, FFmpegContentProperty::AUDIO_STREAM); + film_content_changed (s, FFmpegContentProperty::AUDIO_STREAMS); + film_content_changed (s, FFmpegContentProperty::SUBTITLE_STREAM); } void -FilmEditor::setup_dcp_name () +FilmEditor::setup_content_sensitivity () { - 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)); - } + _content_add->Enable (_generally_sensitive); + + shared_ptr selection = selected_content (); + + _content_remove->Enable (selection && _generally_sensitive); + _content_timeline->Enable (_generally_sensitive); + + _video_panel->Enable (selection && dynamic_pointer_cast (selection) && _generally_sensitive); + _audio_panel->Enable (selection && dynamic_pointer_cast (selection) && _generally_sensitive); + _subtitle_panel->Enable (selection && dynamic_pointer_cast (selection) && _generally_sensitive); + _timing_panel->Enable (selection && _generally_sensitive); } -void -FilmEditor::show_audio_clicked (wxCommandEvent &) +shared_ptr +FilmEditor::selected_content () { - if (_audio_dialog) { - _audio_dialog->Destroy (); - _audio_dialog = 0; + int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (s == -1) { + return shared_ptr (); + } + + Playlist::ContentList c = _film->content (); + if (s < 0 || size_t (s) >= c.size ()) { + return shared_ptr (); } - _audio_dialog = new AudioDialog (this); - _audio_dialog->Show (); - _audio_dialog->set_film (_film); + return c[s]; } -void -FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &) +shared_ptr +FilmEditor::selected_video_content () { - if (!_film) { - return; + shared_ptr c = selected_content (); + if (!c) { + return shared_ptr (); } - - _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->source_frame_rate ())); + + return dynamic_pointer_cast (c); } -void -FilmEditor::setup_show_audio_sensitivity () +shared_ptr +FilmEditor::selected_audio_content () { - _show_audio->Enable (_film && _film->has_audio ()); + shared_ptr c = selected_content (); + if (!c) { + return shared_ptr (); + } + + return dynamic_pointer_cast (c); } void @@@ -1270,22 -1385,20 +1306,22 @@@ FilmEditor::setup_scaling_description ( { wxString d; +#if 0 +XXX int lines = 0; - if (_film->size().width && _film->size().height) { + if (_film->video_size().width && _film->video_size().height) { d << wxString::Format ( _("Original video is %dx%d (%.2f:1)\n"), - _film->size().width, _film->size().height, - float (_film->size().width) / _film->size().height + _film->video_size().width, _film->video_size().height, + float (_film->video_size().width) / _film->video_size().height ); ++lines; } Crop const crop = _film->crop (); - if (crop.left || crop.right || crop.top || crop.bottom) { - libdcp::Size const cropped = _film->cropped_size (_film->video_size ()); + if ((crop.left || crop.right || crop.top || crop.bottom) && _film->size() != libdcp::Size (0, 0)) { + libdcp::Size const cropped = _film->cropped_size (_film->size ()); d << wxString::Format ( _("Cropped to %dx%d (%.2f:1)\n"), cropped.width, cropped.height, @@@ -1320,186 -1433,41 +1356,218 @@@ d << wxT ("\n "); } +#endif _scaling_description->SetLabel (d); } void -FilmEditor::trim_type_changed (wxCommandEvent &) +FilmEditor::loop_content_toggled (wxCommandEvent &) +{ + if (_loop_content->GetValue ()) { + _film->set_loop (_loop_count->GetValue ()); + } else { + _film->set_loop (1); + } + + setup_loop_sensitivity (); +} + +void +FilmEditor::loop_count_changed (wxCommandEvent &) { - _film->set_trim_type (_trim_type->GetSelection () == 0 ? Film::CPL : Film::ENCODE); + _film->set_loop (_loop_count->GetValue ()); +} + +void +FilmEditor::setup_loop_sensitivity () +{ + _loop_count->Enable (_loop_content->GetValue ()); +} + +void +FilmEditor::content_timeline_clicked (wxCommandEvent &) +{ + if (_timeline_dialog) { + _timeline_dialog->Destroy (); + _timeline_dialog = 0; + } + + _timeline_dialog = new TimelineDialog (this, _film); + _timeline_dialog->Show (); +} + +void +FilmEditor::audio_stream_changed (wxCommandEvent &) +{ + shared_ptr c = selected_content (); + if (!c) { + return; + } + + shared_ptr fc = dynamic_pointer_cast (c); + if (!fc) { + return; + } + + vector > a = fc->audio_streams (); + vector >::iterator i = a.begin (); + string const s = string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())); + while (i != a.end() && lexical_cast ((*i)->id) != s) { + ++i; + } + + if (i != a.end ()) { + fc->set_audio_stream (*i); + } + + if (!fc->audio_stream ()) { + _audio_description->SetLabel (wxT ("")); + } else { + wxString s; + if (fc->audio_channels() == 1) { + s << _("1 channel"); + } else { + s << fc->audio_channels() << wxT (" ") << _("channels"); + } + s << wxT (", ") << fc->content_audio_frame_rate() << _("Hz"); + _audio_description->SetLabel (s); + } +} + + + +void +FilmEditor::subtitle_stream_changed (wxCommandEvent &) +{ + shared_ptr c = selected_content (); + if (!c) { + return; + } + + shared_ptr fc = dynamic_pointer_cast (c); + if (!fc) { + return; + } + + vector > a = fc->subtitle_streams (); + vector >::iterator i = a.begin (); + string const s = string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())); + while (i != a.end() && lexical_cast ((*i)->id) != s) { + ++i; + } + + if (i != a.end ()) { + fc->set_subtitle_stream (*i); + } +} + +void +FilmEditor::audio_mapping_changed (AudioMapping m) +{ + shared_ptr c = selected_content (); + if (!c) { + return; + } + + shared_ptr ac = dynamic_pointer_cast (c); + if (!ac) { + return; + } + + ac->set_audio_mapping (m); +} + +void +FilmEditor::start_changed () +{ + shared_ptr c = selected_content (); + if (!c) { + return; + } + + c->set_start (_start->get (_film->dcp_video_frame_rate ())); +} + +void +FilmEditor::length_changed () +{ + shared_ptr c = selected_content (); + if (!c) { + return; + } + + shared_ptr ic = dynamic_pointer_cast (c); + if (ic) { + ic->set_video_length (_length->get(_film->dcp_video_frame_rate()) * ic->video_frame_rate() / TIME_HZ); + } +} + +void +FilmEditor::set_selection (weak_ptr wc) +{ + Playlist::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 +FilmEditor::ratio_changed (wxCommandEvent &) +{ + if (!_film) { + return; + } + + shared_ptr c = selected_content (); + if (!c) { + return; + } + + shared_ptr vc = dynamic_pointer_cast (c); + if (!vc) { + return; + } + + int const n = _ratio->GetSelection (); + if (n >= 0) { + vector ratios = Ratio::all (); + assert (n < int (ratios.size())); + vc->set_ratio (ratios[n]); + } } + + void + FilmEditor::setup_minimum_audio_channels () + { ++#if 0 + if (!_film || !_film->audio_stream ()) { + _pad_with_silence->SetValue (false); + return; + } + + _pad_with_silence->SetValue (_film->audio_stream()->channels() < _film->minimum_audio_channels()); + + AudioMapping m (_film); + _minimum_audio_channels->SetRange (m.minimum_dcp_channels() + 1, MAX_AUDIO_CHANNELS); ++#endif + } + + void + FilmEditor::pad_with_silence_toggled (wxCommandEvent &) + { - setup_audio_control_sensitivity (); ++ + } + + void + FilmEditor::minimum_audio_channels_changed (wxCommandEvent &) + { + if (!_film) { + return; + } + - _film->set_minimum_audio_channels (_minimum_audio_channels->GetValue ()); ++// _film->set_minimum_audio_channels (_minimum_audio_channels->GetValue ()); + } diff --combined src/wx/film_editor.h index 4b096a2e1,c2d064ca2..705eb16af --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@@ -16,7 -16,7 +16,7 @@@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - + /** @file src/film_editor.h * @brief A wx widget to edit a film's metadata, and perform various functions. */ @@@ -29,14 -29,8 +29,14 @@@ #include "lib/film.h" class wxNotebook; +class wxListCtrl; +class wxListEvent; class Film; class AudioDialog; +class TimelineDialog; +class AudioMappingView; +class Ratio; +class Timecode; /** @class FilmEditor * @brief A wx widget to edit a film's metadata, and perform various functions. @@@ -47,17 -41,15 +47,17 @@@ public FilmEditor (boost::shared_ptr, wxWindow *); void set_film (boost::shared_ptr); - void setup_visibility (); + void set_selection (boost::weak_ptr); boost::signals2::signal FileChanged; private: - void make_film_panel (); + void make_dcp_panel (); + void make_content_panel (); void make_video_panel (); void make_audio_panel (); void make_subtitle_panel (); + void make_timing_panel (); void connect_to_widgets (); /* Handle changes to the view */ @@@ -68,13 -60,14 +68,13 @@@ void right_crop_changed (wxCommandEvent &); void top_crop_changed (wxCommandEvent &); void bottom_crop_changed (wxCommandEvent &); - void content_changed (wxCommandEvent &); - void trust_content_header_changed (wxCommandEvent &); - void format_changed (wxCommandEvent &); - void trim_start_changed (wxCommandEvent &); - void trim_end_changed (wxCommandEvent &); - void trim_type_changed (wxCommandEvent &); + void trust_content_headers_changed (wxCommandEvent &); + void content_selection_changed (wxListEvent &); + void content_add_clicked (wxCommandEvent &); + void content_remove_clicked (wxCommandEvent &); + void imagemagick_video_length_changed (wxCommandEvent &); + void container_changed (wxCommandEvent &); void dcp_content_type_changed (wxCommandEvent &); - void dcp_ab_toggled (wxCommandEvent &); void scaler_changed (wxCommandEvent &); void audio_gain_changed (wxCommandEvent &); void audio_gain_calculate_button_clicked (wxCommandEvent &); @@@ -85,98 -78,121 +85,103 @@@ void subtitle_scale_changed (wxCommandEvent &); void colour_lut_changed (wxCommandEvent &); void j2k_bandwidth_changed (wxCommandEvent &); - void still_duration_changed (wxCommandEvent &); - void audio_stream_changed (wxCommandEvent &); - void subtitle_stream_changed (wxCommandEvent &); - void use_audio_changed (wxCommandEvent &); - void external_audio_changed (wxCommandEvent &); void dcp_frame_rate_changed (wxCommandEvent &); void best_dcp_frame_rate_clicked (wxCommandEvent &); + void edit_filters_clicked (wxCommandEvent &); + void loop_content_toggled (wxCommandEvent &); + void loop_count_changed (wxCommandEvent &); + void content_timeline_clicked (wxCommandEvent &); + void audio_stream_changed (wxCommandEvent &); + void subtitle_stream_changed (wxCommandEvent &); + void audio_mapping_changed (AudioMapping); + void start_changed (); + void length_changed (); + void ratio_changed (wxCommandEvent &); + void pad_with_silence_toggled (wxCommandEvent &); + void minimum_audio_channels_changed (wxCommandEvent &); /* Handle changes to the model */ void film_changed (Film::Property); - - /* Button clicks */ - void edit_filters_clicked (wxCommandEvent &); + void film_content_changed (boost::weak_ptr, int); void set_things_sensitive (bool); - void setup_formats (); + void setup_ratios (); void setup_subtitle_control_sensitivity (); - void setup_audio_control_sensitivity (); - void setup_streams (); - void setup_audio_details (); void setup_dcp_name (); void setup_show_audio_sensitivity (); void setup_scaling_description (); - void setup_notebook_size (); - void setup_frame_rate_description (); + void setup_main_notebook_size (); + void setup_content (); + void setup_container (); + void setup_content_sensitivity (); + void setup_loop_sensitivity (); + void setup_minimum_audio_channels (); - wxControl* video_control (wxControl *); - wxControl* still_control (wxControl *); - void active_jobs_changed (bool); - - wxNotebook* _notebook; - wxPanel* _film_panel; - wxSizer* _film_sizer; + boost::shared_ptr selected_content (); + boost::shared_ptr selected_video_content (); + boost::shared_ptr selected_audio_content (); + + wxNotebook* _main_notebook; + wxNotebook* _content_notebook; + wxPanel* _dcp_panel; + wxSizer* _dcp_sizer; + wxPanel* _content_panel; + wxSizer* _content_sizer; wxPanel* _video_panel; - wxSizer* _video_sizer; wxPanel* _audio_panel; - wxSizer* _audio_sizer; wxPanel* _subtitle_panel; - wxSizer* _subtitle_sizer; + wxPanel* _timing_panel; /** The film we are editing */ boost::shared_ptr _film; - /** The Film's name */ wxTextCtrl* _name; wxStaticText* _dcp_name; wxCheckBox* _use_dci_name; + wxChoice* _container; + wxListCtrl* _content; + wxButton* _content_add; + wxButton* _content_remove; + wxButton* _content_earlier; + wxButton* _content_later; + wxButton* _content_timeline; + wxCheckBox* _loop_content; + wxSpinCtrl* _loop_count; wxButton* _edit_dci_button; - /** The Film's format */ - wxChoice* _format; + wxChoice* _ratio; + wxStaticText* _ratio_description; wxStaticText* _scaling_description; - /** The Film's content file */ - wxFilePickerCtrl* _content; - wxCheckBox* _trust_content_header; - /** The Film's left crop */ wxSpinCtrl* _left_crop; - /** The Film's right crop */ wxSpinCtrl* _right_crop; - /** The Film's top crop */ wxSpinCtrl* _top_crop; - /** The Film's bottom crop */ wxSpinCtrl* _bottom_crop; - /** Currently-applied filters */ wxStaticText* _filters; - /** Button to open the filters dialogue */ wxButton* _filters_button; - /** The Film's scaler */ wxChoice* _scaler; - wxRadioButton* _use_content_audio; - wxChoice* _audio_stream; - wxRadioButton* _use_external_audio; - wxFilePickerCtrl* _external_audio[MAX_AUDIO_CHANNELS]; - /** The Film's audio gain */ wxSpinCtrl* _audio_gain; - /** A button to open the gain calculation dialogue */ wxButton* _audio_gain_calculate_button; wxButton* _show_audio; - /** The Film's audio delay */ wxSpinCtrl* _audio_delay; wxCheckBox* _with_subtitles; - wxChoice* _subtitle_stream; wxSpinCtrl* _subtitle_offset; wxSpinCtrl* _subtitle_scale; wxChoice* _colour_lut; wxSpinCtrl* _j2k_bandwidth; - /** The Film's DCP content type */ wxChoice* _dcp_content_type; - /** The Film's source frame rate */ - wxStaticText* _source_frame_rate; wxChoice* _dcp_frame_rate; wxButton* _best_dcp_frame_rate; + wxCheckBox* _pad_with_silence; + wxSpinCtrl* _minimum_audio_channels; - wxStaticText* _frame_rate_description; - /** The Film's length */ - wxStaticText* _length; - /** The Film's audio details */ - wxStaticText* _audio; - /** The Film's duration for still sources */ - wxSpinCtrl* _still_duration; - - wxSpinCtrl* _trim_start; - wxSpinCtrl* _trim_end; - wxChoice* _trim_type; - /** Selector to generate an A/B comparison DCP */ - wxCheckBox* _dcp_ab; - - std::list _video_controls; - std::list _still_controls; + wxChoice* _audio_stream; + wxStaticText* _audio_description; + wxChoice* _subtitle_stream; + AudioMappingView* _audio_mapping; + Timecode* _start; + Timecode* _length; - std::vector _formats; + std::vector _ratios; bool _generally_sensitive; AudioDialog* _audio_dialog; + TimelineDialog* _timeline_dialog; }; diff --combined src/wx/gain_calculator_dialog.cc index 22e6b447a,7499cbf8b..17ebbb983 --- a/src/wx/gain_calculator_dialog.cc +++ b/src/wx/gain_calculator_dialog.cc @@@ -26,14 -26,14 +26,14 @@@ using namespace boost GainCalculatorDialog::GainCalculatorDialog (wxWindow* parent) : wxDialog (parent, wxID_ANY, _("Gain Calculator")) { - wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); - add_label_to_sizer (table, this, _("I want to play this back at fader")); + add_label_to_sizer (table, this, _("I want to play this back at fader"), true); _wanted = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, wxTextValidator (wxFILTER_NUMERIC)); table->Add (_wanted, 1, wxEXPAND); - add_label_to_sizer (table, this, _("But I have to use fader")); + add_label_to_sizer (table, this, _("But I have to use fader"), true); _actual = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, wxTextValidator (wxFILTER_NUMERIC)); table->Add (_actual, 1, wxEXPAND); diff --combined src/wx/imagemagick_content_dialog.cc index dfe4df2e7,000000000..6aa756260 mode 100644,000000..100644 --- a/src/wx/imagemagick_content_dialog.cc +++ b/src/wx/imagemagick_content_dialog.cc @@@ -1,70 -1,0 +1,70 @@@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 +#include "lib/imagemagick_content.h" +#include "imagemagick_content_dialog.h" +#include "wx_util.h" + +using boost::shared_ptr; +using boost::dynamic_pointer_cast; + +ImageMagickContentDialog::ImageMagickContentDialog (wxWindow* parent, shared_ptr content) + : wxDialog (parent, wxID_ANY, _("Image")) + , _content (content) +{ + wxFlexGridSizer* grid = new wxFlexGridSizer (3, 6, 6); + grid->AddGrowableCol (1, 1); + + { - add_label_to_sizer (grid, this, (_("Duration"))); ++ add_label_to_sizer (grid, this, _("Duration"), true); + wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); + _video_length = new wxSpinCtrl (this); + s->Add (_video_length); + /// TRANSLATORS: this is an abbreviation for seconds, the unit of time - add_label_to_sizer (s, this, _("s")); ++ add_label_to_sizer (s, this, _("s"), false); + grid->Add (s); + } + + wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + overall_sizer->Add (grid, 1, wxEXPAND | wxALL, 6); + + wxSizer* buttons = CreateSeparatedButtonSizer (wxOK); + if (buttons) { + overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); + } + + SetSizer (overall_sizer); + overall_sizer->Layout (); + overall_sizer->SetSizeHints (this); + + checked_set (_video_length, content->video_length () / 24); + _video_length->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (ImageMagickContentDialog::video_length_changed), 0, this); +} + +void +ImageMagickContentDialog::video_length_changed (wxCommandEvent &) +{ + shared_ptr c = _content.lock (); + if (!c) { + return; + } + + c->set_video_length (_video_length->GetValue() * 24); +} diff --combined src/wx/job_manager_view.cc index 1594dfc91,cfe09aec8..b5c66bd3e --- a/src/wx/job_manager_view.cc +++ b/src/wx/job_manager_view.cc @@@ -86,7 -86,6 +86,7 @@@ JobManagerView::update ( JobRecord r; int n = 1; r.finalised = false; + r.scroll_nudged = false; r.gauge = new wxGauge (_panel, wxID_ANY, 100); _table->Insert (index + n, r.gauge, 1, wxEXPAND | wxLEFT | wxRIGHT); ++n; @@@ -114,7 -113,6 +114,7 @@@ ++n; _job_records[*i] = r; + } string const st = (*i)->status (); @@@ -128,31 -126,13 +128,30 @@@ checked_set (_job_records[*i].message, wx_to_std (_("Running"))); _job_records[*i].gauge->Pulse (); } + } + if (!_job_records[*i].scroll_nudged && ((*i)->running () || (*i)->finished())) { + int x, y; + _job_records[*i].gauge->GetPosition (&x, &y); + int px, py; + GetScrollPixelsPerUnit (&px, &py); + int vx, vy; + GetViewStart (&vx, &vy); + int sx, sy; + GetClientSize (&sx, &sy); + + if (y > (vy * py + sy / 2)) { + Scroll (-1, y / py); + _job_records[*i].scroll_nudged = true; + } + } + if ((*i)->finished() && !_job_records[*i].finalised) { checked_set (_job_records[*i].message, st); if (!(*i)->finished_cancelled()) { _job_records[*i].gauge->SetValue (100); } - (*i)->Finished (); _job_records[*i].finalised = true; _job_records[*i].cancel->Enable (false); if (!(*i)->error_details().empty ()) { @@@ -175,7 -155,7 +174,7 @@@ JobManagerView::details_clicked (wxComm { wxObject* o = ev.GetEventObject (); - for (map, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) { + for (map, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) { if (i->second.details == o) { string s = i->first->error_summary(); s[0] = toupper (s[0]); @@@ -189,7 -169,7 +188,7 @@@ JobManagerView::cancel_clicked (wxComma { wxObject* o = ev.GetEventObject (); - for (map, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) { + for (map, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) { if (i->second.cancel == o) { i->first->cancel (); } diff --combined src/wx/new_film_dialog.cc index 191482a7c,289926b8e..d4b78d5bf --- a/src/wx/new_film_dialog.cc +++ b/src/wx/new_film_dialog.cc @@@ -21,10 -21,10 +21,10 @@@ #include #include "lib/config.h" #include "new_film_dialog.h" - #ifdef __WXMSW__ + #include "wx_util.h" -#ifdef DVDOMATIC_USE_OWN_DIR_PICKER ++#ifdef DCPOMATIC_USE_OWN_DIR_PICKER #include "dir_picker_ctrl.h" #endif - #include "wx_util.h" using namespace std; using namespace boost; @@@ -37,17 -37,18 +37,18 @@@ NewFilmDialog::NewFilmDialog (wxWindow wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); SetSizer (overall_sizer); - wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6); - add_label_to_sizer (table, this, _("Film name")); + add_label_to_sizer (table, this, _("Film name"), true); _name = new wxTextCtrl (this, wxID_ANY); - table->Add (_name, 1, wxEXPAND); + table->Add (_name, 0, wxEXPAND); + + add_label_to_sizer (table, this, _("Create in folder"), true); - add_label_to_sizer (table, this, _("Create in folder")); - #ifdef __WXMSW__ - _folder = new DirPickerCtrl (this); -#ifdef DVDOMATIC_USE_OWN_DIR_PICKER ++#ifdef DCPOMATIC_USE_OWN_DIR_PICKER + _folder = new DirPickerCtrl (this); #else _folder = new wxDirPickerCtrl (this, wxDD_DIR_MUST_EXIST); #endif diff --combined src/wx/new_film_dialog.h index bfcbd423c,220bba732..f8f3aa08d --- a/src/wx/new_film_dialog.h +++ b/src/wx/new_film_dialog.h @@@ -19,6 -19,7 +19,7 @@@ #include #include + #include "wx_util.h" class DirPickerCtrl; @@@ -32,10 -33,10 +33,10 @@@ public private: wxTextCtrl* _name; - #ifdef __WXMSW__ -#ifdef DVDOMATIC_USE_OWN_DIR_PICKER ++#ifdef DCPOMATIC_USE_OWN_DIR_PICKER DirPickerCtrl* _folder; - #else + #else wxDirPickerCtrl* _folder; - #endif + #endif static boost::optional _directory; }; diff --combined src/wx/properties_dialog.cc index 40527ded7,aa97623cd..d525fe38b --- a/src/wx/properties_dialog.cc +++ b/src/wx/properties_dialog.cc @@@ -36,25 -36,32 +36,25 @@@ PropertiesDialog::PropertiesDialog (wxW : wxDialog (parent, wxID_ANY, _("Film Properties"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE) , _film (film) { - wxFlexGridSizer* table = new wxFlexGridSizer (2, 3, 6); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); - add_label_to_sizer (table, this, _("Frames")); + add_label_to_sizer (table, this, _("Frames"), true); _frames = new wxStaticText (this, wxID_ANY, wxT ("")); table->Add (_frames, 1, wxALIGN_CENTER_VERTICAL); - add_label_to_sizer (table, this, _("Disk space required")); + add_label_to_sizer (table, this, _("Disk space required"), true); _disk = new wxStaticText (this, wxID_ANY, wxT ("")); table->Add (_disk, 1, wxALIGN_CENTER_VERTICAL); - add_label_to_sizer (table, this, _("Frames already encoded")); + add_label_to_sizer (table, this, _("Frames already encoded"), true); _encoded = new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this)); table->Add (_encoded, 1, wxALIGN_CENTER_VERTICAL); - if (_film->length()) { - _frames->SetLabel (std_to_wx (lexical_cast (_film->length().get()))); - FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate()); - int const dcp_length = _film->length().get() * frc.factor(); - double const disk = ((double) _film->j2k_bandwidth() / 8) * dcp_length / (_film->dcp_frame_rate() * 1073741824.0f); - stringstream s; - s << fixed << setprecision (1) << disk << wx_to_std (_("Gb")); - _disk->SetLabel (std_to_wx (s.str ())); - } else { - _frames->SetLabel (_("unknown")); - _disk->SetLabel (_("unknown")); - } + _frames->SetLabel (std_to_wx (lexical_cast (_film->time_to_video_frames (_film->length())))); + double const disk = ((double) _film->j2k_bandwidth() / 8) * _film->length() / (TIME_HZ * 1073741824.0f); + stringstream s; + s << fixed << setprecision (1) << disk << wx_to_std (_("Gb")); + _disk->SetLabel (std_to_wx (s.str ())); wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); overall_sizer->Add (table, 0, wxALL, 6); @@@ -80,7 -87,7 +80,7 @@@ PropertiesDialog::frames_already_encode if (_film->length()) { /* XXX: encoded_frames() should check which frames have been encoded */ - u << " (" << (_film->encoded_frames() * 100 / _film->length().get()) << "%)"; + u << " (" << (_film->encoded_frames() * 100 / _film->time_to_video_frames (_film->length())) << "%)"; } return u.str (); } diff --combined src/wx/server_dialog.cc index 7a9cf95c7,30e3c0f83..33cb392bf --- a/src/wx/server_dialog.cc +++ b/src/wx/server_dialog.cc @@@ -30,14 -30,14 +30,14 @@@ ServerDialog::ServerDialog (wxWindow* p _server = new ServerDescription (wx_to_std (N_("localhost")), 1); } - wxFlexGridSizer* table = new wxFlexGridSizer (2, 4, 4); - wxFlexGridSizer* table = new wxFlexGridSizer (2, DVDOMATIC_SIZER_X_GAP, DVDOMATIC_SIZER_Y_GAP); ++ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); table->AddGrowableCol (1, 1); - add_label_to_sizer (table, this, _("Host name or IP address")); + add_label_to_sizer (table, this, _("Host name or IP address"), true); _host = new wxTextCtrl (this, wxID_ANY); table->Add (_host, 1, wxEXPAND); - add_label_to_sizer (table, this, _("Threads to use")); + add_label_to_sizer (table, this, _("Threads to use"), true); _threads = new wxSpinCtrl (this, wxID_ANY); table->Add (_threads, 1, wxEXPAND); diff --combined src/wx/timecode.cc index 9072fb99e,000000000..6ce1c1cb8 mode 100644,000000..100644 --- a/src/wx/timecode.cc +++ b/src/wx/timecode.cc @@@ -1,115 -1,0 +1,115 @@@ +/* + Copyright (C) 2013 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the 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 +#include "timecode.h" +#include "wx_util.h" + +using std::string; +using std::cout; +using boost::lexical_cast; + +Timecode::Timecode (wxWindow* parent) + : wxPanel (parent) + , _in_set (false) +{ + wxClientDC dc (parent); + wxSize size = dc.GetTextExtent (wxT ("9999")); + size.SetHeight (-1); + + wxTextValidator validator (wxFILTER_INCLUDE_CHAR_LIST); + wxArrayString list; + + wxString n (wxT ("0123456789")); + for (size_t i = 0; i < n.Length(); ++i) { + list.Add (n[i]); + } + + validator.SetIncludes (list); + + wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL); + _hours = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size, 0, validator); + _hours->SetMaxLength (2); + sizer->Add (_hours); - add_label_to_sizer (sizer, this, wxT (":")); ++ add_label_to_sizer (sizer, this, wxT (":"), false); + _minutes = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size); + _minutes->SetMaxLength (2); + sizer->Add (_minutes); - add_label_to_sizer (sizer, this, wxT (":")); ++ add_label_to_sizer (sizer, this, wxT (":"), false); + _seconds = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size); + _seconds->SetMaxLength (2); + sizer->Add (_seconds); - add_label_to_sizer (sizer, this, wxT (".")); ++ add_label_to_sizer (sizer, this, wxT ("."), false); + _frames = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size); + _frames->SetMaxLength (2); + sizer->Add (_frames); + + _hours->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this); + _minutes->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this); + _seconds->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this); + _frames->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this); + + SetSizerAndFit (sizer); +} + +void +Timecode::set (Time t, int fps) +{ + _in_set = true; + + int const h = t / (3600 * TIME_HZ); + t -= h * 3600 * TIME_HZ; + int const m = t / (60 * TIME_HZ); + t -= m * 60 * TIME_HZ; + int const s = t / TIME_HZ; + t -= s * TIME_HZ; + int const f = t * fps / TIME_HZ; + + _hours->SetValue (wxString::Format (wxT ("%d"), h)); + _minutes->SetValue (wxString::Format (wxT ("%d"), m)); + _seconds->SetValue (wxString::Format (wxT ("%d"), s)); + _frames->SetValue (wxString::Format (wxT ("%d"), f)); + + _in_set = false; +} + +Time +Timecode::get (int fps) const +{ + Time t = 0; + string const h = wx_to_std (_hours->GetValue ()); + t += lexical_cast (h.empty() ? "0" : h) * 3600 * TIME_HZ; + string const m = wx_to_std (_minutes->GetValue()); + t += lexical_cast (m.empty() ? "0" : m) * 60 * TIME_HZ; + string const s = wx_to_std (_seconds->GetValue()); + t += lexical_cast (s.empty() ? "0" : s) * TIME_HZ; + string const f = wx_to_std (_frames->GetValue()); + t += lexical_cast (f.empty() ? "0" : f) * TIME_HZ / fps; + return t; +} + +void +Timecode::changed (wxCommandEvent &) +{ + if (_in_set) { + return; + } + + Changed (); +} diff --combined src/wx/wscript index 992f31175,345c02b08..1205fb21b --- a/src/wx/wscript +++ b/src/wx/wscript @@@ -6,7 -6,6 +6,7 @@@ import i18 sources = """ about_dialog.cc audio_dialog.cc + audio_mapping_view.cc audio_plot.cc config_dialog.cc dci_metadata_dialog.cc @@@ -16,21 -15,31 +16,35 @@@ filter_dialog.cc filter_view.cc gain_calculator_dialog.cc + imagemagick_content_dialog.cc job_manager_view.cc job_wrapper.cc new_film_dialog.cc properties_dialog.cc server_dialog.cc + timecode.cc + timeline.cc + timeline_dialog.cc wx_util.cc wx_ui_signaller.cc """ def configure(conf): - conf.check_cfg(package = '', path = conf.options.wx_config, args = '--cppflags --cxxflags --libs', uselib_store = 'WXWIDGETS', mandatory = True) + conf.check_cfg(msg='Checking for wxWidgets', package='', path=conf.options.wx_config, args='--cppflags --cxxflags --libs', + uselib_store='WXWIDGETS', mandatory=True) + + if conf.env.STATIC: + # wx-config returns its static libraries as full paths, without -l prefixes, which confuses + # check_cfg(), so just hard-code it all. + conf.env.STLIB_WXWIDGETS = ['wx_gtk2u_xrc-2.9', 'wx_gtk2u_qa-2.9', 'wx_baseu_net-2.9', 'wx_gtk2u_html-2.9', + 'wx_gtk2u_adv-2.9', 'wx_gtk2u_core-2.9', 'wx_baseu_xml-2.9', 'wx_baseu-2.9'] + conf.env.LIB_WXWIDGETS = ['tiff', 'SM'] + + conf.in_msg = 1 + wx_version = conf.check_cfg(package='', path=conf.options.wx_config, args='--version').strip() + conf.im_msg = 0 + if wx_version != '2.9.4': + conf.fatal('wxwidgets version 2.9.4 is required; %s found' % wx_version) def build(bld): if bld.env.STATIC: @@@ -38,18 -47,20 +52,20 @@@ else: obj = bld(features = 'cxx cxxshlib') - obj.name = 'libdvdomatic-wx' + obj.name = 'libdcpomatic-wx' obj.includes = [ '..' ] obj.export_includes = ['.'] obj.uselib = 'WXWIDGETS' + if bld.env.TARGET_LINUX: + obj.uselib += ' GTK' - obj.use = 'libdvdomatic' + obj.use = 'libdcpomatic' obj.source = sources - obj.target = 'dvdomatic-wx' + obj.target = 'dcpomatic-wx' - i18n.po_to_mo(os.path.join('src', 'wx'), 'libdvdomatic-wx', bld) + i18n.po_to_mo(os.path.join('src', 'wx'), 'libdcpomatic-wx', bld) def pot(bld): - i18n.pot(os.path.join('src', 'wx'), sources, 'libdvdomatic-wx') + i18n.pot(os.path.join('src', 'wx'), sources, 'libdcpomatic-wx') def pot_merge(bld): - i18n.pot_merge(os.path.join('src', 'wx'), 'libdvdomatic-wx') + i18n.pot_merge(os.path.join('src', 'wx'), 'libdcpomatic-wx') diff --combined src/wx/wx_util.cc index 5691d341a,b9e78932a..c5887e17d --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@@ -35,21 -35,37 +35,37 @@@ using namespace boost * @param s Sizer to add to. * @param p Parent window for the wxStaticText. * @param t Text for the wxStaticText. + * @param left true if this label is a `left label'; ie the sort + * of label which should be right-aligned on OS X. * @param prop Proportion to pass when calling Add() on the wxSizer. */ wxStaticText * - add_label_to_sizer (wxSizer* s, wxWindow* p, wxString t, int prop) + add_label_to_sizer (wxSizer* s, wxWindow* p, wxString t, bool left, int prop) { + int flags = wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT; + #ifdef __WXOSX__ + if (left) { + flags |= wxALIGN_RIGHT; + t += wxT (":"); + } + #endif wxStaticText* m = new wxStaticText (p, wxID_ANY, t); - s->Add (m, prop, wxALIGN_CENTER_VERTICAL | wxALL, 6); + s->Add (m, prop, flags, 6); return m; } wxStaticText * - add_label_to_grid_bag_sizer (wxGridBagSizer* s, wxWindow* p, wxString t, wxGBPosition pos, wxGBSpan span) + add_label_to_grid_bag_sizer (wxGridBagSizer* s, wxWindow* p, wxString t, bool left, wxGBPosition pos, wxGBSpan span) { + int flags = wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT; + #ifdef __WXOSX__ + if (left) { + flags |= wxALIGN_RIGHT; + t += wxT (":"); + } + #endif wxStaticText* m = new wxStaticText (p, wxID_ANY, t); - s->Add (m, pos, span, wxALIGN_CENTER_VERTICAL | wxALL, 6); + s->Add (m, pos, span, flags); return m; } @@@ -60,7 -76,7 +76,7 @@@ void error_dialog (wxWindow* parent, wxString m) { - wxMessageDialog* d = new wxMessageDialog (parent, m, _("DVD-o-matic"), wxOK); + wxMessageDialog* d = new wxMessageDialog (parent, m, _("DCP-o-matic"), wxOK); d->ShowModal (); d->Destroy (); } @@@ -68,7 -84,7 +84,7 @@@ bool confirm_dialog (wxWindow* parent, wxString m) { - wxMessageDialog* d = new wxMessageDialog (parent, m, _("DVD-o-matic"), wxYES_NO | wxICON_QUESTION); + wxMessageDialog* d = new wxMessageDialog (parent, m, _("DCP-o-matic"), wxYES_NO | wxICON_QUESTION); int const r = d->ShowModal (); d->Destroy (); return r == wxID_YES; @@@ -215,7 -231,7 +231,7 @@@ checked_set (wxRadioButton* widget, boo } void -dvdomatic_setup_i18n () +dcpomatic_setup_i18n () { int language = wxLANGUAGE_DEFAULT; @@@ -231,12 -247,12 +247,12 @@@ if (wxLocale::IsAvailable (language)) { locale = new wxLocale (language, wxLOCALE_LOAD_DEFAULT); -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS locale->AddCatalogLookupPathPrefix (std_to_wx (mo_path().string())); #endif - locale->AddCatalog (wxT ("libdvdomatic-wx")); - locale->AddCatalog (wxT ("dvdomatic")); + locale->AddCatalog (wxT ("libdcpomatic-wx")); + locale->AddCatalog (wxT ("dcpomatic")); if (!locale->IsOk()) { delete locale; @@@ -246,6 -262,6 +262,6 @@@ } if (locale) { - dvdomatic_setup_gettext_i18n (wx_to_std (locale->GetCanonicalName ())); + dcpomatic_setup_gettext_i18n (wx_to_std (locale->GetCanonicalName ())); } } diff --combined src/wx/wx_util.h index bff11647e,18a9f251c..de6a09c35 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@@ -17,26 -17,35 +17,35 @@@ */ -#ifndef DVDOMATIC_WX_UTIL_H -#define DVDOMATIC_WX_UTIL_H ++#ifndef DCPOMATIC_WX_UTIL_H ++#define DCPOMATIC_WX_UTIL_H + #include #include #include #include + #ifdef __WXGTK__ + #include + #endif class wxFilePickerCtrl; class wxSpinCtrl; class wxGridBagSizer; -#define DVDOMATIC_SIZER_X_GAP 8 -#define DVDOMATIC_SIZER_Y_GAP 8 ++#define DCPOMATIC_SIZER_X_GAP 8 ++#define DCPOMATIC_SIZER_Y_GAP 8 + /** @file src/wx/wx_util.h * @brief Some utility functions and classes. */ extern void error_dialog (wxWindow *, wxString); extern bool confirm_dialog (wxWindow *, wxString); - extern wxStaticText* add_label_to_sizer (wxSizer *, wxWindow *, wxString, int prop = 0); - extern wxStaticText* add_label_to_grid_bag_sizer (wxGridBagSizer *, wxWindow *, wxString, wxGBPosition, wxGBSpan span = wxDefaultSpan); + extern wxStaticText* add_label_to_sizer (wxSizer *, wxWindow *, wxString, bool left, int prop = 0); + extern wxStaticText* add_label_to_grid_bag_sizer (wxGridBagSizer *, wxWindow *, wxString, bool, wxGBPosition, wxGBSpan span = wxDefaultSpan); extern std::string wx_to_std (wxString); extern wxString std_to_wx (std::string); -extern void dvdomatic_setup_i18n (); +extern void dcpomatic_setup_i18n (); /** @class ThreadedStaticText * @@@ -69,3 -78,12 +78,12 @@@ extern void checked_set (wxTextCtrl* wi extern void checked_set (wxCheckBox* widget, bool value); extern void checked_set (wxRadioButton* widget, bool value); extern void checked_set (wxStaticText* widget, std::string value); + + /* GTK 2.24.17 has a buggy GtkFileChooserButton and it was put in Ubuntu 13.04. + Use our own dir picker as this is the least bad option I can think of. + */ + #if defined(__WXMSW__) || (GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION == 24 && GTK_MICRO_VERSION == 17) -#define DVDOMATIC_USE_OWN_DIR_PICKER ++#define DCPOMATIC_USE_OWN_DIR_PICKER + #endif + + #endif diff --combined test/test.cc index 4494540a2,74d967a46..d6c7842d7 --- a/test/test.cc +++ b/test/test.cc @@@ -22,8 -22,7 +22,8 @@@ #include #include #include -#include "format.h" +#include +#include "ratio.h" #include "film.h" #include "filter.h" #include "job_manager.h" @@@ -40,18 -39,16 +40,19 @@@ #include "scaler.h" #include "ffmpeg_decoder.h" #include "sndfile_decoder.h" -#include "trimmer.h" +#include "dcp_content_type.h" #include "ui_signaller.h" ++#include "ratio.h" #define BOOST_TEST_DYN_LINK -#define BOOST_TEST_MODULE dvdomatic_test +#define BOOST_TEST_MODULE dcpomatic_test #include using std::string; using std::list; using std::stringstream; using std::vector; +using std::min; +using std::cout; using boost::shared_ptr; using boost::thread; using boost::dynamic_pointer_cast; @@@ -60,14 -57,14 +61,14 @@@ struct TestConfi { TestConfig() { - dvdomatic_setup(); + dcpomatic_setup(); Config::instance()->set_num_local_encoding_threads (1); Config::instance()->set_servers (vector ()); Config::instance()->set_server_port (61920); Config::instance()->set_default_dci_metadata (DCIMetadata ()); - Config::instance()->set_default_container (0); - Config::instance()->set_default_dcp_content_type (0); - Config::instance()->set_default_format (static_cast (0)); ++ Config::instance()->set_default_container (static_cast (0)); + Config::instance()->set_default_dcp_content_type (static_cast (0)); ui_signaller = new UISignaller (); } @@@ -93,78 -90,18 +94,78 @@@ new_test_film (string name boost::filesystem::remove_all (p); } - return shared_ptr (new Film (p.string(), false)); + shared_ptr f = shared_ptr (new Film (p.string())); + f->write_metadata (); + return f; } +void +check_file (string ref, string check) +{ + uintmax_t N = boost::filesystem::file_size (ref); + BOOST_CHECK_EQUAL (N, boost::filesystem::file_size(check)); + FILE* ref_file = fopen (ref.c_str(), "rb"); + BOOST_CHECK (ref_file); + FILE* check_file = fopen (check.c_str(), "rb"); + BOOST_CHECK (check_file); + + int const buffer_size = 65536; + uint8_t* ref_buffer = new uint8_t[buffer_size]; + uint8_t* check_buffer = new uint8_t[buffer_size]; + + while (N) { + uintmax_t this_time = min (uintmax_t (buffer_size), N); + size_t r = fread (ref_buffer, 1, this_time, ref_file); + BOOST_CHECK_EQUAL (r, this_time); + 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); + N -= this_time; + } + + delete[] ref_buffer; + delete[] check_buffer; + + fclose (ref_file); + fclose (check_file); +} + +static void +note (libdcp::NoteType, string n) +{ + cout << n << "\n"; +} + +void +check_dcp (string ref, string check) +{ + libdcp::DCP ref_dcp (ref); + ref_dcp.read (); + libdcp::DCP check_dcp (check); + check_dcp.read (); + + libdcp::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.mxf_names_can_differ = true; + + BOOST_CHECK (ref_dcp.equals (check_dcp, options, boost::bind (note, _1, _2))); +} + +#include "ffmpeg_pts_offset.cc" +#include "ffmpeg_examiner_test.cc" +#include "black_fill_test.cc" +#include "scaling_test.cc" +#include "ratio_test.cc" #include "pixel_formats_test.cc" #include "make_black_test.cc" -#include "trimmer_test.cc" #include "film_metadata_test.cc" #include "stream_test.cc" -#include "format_test.cc" #include "util_test.cc" -#include "film_test.cc" -#include "dcp_test.cc" +#include "ffmpeg_dcp_test.cc" #include "frame_rate_test.cc" #include "job_test.cc" #include "client_server_test.cc" diff --combined test/wscript index 2fbbdacbf,71636a05d..60d846aea --- a/test/wscript +++ b/test/wscript @@@ -12,8 -12,8 +12,8 @@@ def configure(conf) def build(bld): obj = bld(features = 'cxx cxxprogram') obj.name = 'unit-tests' - obj.uselib = 'BOOST_TEST CXML DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC' + obj.uselib = 'BOOST_TEST DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML' - obj.use = 'libdvdomatic' + obj.use = 'libdcpomatic' obj.source = 'test.cc' obj.target = 'unit-tests' obj.install_path = '' diff --combined wscript index b1d7eafe2,9d3a566fe..2c0a14580 --- a/wscript +++ b/wscript @@@ -2,8 -2,8 +2,8 @@@ import subproces import os import sys -APPNAME = 'dvdomatic' -VERSION = '0.109pre' +APPNAME = 'dcpomatic' +VERSION = '1.00pre' def options(opt): opt.load('compiler_cxx') @@@ -29,10 -29,10 +29,10 @@@ def configure(conf) conf.env.TARGET_LINUX = not conf.env.TARGET_WINDOWS and not conf.env.TARGET_OSX conf.env.append_value('CXXFLAGS', ['-D__STDC_CONSTANT_MACROS', '-D__STDC_LIMIT_MACROS', '-msse', '-mfpmath=sse', '-ffast-math', '-fno-strict-aliasing', - '-Wall', '-Wno-attributes', '-Wextra']) + '-Wall', '-Wno-attributes', '-Wextra', '-D_FILE_OFFSET_BITS=64']) if conf.env.TARGET_WINDOWS: - conf.env.append_value('CXXFLAGS', ['-DDVDOMATIC_WINDOWS', '-DWIN32_LEAN_AND_MEAN', '-DBOOST_USE_WINDOWS_H', '-DUNICODE']) + conf.env.append_value('CXXFLAGS', ['-DDCPOMATIC_WINDOWS', '-DWIN32_LEAN_AND_MEAN', '-DBOOST_USE_WINDOWS_H', '-DUNICODE']) wxrc = os.popen('wx-config --rescomp').read().split()[1:] conf.env.append_value('WINRCFLAGS', wxrc) if conf.options.enable_debug: @@@ -42,12 -42,13 +42,13 @@@ conf.check(lib = 'bfd', uselib_store = 'BFD', msg = "Checking for library bfd") conf.check(lib = 'dbghelp', uselib_store = 'DBGHELP', msg = "Checking for library dbghelp") conf.check(lib = 'iberty', uselib_store = 'IBERTY', msg = "Checking for library iberty") + conf.check(lib = 'shlwapi', uselib_store = 'SHLWAPI', msg = "Checking for library shlwapi") boost_lib_suffix = '-mt' boost_thread = 'boost_thread_win32-mt' else: - conf.env.append_value('CXXFLAGS', '-DDVDOMATIC_POSIX') + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_POSIX') conf.env.append_value('CXXFLAGS', '-DPOSIX_LOCALE_PREFIX="%s/share/locale"' % conf.env['PREFIX']) - conf.env.append_value('CXXFLAGS', '-DPOSIX_ICON_PREFIX="%s/share/dvdomatic"' % conf.env['PREFIX']) + conf.env.append_value('CXXFLAGS', '-DPOSIX_ICON_PREFIX="%s/share/dcpomatic"' % conf.env['PREFIX']) boost_lib_suffix = '' boost_thread = 'boost_thread' conf.env.append_value('LINKFLAGS', '-pthread') @@@ -55,38 -56,37 +56,40 @@@ if conf.env.TARGET_LINUX: # libxml2 seems to be linked against this on Ubuntu but it doesn't mention it in its .pc file conf.env.append_value('LIB', 'lzma') - conf.env.append_value('CXXFLAGS', '-DDVDOMATIC_LINUX') + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_LINUX') if conf.env.TARGET_OSX: - conf.env.append_value('CXXFLAGS', '-DDVDOMATIC_OSX') + conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_OSX') if conf.options.enable_debug: - conf.env.append_value('CXXFLAGS', ['-g', '-DDVDOMATIC_DEBUG']) + conf.env.append_value('CXXFLAGS', ['-g', '-DDCPOMATIC_DEBUG']) else: conf.env.append_value('CXXFLAGS', '-O2') if not conf.options.static: conf.check_cfg(package = 'libdcp', atleast_version = '0.52', args = '--cflags --libs', uselib_store = 'DCP', mandatory = True) + conf.check_cfg(package = 'libcxml', atleast_version = '0.01', args = '--cflags --libs', uselib_store = 'CXML', mandatory = True) conf.check_cfg(package = 'libavformat', args = '--cflags --libs', uselib_store = 'AVFORMAT', mandatory = True) conf.check_cfg(package = 'libavfilter', args = '--cflags --libs', uselib_store = 'AVFILTER', mandatory = True) conf.check_cfg(package = 'libavcodec', args = '--cflags --libs', uselib_store = 'AVCODEC', mandatory = True) conf.check_cfg(package = 'libavutil', args = '--cflags --libs', uselib_store = 'AVUTIL', mandatory = True) conf.check_cfg(package = 'libswscale', args = '--cflags --libs', uselib_store = 'SWSCALE', mandatory = True) - conf.check_cfg(package = 'libswresample', args = '--cflags --libs', uselib_store = 'SWRESAMPLE', mandatory = False) + conf.check_cfg(package = 'libswresample', args = '--cflags --libs', uselib_store = 'SWRESAMPLE', mandatory = True) conf.check_cfg(package = 'libpostproc', args = '--cflags --libs', uselib_store = 'POSTPROC', mandatory = True) else: # This is hackio grotesquio for static builds (ie for .deb packages). We need to link some things # statically and some dynamically, or things get horribly confused and the dynamic linker (I think) - # crashes horribly. These calls do what the check_cfg calls would have done, but specify the + # crashes. These calls do what the check_cfg calls would have done, but specify the # different bits as static or dynamic as required. It'll break if you look at it funny, but # I think anyone else who builds would do so dynamically. + conf.env.HAVE_CXML = 1 + conf.env.STLIB_CXML = ['cxml'] conf.env.HAVE_DCP = 1 conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp'] conf.env.LIB_DCP = ['glibmm-2.4', 'xml++-2.6', 'ssl', 'crypto', 'bz2'] - conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='DCP', mandatory=True) + conf.env.HAVE_CXML = 1 + conf.env.STLIB_CXML = ['cxml'] - conf.check_cfg(package = 'libxml++-2.6', args = '--cflags --libs', uselib_store = 'XML++', mandatory = True) ++ conf.check_cfg(package='libxml++-2.6', args='--cflags --libs', uselib_store='XML++', mandatory=True) conf.env.HAVE_AVFORMAT = 1 conf.env.STLIB_AVFORMAT = ['avformat'] conf.env.HAVE_AVFILTER = 1 @@@ -98,16 -98,25 +101,21 @@@ conf.env.STLIB_AVUTIL = ['avutil'] conf.env.HAVE_SWSCALE = 1 conf.env.STLIB_SWSCALE = ['swscale'] - conf.env.HAVE_SWRESAMPLE = 1 - conf.env.STLIB_SWRESAMPLE = ['swresample'] conf.env.HAVE_POSTPROC = 1 conf.env.STLIB_POSTPROC = ['postproc'] - - # This doesn't seem to be set up, and we need it otherwise resampling support - # won't be included. Hack upon a hack, obviously - conf.env.append_value('CXXFLAGS', ['-DHAVE_SWRESAMPLE=1']) + conf.env.HAVE_SWRESAMPLE = 1 + conv.env.STLIB_SWRESAMPLE = ['swresample'] conf.check_cfg(package = 'sndfile', args = '--cflags --libs', uselib_store = 'SNDFILE', mandatory = True) conf.check_cfg(package = 'glib-2.0', args = '--cflags --libs', uselib_store = 'GLIB', mandatory = True) if conf.env.TARGET_LINUX: conf.check_cfg(package='liblzma', args='--cflags --libs', uselib_store='LZMA', mandatory=True) + if conf.env.STATIC: + conf.check_cfg(package='gtk+-2.0', args='--cflags --libs', uselib_store='GTK', mandatory=True) + else: + # On Linux we need to be able to include to check GTK's version + conf.check_cfg(package='gtk+-2.0', args='--cflags', uselib_store='GTK', mandatory=True) conf.check_cfg(package = '', path = conf.options.magickpp_config, args = '--cppflags --cxxflags --libs', uselib_store = 'MAGICK', mandatory = True) @@@ -197,7 -206,7 +205,7 @@@ conf.recurse('test') def build(bld): - create_version_cc(VERSION) + create_version_cc(VERSION, bld.env.CXXFLAGS) bld.recurse('src') bld.recurse('test') @@@ -209,10 -218,10 +217,10 @@@ bld.recurse('platform/osx') for r in ['22x22', '32x32', '48x48', '64x64', '128x128']: - bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dvdomatic.png' % r) + bld.install_files('${PREFIX}/share/icons/hicolor/%s/apps' % r, 'icons/%s/dcpomatic.png' % r) if not bld.env.TARGET_WINDOWS: - bld.install_files('${PREFIX}/share/dvdomatic', 'icons/taskbar_icon.png') + bld.install_files('${PREFIX}/share/dcpomatic', 'icons/taskbar_icon.png') bld.add_post_fun(post) @@@ -223,7 -232,7 +231,7 @@@ def dist(ctx) GRSYMS GRTAGS GSYMS GTAGS """ - def create_version_cc(version): + def create_version_cc(version, cxx_flags): if os.path.exists('.git'): cmd = "LANG= git log --abbrev HEAD^..HEAD ." output = subprocess.Popen(cmd, shell=True, stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0].splitlines() @@@ -234,8 -243,15 +242,15 @@@ try: text = '#include "version.h"\n' - text += 'char const * dvdomatic_git_commit = \"%s\";\n' % commit - text += 'char const * dvdomatic_version = \"%s\";\n' % version + text += 'char const * dcpomatic_git_commit = \"%s\";\n' % commit + text += 'char const * dcpomatic_version = \"%s\";\n' % version + + t = '' + for f in cxx_flags: + f = f.replace('"', '\\"') + t += f + ' ' - text += 'char const * dvdomatic_cxx_flags = \"%s\";\n' % t[:-1] ++ text += 'char const * dcpomatic_cxx_flags = \"%s\";\n' % t[:-1] + print('Writing version information to src/lib/version.cc') o = open('src/lib/version.cc', 'w') o.write(text)