doc/manual/html
doc/manual/pdf
doc/manual/extensions.ent
+doc/design/*.pdf
+doc/design/*.log
+doc/design/*.aux
.be/id-cache
*.pyc
GPATH
# identify the project. Note that if you do not use Doxywizard you need
# to put quotes around the project name if it contains spaces.
-PROJECT_NAME = DVD-o-matic
+PROJECT_NAME = DCP-o-matic
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
-dvd-o-matic
+DCP-o-matic
-----------
Hello!
libsndfile
libssh
-and also the command line tool:
-
- vobcopy (if you want to rip DVDs straight into DVD-o-matic)
-
Documentation
-------------
-There is a manual available at http://carlh.net/software/dvdomatic
+There is a manual available at http://carlh.net/software/dcpomatic
The DocBook source for this is in doc/manual.
In a nutshell
-------------
-The `dvdomatic' program is a GTK front-end which is probably easiest
+The `dcpomatic' program is a GTK front-end which is probably easiest
to use. It will create a directory for a particular project, and write
its data to that directory. The basic approach is:
"File->New"; specify a directory.
-Choose "Jobs->Copy from DVD" to read a DVD from your drive, if you have one.
Fill in the fields in the window (most importantly the `content' field:
specify your video, and the `Name' field: give your project [and hence DCP]
a name.)
-------------
Running the `servomatic' program on a remote machine will make it
-listen on port 6192 (by default) and process requests from a dvdomatic
+listen on port 6192 (by default) and process requests from a dcpomatic
instance. This has been written with no thought to security, so don't
do it over the public internet! The connection will probably need to
be 1 Gb/s to make it worthwhile.
+++ /dev/null
-Make a DCP with subs using subtitle edit.
-
-Look at http://liblqr.wikidot.com/en:manual
-
-EC2
-
-Small instance $0.085 ph
-Sintel Trailer 1080p @ 200000 Mbps
-1247 frames @ 24fps ie 51.96s
-Took 1h20 to encode
-
-High-CPU medium $0.186 ph
-Sintel Trailer 1080p @ 200000 Mbps
-1247 frames @ 24fps ie 51.96s
-Took 23m to encode
-
-High-CPU extra-large $0.744 ph
-Sintel Trailer 1080p @ 200000 Mbps
-1247 frames @ 24fps ie 51.96s
-
-
-Transfer in free
-Transfer out $0.120 per GB
-
-
-Port DVD rip
-
-Write still j2ks straight to a MXF.
-md5_data to use openssl
-Write all j2ks straight to a MXF? Possible?
-
-Standardise j2c/j2k
-Format name in ~/.dvdomatic screws up with spaces; use ID or something
-Thumbnails are poorly named
-x-thread signaller
-Restartable jobs somehow
-More logging
-Nice error when trying to thumbnail with no content.
-Destroy _buffer_src_context / _buffer_sink_context
-Don't start later jobs when one breaks.
-Compute time remaining based on more recent information.
-Use lexical_cast more
-Do deps better
-
-options summary
-
-1: L
-2: R
-3: C
-4: Lfe
-5: Ls
-6: Rs
-
-City Screen
-
-Screen 1: "1.37" masking preset, projector only has DCI 133 preset.
-
-With 1480x1080 alignment in DCI 133: bottom you see purple, yellow; top purple; left and right no lines
-With 1480x1080 alignment in DCI Flat: outside masks, but you see bottom purple, yellow; left/right all; top purple
-
-
-Screen 2: no real masking preset, projector has DCI 133 and DCI 137
-
-1480x1080, DCI 133
-L yellow purple
-R none
-B purple
-T none
-1480x1080, DCI 137
-L all
-R all but blue
-T purple
-B purple
-
-
-Screen 3: projector has DCI 1.38
-
-1480x1080
-L, R, T none
-B purple + yellow
-
-
-films-0.6: Dolby Countdown looks as though it's 3D. THX Terminator 2 fucked
-(these on default settings)
-fq/gradfun --- no obvious effect
-hqdn3d --- pretty good denoising
-ow --- no obvious effect
-tn --- interesting; much noise reduction, bad artefacts on movement, colour tint even in black
-unsharp --- worse
-
-Benchmark SWS options: lanczos ?
-hqdn3d=0:0:6 ? (turn off chroma/luma blurring)
-
-Lanczos; no visible effect on Ghostbusters.
-
-
-THX_Monster with master Intel Core 2 Duo E4600 (2.4GHz), slave Intel Core i3 M350 (2.27GHz)
-1920 x 1080 original -> DCI Flat
-240 frames
-
-[Gbit: gigabit ethernet rather than 100Mbit]
-[im-mod: after modification to memcpy RGB data then to RGB -> XYZ in the encode thread
-[hack1]: after modification to pass YUV and to swscale in the encode thread (includes im-mod)
-[hack2]: modified hack1
- Time Seconds FPS Speedup relative to 1 local
-1 local: 20m57 1257 0.19 x 1
-2 local: 11m24 684 0.35 x 1.84
-2 local [im-mod]: 13m13
-2 local + 1 slave: 6m34 394 0.61 x 3.19
-2 local + 2 slave: 5m13 313 0.77 x 4.02
-2 local + 4 slave: 5m05 303 0.79 x 4.15
-2 local + 4 slave [Gbit]: 2m50 170 1.41 x 7.39
-2 local + 4 slave [Gbit,im-mod]:2m33
-2 local + 4 slave [Gbit,hack1]: 3m20
-2 local + 4 slave [Gbit,hack2]: 2m22
-1 local + 8 slave [Gbit]: 2m28 148 1.62 x 8.49
-2 local + 8 slave [Gbit]: 2m41 161 1.49 x 7.81
-2 local + 8 slave [Gbit,im-mod]:2m35
-
-
-
-Just encode 52s
-Encode + Image create 1m27
-Encode + Image create (memcpy, not convert) 53s.
-
-THX_Monster with master Intel Core i3 M350 (2.27GHz), slave Intel Core 2 Duo E4600 (2.4GHz)
-1920 x 1080 original -> DCI Flat
-240 frames
-
-
-4 local: 2m45
-4 local [im-mod]: 2m53
-4 local + 2 slave [Gbit]: 2m22
-4 local + 4 slave [Gbit]: 2m21
-4 local + 4 slave [Gbit,in-mod]:2m21
-
-
--- /dev/null
+audio map view is screwed up on windows, apparently not extended to full height
+still image stuff pretty slow
+
-Source: dvdomatic
+Source: dcpomatic
Section: video
Priority: extra
Maintainer: Carl Hetherington <cth@carlh.net>
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)
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)
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.
-Source: dvdomatic
+Source: dcpomatic
Section: video
Priority: extra
Maintainer: Carl Hetherington <cth@carlh.net>
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)
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.
-Source: dvdomatic
+Source: dcpomatic
Section: video
Priority: extra
Maintainer: Carl Hetherington <cth@carlh.net>
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)
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)
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.
-Source: dvdomatic
+Source: dcpomatic
Section: video
Priority: extra
Maintainer: Carl Hetherington <cth@carlh.net>
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)
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)
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.
return ()
else:
return (('openjpeg-cdist', None),
+ ('libcxml', None),
('ffmpeg-cdist', '488d5d4496af5e3a3b9d31d6b221e8eeada6b77e'),
('libdcp', 'v0.45'))
shutil.copyfile('builds/control-%s-%d' % (target.version, target.bits), 'debian/control')
env.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)
- env.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)
+ 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'))
def make_pot(env):
env.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):
os.chdir('doc/manual')
--- /dev/null
+[Desktop Entry]
+Encoding=UTF-8
+Version=1.0
+Type=Application
+Terminal=false
+Exec=@PREFIX@/bin/dcpomatic
+Name=DCP-o-matic
+Icon=dcpomatic
+Comment=DCP generator
+Categories=AudioVideo;Video
-- Carl Hetherington <carl@houllier.lan> Sun, 21 Apr 2013 17:49:54 +0100
-dvdomatic (0.84beta5-1) UNRELEASED; urgency=low
+dcpomatic (0.84beta5-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 21 Apr 2013 00:06:12 +0100
-dvdomatic (0.84beta4-1) UNRELEASED; urgency=low
+dcpomatic (0.84beta4-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Fri, 19 Apr 2013 17:41:58 +0100
-dvdomatic (0.84beta3-1) UNRELEASED; urgency=low
+dcpomatic (0.84beta3-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Fri, 19 Apr 2013 11:36:37 +0100
-dvdomatic (0.84beta2-1) UNRELEASED; urgency=low
+dcpomatic (0.84beta2-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Fri, 19 Apr 2013 11:12:09 +0100
-dvdomatic (0.84beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.84beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 18 Apr 2013 23:32:17 +0100
-dvdomatic (0.83-1) UNRELEASED; urgency=low
+dcpomatic (0.83-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 10 Apr 2013 12:48:25 +0100
-dvdomatic (0.82-1) UNRELEASED; urgency=low
+dcpomatic (0.82-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 09 Apr 2013 23:43:35 +0100
-dvdomatic (0.82beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.82beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 09 Apr 2013 21:48:56 +0100
-dvdomatic (0.81-1) UNRELEASED; urgency=low
+dcpomatic (0.81-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 09 Apr 2013 19:48:04 +0100
-dvdomatic (0.81beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.81beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 09 Apr 2013 15:37:32 +0100
-dvdomatic (0.80-1) UNRELEASED; urgency=low
+dcpomatic (0.80-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 07 Apr 2013 23:48:12 +0100
-dvdomatic (0.80beta4-1) UNRELEASED; urgency=low
+dcpomatic (0.80beta4-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 07 Apr 2013 23:08:49 +0100
-dvdomatic (0.80beta3-1) UNRELEASED; urgency=low
+dcpomatic (0.80beta3-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 07 Apr 2013 22:44:29 +0100
-dvdomatic (0.80beta2-1) UNRELEASED; urgency=low
+dcpomatic (0.80beta2-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 07 Apr 2013 22:19:34 +0100
-dvdomatic (0.80beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.80beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 07 Apr 2013 18:21:33 +0100
-dvdomatic (0.79-1) UNRELEASED; urgency=low
+dcpomatic (0.79-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Mon, 01 Apr 2013 22:37:03 +0100
-dvdomatic (0.78-1) UNRELEASED; urgency=low
+dcpomatic (0.78-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 31 Mar 2013 02:43:03 +0100
-dvdomatic (0.78beta16-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta16-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 28 Mar 2013 16:28:05 +0000
-dvdomatic (0.78beta15-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta15-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 28 Mar 2013 14:25:56 +0000
-dvdomatic (0.78beta14-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta14-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 28 Mar 2013 10:38:07 +0000
-dvdomatic (0.78beta13-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta13-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 27 Mar 2013 12:26:55 +0000
-dvdomatic (0.78beta12-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta12-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 26 Mar 2013 21:13:54 +0000
-dvdomatic (0.78beta11-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta11-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 26 Mar 2013 17:34:49 +0000
-dvdomatic (0.78beta10-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta10-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 26 Mar 2013 11:35:15 +0000
-dvdomatic (0.78beta9-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta9-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 26 Mar 2013 10:36:05 +0000
-dvdomatic (0.78beta8-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta8-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 26 Mar 2013 00:59:36 +0000
-dvdomatic (0.78beta7-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta7-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 26 Mar 2013 00:19:21 +0000
-dvdomatic (0.78beta6-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta6-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Mon, 25 Mar 2013 00:08:10 +0000
-dvdomatic (0.78beta5-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta5-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 21 Mar 2013 16:32:21 +0000
-dvdomatic (0.78beta4-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta4-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 20 Mar 2013 15:01:10 +0000
-dvdomatic (0.78beta3-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta3-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 20 Mar 2013 10:49:17 +0000
-dvdomatic (0.78beta2-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta2-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 19 Mar 2013 21:35:50 +0000
-dvdomatic (0.78beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.78beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 19 Mar 2013 20:50:54 +0000
-dvdomatic (0.77-1) UNRELEASED; urgency=low
+dcpomatic (0.77-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 14 Mar 2013 17:12:03 +0000
-dvdomatic (0.77beta2-1) UNRELEASED; urgency=low
+dcpomatic (0.77beta2-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 14 Mar 2013 15:50:43 +0000
-dvdomatic (0.77beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.77beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 14 Mar 2013 15:14:01 +0000
-dvdomatic (0.76-1) UNRELEASED; urgency=low
+dcpomatic (0.76-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 05 Mar 2013 13:30:28 +0000
-dvdomatic (0.76beta3-1) UNRELEASED; urgency=low
+dcpomatic (0.76beta3-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 05 Mar 2013 12:47:20 +0000
-dvdomatic (0.76beta2-1) UNRELEASED; urgency=low
+dcpomatic (0.76beta2-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Fri, 01 Mar 2013 18:32:16 +0000
-dvdomatic (0.76beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.76beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Fri, 01 Mar 2013 17:36:55 +0000
-dvdomatic (0.75-1) UNRELEASED; urgency=low
+dcpomatic (0.75-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 27 Feb 2013 11:03:07 +0000
-dvdomatic (0.75beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.75beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 27 Feb 2013 08:20:42 +0000
-dvdomatic (0.74-1) UNRELEASED; urgency=low
+dcpomatic (0.74-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sat, 23 Feb 2013 22:57:20 +0000
-dvdomatic (0.74beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.74beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sat, 23 Feb 2013 21:44:22 +0000
-dvdomatic (0.73-1) UNRELEASED; urgency=low
+dcpomatic (0.73-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 21 Feb 2013 00:43:40 +0000
-dvdomatic (0.73beta9-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta9-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 20 Feb 2013 23:40:24 +0000
-dvdomatic (0.73beta8-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta8-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Mon, 18 Feb 2013 22:35:51 +0000
-dvdomatic (0.73beta7-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta7-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Mon, 18 Feb 2013 20:38:51 +0000
-dvdomatic (0.73beta6-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta6-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 17 Feb 2013 23:05:56 +0000
-dvdomatic (0.73beta3-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta3-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 17 Feb 2013 23:05:05 +0000
-dvdomatic (0.73beta2-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta2-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sat, 16 Feb 2013 22:42:32 +0000
-dvdomatic (0.73beta1-1) UNRELEASED; urgency=low
+dcpomatic (0.73beta1-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sat, 16 Feb 2013 21:19:24 +0000
-dvdomatic (0.72-1) UNRELEASED; urgency=low
+dcpomatic (0.72-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 24 Jan 2013 15:31:57 +0000
-dvdomatic (0.71-1) UNRELEASED; urgency=low
+dcpomatic (0.71-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 24 Jan 2013 11:36:04 +0000
-dvdomatic (0.70-1) UNRELEASED; urgency=low
+dcpomatic (0.70-1) UNRELEASED; urgency=low
* New upstream release.
* New upstream release.
-- Carl Hetherington <cth@carlh.net> Sat, 12 Jan 2013 23:07:15 +0000
-dvdomatic (0.70beta3-1) UNRELEASED; urgency=low
+dcpomatic (0.70beta3-1) UNRELEASED; urgency=low
* New upstream release.
* New upstream release.
-- Carl Hetherington <cth@carlh.net> Sun, 06 Jan 2013 23:44:24 +0000
-dvdomatic (0.68-1) UNRELEASED; urgency=low
+dcpomatic (0.68-1) UNRELEASED; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 23 Dec 2012 01:43:44 +0000
-dvdomatic (0.68beta10-1) UNRELEASED; urgency=low
+dcpomatic (0.68beta10-1) UNRELEASED; urgency=low
* New upstream release.
* New upstream release.
-- Carl Hetherington <cth@carlh.net> Sat, 22 Dec 2012 13:27:27 +0000
-dvdomatic (0.68beta5-1) unstable; urgency=low
+dcpomatic (0.68beta5-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 20 Dec 2012 07:53:46 +0000
-dvdomatic (0.68beta4-1) unstable; urgency=low
+dcpomatic (0.68beta4-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 20 Dec 2012 07:48:45 +0000
-dvdomatic (0.68beta3-1) unstable; urgency=low
+dcpomatic (0.68beta3-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 20 Dec 2012 00:35:45 +0000
-dvdomatic (0.68beta2-1) unstable; urgency=low
+dcpomatic (0.68beta2-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 19 Dec 2012 11:22:58 +0000
-dvdomatic (0.68beta1-1) unstable; urgency=low
+dcpomatic (0.68beta1-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Wed, 19 Dec 2012 10:11:13 +0000
-dvdomatic (0.67-1) unstable; urgency=low
+dcpomatic (0.67-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 18 Dec 2012 23:49:27 +0000
-dvdomatic (0.66-1) unstable; urgency=low
+dcpomatic (0.66-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 18 Dec 2012 11:29:04 +0000
-dvdomatic (0.65-1) unstable; urgency=low
+dcpomatic (0.65-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 18 Dec 2012 09:24:56 +0000
-dvdomatic (0.64-1) unstable; urgency=low
+dcpomatic (0.64-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Thu, 13 Dec 2012 21:52:09 +0000
-dvdomatic (0.63pre-1) unstable; urgency=low
+dcpomatic (0.63pre-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 11 Dec 2012 23:15:52 +0000
-dvdomatic (0.60-1) unstable; urgency=low
+dcpomatic (0.60-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Tue, 11 Dec 2012 22:46:04 +0000
-dvdomatic (0.59-1) unstable; urgency=low
+dcpomatic (0.59-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Mon, 10 Dec 2012 20:58:19 +0000
-dvdomatic (0.59beta5-1) unstable; urgency=low
+dcpomatic (0.59beta5-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 09 Dec 2012 23:51:55 +0000
-dvdomatic (0.59beta4-1) unstable; urgency=low
+dcpomatic (0.59beta4-1) unstable; urgency=low
* New upstream release.
-- Carl Hetherington <carl@houllier.lan> Sun, 09 Dec 2012 21:38:00 +0000
-dvdomatic (0.59beta1-1) unstable; urgency=low
+dcpomatic (0.59beta1-1) unstable; urgency=low
* Initial release.
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-Upstream-Name: dvdomatic
-Source: <url://carlh.net/software/dvdomatic>
+Upstream-Name: dcpomatic
+Source: <url://carlh.net/software/dcpomatic>
Files: *
Copyright: 2012 Carl Hetherington <cth@carlh.net>
-dvdomatic_0.59beta1-1_i386.deb video extra
+dcpomatic_0.59beta1-1_i386.deb video extra
./waf --nocache build
override_dh_auto_install:
- ./waf --nocache install --destdir=debian/dvdomatic
+ ./waf --nocache install --destdir=debian/dcpomatic
.PHONY: override_dh_strip
override_dh_strip:
- dh_strip --dbg-package=dvdomatic-dbg
+ dh_strip --dbg-package=dcpomatic-dbg
--- /dev/null
+\documentclass{article}
+\begin{document}
+
+\section{Status quo}
+
+As at 0.78 there is an unfortunate mish-mash of code to handle the
+input `content' the goes into a DCP.
+
+The Film has a `content' file name. This is guessed to be either a
+movie (for FFmpeg) or a still-image (for ImageMagick) based on its
+extension. We also have `external audio', which is a set of WAV files
+for libsndfile, and a flag to enable that.
+
+The `content' file is badly named and limiting. We can't have
+multiple content files, and it's not really the `content' as such (it
+used to be, but increasingly it's only a part of the content, on equal
+footing with `external' audio).
+
+The choice of sources for sound is expressed clumsily by the
+AudioStream class hierarchy.
+
+
+\section{Targets}
+
+We want to be able to implement the following:
+
+\begin{itemize}
+\item Immediately:
+\begin{itemize}
+\item Multiple still images, each with their own duration, made into a `slide-show'
+\item Lack of bugs in adding WAV-file audio to still images.
+\item External subtitle files (either XML or SRT) to be converted to XML subtitles in the DCP.
+\end{itemize}
+
+\item In the future:
+\begin{itemize}
+\item Playlist-style multiple video / audio (perhaps).
+\end{itemize}
+\end{itemize}
+
+
+\section{Content hierarchy}
+
+One idea is to have a hierarchy of Content classes (\texttt{Content},
+\texttt{\{Video/Audio\}Content}, \texttt{FFmpegContent}, \texttt{ImageMagickContent},
+\texttt{SndfileContent}).
+
+Then the Film has a list of these, and decides what to put into the
+DCP based on some rules. These rules would probably be fixed (for
+now), with the possibility to expand later into some kind of playlist.
+
+
+\section{Immediate questions}
+
+\subsection{What Film attributes are video-content specific, and which are general?}
+
+Questionable attributes:
+
+\begin{itemize}
+\item Trust content header
+\item Crop
+\item Filters
+
+Post-processing (held as part of the filters description) is done in
+the encoder, by which time all knowledge of the source is lost.
+
+\item Scaler
+\item Trim start/end
+
+Messily tied in with the encoding side. We want to implement this
+using start/end point specifications in the DCP reel, otherwise
+modifying the trim points requires a complete re-encode.
+
+\item Audio gain
+\item Audio delay
+\item With subtitles
+\item Subtitle offset/scale
+\item Colour LUT
+\end{itemize}
+
+Attributes that I think must remain in Film:
+\begin{itemize}
+\item DCP content type
+\item Format
+\item A/B
+\item J2K bandwidth
+\end{itemize}
+
+Part of the consideration here is that per-content attributes need to
+be represented in the GUI differently to how things are represented
+now.
+
+Bear in mind also that, as it stands, the only options for video are:
+
+\begin{enumerate}
+\item An FFmpeg video
+\item A set of stills
+\end{enumerate}
+
+and so the need for multiple scalers, crop and filters is
+questionable. Also, there is one set of audio (either from WAVs or
+from the FFMpeg file), so per-content audio gain/delay is also
+questionable. Trust content header is only applicable for FFmpeg
+content, really. Similarly trim, with-subtitles, subtitle details,
+colour LUT; basically none of it is really important right now.
+
+Hence it may be sensible to keep everything in Film and move it later
+along YAGNI lines.
+
+
+\subsection{Who answers questions like: ``what is the length of video?''?}
+
+If we have FFmpeg video, the question is easy to answer. For a set of
+stills, it is less easy. Who knows that we are sticking them all
+together end-to-end, with different durations for each?
+
+If we have one-content-object equalling one file, the content objects
+will presumably know how long their file should be displayed for.
+There would appear to be two options following this:
+
+\begin{enumerate}
+\item There is one \texttt{ImageMagickDecoder} which is fed all the
+ files, and outputs them in order. The magic knowledge is then
+ within this class, really.
+\item There are multiple \texttt{ImageMagickDecoder} classes, one per
+ \texttt{..Content}, and some controlling (`playlist') class to manage
+ them. The `playlist' is then itself a
+ \texttt{\{Video/Audio\}Source}, and has the magic knowledge.
+\end{enumerate}
+
+
+\section{Playlist approach}
+
+Let's try the playlist approach. We define a hierarchy of content classes:
+
+\begin{verbatim}
+
+class Content
+{
+public:
+ boost::filesystem::path file () const;
+};
+
+class VideoContent : virtual public Content
+{
+public:
+ VideoContentFrame video_length () const;
+ float video_frame_rate () const;
+ libdcp::Size size () const;
+
+};
+
+class AudioContent : virtual public Content
+{
+
+};
+
+class FFmpegContent : public VideoContent, public AudioContent
+{
+public:
+ .. stream stuff ..
+};
+
+class ImageMagickContent : public VideoContent
+{
+
+};
+
+class SndfileContent : public AudioContent
+{
+public:
+ .. channel allocation for this file ..
+};
+\end{verbatim}
+
+Then Film has a \texttt{Playlist} which has a
+\texttt{vector<shared\_ptr<Content> >}. It can answer questions
+about audio/video length, frame rate, audio channels and so on.
+
+\texttt{Playlist} can also be a source of video and audio, so clients can do:
+
+\begin{verbatim}
+shared_ptr<Playlist> p = film->playlist ();
+p->Video.connect (foo);
+p->Audio.connect (foo);
+while (!p->pass ()) {
+ /* carry on */
+}
+\end{verbatim}
+
+Playlist could be created on-demand for all the difference it would
+make. And perhaps it should, since it will hold Decoders which are
+probably run-once.
+
+\end{document}
-/** @mainpage DVD-o-matic
+/** @mainpage DCP-o-matic
*
- * DVD-o-matic is a tool to create digital cinema packages (DCPs) from
+ * DCP-o-matic is a tool to create digital cinema packages (DCPs) from
* video files, or from sets of TIFF image files. It is written in C++
* and distributed under the GPL.
*
* Video files are decoded using FFmpeg (http://ffmpeg.org), so any video
- * supported by FFmpeg should be usable with DVD-o-matic. DVD-o-matic's output has been
+ * supported by FFmpeg should be usable with DCP-o-matic. DCP-o-matic's output has been
* tested on numerous digital projectors.
*
- * DVD-o-matic allows you to crop black borders from movies, scale them to the correct
+ * DCP-o-matic allows you to crop black borders from movies, scale them to the correct
* aspect ratio and apply FFmpeg filters. The time-consuming encoding of JPEG2000 files
* can be parallelised amongst any number of processors on the local host and any number
* of servers over a network.
*
- * DVD-o-matic can also make DCPs from still images, for advertisements and such-like.
+ * DCP-o-matic can also make DCPs from still images, for advertisements and such-like.
*
- * Parts of DVD-o-matic are based on OpenDCP (http://code.google.com/p/opendcp),
+ * Parts of DCP-o-matic are based on OpenDCP (http://code.google.com/p/opendcp),
* written by Terrence Meiczinger.
*
- * DVD-o-matic uses libopenjpeg (http://code.google.com/p/openjpeg/) for JPEG2000 encoding
+ * DCP-o-matic uses libopenjpeg (http://code.google.com/p/openjpeg/) for JPEG2000 encoding
* and libsndfile (http://www.mega-nerd.com/libsndfile/) for WAV file manipulation. It
* also makes heavy use of the boost libraries (http://www.boost.org/). ImageMagick
* (http://www.imagemagick.org/) is used for still-image encoding and decoding, and the GUI is
* built using wxWidgets (http://wxwidgets.org/). It also uses libmhash (http://mhash.sourceforge.net/)
* for debugging purposes.
*
- * Thanks are due to the authors and communities of all DVD-o-matic's dependencies.
+ * Thanks are due to the authors and communities of all DCP-o-matic's dependencies.
*
- * DVD-o-matic is distributed in the hope that there are still cinemas with projectionists
+ * DCP-o-matic is distributed in the hope that there are still cinemas with projectionists
* who might want to use it. As Mark Kermode says, "if it doesn't have a projectionist
* it's not a cinema - it's a sweetshop with a video-screen."
*
* Email correspondance is welcome to cth@carlh.net
*
- * More details can be found at http://carlh.net/software/dvdomatic
+ * More details can be found at http://carlh.net/software/dcpomatic
*/
-# DVD-o-matic manual makefile
+# DCP-o-matic manual makefile
all: html pdf
still-select-content-file.png examine-thumbs.png \
calculate-audio-gain.png prefs.png making-dcp.png filters.png film-tab.png video-tab.png audio-tab.png subtitles-tab.png
-XML := dvdomatic.xml
+XML := dcpomatic.xml
GRAPHICS :=
# HTML
#
-html: $(XML) dvdomatic-html.xsl extensions-html.ent dvdomatic.css \
+html: $(XML) dcpomatic-html.xsl extensions-html.ent dcpomatic.css \
$(addprefix html/screenshots/,$(SCREENSHOTS)) \
$(subst .svg,.png,$(addprefix diagrams/,$(DIAGRAMS))) \
$(subst .svg,.png,$(addprefix graphics/,$(GRAPHICS))) \
cp extensions-html.ent extensions.ent
# DocBoox -> html
- xmlto html -m dvdomatic-html.xsl dvdomatic.xml --skip-validation -o html
+ xmlto html -m dcpomatic-html.xsl dcpomatic.xml --skip-validation -o html
# Copy graphics and CSS in
# mkdir -p html/diagrams html/graphics
# cp diagrams/*.png html/diagrams
# cp graphics/*.png html/graphics
- cp dvdomatic.css html
+ cp dcpomatic.css html
#
# PDF
#
-pdf: $(XML) dvdomatic-pdf.xsl extensions-pdf.ent screenshots/*.png $(subst .svg,.pdf,$(addprefix diagrams/,$(DIAGRAMS)))
+pdf: $(XML) dcpomatic-pdf.xsl extensions-pdf.ent screenshots/*.png $(subst .svg,.pdf,$(addprefix diagrams/,$(DIAGRAMS)))
# The DocBook needs to know what file extensions to look for
# for screenshots and diagrams; use the correct file to tell it.
mkdir -p pdf
- dblatex -p dvdomatic-pdf.xsl -s dvdomatic.sty -r pptex.py -T native dvdomatic.xml -t pdf -o pdf/dvdomatic.pdf
+ dblatex -p dcpomatic-pdf.xsl -s dcpomatic.sty -r pptex.py -T native dcpomatic.xml -t pdf -o pdf/dcpomatic.pdf
#
# LaTeX (handy for debugging)
#
-tex: $(XML) dvdomatic-pdf.xsl extensions-pdf.ent
+tex: $(XML) dcpomatic-pdf.xsl extensions-pdf.ent
# The DocBook needs to know what file extensions to look for
# for screenshots and diagrams; use the correct file to tell it.
mkdir -p tex
# -P <foo> removes the revhistory table
- dblatex -P doc.collab.show=0 -P latex.output.revhistory=0 -p dvdomatic-pdf.xsl -s dvdomatic.sty -r pptex.py -T native dvdomatic.xml -t tex -o tex/dvdomatic.tex
+ dblatex -P doc.collab.show=0 -P latex.output.revhistory=0 -p dcpomatic-pdf.xsl -s dcpomatic.sty -r pptex.py -T native dcpomatic.xml -t tex -o tex/dcpomatic.tex
-clean:; rm -rf html pdf diagrams/*.pdf diagrams/*.png graphics/*.png *.aux dvdomatic.cb dvdomatic.cb2 dvdomatic.glo dvdomatic.idx dvdomatic.ilg
- rm -rf dvdomatic.ind dvdomatic.lof dvdomatic.log dvdomatic.tex dvdomatic.toc extensions.ent dvdomatic.out
+clean:; rm -rf html pdf diagrams/*.pdf diagrams/*.png graphics/*.png *.aux dcpomatic.cb dcpomatic.cb2 dcpomatic.glo dcpomatic.idx dcpomatic.ilg
+ rm -rf dcpomatic.ind dcpomatic.lof dcpomatic.log dcpomatic.tex dcpomatic.toc extensions.ent dcpomatic.out
--- /dev/null
+<?xml version='1.0'?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ version="1.0">
+
+<!-- Our CSS -->
+<xsl:param name="html.stylesheet" select="'dcpomatic.css'"/>
+
+<!-- I can't fathom xmlto's logic with image scaling, so I've turned it off -->
+<xsl:param name="ignore.image.scaling" select="1"/>
+
+</xsl:stylesheet>
--- /dev/null
+<?xml version='1.0' encoding="iso-8859-1"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>
+
+<!-- colour links in black -->
+<xsl:param name="latex.hyperparam">colorlinks,linkcolor=black,urlcolor=black</xsl:param>
+
+<!-- no revhistory table -->
+<xsl:param name="doc.collab.show">0</xsl:param>
+<xsl:param name="latex.output.revhistory">0</xsl:param>
+
+<!-- hack images to vaguely the right size -->
+<xsl:param name="imagedata.default.scale">scale=0.6</xsl:param>
+
+<!-- don't make too-ridiculous section numbers -->
+<xsl:param name="doc.section.depth">3</xsl:param>
+
+</xsl:stylesheet>
--- /dev/null
+body {
+ font-family: luxi sans, sans-serif;
+ margin-left: 4em;
+ margin-right: 4em;
+ margin-top: 1em;
+ margin-bottom: 1em;
+ background-color: #E2E8EE;
+}
+
+div.sidebar {
+ margin-left: 1em;
+ margin-right: 1em;
+ padding-left: 1em;
+ padding-right: 1em;
+ border-color: #000000;
+ border-width: 2px;
+ border-style: solid;
+ background-color: #E2E8EE;
+}
--- /dev/null
+%%
+%% This style is derivated from the docbook one
+%%
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{ardour}[2007/04/04 My DocBook Style]
+
+%% Just use the original package and pass the options
+\RequirePackageWithOptions{docbook}
+
+% Use a nice font
+\usepackage{lmodern}
+
+% Define \dbend as the dangerous bend sign
+\font\manual=manfnt
+\def\dbend{{\manual\char127}}
+
+% Redefine sidebar environment to use the dangerous bend style
+% Danger, Will Robinson!
+\def\sidebar{\begin{trivlist}\item[]\noindent%
+\begingroup\hangindent=2pc\hangafter=-2%\clubpenalty=10000%
+\def\par{\endgraf\endgroup}%
+\hbox to0pt{\hskip-\hangindent\dbend\hfill}\ignorespaces}
+\def\endsidebar{\par\end{trivlist}}
+
+
+% Futz with the title page; basically a copy of
+% /usr/share/texmf/tex/latex/dblatex/style/dbk_title.sty
+% with authors added.
+
+\def\DBKcover{
+\ifthenelse{\equal{\DBKedition}{}}{\def\edhead{}}{\def\edhead{Ed. \DBKedition}}
+
+\pagestyle{empty}
+
+% interligne double
+\setlength{\oldbaselineskip}{\baselineskip}
+\setlength{\baselineskip}{2\oldbaselineskip}
+\textsf{
+\vfill
+\vspace{2.5cm}
+\begin{center}
+ \huge{\textbf{\DBKtitle}}\\ %
+ \ \\ %
+ \ \\ %
+ \Large{\DBKauthor}\\ %
+ \ifx\DBKsubtitle\relax\else%
+ \underline{\ \ \ \ \ \ \ \ \ \ \ }\\ %
+ \ \\ %
+ \huge{\textbf{\DBKsubtitle}}\\ %
+ \fi
+\end{center}
+\vfill
+\setlength{\baselineskip}{\oldbaselineskip}
+\hspace{1cm}
+\vspace{1cm}
+\begin{center}
+\begin{tabular}{p{7cm} p{7cm}}
+\Large{\DBKreference{} \edhead} & \\
+\end{tabular}
+\end{center}
+}
+
+% Format for the other pages
+\newpage
+\setlength{\baselineskip}{\oldbaselineskip}
+%\chead[]{\DBKcheadfront}
+\lfoot[]{}
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE book [
+<!ENTITY % sgml.features "IGNORE">
+<!ENTITY % xml.features "INCLUDE">
+<!ENTITY % dbcent PUBLIC "-//OASIS//ENTITIES DocBook Character Entities V4.5//EN"
+ "/usr/share/xml/docbook/schema/dtd/4.5/dbcentx.mod">
+%dbcent;
+<!ENTITY % extensions SYSTEM "extensions.ent">
+%extensions;
+]>
+<book xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+
+<bookinfo>
+<title>DCP-o-matic</title>
+<author><firstname>Carl</firstname><surname>Hetherington</surname></author>
+</bookinfo>
+
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<title>Introduction</title>
+
+<para>
+Hello, and welcome to DCP-o-matic!
+</para>
+
+<section>
+<title>What is DCP-o-matic?</title>
+
+<para>
+DCP-o-matic is a program to generate <ulink
+url="http://en.wikipedia.org/wiki/Digital_Cinema_Package">Digital
+Cinema Packages</ulink> (DCPs) from DVDs, Blu-Rays, video files such as MP4
+and AVI, or still images. The resulting DCPs will play on modern digital
+cinema projectors.
+</para>
+
+<para>
+You might find it useful to make DVDs easier to present, to encode
+independently-shot feature films, or to generate local advertising for
+your cinema.
+</para>
+
+</section>
+
+<section>
+<title>Licence</title>
+
+<para>
+DCP-o-matic is licensed under the <ulink url="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU GPL</ulink>.
+</para>
+
+</section>
+
+</chapter>
+
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<title>Installation</title>
+
+<section>
+<title>Windows</title>
+
+<para>
+To install DCP-o-matic on Windows, simply download the installer from
+<ulink url="http://carlh.net/software/dcpomatic">http://carlh.net</ulink>
+and double-click it. Click through the installer wizard, and
+DCP-o-matic will be installed onto your machine.
+</para>
+
+<para>
+If you are using a 32-bit version of Windows, you will need the 32-bit
+installer. For 64-bit Windows, either installer will work, but I
+suggest you used the 64-bit version as it will allow DCP-o-matic to
+use more memory. You may find that DCP-o-matic crashes if you run
+many parallel encoding threads (more than 4) on the 32-bit
+version.
+</para>
+
+</section>
+
+<section>
+<title>Ubuntu Linux</title>
+
+<para>
+You can install DCP-o-matic on Ubuntu 12.04 (‘Precise
+Pangolin’) or 12.10 (‘Quantal Quetzal’) using
+<code>.deb</code> packages: download the appropriate package from
+<ulink
+url="http://carlh.net/software/dcpomatic">http://carlh.net</ulink> and
+double-click it. Ubuntu will install the necessary bits and pieces
+and set DCP-o-matic up for you.
+</para>
+
+</section>
+
+<section>
+<title>Other Linux distributions</title>
+
+<para>
+Installation on non-Ubuntu Linux is currently a little involved, as
+there are no packages available (yet); you will have to compile it
+from source. If you are using a non-Ubuntu distribution, do let me
+know via the <ulink url="mailto:dcpomatic@carlh.net">mailing
+list</ulink> and I will see about building some packages.
+</para>
+
+<para>
+The following dependencies are required:
+<itemizedlist>
+<listitem><ulink url="http://ffmpeg.org/">FFmpeg</ulink></listitem>
+<listitem><ulink url="http://www.mega-nerd.com/libsndfile/">libsndfile</ulink></listitem>
+<listitem><ulink url="http://www.openssl.org/">OpenSSL</ulink></listitem>
+<listitem><ulink url="http://www.openjpeg.org/">libopenjpeg</ulink></listitem>
+<listitem><ulink url="http://www.imagemagick.org/script/index.php">ImageMagick</ulink></listitem>
+<listitem><ulink url="http://www.boost.org/">Boost</ulink></listitem>
+<listitem><ulink url="http://www.libssh.org/">libssh</ulink></listitem>
+<listitem><ulink url="http://www.gtk.org/">GTK</ulink></listitem>
+<listitem><ulink url="http://www.wxwidgets.org/">wxWidgets</ulink></listitem>
+<listitem><ulink url="http://carlh.net/software/libdcp/">libdcp</ulink></listitem>
+</itemizedlist>
+</para>
+
+<para>
+Once you have installed the development packages for the dependencies,
+download the source code from <ulink
+url="http://carlh.net/software/dcpomatic">http://carlh.net</ulink>,
+unpack it and run the following commands from inside the source
+directory:
+</para>
+
+<programlisting>
+./waf configure
+./waf build
+sudo ./waf install
+</programlisting>
+
+<para>
+With any luck, this will build and install DCP-o-matic on your system. To run it, enter:
+</para>
+
+<programlisting>
+dcpomatic
+</programlisting>
+
+<para>
+in a shell.
+</para>
+
+</section>
+</chapter>
+
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<title>Creating a video DCP</title>
+
+<para>
+In this chapter we will see how to create a video DCP using
+DCP-o-matic. We will gloss over some of the finer details, which are
+explained in later chapters.
+</para>
+
+<section>
+<title>Creating a new film</title>
+
+<para>
+Let's make a very simple DCP to see how DCP-o-matic works. First, we
+need some content. Download the low-resolution trailer for the open
+movie <ulink url="http://sintel.org/">Sintel</ulink> from <ulink
+url="http://ftp.nluug.nl/ftp/graphics/blender/apricot/trailer/Sintel_Trailer1.480p.DivX_Plus_HD.mkv">their
+website</ulink>. Generally, of course, one would want to use the
+highest-resolution material available, but for this test we will use
+the low-resolution version to save everyone's bandwidth bills.
+</para>
+
+<para>
+Now, start DCP-o-matic and its window will open. First, we will
+create a new ‘film’. A ‘film’ is how DCP-o-matic refers to
+a piece of content, along with some settings, which we will make into
+a DCP. DCP-o-matic stores its data in a folder on your disk while it
+creates the DCP. You can create a new film by selecting
+<guilabel>New</guilabel> from the <guilabel>File</guilabel> menu, as
+shown in <xref linkend="fig-file-new"/>.
+</para>
+
+<figure id="fig-file-new">
+ <title>Creating a new film</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/file-new&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+This will open a dialogue box for the new film, as shown in <xref
+linkend="fig-video-new-film"/>.
+</para>
+
+<figure id="fig-video-new-film">
+ <title>Dialogue box for creating a new film</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/video-new-film&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+In this dialogue box you can choose a name for the film. This will be
+used to name the folder to store its data in, and also as the initial
+name for the DCP itself. You can also choose whereabouts you want to create
+the film. In the example from the figure, DCP-o-matic will create a
+folder called ‘DCP Test’ inside my home folder (carl) into which it
+will write its working files.
+</para>
+
+<para>
+If you always create your DCPs in a particular folder, you can use
+DCP-o-matic's <guilabel>Preferences</guilabel> to make life a little
+easier by setting the default folder that DCP-o-matic will offer in this dialogue.
+See <xref linkend="ch-preferences"/>.
+</para>
+
+</section>
+
+<section>
+<title>Selecting content</title>
+
+<para>
+The next step is to set the content that you want to use. Click the
+content selector, as shown in <xref
+linkend="fig-click-content-selector"/>, and a file chooser will
+open for you to select the content file to use, as shown in <xref
+linkend="fig-video-select-content-file"/>.
+</para>
+
+<figure id="fig-click-content-selector">
+ <title>Opening the content selector</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/click-content-selector&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<figure id="fig-video-select-content-file">
+ <title>Selecting a video content file</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/video-select-content-file&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+Select your content file and click <guilabel>Open</guilabel>. In this
+case we are using the Sintel trailer that we downloaded earlier.
+</para>
+
+<para>
+When you do this, DCP-o-matic will take a look at your file. After a
+short while (when the progress bar at the bottom right of the window
+has finished), you can look through your content using the slider to
+the right of the window, as shown in <xref linkend="fig-examine-thumbs"/>.
+</para>
+
+<figure id="fig-examine-thumbs">
+ <title>Examining the content</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/examine-thumbs&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+Dragging the slider will move through your video. You can also click
+the <guilabel>Play</guilabel> button to play the content back. Note
+that there will be no sound, and playback might not be entirely
+accurate (it may be slightly slower or faster than it should be, for
+example). This player is really only intended for brief inspection of
+content; if you need to check it more thoroughly, use another player
+such as <ulink url="http://projects.gnome.org/totem/index.html">Totem</ulink>, <ulink url="http://www.mplayerhq.hu/design7/news.html">mplayer</ulink> or <ulink url="http://www.videolan.org/vlc/index.html">VLC</ulink>.
+</para>
+
+</section>
+
+<section>
+<title>Setting up</title>
+
+<para>
+Now there are a few things to set up to describe how the DCP should be
+created. The settings are divided into four tabs: film, video, audio and subtitles.
+</para>
+
+<section>
+<title>Film tab</title>
+
+<para>
+The ‘film’ tab contains settings that pertain to the whole film, as shown in <xref linkend="fig-film-tab"/>.
+</para>
+
+<figure id="fig-film-tab">
+ <title>Film settings tab</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/film-tab&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+The first thing here is the name. This is generally set to the title
+of the film that is being encoded. If <guilabel>Use DCI
+name</guilabel> is not ticked, the name that you specify will be used
+as-is for the name of the DCP. If <guilabel>Use DCI name</guilabel>
+is ticked, the name that you enter will be used as part of a
+DCI-compliant name.
+</para>
+
+<para>
+Underneath the name field is a preview of the name that the DCP will
+get. To use a DCI-compliant name, tick the <guilabel>Use DCI
+name</guilabel> checkbox. The DCI name will be composed using details
+of your content's soundtrack, the current date and other things that
+can be specified in the DCI name details dialogue box, which you can
+open by clicking on the <guilabel>Details</guilabel> button.
+</para>
+
+<para>
+If the DCP name is long, it may not all be visible. You can see the
+full name by hovering the mouse pointer over the partial name.
+</para>
+
+<para>
+The <guilabel>Trust content's header</guilabel> button starts off
+checked, and this means that DCP-o-matic will use the content's header
+information to determine its length. If, for some reason, this header
+length is wrong, uncheck the <guilabel>Trust content's
+header</guilabel> button and DCP-o-matic will run through the content
+to find its exact length. This may take a while for large pieces of content.
+</para>
+
+<para>
+Next up is the content type. This can be
+‘feature’, ‘trailer’ or whatever; select the
+required type from the drop-down list.
+</para>
+
+<para>
+The <guilabel>trim frames</guilabel> settings allow you to trim frames
+from the beginning and end of the content; any trimmed frames will not
+be included in the DCP.
+</para>
+
+</section>
+
+<section>
+<title>Video tab</title>
+
+<para>
+This tab contains settings related to the picture in your DCP, as shown in <xref linkend="fig-video-tab"/>.
+</para>
+
+<figure id="fig-video-tab">
+ <title>Video settings tab</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/video-tab&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+The first option on this tab is the format. This will govern the
+shape that DCP-o-matic will make your image into. Select the aspect
+ratio that your content should be presented in. The ‘4:3 within
+Flat’ and ‘16:9 within Flat’ settings will put the
+image at the specified ratio within a Flat (1.85:1) frame, so that you
+can project the DCP using your projector's Flat preset.
+</para>
+
+<para>
+The remaining options can often be left alone, but may sometimes be
+useful. The ‘crop’ settings can be used to crop your
+content, which can be used to remove black borders from round the
+edges of DVD images, for example. The specified number of pixels will
+be trimmed from each edge, and the content image in the right of the
+window will be updated to show the effect of the crop.
+</para>
+
+<para>
+The ‘filters’ settings allow you to apply various video
+filters to the image. These may be useful to try to improve
+poor-quality sources like DVDs. We will discuss filtering later in the manual.
+<!-- XXX: link -->
+</para>
+
+<para>
+The ‘scaler’ is the method that will be used to scale up
+your content to the required size for the DCP, if required. We will
+discuss the options in more detail later; Bicubic is a fine choice in
+most situations.
+<!-- XXX: link -->
+</para>
+
+<para>
+The ‘colour look-up table’ specifies the colour space that
+your input content will be expected to be in. If in doubt, leave it
+set to ‘sRGB’.
+</para>
+
+<para>
+Finally, the ‘JPEG2000 bandwidth’ setting changes how big the final
+image files used within the DCP will be. Larger numbers will give
+better quality, but correspondingly larger DCPs. The bandwidth can be
+between 50 and 250 megabits per second (MBps).
+</para>
+
+</section>
+
+<section>
+<title>Audio tab</title>
+
+<para>
+This tab contains settings related to the sound in your DCP, as shown in <xref linkend="fig-audio-tab"/>.
+</para>
+
+<figure id="fig-audio-tab">
+ <title>Audio settings tab</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/audio-tab&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+
+<para>
+‘Audio Gain’ is used to alter the volume of the
+soundtrack. The specified gain (in dB) will be applied to each sound
+channel before it is written to the DCP.
+</para>
+
+<para>
+If you use a sound processor that DCP-o-matic knows about, it can help
+you calculate changes in gain that you should apply. Say, for
+example, that you make a test DCP and find that you have to run it at
+volume 5 instead of volume 7 to get a good sound level in the screen.
+If this is the case, click the <guilabel>Calculate...</guilabel>
+button next to the audio gain entry, and the dialogue box in <xref
+linkend="fig-calculate-audio-gain"/> will open.
+</para>
+
+<figure id="fig-calculate-audio-gain">
+ <title>Calculating audio gain</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/calculate-audio-gain&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+For our example, put 5 in the first box and 7 in the second and click
+<guilabel>OK</guilabel>. DCP-o-matic will calculate the audio gain
+that it should apply to make this happen. Then you can re-make the
+DCP (this will be reasonably fast, as the video data will already have
+been done) and it should play back at the correct volume with 7 on
+your sound-rack fader.
+</para>
+
+<para>
+Current versions of DCP-o-matic only know about the Dolby CP750. If
+you use a different sound processor, and know the gain curve of its
+volume control, <ulink url="mailto:cth@carlh.net">get in
+touch</ulink>.
+</para>
+
+<para>
+‘Audio Delay’ is used to adjust the synchronisation
+between audio and video. A positive delay will move the audio later
+with respect to the video, and a negative delay will move it earlier.
+</para>
+
+<para>
+By default the <guilabel>Use content‘s audio</guilabel> button
+will be selected. This means that the DCP will use one of the
+soundtracks from your content file; you can select the soundtrack that
+you wish to use from the drop-down box.
+</para>
+
+<para>
+Note that if your content's audio is mono, DCP-o-matic will place it
+in the centre channel in the DCP.
+</para>
+
+<para>
+Alternatively, you can supply different sound files by clicking the
+<guilabel>Use external audio</guilabel> button and choosing a WAV file
+for any channels that you want to appear in the DCP. These files can
+be any bit depth and sampling rate, and will be re-sampled and
+bit-depth converted if required.
+</para>
+
+</section>
+<section>
+<title>Subtitles tab</title>
+
+<para>
+This tab contains settings related to subtitles in your DCP, as shown in <xref linkend="fig-subtitles-tab"/>.
+</para>
+
+<figure id="fig-subtitles-tab">
+ <title>Subtitle settings tab</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/subtitles-tab&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+DCP-o-matic will extract subtitles from the content, if present, and
+they can be ‘burnt into’ the DCP (that is, they are
+included in the image and not overlaid by the projector). Note that
+DVD and Blu-Ray subtitles are stored as bitmaps, so it is not possible
+(automatically) to use non-burnt-in subtitles with these sources.
+Select the <guilabel>With Subtitles</guilabel> checkbox to enable
+subtitles. The <guilabel>offset</guilabel> control moves the
+subtitles up and down the image, and the <guilabel>scale</guilabel>
+control changes their size.
+</para>
+
+<para>
+Future versions of DCP-o-matic will hopefully include the option to
+use text subtitles (as is the norm with most professionally-mastered
+DCPs).
+</para>
+
+</section>
+</section>
+
+<section>
+<title>Making the DCP</title>
+
+<para>
+Now that we have set everything up, choose <guilabel>Make
+DCP</guilabel> from the <guilabel>Jobs</guilabel> menu. DCP-o-matic
+will encode your DCP. This may take some time (many hours in some
+cases). While the job is in progress, DCP-o-matic will update you on
+how it is getting on with the progress bar in the bottom of its window, as shown in <xref linkend="fig-making-dcp"/>.
+</para>
+
+<figure id="fig-making-dcp">
+ <title>Making the DCP</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/making-dcp&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+When it has finished, the DCP will end up on your disk inside the
+film's directory. You can then copy this to a projector via a USB
+stick, hard-drive or network connection.
+</para>
+
+<para>
+Alternatively, if you have a projector or TMS that is accessible via
+SCP across your network, you can upload the content directly from
+DCP-o-matic. See <xref linkend="sec-tms-upload"/>.
+</para>
+
+</section>
+</chapter>
+
+
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<title>Creating a still-image DCP</title>
+
+<para>
+DCP-o-matic can also be used to create DCPs of a still image, perhaps
+for an advertisement or an on-screen announcement. This chapter shows you
+how to do it.
+</para>
+
+<para>
+As with video DCPs, the first step is to create a new
+‘Film’; select <guilabel>New</guilabel> from the
+<guilabel>File</guilabel> menu and the new film dialogue will open as
+shown in <xref linkend="fig-still-new-film"/>.
+</para>
+
+<figure id="fig-still-new-film">
+ <title>Dialogue box for creating a new film</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/still-new-film&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+Enter a name and click <guilabel>OK</guilabel>. Then we set up the
+content; click the content selector as before, and this time we will
+choose an image file, as shown in <xref
+linkend="fig-still-select-content-file"/>.
+</para>
+
+<figure id="fig-still-select-content-file">
+ <title>Selecting a still content file</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/still-select-content-file&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+Setting up for a still image DCP is somewhat simpler than for a video;
+the tabs are all the same, but many options are removed and a few are added.
+</para>
+
+<para>
+As with video, you can select a content type and the format (ratio)
+that your image should be presented in. It will be scaled and padded
+to fit the selected ratio, but in such a way that the pixel aspect
+ratio is preserved. In other words, the image will not be stretched,
+merely scaled; if you want to stretch your image, you will need to do
+so in a separate program before importing it into DCP-o-matic. You
+can also crop your image, if you so choose, and then set a duration
+(in seconds) that the image should appear on screen.
+</para>
+
+<para>
+Still-image DCPs can include sound; this can be added from the
+<guilabel>Audio</guilabel> tab. If your specified duration is shorter
+than the audio, the audio will be cut off at the duration; if it is
+longer, silence will be added after your audio.
+</para>
+
+<para>
+Finally, as with video, you can choose <guilabel>Make DCP</guilabel>
+from the <guilabel>Jobs</guilabel> menu to create your DCP. This will
+be much quicker than creating a video DCP, as DCP-o-matic only needs
+to encode a single frame which it can then repeat.
+</para>
+
+</chapter>
+
+
+<chapter xml:id="ch-preferences" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<title>Preferences</title>
+
+<para>
+DCP-o-matic provides a few preferences which can be used to modify its
+behaviour. This chapter explains those options.
+</para>
+
+<section>
+<title>The preferences dialogue</title>
+
+<para>
+The preferences dialogue is opened by choosing
+<guilabel>Preferences...</guilabel> from the <guilabel>Edit</guilabel>
+menu. The dialogue is shown in <xref linkend="fig-prefs"/>.
+</para>
+
+<figure id="fig-prefs">
+ <title>Preferences</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/prefs&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<section>
+<title>TMS setup</title>
+
+<para>
+The first part of the dialogue gives some options for specifying
+details about your TMS. If you do this, and your TMS accepts SSH
+connections, you can upload DCPs directly from DCP-o-matic to the TMS.
+This is discussed in <xref linkend="sec-tms-upload"/>.
+</para>
+
+<para>
+<guilabel>TMS IP address</guilabel> should be set to the IP address of
+your TMS, <guilabel>TMS target path</guilabel> to the place that DCPs
+should be uploaded to (which will be relative to the home directory of
+the SSH user). Finally, the user name and password are the
+credentials required to log into the TMS via SSH.
+</para>
+</section>
+
+<section>
+<title>Threads</title>
+
+<para>
+When DCP-o-matic is encoding DCPs it can use multiple parallel threads
+to speed things up. Set this value to the number of threads
+DCP-o-matic should use. This would typically be set to the number of
+processors (or processor cores) in your machine.
+</para>
+
+</section>
+
+<section>
+<title>Default directory for new films</title>
+
+<para>
+This is the directory which DCP-o-matic will suggest initially as a place to put new films.
+</para>
+
+</section>
+
+<section>
+<title>A/B options</title>
+
+<para>
+These options are for DCP-o-matic's special mode of making A/B
+comparison DCPs for checking the performance of video filters. Their
+use is described in <xref linkend="sec-ab"/>.
+</para>
+
+</section>
+
+<section>
+<title>Encoding servers</title>
+
+<para>
+If you have spare machines sitting around on your network not doing
+much, they can be pressed into service to speed up DCP encodes. This
+is done by running a small server program on the machine, which will
+encode video sent to it by the ‘master’ DCP-o-matic. This
+option is described in more detail in <xref linkend="sec-servers"/>.
+Use these preferences to specify the encoding servers that should be
+used.
+</para>
+
+</section>
+
+</section>
+</chapter>
+
+<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<title>Advanced topics</title>
+
+<para>This chapter describes some parts of DCP-o-matic that are
+probably not essential, but which you might find useful in some
+circumstances.
+</para>
+
+<section>
+<title>Filtering</title>
+
+<para>
+DCP-o-matic offers a variety of filters that can be applied to your
+video content. You can set up the filters by clicking the
+<guilabel>Edit</guilabel> button next to the filters entry in the
+setup area of the DCP-o-matic window; this opens the filters selector
+as shown in <xref linkend="fig-filters"/>.
+</para>
+
+<figure id="fig-filters">
+ <title>Filters selector</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="screenshots/filters&scs;"/>
+ </imageobject>
+ </mediaobject>
+</figure>
+
+<para>
+After changing the filters setup, you will need to regenerate the DCP
+to see the effect on the cinema screen. The preview in DCP-o-matic
+will update itself whenever filters are changed, though of course this
+image is much smaller and of lower resolution than a projected image!
+</para>
+
+</section>
+
+<section>
+<title>Scaling</title>
+
+<para>
+If your source material is not of the DCI-specified size, or if it
+uses non-square pixels, DCP-o-matic will need to scale it. The
+algorithm used to scale is set up by the <guilabel>Scaler</guilabel>
+entry in the film setup area. We think ‘Bicubic’ is the
+best all-round option, but tests are ongoing.
+</para>
+
+</section>
+
+<section xml:id="sec-tms-upload">
+<title>TMS upload</title>
+
+<para>
+If you have configured details of a TMS in the preferences dialogue
+(<xref linkend="ch-preferences"/>) you can upload a completed DCP
+straight to your TMS buy choosing <guilabel>Send DCP to TMS</guilabel>
+from the <guilabel>Jobs</guilabel> menu.
+</para>
+
+</section>
+
+
+<section xml:id="sec-ab">
+<title>A/B comparison</title>
+
+<para>
+When evaluating the effects of different filters or scalers on the
+image quality, A/B mode might be useful. In this mode, DCP-o-matic
+will generate a DCP where the left half of the image uses some
+‘reference’ filtering and scaling, and the right half of
+the image uses a different set of filters and a different scaler.
+This DCP can then be played back on a projector and the image quality
+evaluated.
+</para>
+
+<para>
+To enable A/B mode, click the A/B checkbox in the setup area of the
+DCP-o-matic window. When you generate your DCP, the left half of the
+screen will use the filters and scaler specified in the <xref
+linkend="ch-preferences">preferences</xref> dialogue, and the right
+half will use the filters and scaler specified in the film setup.
+</para>
+
+</section>
+
+<section xml:id="sec-servers">
+<title>Encoding servers</title>
+
+<para>
+One way to increase the speed of DCP encoding is to use more
+than one machine at the same time. An instance of DCP-o-matic can
+offload some of the time-consuming JPEG2000 encoding to any number of
+other machines on a network. To do this, one ‘master’
+machine runs DCP-o-matic, and the ‘server’ machines run
+a small program called ‘servomatic’.
+</para>
+
+<section>
+<title>Running the servers</title>
+
+<para>
+There are two options for the encoding server;
+<code>servomatic_cli</code>, which runs on the command line, and
+<code>servomatic_gui</code>, which has a simple GUI. The command line
+version is well-suited to headless servers, especially on Linux, and
+the GUI version works best on Windows where it will put an icon in the
+system tray.
+</para>
+
+<para>
+To run the command line version, simply enter:
+</para>
+
+<programlisting>
+servomatic_cli
+</programlisting>
+
+<para>
+at a command prompt. If you are running the program on a machine with
+a multi-core processor, you can run multiple parallel encoding threads
+by doing something like:
+</para>
+
+<programlisting>
+servomatic_cli -t 4
+</programlisting>
+
+<para>
+to run 4 threads in parallel.
+</para>
+
+<para>
+To run the GUI version on windows, run the ‘DCP-o-matic encode
+server’ from the start menu. An icon will appear in the system
+tray; right-click it to open a menu from whence you can quit the
+server or open a window to show its status.
+</para>
+
+</section>
+<section>
+<title>Setting up DCP-o-matic</title>
+
+<para>
+Once your servers are running, you need to tell your master
+DCP-o-matic instance about them. Start DCP-o-matic and open the
+<guilabel>Preferences</guilabel> dialog from the
+<guilabel>Edit</guilabel> menu. At the bottom of this dialog is a
+section where you can add, edit and remove encoding servers. For each
+encoding server you need only specify its IP address and the number of
+threads that it is running, so that DCP-o-matic knows how many
+parallel encode jobs to send to the server.
+</para>
+
+<para>
+Once this is done, any encodes that you start will split the workload
+up between the master machine and the servers.
+</para>
+
+</section>
+<section>
+<title>Some notes about encode servers</title>
+
+<para>
+DCP-o-matic does not mind if servers come and go; if a server
+disappears, DCP-o-matic will stop sending work to it, and will check
+it every minute or so in case it has come back online.
+</para>
+
+<para>
+You will probably find that using a 1Gb/s or faster network will
+provide a significant speed-up compared to a 100Mb/s network.
+</para>
+
+<para>
+Making changes to the server configuration in the master DCP-o-matic
+will have no effect while an encode is running; the changes will only
+be noticed when a new encode is started.
+</para>
+
+</section>
+</section>
+
+</chapter>
+
+
+</book>
+++ /dev/null
-<?xml version='1.0'?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:fo="http://www.w3.org/1999/XSL/Format"
- version="1.0">
-
-<!-- Our CSS -->
-<xsl:param name="html.stylesheet" select="'dvdomatic.css'"/>
-
-<!-- I can't fathom xmlto's logic with image scaling, so I've turned it off -->
-<xsl:param name="ignore.image.scaling" select="1"/>
-
-</xsl:stylesheet>
+++ /dev/null
-<?xml version='1.0' encoding="iso-8859-1"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version='1.0'>
-
-<!-- colour links in black -->
-<xsl:param name="latex.hyperparam">colorlinks,linkcolor=black,urlcolor=black</xsl:param>
-
-<!-- no revhistory table -->
-<xsl:param name="doc.collab.show">0</xsl:param>
-<xsl:param name="latex.output.revhistory">0</xsl:param>
-
-<!-- hack images to vaguely the right size -->
-<xsl:param name="imagedata.default.scale">scale=0.6</xsl:param>
-
-<!-- don't make too-ridiculous section numbers -->
-<xsl:param name="doc.section.depth">3</xsl:param>
-
-</xsl:stylesheet>
+++ /dev/null
-body {
- font-family: luxi sans, sans-serif;
- margin-left: 4em;
- margin-right: 4em;
- margin-top: 1em;
- margin-bottom: 1em;
- background-color: #E2E8EE;
-}
-
-div.sidebar {
- margin-left: 1em;
- margin-right: 1em;
- padding-left: 1em;
- padding-right: 1em;
- border-color: #000000;
- border-width: 2px;
- border-style: solid;
- background-color: #E2E8EE;
-}
+++ /dev/null
-%%
-%% This style is derivated from the docbook one
-%%
-\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{ardour}[2007/04/04 My DocBook Style]
-
-%% Just use the original package and pass the options
-\RequirePackageWithOptions{docbook}
-
-% Use a nice font
-\usepackage{lmodern}
-
-% Define \dbend as the dangerous bend sign
-\font\manual=manfnt
-\def\dbend{{\manual\char127}}
-
-% Redefine sidebar environment to use the dangerous bend style
-% Danger, Will Robinson!
-\def\sidebar{\begin{trivlist}\item[]\noindent%
-\begingroup\hangindent=2pc\hangafter=-2%\clubpenalty=10000%
-\def\par{\endgraf\endgroup}%
-\hbox to0pt{\hskip-\hangindent\dbend\hfill}\ignorespaces}
-\def\endsidebar{\par\end{trivlist}}
-
-
-% Futz with the title page; basically a copy of
-% /usr/share/texmf/tex/latex/dblatex/style/dbk_title.sty
-% with authors added.
-
-\def\DBKcover{
-\ifthenelse{\equal{\DBKedition}{}}{\def\edhead{}}{\def\edhead{Ed. \DBKedition}}
-
-\pagestyle{empty}
-
-% interligne double
-\setlength{\oldbaselineskip}{\baselineskip}
-\setlength{\baselineskip}{2\oldbaselineskip}
-\textsf{
-\vfill
-\vspace{2.5cm}
-\begin{center}
- \huge{\textbf{\DBKtitle}}\\ %
- \ \\ %
- \ \\ %
- \Large{\DBKauthor}\\ %
- \ifx\DBKsubtitle\relax\else%
- \underline{\ \ \ \ \ \ \ \ \ \ \ }\\ %
- \ \\ %
- \huge{\textbf{\DBKsubtitle}}\\ %
- \fi
-\end{center}
-\vfill
-\setlength{\baselineskip}{\oldbaselineskip}
-\hspace{1cm}
-\vspace{1cm}
-\begin{center}
-\begin{tabular}{p{7cm} p{7cm}}
-\Large{\DBKreference{} \edhead} & \\
-\end{tabular}
-\end{center}
-}
-
-% Format for the other pages
-\newpage
-\setlength{\baselineskip}{\oldbaselineskip}
-%\chead[]{\DBKcheadfront}
-\lfoot[]{}
-}
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE book [
-<!ENTITY % sgml.features "IGNORE">
-<!ENTITY % xml.features "INCLUDE">
-<!ENTITY % dbcent PUBLIC "-//OASIS//ENTITIES DocBook Character Entities V4.5//EN"
- "/usr/share/xml/docbook/schema/dtd/4.5/dbcentx.mod">
-%dbcent;
-<!ENTITY % extensions SYSTEM "extensions.ent">
-%extensions;
-]>
-<book xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-
-<bookinfo>
-<title>DVD-o-matic</title>
-<author><firstname>Carl</firstname><surname>Hetherington</surname></author>
-</bookinfo>
-
-<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-<title>Introduction</title>
-
-<para>
-Hello, and welcome to DVD-o-matic!
-</para>
-
-<section>
-<title>What is DVD-o-matic?</title>
-
-<para>
-DVD-o-matic is a program to generate <ulink
-url="http://en.wikipedia.org/wiki/Digital_Cinema_Package">Digital
-Cinema Packages</ulink> (DCPs) from DVDs, Blu-Rays, video files such as MP4
-and AVI, or still images. The resulting DCPs will play on modern digital
-cinema projectors.
-</para>
-
-<para>
-You might find it useful to make DVDs easier to present, to encode
-independently-shot feature films, or to generate local advertising for
-your cinema.
-</para>
-
-</section>
-
-<section>
-<title>Licence</title>
-
-<para>
-DVD-o-matic is licensed under the <ulink url="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GNU GPL</ulink>.
-</para>
-
-</section>
-
-</chapter>
-
-<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-<title>Installation</title>
-
-<section>
-<title>Windows</title>
-
-<para>
-To install DVD-o-matic on Windows, simply download the installer from
-<ulink url="http://carlh.net/software/dvdomatic">http://carlh.net</ulink>
-and double-click it. Click through the installer wizard, and
-DVD-o-matic will be installed onto your machine.
-</para>
-
-<para>
-If you are using a 32-bit version of Windows, you will need the 32-bit
-installer. For 64-bit Windows, either installer will work, but I
-suggest you used the 64-bit version as it will allow DVD-o-matic to
-use more memory. You may find that DVD-o-matic crashes if you run
-many parallel encoding threads (more than 4) on the 32-bit
-version.
-</para>
-
-</section>
-
-<section>
-<title>Ubuntu Linux</title>
-
-<para>
-You can install DVD-o-matic on Ubuntu 12.04 (‘Precise
-Pangolin’) or 12.10 (‘Quantal Quetzal’) using
-<code>.deb</code> packages: download the appropriate package from
-<ulink
-url="http://carlh.net/software/dvdomatic">http://carlh.net</ulink> and
-double-click it. Ubuntu will install the necessary bits and pieces
-and set DVD-o-matic up for you.
-</para>
-
-</section>
-
-<section>
-<title>Other Linux distributions</title>
-
-<para>
-Installation on non-Ubuntu Linux is currently a little involved, as
-there are no packages available (yet); you will have to compile it
-from source. If you are using a non-Ubuntu distribution, do let me
-know via the <ulink url="mailto:dvdomatic@carlh.net">mailing
-list</ulink> and I will see about building some packages.
-</para>
-
-<para>
-The following dependencies are required:
-<itemizedlist>
-<listitem><ulink url="http://ffmpeg.org/">FFmpeg</ulink></listitem>
-<listitem><ulink url="http://www.mega-nerd.com/libsndfile/">libsndfile</ulink></listitem>
-<listitem><ulink url="http://www.openssl.org/">OpenSSL</ulink></listitem>
-<listitem><ulink url="http://www.openjpeg.org/">libopenjpeg</ulink></listitem>
-<listitem><ulink url="http://www.imagemagick.org/script/index.php">ImageMagick</ulink></listitem>
-<listitem><ulink url="http://www.boost.org/">Boost</ulink></listitem>
-<listitem><ulink url="http://www.libssh.org/">libssh</ulink></listitem>
-<listitem><ulink url="http://www.gtk.org/">GTK</ulink></listitem>
-<listitem><ulink url="http://www.wxwidgets.org/">wxWidgets</ulink></listitem>
-<listitem><ulink url="http://carlh.net/software/libdcp/">libdcp</ulink></listitem>
-</itemizedlist>
-</para>
-
-<para>
-Once you have installed the development packages for the dependencies,
-download the source code from <ulink
-url="http://carlh.net/software/dvdomatic">http://carlh.net</ulink>,
-unpack it and run the following commands from inside the source
-directory:
-</para>
-
-<programlisting>
-./waf configure
-./waf build
-sudo ./waf install
-</programlisting>
-
-<para>
-With any luck, this will build and install DVD-o-matic on your system. To run it, enter:
-</para>
-
-<programlisting>
-dvdomatic
-</programlisting>
-
-<para>
-in a shell.
-</para>
-
-</section>
-</chapter>
-
-<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-<title>Creating a video DCP</title>
-
-<para>
-In this chapter we will see how to create a video DCP using
-DVD-o-matic. We will gloss over some of the finer details, which are
-explained in later chapters.
-</para>
-
-<section>
-<title>Creating a new film</title>
-
-<para>
-Let's make a very simple DCP to see how DVD-o-matic works. First, we
-need some content. Download the low-resolution trailer for the open
-movie <ulink url="http://sintel.org/">Sintel</ulink> from <ulink
-url="http://ftp.nluug.nl/ftp/graphics/blender/apricot/trailer/Sintel_Trailer1.480p.DivX_Plus_HD.mkv">their
-website</ulink>. Generally, of course, one would want to use the
-highest-resolution material available, but for this test we will use
-the low-resolution version to save everyone's bandwidth bills.
-</para>
-
-<para>
-Now, start DVD-o-matic and its window will open. First, we will
-create a new ‘film’. A ‘film’ is how DVD-o-matic refers to
-a piece of content, along with some settings, which we will make into
-a DCP. DVD-o-matic stores its data in a folder on your disk while it
-creates the DCP. You can create a new film by selecting
-<guilabel>New</guilabel> from the <guilabel>File</guilabel> menu, as
-shown in <xref linkend="fig-file-new"/>.
-</para>
-
-<figure id="fig-file-new">
- <title>Creating a new film</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/file-new&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-This will open a dialogue box for the new film, as shown in <xref
-linkend="fig-video-new-film"/>.
-</para>
-
-<figure id="fig-video-new-film">
- <title>Dialogue box for creating a new film</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/video-new-film&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-In this dialogue box you can choose a name for the film. This will be
-used to name the folder to store its data in, and also as the initial
-name for the DCP itself. You can also choose whereabouts you want to create
-the film. In the example from the figure, DVD-o-matic will create a
-folder called ‘DCP Test’ inside my home folder (carl) into which it
-will write its working files.
-</para>
-
-<para>
-If you always create your DCPs in a particular folder, you can use
-DVD-o-matic's <guilabel>Preferences</guilabel> to make life a little
-easier by setting the default folder that DVD-o-matic will offer in this dialogue.
-See <xref linkend="ch-preferences"/>.
-</para>
-
-</section>
-
-<section>
-<title>Selecting content</title>
-
-<para>
-The next step is to set the content that you want to use. Click the
-content selector, as shown in <xref
-linkend="fig-click-content-selector"/>, and a file chooser will
-open for you to select the content file to use, as shown in <xref
-linkend="fig-video-select-content-file"/>.
-</para>
-
-<figure id="fig-click-content-selector">
- <title>Opening the content selector</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/click-content-selector&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<figure id="fig-video-select-content-file">
- <title>Selecting a video content file</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/video-select-content-file&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-Select your content file and click <guilabel>Open</guilabel>. In this
-case we are using the Sintel trailer that we downloaded earlier.
-</para>
-
-<para>
-When you do this, DVD-o-matic will take a look at your file. After a
-short while (when the progress bar at the bottom right of the window
-has finished), you can look through your content using the slider to
-the right of the window, as shown in <xref linkend="fig-examine-thumbs"/>.
-</para>
-
-<figure id="fig-examine-thumbs">
- <title>Examining the content</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/examine-thumbs&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-Dragging the slider will move through your video. You can also click
-the <guilabel>Play</guilabel> button to play the content back. Note
-that there will be no sound, and playback might not be entirely
-accurate (it may be slightly slower or faster than it should be, for
-example). This player is really only intended for brief inspection of
-content; if you need to check it more thoroughly, use another player
-such as <ulink url="http://projects.gnome.org/totem/index.html">Totem</ulink>, <ulink url="http://www.mplayerhq.hu/design7/news.html">mplayer</ulink> or <ulink url="http://www.videolan.org/vlc/index.html">VLC</ulink>.
-</para>
-
-</section>
-
-<section>
-<title>Setting up</title>
-
-<para>
-Now there are a few things to set up to describe how the DCP should be
-created. The settings are divided into four tabs: film, video, audio and subtitles.
-</para>
-
-<section>
-<title>Film tab</title>
-
-<para>
-The ‘film’ tab contains settings that pertain to the whole film, as shown in <xref linkend="fig-film-tab"/>.
-</para>
-
-<figure id="fig-film-tab">
- <title>Film settings tab</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/film-tab&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-The first thing here is the name. This is generally set to the title
-of the film that is being encoded. If <guilabel>Use DCI
-name</guilabel> is not ticked, the name that you specify will be used
-as-is for the name of the DCP. If <guilabel>Use DCI name</guilabel>
-is ticked, the name that you enter will be used as part of a
-DCI-compliant name.
-</para>
-
-<para>
-Underneath the name field is a preview of the name that the DCP will
-get. To use a DCI-compliant name, tick the <guilabel>Use DCI
-name</guilabel> checkbox. The DCI name will be composed using details
-of your content's soundtrack, the current date and other things that
-can be specified in the DCI name details dialogue box, which you can
-open by clicking on the <guilabel>Details</guilabel> button.
-</para>
-
-<para>
-If the DCP name is long, it may not all be visible. You can see the
-full name by hovering the mouse pointer over the partial name.
-</para>
-
-<para>
-The <guilabel>Trust content's header</guilabel> button starts off
-checked, and this means that DVD-o-matic will use the content's header
-information to determine its length. If, for some reason, this header
-length is wrong, uncheck the <guilabel>Trust content's
-header</guilabel> button and DVD-o-matic will run through the content
-to find its exact length. This may take a while for large pieces of content.
-</para>
-
-<para>
-Next up is the content type. This can be
-‘feature’, ‘trailer’ or whatever; select the
-required type from the drop-down list.
-</para>
-
-<para>
-The <guilabel>trim frames</guilabel> settings allow you to trim frames
-from the beginning and end of the content; any trimmed frames will not
-be included in the DCP.
-</para>
-
-</section>
-
-<section>
-<title>Video tab</title>
-
-<para>
-This tab contains settings related to the picture in your DCP, as shown in <xref linkend="fig-video-tab"/>.
-</para>
-
-<figure id="fig-video-tab">
- <title>Video settings tab</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/video-tab&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-The first option on this tab is the format. This will govern the
-shape that DVD-o-matic will make your image into. Select the aspect
-ratio that your content should be presented in. The ‘4:3 within
-Flat’ and ‘16:9 within Flat’ settings will put the
-image at the specified ratio within a Flat (1.85:1) frame, so that you
-can project the DCP using your projector's Flat preset.
-</para>
-
-<para>
-The remaining options can often be left alone, but may sometimes be
-useful. The ‘crop’ settings can be used to crop your
-content, which can be used to remove black borders from round the
-edges of DVD images, for example. The specified number of pixels will
-be trimmed from each edge, and the content image in the right of the
-window will be updated to show the effect of the crop.
-</para>
-
-<para>
-The ‘filters’ settings allow you to apply various video
-filters to the image. These may be useful to try to improve
-poor-quality sources like DVDs. We will discuss filtering later in the manual.
-<!-- XXX: link -->
-</para>
-
-<para>
-The ‘scaler’ is the method that will be used to scale up
-your content to the required size for the DCP, if required. We will
-discuss the options in more detail later; Bicubic is a fine choice in
-most situations.
-<!-- XXX: link -->
-</para>
-
-<para>
-The ‘colour look-up table’ specifies the colour space that
-your input content will be expected to be in. If in doubt, leave it
-set to ‘sRGB’.
-</para>
-
-<para>
-Finally, the ‘JPEG2000 bandwidth’ setting changes how big the final
-image files used within the DCP will be. Larger numbers will give
-better quality, but correspondingly larger DCPs. The bandwidth can be
-between 50 and 250 megabits per second (MBps).
-</para>
-
-</section>
-
-<section>
-<title>Audio tab</title>
-
-<para>
-This tab contains settings related to the sound in your DCP, as shown in <xref linkend="fig-audio-tab"/>.
-</para>
-
-<figure id="fig-audio-tab">
- <title>Audio settings tab</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/audio-tab&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-
-<para>
-‘Audio Gain’ is used to alter the volume of the
-soundtrack. The specified gain (in dB) will be applied to each sound
-channel before it is written to the DCP.
-</para>
-
-<para>
-If you use a sound processor that DVD-o-matic knows about, it can help
-you calculate changes in gain that you should apply. Say, for
-example, that you make a test DCP and find that you have to run it at
-volume 5 instead of volume 7 to get a good sound level in the screen.
-If this is the case, click the <guilabel>Calculate...</guilabel>
-button next to the audio gain entry, and the dialogue box in <xref
-linkend="fig-calculate-audio-gain"/> will open.
-</para>
-
-<figure id="fig-calculate-audio-gain">
- <title>Calculating audio gain</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/calculate-audio-gain&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-For our example, put 5 in the first box and 7 in the second and click
-<guilabel>OK</guilabel>. DVD-o-matic will calculate the audio gain
-that it should apply to make this happen. Then you can re-make the
-DCP (this will be reasonably fast, as the video data will already have
-been done) and it should play back at the correct volume with 7 on
-your sound-rack fader.
-</para>
-
-<para>
-Current versions of DVD-o-matic only know about the Dolby CP750. If
-you use a different sound processor, and know the gain curve of its
-volume control, <ulink url="mailto:cth@carlh.net">get in
-touch</ulink>.
-</para>
-
-<para>
-‘Audio Delay’ is used to adjust the synchronisation
-between audio and video. A positive delay will move the audio later
-with respect to the video, and a negative delay will move it earlier.
-</para>
-
-<para>
-By default the <guilabel>Use content‘s audio</guilabel> button
-will be selected. This means that the DCP will use one of the
-soundtracks from your content file; you can select the soundtrack that
-you wish to use from the drop-down box.
-</para>
-
-<para>
-Note that if your content's audio is mono, DVD-o-matic will place it
-in the centre channel in the DCP.
-</para>
-
-<para>
-Alternatively, you can supply different sound files by clicking the
-<guilabel>Use external audio</guilabel> button and choosing a WAV file
-for any channels that you want to appear in the DCP. These files can
-be any bit depth and sampling rate, and will be re-sampled and
-bit-depth converted if required.
-</para>
-
-</section>
-<section>
-<title>Subtitles tab</title>
-
-<para>
-This tab contains settings related to subtitles in your DCP, as shown in <xref linkend="fig-subtitles-tab"/>.
-</para>
-
-<figure id="fig-subtitles-tab">
- <title>Subtitle settings tab</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/subtitles-tab&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-DVD-o-matic will extract subtitles from the content, if present, and
-they can be ‘burnt into’ the DCP (that is, they are
-included in the image and not overlaid by the projector). Note that
-DVD and Blu-Ray subtitles are stored as bitmaps, so it is not possible
-(automatically) to use non-burnt-in subtitles with these sources.
-Select the <guilabel>With Subtitles</guilabel> checkbox to enable
-subtitles. The <guilabel>offset</guilabel> control moves the
-subtitles up and down the image, and the <guilabel>scale</guilabel>
-control changes their size.
-</para>
-
-<para>
-Future versions of DVD-o-matic will hopefully include the option to
-use text subtitles (as is the norm with most professionally-mastered
-DCPs).
-</para>
-
-</section>
-</section>
-
-<section>
-<title>Making the DCP</title>
-
-<para>
-Now that we have set everything up, choose <guilabel>Make
-DCP</guilabel> from the <guilabel>Jobs</guilabel> menu. DVD-o-matic
-will encode your DCP. This may take some time (many hours in some
-cases). While the job is in progress, DVD-o-matic will update you on
-how it is getting on with the progress bar in the bottom of its window, as shown in <xref linkend="fig-making-dcp"/>.
-</para>
-
-<figure id="fig-making-dcp">
- <title>Making the DCP</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/making-dcp&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-When it has finished, the DCP will end up on your disk inside the
-film's directory. You can then copy this to a projector via a USB
-stick, hard-drive or network connection.
-</para>
-
-<para>
-Alternatively, if you have a projector or TMS that is accessible via
-SCP across your network, you can upload the content directly from
-DVD-o-matic. See <xref linkend="sec-tms-upload"/>.
-</para>
-
-</section>
-</chapter>
-
-
-<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-<title>Creating a still-image DCP</title>
-
-<para>
-DVD-o-matic can also be used to create DCPs of a still image, perhaps
-for an advertisement or an on-screen announcement. This chapter shows you
-how to do it.
-</para>
-
-<para>
-As with video DCPs, the first step is to create a new
-‘Film’; select <guilabel>New</guilabel> from the
-<guilabel>File</guilabel> menu and the new film dialogue will open as
-shown in <xref linkend="fig-still-new-film"/>.
-</para>
-
-<figure id="fig-still-new-film">
- <title>Dialogue box for creating a new film</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/still-new-film&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-Enter a name and click <guilabel>OK</guilabel>. Then we set up the
-content; click the content selector as before, and this time we will
-choose an image file, as shown in <xref
-linkend="fig-still-select-content-file"/>.
-</para>
-
-<figure id="fig-still-select-content-file">
- <title>Selecting a still content file</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/still-select-content-file&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-Setting up for a still image DCP is somewhat simpler than for a video;
-the tabs are all the same, but many options are removed and a few are added.
-</para>
-
-<para>
-As with video, you can select a content type and the format (ratio)
-that your image should be presented in. It will be scaled and padded
-to fit the selected ratio, but in such a way that the pixel aspect
-ratio is preserved. In other words, the image will not be stretched,
-merely scaled; if you want to stretch your image, you will need to do
-so in a separate program before importing it into DVD-o-matic. You
-can also crop your image, if you so choose, and then set a duration
-(in seconds) that the image should appear on screen.
-</para>
-
-<para>
-Still-image DCPs can include sound; this can be added from the
-<guilabel>Audio</guilabel> tab. If your specified duration is shorter
-than the audio, the audio will be cut off at the duration; if it is
-longer, silence will be added after your audio.
-</para>
-
-<para>
-Finally, as with video, you can choose <guilabel>Make DCP</guilabel>
-from the <guilabel>Jobs</guilabel> menu to create your DCP. This will
-be much quicker than creating a video DCP, as DVD-o-matic only needs
-to encode a single frame which it can then repeat.
-</para>
-
-</chapter>
-
-
-<chapter xml:id="ch-preferences" xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-<title>Preferences</title>
-
-<para>
-DVD-o-matic provides a few preferences which can be used to modify its
-behaviour. This chapter explains those options.
-</para>
-
-<section>
-<title>The preferences dialogue</title>
-
-<para>
-The preferences dialogue is opened by choosing
-<guilabel>Preferences...</guilabel> from the <guilabel>Edit</guilabel>
-menu. The dialogue is shown in <xref linkend="fig-prefs"/>.
-</para>
-
-<figure id="fig-prefs">
- <title>Preferences</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/prefs&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<section>
-<title>TMS setup</title>
-
-<para>
-The first part of the dialogue gives some options for specifying
-details about your TMS. If you do this, and your TMS accepts SSH
-connections, you can upload DCPs directly from DVD-o-matic to the TMS.
-This is discussed in <xref linkend="sec-tms-upload"/>.
-</para>
-
-<para>
-<guilabel>TMS IP address</guilabel> should be set to the IP address of
-your TMS, <guilabel>TMS target path</guilabel> to the place that DCPs
-should be uploaded to (which will be relative to the home directory of
-the SSH user). Finally, the user name and password are the
-credentials required to log into the TMS via SSH.
-</para>
-</section>
-
-<section>
-<title>Threads</title>
-
-<para>
-When DVD-o-matic is encoding DCPs it can use multiple parallel threads
-to speed things up. Set this value to the number of threads
-DVD-o-matic should use. This would typically be set to the number of
-processors (or processor cores) in your machine.
-</para>
-
-</section>
-
-<section>
-<title>Default directory for new films</title>
-
-<para>
-This is the directory which DVD-o-matic will suggest initially as a place to put new films.
-</para>
-
-</section>
-
-<section>
-<title>A/B options</title>
-
-<para>
-These options are for DVD-o-matic's special mode of making A/B
-comparison DCPs for checking the performance of video filters. Their
-use is described in <xref linkend="sec-ab"/>.
-</para>
-
-</section>
-
-<section>
-<title>Encoding servers</title>
-
-<para>
-If you have spare machines sitting around on your network not doing
-much, they can be pressed into service to speed up DCP encodes. This
-is done by running a small server program on the machine, which will
-encode video sent to it by the ‘master’ DVD-o-matic. This
-option is described in more detail in <xref linkend="sec-servers"/>.
-Use these preferences to specify the encoding servers that should be
-used.
-</para>
-
-</section>
-
-</section>
-</chapter>
-
-<chapter xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
-<title>Advanced topics</title>
-
-<para>This chapter describes some parts of DVD-o-matic that are
-probably not essential, but which you might find useful in some
-circumstances.
-</para>
-
-<section>
-<title>Filtering</title>
-
-<para>
-DVD-o-matic offers a variety of filters that can be applied to your
-video content. You can set up the filters by clicking the
-<guilabel>Edit</guilabel> button next to the filters entry in the
-setup area of the DVD-o-matic window; this opens the filters selector
-as shown in <xref linkend="fig-filters"/>.
-</para>
-
-<figure id="fig-filters">
- <title>Filters selector</title>
- <mediaobject>
- <imageobject>
- <imagedata fileref="screenshots/filters&scs;"/>
- </imageobject>
- </mediaobject>
-</figure>
-
-<para>
-After changing the filters setup, you will need to regenerate the DCP
-to see the effect on the cinema screen. The preview in DVD-o-matic
-will update itself whenever filters are changed, though of course this
-image is much smaller and of lower resolution than a projected image!
-</para>
-
-</section>
-
-<section>
-<title>Scaling</title>
-
-<para>
-If your source material is not of the DCI-specified size, or if it
-uses non-square pixels, DVD-o-matic will need to scale it. The
-algorithm used to scale is set up by the <guilabel>Scaler</guilabel>
-entry in the film setup area. We think ‘Bicubic’ is the
-best all-round option, but tests are ongoing.
-</para>
-
-</section>
-
-<section xml:id="sec-tms-upload">
-<title>TMS upload</title>
-
-<para>
-If you have configured details of a TMS in the preferences dialogue
-(<xref linkend="ch-preferences"/>) you can upload a completed DCP
-straight to your TMS buy choosing <guilabel>Send DCP to TMS</guilabel>
-from the <guilabel>Jobs</guilabel> menu.
-</para>
-
-</section>
-
-
-<section xml:id="sec-ab">
-<title>A/B comparison</title>
-
-<para>
-When evaluating the effects of different filters or scalers on the
-image quality, A/B mode might be useful. In this mode, DVD-o-matic
-will generate a DCP where the left half of the image uses some
-‘reference’ filtering and scaling, and the right half of
-the image uses a different set of filters and a different scaler.
-This DCP can then be played back on a projector and the image quality
-evaluated.
-</para>
-
-<para>
-To enable A/B mode, click the A/B checkbox in the setup area of the
-DVD-o-matic window. When you generate your DCP, the left half of the
-screen will use the filters and scaler specified in the <xref
-linkend="ch-preferences">preferences</xref> dialogue, and the right
-half will use the filters and scaler specified in the film setup.
-</para>
-
-</section>
-
-<section xml:id="sec-servers">
-<title>Encoding servers</title>
-
-<para>
-One way to increase the speed of DCP encoding is to use more
-than one machine at the same time. An instance of DVD-o-matic can
-offload some of the time-consuming JPEG2000 encoding to any number of
-other machines on a network. To do this, one ‘master’
-machine runs DVD-o-matic, and the ‘server’ machines run
-a small program called ‘servomatic’.
-</para>
-
-<section>
-<title>Running the servers</title>
-
-<para>
-There are two options for the encoding server;
-<code>servomatic_cli</code>, which runs on the command line, and
-<code>servomatic_gui</code>, which has a simple GUI. The command line
-version is well-suited to headless servers, especially on Linux, and
-the GUI version works best on Windows where it will put an icon in the
-system tray.
-</para>
-
-<para>
-To run the command line version, simply enter:
-</para>
-
-<programlisting>
-servomatic_cli
-</programlisting>
-
-<para>
-at a command prompt. If you are running the program on a machine with
-a multi-core processor, you can run multiple parallel encoding threads
-by doing something like:
-</para>
-
-<programlisting>
-servomatic_cli -t 4
-</programlisting>
-
-<para>
-to run 4 threads in parallel.
-</para>
-
-<para>
-To run the GUI version on windows, run the ‘DVD-o-matic encode
-server’ from the start menu. An icon will appear in the system
-tray; right-click it to open a menu from whence you can quit the
-server or open a window to show its status.
-</para>
-
-</section>
-<section>
-<title>Setting up DVD-o-matic</title>
-
-<para>
-Once your servers are running, you need to tell your master
-DVD-o-matic instance about them. Start DVD-o-matic and open the
-<guilabel>Preferences</guilabel> dialog from the
-<guilabel>Edit</guilabel> menu. At the bottom of this dialog is a
-section where you can add, edit and remove encoding servers. For each
-encoding server you need only specify its IP address and the number of
-threads that it is running, so that DVD-o-matic knows how many
-parallel encode jobs to send to the server.
-</para>
-
-<para>
-Once this is done, any encodes that you start will split the workload
-up between the master machine and the servers.
-</para>
-
-</section>
-<section>
-<title>Some notes about encode servers</title>
-
-<para>
-DVD-o-matic does not mind if servers come and go; if a server
-disappears, DVD-o-matic will stop sending work to it, and will check
-it every minute or so in case it has come back online.
-</para>
-
-<para>
-You will probably find that using a 1Gb/s or faster network will
-provide a significant speed-up compared to a 100Mb/s network.
-</para>
-
-<para>
-Making changes to the server configuration in the master DVD-o-matic
-will have no effect while an encode is running; the changes will only
-be noticed when a new encode is started.
-</para>
-
-</section>
-</section>
-
-</chapter>
-
-
-</book>
+++ /dev/null
-[Desktop Entry]
-Encoding=UTF-8
-Version=1.0
-Type=Application
-Terminal=false
-Exec=@PREFIX@/bin/dvdomatic
-Name=DVD-o-matic
-Icon=dvdomatic
-Comment=DCP generator
-Categories=AudioVideo;Video
+++ /dev/null
-
-LEFT_SCREEN_WIDTH = 1366
+++ /dev/null
-#!/usr/bin/python
-
-import os
-import operator
-import traceback
-import pygtk
-pygtk.require('2.0')
-import gtk
-import glib
-import gobject
-import film
-import film_view
-import player
-import screens
-import thumbs
-import ratio
-import util
-
-FILM_DIRECTORY = '/home/carl/DVD'
-
-current_player = None
-films = []
-inhibit_selection_update = False
-
-def find_films():
- global films
- films = []
- for root, dirs, files in os.walk(FILM_DIRECTORY):
- for name in files:
- if os.path.basename(name) == 'info':
- films.append(film.Film(os.path.join(root, os.path.dirname(name))))
-
- films.sort(key = operator.attrgetter('name'))
-
-def update_film_store():
- global film_store
- global films
- global inhibit_selection_update
- inhibit_selection_update = True
- film_store.clear()
- for f in films:
- film_store.append([f.name])
- inhibit_selection_update = False
-
-def update_screen_store(screen_store, screens):
- screen_store.clear()
- for s in screens.screens:
- screen_store.append([s.name])
-
-def create_film_tree_view(film_store):
- view = gtk.TreeView(film_store)
- column = gtk.TreeViewColumn()
- view.append_column(column)
- cell = gtk.CellRendererText()
- column.pack_start(cell)
- column.add_attribute(cell, 'text', 0)
- view.get_selection().set_mode(gtk.SELECTION_SINGLE)
- return view
-
-def create_screen_view(screen_store):
- view = gtk.TreeView(screen_store)
- column = gtk.TreeViewColumn()
- view.append_column(column)
- cell = gtk.CellRendererText()
- column.pack_start(cell)
- column.add_attribute(cell, 'text', 0)
- view.get_selection().set_mode(gtk.SELECTION_SINGLE)
- return view
-
-def get_selected_film():
- (model, iter) = film_tree_view.get_selection().get_selected()
-
- for f in films:
- if f.name == model.get(iter, 0)[0]:
- return f
-
- return None
-
-# @return Selected screen name
-def get_selected_screen():
- (model, iter) = screen_view.get_selection().get_selected()
- return model.get(iter, 0)[0]
-
-def film_selected(selection):
- if inhibit_selection_update:
- return
-
- film_view.set(get_selected_film())
- check_for_playability()
-
-def screen_selected(selection):
- check_for_playability()
-
-def check_for_playability():
- f = get_selected_film()
- if screens.get_format(get_selected_screen(), f.ratio) is not None:
- play_button.set_label("Play")
- play_button.set_sensitive(True)
- else:
- play_button.set_label("Cannot play: no setting for %s on screen %s" % (ratio.find(f.ratio).name(), get_selected_screen()))
- play_button.set_sensitive(False)
-
-def update_status(s):
- global current_player
- if current_player is None:
- s.set_text("Not playing")
- return True
-
- position = current_player.time_pos
- if position is None:
- return True
- position_hms = util.s_to_hms(position)
-
- length = current_player.length
- if length is None:
- return True
-
- remaining = length - position
- remaining_hms = util.s_to_hms(remaining)
- s.set_text("Playing: %d:%02d:%02d, %d:%02d:%02d remaining" % (position_hms[0], position_hms[1], position_hms[2], remaining_hms[0], remaining_hms[1], remaining_hms[2]))
- return True
-
-def play_clicked(b):
- global current_player
- f = get_selected_film()
- current_player = player.get_player(f, screens.get_format(get_selected_screen(), f.ratio))
- print current_player.args
-
-def stop_clicked(b):
- global current_player
- if current_player is not None:
- current_player.stop()
- current_player = None
-
-def add_film_clicked(b):
- global films
- c = gtk.FileChooserDialog("New Film", main_window, gtk.FILE_CHOOSER_ACTION_CREATE_FOLDER, (("Add", gtk.RESPONSE_OK)))
- c.set_current_folder(FILM_DIRECTORY)
- if c.run() == gtk.RESPONSE_OK:
- f = film.Film()
- f.data = c.get_filename()
- f.name = os.path.basename(c.get_filename())
- f.write()
- find_films()
- update_film_store()
- c.hide()
-
- for i in range(0, len(films)):
- if films[i].name == f.name:
- film_tree_view.get_selection().select_path((i, ))
-
-main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
-main_window.set_title("DVD-o-matic")
-main_window.maximize()
-
-main_hbox = gtk.HBox()
-main_hbox.set_spacing(12)
-main_hbox.set_border_width(12)
-main_window.add(main_hbox)
-
-find_films()
-film_view = film_view.FilmView(main_window)
-screens = screens.Screens("screens")
-
-left_vbox = gtk.VBox()
-left_vbox.set_spacing(12)
-main_hbox.pack_start(left_vbox, False, False)
-right_vbox = gtk.VBox()
-right_vbox.set_spacing(12)
-main_hbox.pack_start(right_vbox)
-
-film_store = gtk.ListStore(gobject.TYPE_STRING)
-update_film_store()
-
-film_tree_view = create_film_tree_view(film_store)
-left_vbox.pack_start(film_tree_view, True, True)
-film_tree_view.get_selection().select_path((0, ))
-film_tree_view.get_selection().connect("changed", film_selected)
-
-add_film_button = gtk.Button(stock = gtk.STOCK_ADD)
-left_vbox.pack_start(add_film_button, False, False)
-add_film_button.connect("clicked", add_film_clicked)
-
-screen_store = gtk.ListStore(gobject.TYPE_STRING)
-update_screen_store(screen_store, screens)
-
-screen_view = create_screen_view(screen_store)
-left_vbox.pack_start(screen_view, False, False)
-screen_view.get_selection().select_path((0, ))
-screen_view.get_selection().connect("changed", screen_selected)
-
-right_vbox.pack_start(film_view, False, False)
-film_view.set(films[0])
-
-play_button = gtk.Button("Play")
-right_vbox.pack_start(play_button)
-play_button.connect("clicked", play_clicked)
-
-stop_button = gtk.Button("Stop")
-right_vbox.pack_start(stop_button)
-stop_button.connect("clicked", stop_clicked)
-
-status = gtk.Label()
-right_vbox.pack_start(status, False, False)
-glib.timeout_add_seconds(1, update_status, status)
-
-check_for_playability()
-main_window.show_all()
-gtk.main()
+++ /dev/null
-import os
-import subprocess
-import shlex
-import shutil
-import player
-
-class Film:
- def __init__(self, data = None):
- # File or directory containing content
- self.content = None
- # True if content is in DVD format
- self.dvd = False
- # DVD title number
- self.dvd_title = 1
- # Directory containing metadata
- self.data = None
- # Film name
- self.name = None
- # Number of pixels by which to crop the content from each edge
- self.left_crop = 0
- self.top_crop = 0
- self.right_crop = 0
- self.bottom_crop = 0
- # Use deinterlacing filter
- self.deinterlace = False
- # Target ratio
- self.ratio = 1.85
- # Audio stream ID to play
- self.aid = None
-
- self.width = None
- self.height = None
- self.fps = None
- self.length = None
-
- if data is not None:
- self.data = data
- f = open(os.path.join(self.data, 'info'), 'r')
- while 1:
- l = f.readline()
- if l == '':
- break
-
- d = l.strip()
-
- s = d.find(' ')
- if s != -1:
- key = d[:s]
- value = d[s+1:]
-
- if key == 'name':
- self.name = value
- elif key == 'content':
- self.content = value
- elif key == 'dvd':
- self.dvd = int(value) == 1
- elif key == 'dvd_title':
- self.dvd_title = int(value)
- elif key == 'left_crop':
- self.left_crop = int(value)
- elif key == 'top_crop':
- self.top_crop = int(value)
- elif key == 'right_crop':
- self.right_crop = int(value)
- elif key == 'bottom_crop':
- self.bottom_crop = int(value)
- elif key == 'deinterlace':
- self.deinterlace = int(value) == 1
- elif key == 'ratio':
- self.ratio = float(value)
- elif key == 'aid':
- self.aid = int(value)
- elif key == 'width':
- self.width = int(value)
- elif key == 'height':
- self.height = int(value)
- elif key == 'fps':
- self.fps = float(value)
- elif key == 'length':
- self.length = float(value)
-
- if self.width is None or self.height is None or self.fps is None or self.length is None:
- self.update_content_metadata()
-
- def write(self):
- try:
- os.mkdir(self.data)
- except OSError:
- pass
-
- f = open(os.path.join(self.data, 'info'), 'w')
- self.write_datum(f, 'name', self.name)
- self.write_datum(f, 'content', self.content)
- self.write_datum(f, 'dvd', int(self.dvd))
- self.write_datum(f, 'dvd_title', self.dvd_title)
- self.write_datum(f, 'left_crop', self.left_crop)
- self.write_datum(f, 'top_crop', self.top_crop)
- self.write_datum(f, 'right_crop', self.right_crop)
- self.write_datum(f, 'bottom_crop', self.bottom_crop)
- self.write_datum(f, 'deinterlace', int(self.deinterlace))
- self.write_datum(f, 'ratio', self.ratio)
- self.write_datum(f, 'aid', self.aid)
- self.write_datum(f, 'width', self.width)
- self.write_datum(f, 'height', self.height)
- self.write_datum(f, 'fps', self.fps)
- self.write_datum(f, 'length', self.length)
-
- def write_datum(self, f, key, value):
- if value is not None:
- print >>f,'%s %s' % (key, str(value))
-
- def thumbs_dir(self):
- t = os.path.join(self.data, 'thumbs')
-
- try:
- os.mkdir(t)
- except OSError:
- pass
-
- return t
-
- def thumb(self, n):
- return os.path.join(self.thumbs_dir(), str('%08d.png' % (n + 1)))
-
- def thumbs(self):
- return len(os.listdir(self.thumbs_dir()))
-
- def remove_thumbs(self):
- shutil.rmtree(self.thumbs_dir())
-
- def make_thumbs(self):
- num_thumbs = 128
- cl = self.player_command_line()
- if self.length is not None:
- sstep = self.length / num_thumbs
- else:
- sstep = 100
- cl.extra = '-vo png -frames %d -sstep %d -nosound' % (num_thumbs, sstep)
- os.chdir(self.thumbs_dir())
- os.system(cl.get(True))
-
- def set_dvd(self, d):
- self.dvd = d
- self.remove_thumbs()
-
- def set_dvd_title(self, t):
- self.dvd_title = t
- self.remove_thumbs()
-
- def set_content(self, c):
- if c == self.content:
- return
-
- self.content = c
- self.update_content_metadata()
-
- def player_command_line(self):
- cl = player.CommandLine()
- cl.dvd = self.dvd
- cl.dvd_title = self.dvd_title
- cl.content = self.content
- return cl
-
- def update_content_metadata(self):
- if self.content is None:
- return
-
- self.width = None
- self.height = None
- self.fps = None
- self.length = None
-
- cl = self.player_command_line()
- cl.extra = '-identify -vo null -ao null -frames 0'
- text = subprocess.check_output(shlex.split(cl.get(True))).decode('utf-8')
- lines = text.split('\n')
- for l in lines:
- s = l.strip()
- b = s.split('=')
- if len(b) == 2:
- if b[0] == 'ID_VIDEO_WIDTH':
- self.width = int(b[1])
- elif b[0] == 'ID_VIDEO_HEIGHT':
- self.height = int(b[1])
- elif b[0] == 'ID_VIDEO_FPS':
- self.fps = float(b[1])
- elif b[0] == 'ID_LENGTH':
- self.length = float(b[1])
+++ /dev/null
-import os
-import pygtk
-pygtk.require('2.0')
-import gtk
-import ratio
-import util
-import thumbs
-
-class FilmView(gtk.HBox):
- def __init__(self, parent):
- gtk.HBox.__init__(self)
-
- self.parent_window = parent
-
- self.inhibit_save = True
-
- self.table = gtk.Table()
- self.pack_start(self.table, True, True)
-
- self.table.set_row_spacings(4)
- self.table.set_col_spacings(12)
- self.film_name = gtk.Entry()
- self.ratio = gtk.combo_box_new_text()
- for r in ratio.ratios:
- self.ratio.append_text(r.name())
- self.content_file_radio = gtk.RadioButton()
- self.content_file_radio.set_label("File")
- self.content_file_chooser = gtk.FileChooserDialog("Content", self.parent_window, gtk.FILE_CHOOSER_ACTION_OPEN, (("Select", gtk.RESPONSE_OK)))
- self.content_file_button = gtk.FileChooserButton(self.content_file_chooser)
- self.content_folder_radio = gtk.RadioButton()
- self.content_folder_radio.set_label("Folder")
- self.content_folder_radio.set_group(self.content_file_radio)
- self.content_folder_chooser = gtk.FileChooserDialog("Content", self.parent_window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (("Select", gtk.RESPONSE_OK)))
- self.content_folder_button = gtk.FileChooserButton(self.content_folder_chooser)
- self.dvd = gtk.CheckButton("DVD")
- self.dvd_title = gtk.SpinButton()
- self.dvd_title.set_range(0, 32)
- self.dvd_title.set_increments(1, 4)
- self.deinterlace = gtk.CheckButton("Deinterlace")
- self.left_crop = self.crop_spinbutton()
- self.right_crop = self.crop_spinbutton()
- self.top_crop = self.crop_spinbutton()
- self.bottom_crop = self.crop_spinbutton()
-
- # Information about the content (immutable)
- self.source_size = self.label()
- self.fps = self.label()
- self.length = self.label()
-
- # Buttons
- self.thumbs_button = gtk.Button("Show Thumbnails")
-
- self.film_name.connect("changed", self.changed, self)
- self.ratio.connect("changed", self.changed, self)
- self.deinterlace.connect("toggled", self.changed, self)
- self.thumbs_button.connect("clicked", self.thumbs_clicked, self)
- self.content_file_radio.connect("toggled", self.content_radio_toggled, self)
- self.content_folder_radio.connect("toggled", self.content_radio_toggled, self)
- self.content_file_button.connect("file-set", self.content_file_chooser_file_set, self)
- self.content_folder_button.connect("file-set", self.content_folder_chooser_file_set, self)
- self.dvd.connect("toggled", self.changed, self)
- self.dvd_title.connect("value-changed", self.changed, self)
-
- n = 0
- self.table.attach(self.label("Film"), 0, 1, n, n + 1)
- self.table.attach(self.film_name, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Ratio"), 0, 1, n, n + 1)
- self.table.attach(self.ratio, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Content"), 0, 1, n, n + 1)
- b = gtk.HBox()
- b.set_spacing(4)
- b.pack_start(self.content_file_radio, False, False)
- b.pack_start(self.content_file_button, True, True)
- b.pack_start(self.content_folder_radio, False, False)
- b.pack_start(self.content_folder_button, True, True)
- self.table.attach(b, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.dvd, 0, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("DVD title"), 0, 1, n, n + 1)
- self.table.attach(self.dvd_title, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.deinterlace, 0, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Left Crop"), 0, 1, n, n + 1)
- self.table.attach(self.left_crop, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Right Crop"), 0, 1, n, n + 1)
- self.table.attach(self.right_crop, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Top Crop"), 0, 1, n, n + 1)
- self.table.attach(self.top_crop, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Bottom Crop"), 0, 1, n, n + 1)
- self.table.attach(self.bottom_crop, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Source size"), 0, 1, n, n + 1)
- self.table.attach(self.source_size, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Frames per second"), 0, 1, n, n + 1)
- self.table.attach(self.fps, 1, 2, n, n + 1)
- n += 1
- self.table.attach(self.label("Length"), 0, 1, n, n + 1)
- self.table.attach(self.length, 1, 2, n, n + 1)
-
- self.right_vbox = gtk.VBox()
- self.pack_start(self.right_vbox, False, False)
-
- self.right_vbox.pack_start(self.thumbs_button, False, False)
-
- self.inhibit_save = False
-
- def set(self, film):
- self.inhibit_save = True
-
- self.film = film
- self.film_name.set_text(film.name)
- self.ratio.set_active(ratio.ratio_to_index(film.ratio))
- if film.content is not None:
- if os.path.isfile(film.content):
- self.set_content_is_file(True)
- self.content_file_button.set_filename(film.content)
- else:
- self.set_content_is_file(False)
- self.content_folder_button.set_filename(film.content)
- self.dvd.set_active(film.dvd)
- self.dvd_title.set_value(film.dvd_title)
- self.dvd_title.set_sensitive(film.dvd)
- self.deinterlace.set_active(film.deinterlace)
- self.left_crop.set_value(film.left_crop)
- self.right_crop.set_value(film.right_crop)
- self.top_crop.set_value(film.top_crop)
- self.bottom_crop.set_value(film.bottom_crop)
-
- # Content information
- if film.width is not None and film.height is not None:
- self.source_size.set_text("%dx%d" % (film.width, film.height))
- else:
- self.source_size.set_text("Unknown")
- if film.fps is not None:
- self.fps.set_text(str(film.fps))
- if film.length is not None:
- self.length.set_text("%d:%02d:%02d" % util.s_to_hms(film.length))
-
- self.inhibit_save = False
-
- def set_content_is_file(self, f):
- self.content_file_button.set_sensitive(f)
- self.content_folder_button.set_sensitive(not f)
- self.content_file_radio.set_active(f)
- self.content_folder_radio.set_active(not f)
-
- def label(self, text = ""):
- l = gtk.Label(text)
- l.set_alignment(0, 0.5)
- return l
-
- def changed(self, a, b):
- self.dvd_title.set_sensitive(self.dvd.get_active())
- self.save_film()
-
- def crop_spinbutton(self):
- s = gtk.SpinButton()
- s.set_range(0, 1024)
- s.set_increments(1, 16)
- s.connect("value-changed", self.changed, self)
- return s
-
- def save_film(self):
- if self.inhibit_save:
- return
-
- self.film.name = self.film_name.get_text()
- self.film.ratio = ratio.index_to_ratio(self.ratio.get_active()).ratio
-
- if self.content_file_radio.get_active():
- self.film.set_content(self.content_file_button.get_filename())
- else:
- self.film.set_content(self.content_folder_button.get_filename())
- self.film.set_dvd(self.dvd.get_active())
- self.film.set_dvd_title(self.dvd_title.get_value_as_int())
- self.film.deinterlace = self.deinterlace.get_active()
- self.film.left_crop = self.left_crop.get_value_as_int()
- self.film.right_crop = self.right_crop.get_value_as_int()
- self.film.top_crop = self.top_crop.get_value_as_int()
- self.film.bottom_crop = self.bottom_crop.get_value_as_int()
- self.film.write()
-
- def thumbs_clicked(self, a, b):
- if self.film.thumbs() == 0:
- self.film.make_thumbs()
-
- t = thumbs.Thumbs(self.film)
- t.run()
- t.hide()
- self.left_crop.set_value(t.left_crop())
- self.right_crop.set_value(t.right_crop())
- self.top_crop.set_value(t.top_crop())
- self.bottom_crop.set_value(t.bottom_crop())
-
- def content_file_chooser_file_set(self, a, b):
- self.changed(a, b)
-
- def content_folder_chooser_file_set(self, a, b):
- self.changed(a, b)
-
- def content_radio_toggled(self, a, b):
- self.set_content_is_file(self.content_file_radio.get_active())
- self.changed(a, b)
-
+++ /dev/null
-import os
-import threading
-import subprocess
-import shlex
-import select
-import film
-import config
-import mplayer
-
-class CommandLine:
- def __init__(self):
- self.position_x = 0
- self.position_y = 0
- self.output_width = None
- self.output_height = None
- self.mov = False
- self.delay = None
- self.dvd = False
- self.dvd_title = 1
- self.content = None
- self.extra = ''
- self.crop_x = None
- self.crop_y = None
- self.crop_w = None
- self.crop_h = None
- self.deinterlace = False
- self.aid = None
-
- def get(self, with_binary):
- # hqdn3d?
- # nr, unsharp?
- # -vo x11 appears to be necessary to prevent unwanted hardware scaling
- # -noaspect stops mplayer rescaling to the movie's specified aspect ratio
- args = '-vo x11 -noaspect -ao pulse -noborder -noautosub -nosub -sws 10 '
- args += '-geometry %d:%d ' % (self.position_x, self.position_y)
-
- # Video filters (passed to -vf)
-
- filters = []
-
- if self.crop_x is not None or self.crop_y is not None or self.crop_w is not None or self.crop_h is not None:
- crop = 'crop='
- if self.crop_w is not None and self.crop_h is not None:
- crop += '%d:%d' % (self.crop_w, self.crop_h)
- if self.crop_x is not None and self.crop_x is not None:
- crop += ':%d:%d' % (self.crop_x, self.crop_y)
- filters.append(crop)
-
- if self.output_width is not None or self.output_height is not None:
- filters.append('scale=%d:%d' % (self.output_width, self.output_height))
-
- # Post processing
- pp = []
- if self.deinterlace:
- pp.append('lb')
-
- # Deringing filter
- pp.append('dr')
-
- if len(pp) > 0:
- pp_string = 'pp='
- for i in range(0, len(pp)):
- pp_string += pp[i]
- if i < len(pp) - 1:
- pp += ','
-
- filters.append(pp_string)
-
- if len(filters) > 0:
- args += '-vf '
- for i in range(0, len(filters)):
- args += filters[i]
- if i < len(filters) - 1:
- args += ','
- args += ' '
-
- if self.mov:
- args += '-demuxer mov '
- if self.delay is not None:
- args += '-delay %f ' % float(args.delay)
- if self.aid is not None:
- args += '-aid %s ' % self.aid
-
- args += self.extra
-
- if self.dvd:
- data_specifier = 'dvd://%d -dvd-device \"%s\"' % (self.dvd_title, self.content)
- else:
- data_specifier = '\"%s\"' % self.content
-
- if with_binary:
- return 'mplayer %s %s' % (args, data_specifier)
-
- return '%s %s' % (args, data_specifier)
-
-def get_player(film, format):
- cl = CommandLine()
- cl.dvd = film.dvd
- cl.dvd_title = film.dvd_title
- cl.content = film.content
- cl.crop_w = film.width - film.left_crop - film.right_crop
- cl.crop_h = film.height - film.top_crop - film.bottom_crop
- cl.position_x = format.x
- if format.external:
- cl.position_x = format.x + config.LEFT_SCREEN_WIDTH
- cl.position_y = format.y
- cl.output_width = format.width
- cl.output_height = format.height
- cl.deinterlace = film.deinterlace
- cl.aid = film.aid
- return mplayer.Player(cl.get(False))
-
+++ /dev/null
-# Class to describe a Ratio, and a collection of common
-# (and not-so-common) film ratios collected from Wikipedia.
-
-class Ratio:
- def __init__(self, ratio, nickname = None):
- self.nickname = nickname
- self.ratio = ratio
-
- # @return presentation name of this ratio
- def name(self):
- if self.nickname is not None:
- return "%.2f (%s)" % (self.ratio, self.nickname)
-
- return "%.2f" % self.ratio
-
-ratios = []
-ratios.append(Ratio(1.33, '4:3'))
-ratios.append(Ratio(1.37, 'Academy'))
-ratios.append(Ratio(1.78, '16:9'))
-ratios.append(Ratio(1.85, 'Flat / widescreen'))
-ratios.append(Ratio(2.39, 'CinemaScope / Panavision'))
-ratios.append(Ratio(1.15, 'Movietone'))
-ratios.append(Ratio(1.43, 'IMAX'))
-ratios.append(Ratio(1.5))
-ratios.append(Ratio(1.56, '14:9'))
-ratios.append(Ratio(1.6, '16:10'))
-ratios.append(Ratio(1.67))
-ratios.append(Ratio(2, 'SuperScope'))
-ratios.append(Ratio(2.2, 'Todd-AO'))
-ratios.append(Ratio(2.35, 'Early CinemaScope / Panavision'))
-ratios.append(Ratio(2.37, '21:9'))
-ratios.append(Ratio(2.55, 'CinemaScope 55'))
-ratios.append(Ratio(2.59, 'Cinerama'))
-ratios.append(Ratio(2.76, 'Ultra Panavision'))
-ratios.append(Ratio(2.93, 'MGM Camera 65'))
-ratios.append(Ratio(4, 'Polyvision'))
-
-# Find a Ratio object from a fractional ratio
-def find(ratio):
- for r in ratios:
- if r.ratio == ratio:
- return r
-
- return None
-
-# @return the ith ratio
-def index_to_ratio(i):
- return ratios[i]
-
-# @return the index within the ratios list of a given fractional ratio
-def ratio_to_index(r):
- for i in range(0, len(ratios)):
- if ratios[i].ratio == r:
- return i
-
- return None
+++ /dev/null
-# Screen 1 (untested)
-screen 1
-ratio 1.85
-x 175
-y 100
-width 1550
-height 950
-external 1
-ratio 2.39
-x 0
-y 200
-width 2000
-height 860
-external 1
-
-# Screen 2
-screen 2
-ratio 1.85
-x 175
-y 100
-width 1550
-height 950
-external 1
-ratio 2.39
-x 0
-y 200
-width 2000
-height 860
-external 1
-
-# Screen 3 (untested)
-screen 3
-ratio 1.85
-x 175
-y 100
-width 1550
-height 950
-external 1
-ratio 2.39
-x 0
-y 200
-width 2000
-height 860
-external 1
-
-# Carl's Laptop
-screen laptop
-ratio 1.85
-x 0
-y 0
-width 1366
-height 738
-ratio 2.39
-x 0
-y 0
-width 1366
-height 572
-ratio 1.78
-x 0
-y 0
-width 1366
-height 767
+++ /dev/null
-#!/usr/bin/python
-
-class Screen:
- def __init__(self):
- self.name = None
- self.formats = []
-
-class Format:
- def __init__(self):
- self.ratio = None
- self.x = None
- self.y = None
- self.width = None
- self.height = None
- self.external = False
-
-class Screens:
- def __init__(self, file):
-
- self.screens = []
-
- f = open(file, 'r')
- current_screen = None
- current_format = None
- while 1:
- l = f.readline()
- if l == '':
- break
- if len(l) > 0 and l[0] == '#':
- continue
-
- s = l.strip()
-
- if len(s) == 0:
- continue
-
- b = s.split()
-
- if len(b) != 2:
- print "WARNING: ignored line `%s' in screens file" % (s)
- continue
-
- if b[0] == 'screen':
- if current_format is not None:
- current_screen.formats.append(current_format)
- current_format = None
-
- if current_screen is not None:
- self.screens.append(current_screen)
- current_screen = None
-
- current_screen = Screen()
- current_screen.name = b[1]
- elif b[0] == 'ratio':
- if current_format is not None:
- current_screen.formats.append(current_format)
- current_format = None
-
- current_format = Format()
- current_format.ratio = float(b[1])
- elif b[0] == 'x':
- current_format.x = int(b[1])
- elif b[0] == 'y':
- current_format.y = int(b[1])
- elif b[0] == 'width':
- current_format.width = int(b[1])
- elif b[0] == 'height':
- current_format.height = int(b[1])
- elif b[0] == 'external':
- current_format.external = int(b[1]) == 1
-
- if current_format is not None:
- current_screen.formats.append(current_format)
-
- if current_screen is not None:
- self.screens.append(current_screen)
-
- def get_format(self, screen, ratio):
- for s in self.screens:
- if s.name == screen:
- for f in s.formats:
- if f.ratio == ratio:
- return f
-
- return None
+++ /dev/null
-# GUI to display thumbnails and allow cropping
-# to be set up
-
-import os
-import sys
-import pygtk
-pygtk.require('2.0')
-import gtk
-import film
-import player
-
-class Thumbs(gtk.Dialog):
- def __init__(self, film):
- gtk.Dialog.__init__(self)
- self.film = film
- self.controls = gtk.Table()
- self.controls.set_col_spacings(4)
- self.thumb_adj = gtk.Adjustment(0, 0, self.film.thumbs() - 1, 1, 10)
- self.add_control("Thumbnail", self.thumb_adj, 0)
- self.left_crop_adj = gtk.Adjustment(self.film.left_crop, 0, 1024, 1, 128)
- self.add_control("Left crop", self.left_crop_adj, 1)
- self.right_crop_adj = gtk.Adjustment(self.film.right_crop, 0, 1024, 1, 128)
- self.add_control("Right crop", self.right_crop_adj, 2)
- self.top_crop_adj = gtk.Adjustment(self.film.top_crop, 0, 1024, 1, 128)
- self.add_control("Top crop", self.top_crop_adj, 3)
- self.bottom_crop_adj = gtk.Adjustment(self.film.bottom_crop, 0, 1024, 1, 128)
- self.add_control("Bottom crop", self.bottom_crop_adj, 4)
- self.display_image = gtk.Image()
- self.update_display()
- window_box = gtk.HBox()
- window_box.set_spacing(12)
-
- controls_vbox = gtk.VBox()
- controls_vbox.set_spacing(4)
- controls_vbox.pack_start(self.controls, False, False)
-
- window_box.pack_start(controls_vbox, True, True)
- window_box.pack_start(self.display_image)
-
- self.set_title("%s Thumbnails" % film.name)
- self.get_content_area().add(window_box)
- self.add_button("Close", gtk.RESPONSE_ACCEPT)
- self.show_all()
-
- for a in [self.thumb_adj, self.left_crop_adj, self.right_crop_adj, self.top_crop_adj, self.bottom_crop_adj]:
- a.connect('value-changed', self.update_display, self)
-
- def add_control(self, name, adj, n):
- l = gtk.Label(name)
- l.set_alignment(1, 0.5)
- self.controls.attach(l, 0, 1, n, n + 1)
- s = gtk.SpinButton(adj)
- self.controls.attach(s, 1, 2, n, n + 1)
-
- def update_display(self, a = None, b = None):
- thumb_pixbuf = gtk.gdk.pixbuf_new_from_file(self.film.thumb(self.thumb_adj.get_value()))
- self.width = thumb_pixbuf.get_width()
- self.height = thumb_pixbuf.get_height()
- left = self.left_crop()
- right = self.right_crop()
- top = self.top_crop()
- bottom = self.bottom_crop()
- pixbuf = thumb_pixbuf.subpixbuf(left, top, self.width - left - right, self.height - top - bottom)
- self.display_image.set_from_pixbuf(pixbuf)
-
- def top_crop(self):
- return int(self.top_crop_adj.get_value())
-
- def bottom_crop(self):
- return int(self.bottom_crop_adj.get_value())
-
- def left_crop(self):
- return int(self.left_crop_adj.get_value())
-
- def right_crop(self):
- return int(self.right_crop_adj.get_value())
+++ /dev/null
-
-def s_to_hms(s):
- m = int(s / 60)
- s -= (m * 60)
- h = int(m / 60)
- m -= (h * 60)
- return (h, m, s)
+++ /dev/null
-Recommended 1680 x 1050, 60 fps
-xrandr --output HDMI1 --mode 0xbc
-
-List modes
-xrandr --verbose -q
-
-2048 x 1024, 24 fps
-xrandr --output HDMI1 --mode 0xd1
-
-cvt <xres> <yres> <fps>
-to give modeline, then
-xrandr --newmode modeline
-then add
-xrandr --verbose --addmode HDMI1 modename
-then activate
-xrandr --output HDMI1 --mode foo
-
--- /dev/null
+#!/bin/bash
+
+export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH
+if [ "$1" == "--debug" ]; then
+ shift
+ gdb --args build/src/tools/dcpomatic $*
+elif [ "$1" == "--valgrind" ]; then
+ shift
+ valgrind --tool="memcheck" build/src/tools/dcpomatic $*
+elif [ "$1" == "--i18n" ]; then
+ shift
+ LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 build/src/tools/dcpomatic "$*"
+else
+ build/src/tools/dcpomatic $*
+fi
+++ /dev/null
-#!/bin/bash
-
-export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH
-if [ "$1" == "--debug" ]; then
- shift
- gdb --args build/src/tools/dvdomatic "$*"
-elif [ "$1" == "--valgrind" ]; then
- shift
- valgrind --tool="memcheck" build/src/tools/dvdomatic $*
-elif [ "$1" == "--i18n" ]; then
- shift
- LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 build/src/tools/dvdomatic "$*"
-else
- build/src/tools/dvdomatic "$*"
-fi
using boost::shared_ptr;
/** @param f Film to compare.
- * @param o Decode options.
*/
-ABTranscodeJob::ABTranscodeJob (shared_ptr<Film> f, DecodeOptions o)
+ABTranscodeJob::ABTranscodeJob (shared_ptr<Film> f)
: Job (f)
- , _decode_opt (o)
{
_film_b.reset (new Film (*_film));
_film_b->set_scaler (Config::instance()->reference_scaler ());
{
try {
/* _film_b is the one with reference filters */
- ABTranscoder w (_film_b, _film, _decode_opt, this, shared_ptr<Encoder> (new Encoder (_film)));
+ ABTranscoder w (_film_b, _film, shared_from_this ());
w.go ();
set_progress (1);
set_state (FINISHED_OK);
#include <boost/shared_ptr.hpp>
#include "job.h"
-#include "options.h"
class Film;
{
public:
ABTranscodeJob (
- boost::shared_ptr<Film> f,
- DecodeOptions o
+ boost::shared_ptr<Film> f
);
std::string name () const;
void run ();
private:
- DecodeOptions _decode_opt;
-
/** Copy of our Film using the reference filters and scaler */
boost::shared_ptr<Film> _film_b;
};
#include <boost/shared_ptr.hpp>
#include "ab_transcoder.h"
#include "film.h"
-#include "video_decoder.h"
-#include "audio_decoder.h"
#include "encoder.h"
#include "job.h"
-#include "options.h"
#include "image.h"
-#include "decoder_factory.h"
+#include "player.h"
#include "matcher.h"
#include "delay_line.h"
#include "gain.h"
* @param e Encoder to use.
*/
-ABTranscoder::ABTranscoder (
- shared_ptr<Film> a, shared_ptr<Film> b, DecodeOptions o, Job* j, shared_ptr<Encoder> e)
+ABTranscoder::ABTranscoder (shared_ptr<Film> a, shared_ptr<Film> b, shared_ptr<Job> j)
: _film_a (a)
, _film_b (b)
+ , _player_a (_film_a->player ())
+ , _player_b (_film_b->player ())
, _job (j)
- , _encoder (e)
+ , _encoder (new Encoder (_film_a))
, _combiner (new Combiner (a->log()))
{
- _da = decoder_factory (_film_a, o);
- _db = decoder_factory (_film_b, o);
-
- shared_ptr<AudioStream> st = _film_a->audio_stream();
- if (st) {
- _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->source_frame_rate()));
- }
- _delay_line.reset (new DelayLine (_film_a->log(), _film_a->audio_delay() / 1000.0f));
+ _matcher.reset (new Matcher (_film_a->log(), _film_a->audio_frame_rate(), _film_a->video_frame_rate()));
+ _delay_line.reset (new DelayLine (_film_a->log(), _film_a->audio_delay() * _film_a->audio_frame_rate() / 1000));
_gain.reset (new Gain (_film_a->log(), _film_a->audio_gain()));
- int const sr = st ? st->sample_rate() : 0;
+ _player_a->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3, _4));
+ _player_b->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3, _4));
+
int const trim_start = _film_a->trim_type() == Film::ENCODE ? _film_a->trim_start() : 0;
int const trim_end = _film_a->trim_type() == Film::ENCODE ? _film_a->trim_end() : 0;
_trimmer.reset (new Trimmer (
- _film_a->log(), trim_start, trim_end, _film_a->length().get(),
- sr, _film_a->source_frame_rate(), _film_a->dcp_frame_rate()
+ _film_a->log(), trim_start, trim_end, _film_a->content_length(),
+ _film_a->audio_frame_rate(), _film_a->video_frame_rate(), _film_a->dcp_frame_rate()
));
- /* Set up the decoder to use the film's set streams */
- _da.video->set_subtitle_stream (_film_a->subtitle_stream ());
- _db.video->set_subtitle_stream (_film_a->subtitle_stream ());
- if (_film_a->audio_stream ()) {
- _da.audio->set_audio_stream (_film_a->audio_stream ());
- }
-
- _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3, _4));
- _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3, _4));
_combiner->connect_video (_delay_line);
- if (_matcher) {
- _delay_line->connect_video (_matcher);
- _matcher->connect_video (_trimmer);
- } else {
- _delay_line->connect_video (_trimmer);
- }
+ _delay_line->connect_video (_matcher);
+ _matcher->connect_video (_trimmer);
_trimmer->connect_video (_encoder);
- _da.audio->connect_audio (_delay_line);
- if (_matcher) {
- _delay_line->connect_audio (_matcher);
- _matcher->connect_audio (_gain);
- } else {
- _delay_line->connect_audio (_gain);
- }
+ _player_a->connect_audio (_delay_line);
+ _delay_line->connect_audio (_matcher);
+ _matcher->connect_audio (_gain);
_gain->connect_audio (_trimmer);
_trimmer->connect_audio (_encoder);
}
{
_encoder->process_begin ();
- bool done[3] = { false, false, false };
+ bool done[2] = { false, false };
while (1) {
- done[0] = _da.video->pass ();
- done[1] = _db.video->pass ();
-
- if (!done[2] && _da.audio && dynamic_pointer_cast<Decoder> (_da.audio) != dynamic_pointer_cast<Decoder> (_da.video)) {
- done[2] = _da.audio->pass ();
- } else {
- done[2] = true;
- }
-
+ done[0] = _player_a->pass ();
+ done[1] = _player_b->pass ();
+
if (_job) {
- _da.video->set_progress (_job);
+ _player_a->set_progress (_job);
}
-
- if (done[0] && done[1] && done[2]) {
+
+ if (done[0] && done[1]) {
break;
}
}
_delay_line->process_end ();
- if (_matcher) {
- _matcher->process_end ();
- }
+ _matcher->process_end ();
_gain->process_end ();
+ _trimmer->process_end ();
_encoder->process_end ();
}
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include "util.h"
-#include "decoder_factory.h"
class Job;
class Encoder;
-class VideoDecoder;
-class AudioDecoder;
class Image;
class Log;
-class Subtitle;
class Film;
class Matcher;
class DelayLine;
class Gain;
class Combiner;
+class Player;
class Trimmer;
/** @class ABTranscoder
ABTranscoder (
boost::shared_ptr<Film> a,
boost::shared_ptr<Film> b,
- DecodeOptions o,
- Job* j,
- boost::shared_ptr<Encoder> e
+ boost::shared_ptr<Job> j
);
void go ();
private:
boost::shared_ptr<Film> _film_a;
boost::shared_ptr<Film> _film_b;
- Job* _job;
+ boost::shared_ptr<Player> _player_a;
+ boost::shared_ptr<Player> _player_b;
+ boost::shared_ptr<Job> _job;
boost::shared_ptr<Encoder> _encoder;
- Decoders _da;
- Decoders _db;
boost::shared_ptr<Combiner> _combiner;
boost::shared_ptr<Matcher> _matcher;
boost::shared_ptr<DelayLine> _delay_line;
#include "analyse_audio_job.h"
#include "compose.hpp"
#include "film.h"
-#include "options.h"
-#include "decoder_factory.h"
-#include "audio_decoder.h"
+#include "player.h"
#include "i18n.h"
void
AnalyseAudioJob::run ()
{
- if (!_film->audio_stream () || !_film->length()) {
- set_progress (1);
- set_state (FINISHED_ERROR);
- return;
- }
-
- DecodeOptions options;
- options.decode_video = false;
-
- Decoders decoders = decoder_factory (_film, options);
- assert (decoders.audio);
+ shared_ptr<Player> player = _film->player ();
+ player->disable_video ();
- decoders.audio->set_audio_stream (_film->audio_stream ());
- decoders.audio->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1));
+ player->Audio.connect (bind (&AnalyseAudioJob::audio, this, _1));
- int64_t total_audio_frames = video_frames_to_audio_frames (_film->length().get(), _film->audio_stream()->sample_rate(), _film->source_frame_rate());
- _samples_per_point = max (int64_t (1), total_audio_frames / _num_points);
+ _samples_per_point = max (int64_t (1), _film->audio_length() / _num_points);
- _current.resize (_film->audio_stream()->channels ());
- _analysis.reset (new AudioAnalysis (_film->audio_stream()->channels()));
+ _current.resize (MAX_AUDIO_CHANNELS);
+ _analysis.reset (new AudioAnalysis (MAX_AUDIO_CHANNELS));
- while (!decoders.audio->pass()) {
- set_progress (float (_done) / total_audio_frames);
+ while (!player->pass()) {
+ set_progress (float (_done) / _film->audio_length ());
}
_analysis->write (_film->audio_analysis_path ());
*/
-#ifndef DVDOMATIC_AUDIO_ANALYSIS_H
-#define DVDOMATIC_AUDIO_ANALYSIS_H
+#ifndef DCPOMATIC_AUDIO_ANALYSIS_H
+#define DCPOMATIC_AUDIO_ANALYSIS_H
#include <iostream>
#include <vector>
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include "audio_content.h"
+
+using boost::shared_ptr;
+
+int const AudioContentProperty::AUDIO_CHANNELS = 200;
+int const AudioContentProperty::AUDIO_LENGTH = 201;
+int const AudioContentProperty::AUDIO_FRAME_RATE = 202;
+
+AudioContent::AudioContent (boost::filesystem::path f)
+ : Content (f)
+{
+
+}
+
+AudioContent::AudioContent (shared_ptr<const cxml::Node> node)
+ : Content (node)
+{
+
+}
+
+AudioContent::AudioContent (AudioContent const & o)
+ : Content (o)
+{
+
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_AUDIO_CONTENT_H
+#define DCPOMATIC_AUDIO_CONTENT_H
+
+#include "content.h"
+#include "util.h"
+
+namespace cxml {
+ class Node;
+}
+
+class AudioContentProperty
+{
+public:
+ static int const AUDIO_CHANNELS;
+ static int const AUDIO_LENGTH;
+ static int const AUDIO_FRAME_RATE;
+};
+
+class AudioContent : public virtual Content
+{
+public:
+ AudioContent (boost::filesystem::path);
+ AudioContent (boost::shared_ptr<const cxml::Node>);
+ AudioContent (AudioContent const &);
+
+ virtual int audio_channels () const = 0;
+ virtual ContentAudioFrame audio_length () const = 0;
+ virtual int audio_frame_rate () const = 0;
+};
+
+#endif
*/
#include "audio_decoder.h"
-#include "stream.h"
using boost::optional;
using boost::shared_ptr;
-AudioDecoder::AudioDecoder (shared_ptr<Film> f, DecodeOptions o)
- : Decoder (f, o)
+AudioDecoder::AudioDecoder (shared_ptr<const Film> f)
+ : Decoder (f)
{
}
-
-void
-AudioDecoder::set_audio_stream (shared_ptr<AudioStream> s)
-{
- _audio_stream = s;
-}
* @brief Parent class for audio decoders.
*/
-#ifndef DVDOMATIC_AUDIO_DECODER_H
-#define DVDOMATIC_AUDIO_DECODER_H
+#ifndef DCPOMATIC_AUDIO_DECODER_H
+#define DCPOMATIC_AUDIO_DECODER_H
#include "audio_source.h"
-#include "stream.h"
#include "decoder.h"
+class AudioContent;
+
/** @class AudioDecoder.
* @brief Parent class for audio decoders.
*/
class AudioDecoder : public TimedAudioSource, public virtual Decoder
{
public:
- AudioDecoder (boost::shared_ptr<Film>, DecodeOptions);
-
- virtual void set_audio_stream (boost::shared_ptr<AudioStream>);
-
- /** @return Audio stream that we are using */
- boost::shared_ptr<AudioStream> audio_stream () const {
- return _audio_stream;
- }
-
- /** @return All available audio streams */
- std::vector<boost::shared_ptr<AudioStream> > audio_streams () const {
- return _audio_streams;
- }
-
-protected:
- /** Audio stream that we are using */
- boost::shared_ptr<AudioStream> _audio_stream;
- /** All available audio streams */
- std::vector<boost::shared_ptr<AudioStream> > _audio_streams;
+ AudioDecoder (boost::shared_ptr<const Film>);
};
#endif
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/lexical_cast.hpp>
+#include <libcxml/cxml.h>
+#include "audio_mapping.h"
+
+using std::list;
+using std::cout;
+using std::make_pair;
+using std::pair;
+using std::string;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using boost::dynamic_pointer_cast;
+
+void
+AudioMapping::add (Channel c, libdcp::Channel d)
+{
+ _content_to_dcp.push_back (make_pair (c, d));
+}
+
+/* XXX: this is grotty */
+int
+AudioMapping::dcp_channels () const
+{
+ for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ if (((int) i->second) >= 2) {
+ return 6;
+ }
+ }
+
+ return 2;
+}
+
+list<AudioMapping::Channel>
+AudioMapping::dcp_to_content (libdcp::Channel d) const
+{
+ list<AudioMapping::Channel> c;
+ for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ if (i->second == d) {
+ c.push_back (i->first);
+ }
+ }
+
+ return c;
+}
+
+list<AudioMapping::Channel>
+AudioMapping::content_channels () const
+{
+ list<AudioMapping::Channel> c;
+ for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ if (find (c.begin(), c.end(), i->first) == c.end ()) {
+ c.push_back (i->first);
+ }
+ }
+
+ return c;
+}
+
+list<libdcp::Channel>
+AudioMapping::content_to_dcp (Channel c) const
+{
+ list<libdcp::Channel> d;
+ for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ if (i->first == c) {
+ d.push_back (i->second);
+ }
+ }
+
+ return d;
+}
+
+void
+AudioMapping::as_xml (xmlpp::Node* node) const
+{
+ for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+ xmlpp::Node* t = node->add_child ("Map");
+ shared_ptr<const AudioContent> c = i->first.content.lock ();
+ t->add_child ("Content")->add_child_text (c->digest ());
+ t->add_child ("ContentIndex")->add_child_text (lexical_cast<string> (i->first.index));
+ t->add_child ("DCP")->add_child_text (lexical_cast<string> (i->second));
+ }
+}
+
+void
+AudioMapping::set_from_xml (ContentList const & content, shared_ptr<const cxml::Node> node)
+{
+ list<shared_ptr<cxml::Node> > const c = node->node_children ("Map");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
+ string const c = (*i)->string_child ("Content");
+ ContentList::const_iterator j = content.begin ();
+ while (j != content.end() && (*j)->digest() != c) {
+ ++j;
+ }
+
+ if (j == content.end ()) {
+ continue;
+ }
+
+ shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (*j);
+ assert (ac);
+
+ add (AudioMapping::Channel (ac, (*i)->number_child<int> ("ContentIndex")), static_cast<libdcp::Channel> ((*i)->number_child<int> ("DCP")));
+ }
+}
+
+bool
+operator== (AudioMapping::Channel const & a, AudioMapping::Channel const & b)
+{
+ shared_ptr<const AudioContent> sa = a.content.lock ();
+ shared_ptr<const AudioContent> sb = b.content.lock ();
+ return sa == sb && a.index == b.index;
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_AUDIO_MAPPING_H
+#define DCPOMATIC_AUDIO_MAPPING_H
+
+#include <list>
+#include <string>
+#include <libdcp/types.h>
+#include <boost/shared_ptr.hpp>
+#include "audio_content.h"
+
+class AudioMapping
+{
+public:
+ void as_xml (xmlpp::Node *) const;
+ void set_from_xml (ContentList const &, boost::shared_ptr<const cxml::Node>);
+
+ struct Channel {
+ Channel (boost::weak_ptr<const AudioContent> c, int i)
+ : content (c)
+ , index (i)
+ {}
+
+ boost::weak_ptr<const AudioContent> content;
+ int index;
+ };
+
+ void add (Channel, libdcp::Channel);
+
+ int dcp_channels () const;
+ std::list<Channel> dcp_to_content (libdcp::Channel) const;
+ std::list<std::pair<Channel, libdcp::Channel> > content_to_dcp () const {
+ return _content_to_dcp;
+ }
+
+ std::list<Channel> content_channels () const;
+ std::list<libdcp::Channel> content_to_dcp (Channel) const;
+
+private:
+ std::list<std::pair<Channel, libdcp::Channel> > _content_to_dcp;
+};
+
+extern bool operator== (AudioMapping::Channel const &, AudioMapping::Channel const &);
+
+#endif
*/
-#ifndef DVDOMATIC_AUDIO_SINK_H
-#define DVDOMATIC_AUDIO_SINK_H
+#ifndef DCPOMATIC_AUDIO_SINK_H
+#define DCPOMATIC_AUDIO_SINK_H
class AudioSink
{
#include "audio_sink.h"
using boost::shared_ptr;
+using boost::weak_ptr;
using boost::bind;
+static void
+process_audio_proxy (weak_ptr<AudioSink> sink, shared_ptr<const AudioBuffers> audio)
+{
+ shared_ptr<AudioSink> p = sink.lock ();
+ if (p) {
+ p->process_audio (audio);
+ }
+}
+
void
AudioSource::connect_audio (shared_ptr<AudioSink> s)
{
- Audio.connect (bind (&AudioSink::process_audio, s, _1));
+ Audio.connect (bind (process_audio_proxy, weak_ptr<AudioSink> (s), _1));
}
void
-TimedAudioSource::connect_audio (shared_ptr<TimedAudioSink> s)
+TimedAudioSource::connect_audio (shared_ptr<AudioSink> s)
{
- Audio.connect (bind (&TimedAudioSink::process_audio, s, _1, _2));
+ Audio.connect (bind (process_audio_proxy, weak_ptr<AudioSink> (s), _1));
+}
+
+static void
+timed_process_audio_proxy (weak_ptr<TimedAudioSink> sink, shared_ptr<const AudioBuffers> audio, double t)
+{
+ shared_ptr<TimedAudioSink> p = sink.lock ();
+ if (p) {
+ p->process_audio (audio, t);
+ }
}
void
-TimedAudioSource::connect_audio (shared_ptr<AudioSink> s)
+TimedAudioSource::connect_audio (shared_ptr<TimedAudioSink> s)
{
- Audio.connect (bind (&AudioSink::process_audio, s, _1));
+ Audio.connect (bind (timed_process_audio_proxy, weak_ptr<TimedAudioSink> (s), _1, _2));
}
+
* @brief Parent class for classes which emit audio data.
*/
-#ifndef DVDOMATIC_AUDIO_SOURCE_H
-#define DVDOMATIC_AUDIO_SOURCE_H
+#ifndef DCPOMATIC_AUDIO_SOURCE_H
+#define DCPOMATIC_AUDIO_SOURCE_H
#include <boost/signals2.hpp>
#include <fstream>
#include <glib.h>
#include <boost/filesystem.hpp>
+#include <libcxml/cxml.h>
#include "config.h"
#include "server.h"
#include "scaler.h"
using std::ifstream;
using std::string;
using std::ofstream;
+using std::list;
using boost::shared_ptr;
+using boost::lexical_cast;
+using boost::optional;
Config* Config::_instance = 0;
, _reference_scaler (Scaler::from_id (N_("bicubic")))
, _tms_path (N_("."))
, _sound_processor (SoundProcessor::from_id (N_("dolby_cp750")))
+ , _default_still_length (10)
, _default_format (0)
, _default_dcp_content_type (0)
{
_allowed_dcp_frame_rates.push_back (48);
_allowed_dcp_frame_rates.push_back (50);
_allowed_dcp_frame_rates.push_back (60);
+
+ if (!boost::filesystem::exists (file (false))) {
+ read_old_metadata ();
+ return;
+ }
+
+ cxml::File f (file (false), "Config");
+ optional<string> c;
+
+ _num_local_encoding_threads = f.number_child<int> ("NumLocalEncodingThreads");
+ _default_directory = f.string_child ("DefaultDirectory");
+ _server_port = f.number_child<int> ("ServerPort");
+ c = f.optional_string_child ("ReferenceScaler");
+ if (c) {
+ _reference_scaler = Scaler::from_id (c.get ());
+ }
+
+ list<shared_ptr<cxml::Node> > filters = f.node_children ("ReferenceFilter");
+ for (list<shared_ptr<cxml::Node> >::iterator i = filters.begin(); i != filters.end(); ++i) {
+ _reference_filters.push_back (Filter::from_id ((*i)->content ()));
+ }
- ifstream f (file().c_str ());
+ list<shared_ptr<cxml::Node> > servers = f.node_children ("Server");
+ for (list<shared_ptr<cxml::Node> >::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 ("DefaultFormat");
+ if (c) {
+ _default_format = Format::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<int>("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 ()) {
} else if (k == "language") {
_language = v;
} else if (k == "default_format") {
- _default_format = Format::from_metadata (v);
+ _default_format = Format::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") {
_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 ();
- p /= N_(".dvdomatic");
+ if (old) {
+ p /= ".dvdomatic";
+ } else {
+ p /= ".dcpomatic.xml";
+ }
return p.string ();
}
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";
+ xmlpp::Document doc;
+ xmlpp::Element* root = doc.create_root_node ("Config");
+ root->add_child("NumLocalEncodingThreads")->add_child_text (lexical_cast<string> (_num_local_encoding_threads));
+ root->add_child("DefaultDirectory")->add_child_text (_default_directory);
+ root->add_child("ServerPort")->add_child_text (lexical_cast<string> (_server_port));
if (_reference_scaler) {
- f << "reference_scaler " << _reference_scaler->id () << "\n";
+ root->add_child("ReferenceScaler")->add_child_text (_reference_scaler->id ());
}
for (vector<Filter const *>::const_iterator i = _reference_filters.begin(); i != _reference_filters.end(); ++i) {
- f << "reference_filter " << (*i)->id () << "\n";
+ root->add_child("ReferenceFilter")->add_child_text ((*i)->id ());
}
for (vector<ServerDescription*>::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";
+ root->add_child("DefaultFormat")->add_child_text (_default_format->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<string> (_default_still_length));
- _default_dci_metadata.write (f);
+ doc.write_to_file_formatted (file (false));
}
string
* @brief Class holding configuration.
*/
-#ifndef DVDOMATIC_CONFIG_H
-#define DVDOMATIC_CONFIG_H
+#ifndef DCPOMATIC_CONFIG_H
+#define DCPOMATIC_CONFIG_H
#include <vector>
#include <boost/shared_ptr.hpp>
return _language;
}
+ int default_still_length () const {
+ return _default_still_length;
+ }
+
Format const * default_format () const {
return _default_format;
}
_language = boost::none;
}
+ void set_default_still_length (int s) {
+ _default_still_length = s;
+ }
+
void set_default_format (Format const * f) {
_default_format = f;
}
private:
Config ();
- std::string file () const;
+ std::string file (bool) const;
+ void read_old_metadata ();
/** number of threads to use for J2K encoding on the local machine */
int _num_local_encoding_threads;
/** Default DCI metadata for newly-created Films */
DCIMetadata _default_dci_metadata;
boost::optional<std::string> _language;
+ int _default_still_length;
Format const * _default_format;
DCPContentType const * _default_dcp_content_type;
libdcp::XMLMetadata _dcp_metadata;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/thread/mutex.hpp>
+#include <libxml++/libxml++.h>
+#include <libcxml/cxml.h>
+#include "content.h"
+#include "util.h"
+
+using std::string;
+using boost::shared_ptr;
+
+Content::Content (boost::filesystem::path f)
+ : _file (f)
+{
+
+}
+
+Content::Content (shared_ptr<const cxml::Node> node)
+{
+ _file = node->string_child ("File");
+ _digest = node->string_child ("Digest");
+}
+
+Content::Content (Content const & o)
+ : boost::enable_shared_from_this<Content> (o)
+ , _file (o._file)
+ , _digest (o._digest)
+{
+
+}
+
+void
+Content::as_xml (xmlpp::Node* node) const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ node->add_child("File")->add_child_text (_file.string());
+ node->add_child("Digest")->add_child_text (_digest);
+}
+
+void
+Content::examine (shared_ptr<Film>, shared_ptr<Job>, bool)
+{
+ string const d = md5_digest (_file);
+ boost::mutex::scoped_lock lm (_mutex);
+ _digest = d;
+}
+
+void
+Content::signal_changed (int p)
+{
+ Changed (shared_from_this (), p);
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_CONTENT_H
+#define DCPOMATIC_CONTENT_H
+
+#include <boost/filesystem.hpp>
+#include <boost/signals2.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <libxml++/libxml++.h>
+
+namespace cxml {
+ class Node;
+}
+
+class Job;
+class Film;
+
+class Content : public boost::enable_shared_from_this<Content>
+{
+public:
+ Content (boost::filesystem::path);
+ Content (boost::shared_ptr<const cxml::Node>);
+ Content (Content const &);
+
+ virtual void examine (boost::shared_ptr<Film>, boost::shared_ptr<Job>, bool);
+ virtual std::string summary () const = 0;
+ virtual std::string information () const = 0;
+ virtual void as_xml (xmlpp::Node *) const;
+ virtual boost::shared_ptr<Content> clone () const = 0;
+
+ boost::filesystem::path file () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _file;
+ }
+
+ std::string digest () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _digest;
+ }
+
+ boost::signals2::signal<void (boost::weak_ptr<Content>, int)> Changed;
+
+protected:
+ void signal_changed (int);
+
+ mutable boost::mutex _mutex;
+
+private:
+ boost::filesystem::path _file;
+ std::string _digest;
+};
+
+#endif
*/
#include "cross.h"
-#ifdef DVDOMATIC_POSIX
+#ifdef DCPOMATIC_POSIX
#include <unistd.h>
#endif
-#ifdef DVDOMATIC_WINDOWS
+#ifdef DCPOMATIC_WINDOWS
#include "windows.h"
#endif
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
}
*/
-#ifdef DVDOMATIC_WINDOWS
+#ifdef DCPOMATIC_WINDOWS
#define WEXITSTATUS(w) (w)
#endif
-void dvdomatic_sleep (int);
+void dcpomatic_sleep (int);
*/
#include <iostream>
+#include <libcxml/cxml.h>
#include "dci_metadata.h"
#include "i18n.h"
-using namespace std;
+using std::string;
+using boost::shared_ptr;
+
+DCIMetadata::DCIMetadata (shared_ptr<const cxml::Node> node)
+{
+ audio_language = node->string_child ("AudioLanguage");
+ subtitle_language = node->string_child ("SubtitleLanguage");
+ territory = node->string_child ("Territory");
+ rating = node->string_child ("Rating");
+ studio = node->string_child ("Studio");
+ facility = node->string_child ("Facility");
+ package_type = node->string_child ("PackageType");
+}
void
-DCIMetadata::write (ostream& f) const
+DCIMetadata::as_xml (xmlpp::Node* root) const
{
- f << N_("audio_language ") << audio_language << N_("\n");
- f << N_("subtitle_language ") << subtitle_language << N_("\n");
- f << N_("territory ") << territory << N_("\n");
- f << N_("rating ") << rating << N_("\n");
- f << N_("studio ") << studio << N_("\n");
- f << N_("facility ") << facility << N_("\n");
- f << N_("package_type ") << package_type << N_("\n");
+ root->add_child("AudioLanguage")->add_child_text (audio_language);
+ root->add_child("SubtitleLanguage")->add_child_text (subtitle_language);
+ root->add_child("Territory")->add_child_text (territory);
+ root->add_child("Rating")->add_child_text (rating);
+ root->add_child("Studio")->add_child_text (studio);
+ root->add_child("Facility")->add_child_text (facility);
+ root->add_child("PackageType")->add_child_text (package_type);
}
void
-DCIMetadata::read (string k, string v)
+DCIMetadata::read_old_metadata (string k, string v)
{
if (k == N_("audio_language")) {
audio_language = v;
*/
-#ifndef DVDOMATIC_DCI_METADATA_H
-#define DVDOMATIC_DCI_METADATA_H
+#ifndef DCPOMATIC_DCI_METADATA_H
+#define DCPOMATIC_DCI_METADATA_H
#include <string>
+#include <libxml++/libxml++.h>
+
+namespace cxml {
+ class Node;
+}
class DCIMetadata
{
public:
- void read (std::string, std::string);
- void write (std::ostream &) const;
+ DCIMetadata () {}
+ DCIMetadata (boost::shared_ptr<const cxml::Node>);
+
+ void as_xml (xmlpp::Node *) const;
+ void read_old_metadata (std::string, std::string);
std::string audio_language;
std::string subtitle_language;
*/
-#ifndef DVDOMATIC_DCP_CONTENT_TYPE_H
-#define DVDOMATIC_DCP_CONTENT_TYPE_H
+#ifndef DCPOMATIC_DCP_CONTENT_TYPE_H
+#define DCPOMATIC_DCP_CONTENT_TYPE_H
/** @file src/content_type.h
* @brief A description of the type of content for a DCP (e.g. feature, trailer etc.)
#include "dcp_video_frame.h"
#include "lut.h"
#include "config.h"
-#include "options.h"
#include "exceptions.h"
#include "server.h"
#include "util.h"
_parameters->tcp_numlayers++;
_parameters->cp_disto_alloc = 1;
_parameters->cp_rsiz = CINEMA2K;
- _parameters->cp_comment = strdup (N_("DVD-o-matic"));
+ _parameters->cp_comment = strdup (N_("DCP-o-matic"));
_parameters->cp_cinema = CINEMA2K_24;
/* 3 components, so use MCT */
*/
#include <iostream>
-#include <stdint.h>
-#include <boost/lexical_cast.hpp>
#include "film.h"
-#include "format.h"
-#include "options.h"
#include "exceptions.h"
-#include "image.h"
#include "util.h"
-#include "log.h"
#include "decoder.h"
-#include "delay_line.h"
-#include "subtitle.h"
-#include "filter_graph.h"
#include "i18n.h"
using std::string;
-using std::stringstream;
-using std::min;
-using std::pair;
-using std::list;
using boost::shared_ptr;
-using boost::optional;
/** @param f Film.
* @param o Decode options.
*/
-Decoder::Decoder (boost::shared_ptr<Film> f, DecodeOptions o)
+Decoder::Decoder (shared_ptr<const Film> f)
: _film (f)
- , _opt (o)
{
_film_connection = f->Changed.connect (bind (&Decoder::film_changed, this, _1));
}
-/** Seek to a position as a source timestamp in seconds.
+/** Seek to a position as a content timestamp in seconds.
* @return true on error.
*/
bool
{
throw DecodeError (N_("decoder does not support seek"));
}
-
-/** Seek so that the next frame we will produce is the same as the last one.
- * @return true on error.
- */
-bool
-Decoder::seek_to_last ()
-{
- throw DecodeError (N_("decoder does not support seek"));
-}
* @brief Parent class for decoders of content.
*/
-#ifndef DVDOMATIC_DECODER_H
-#define DVDOMATIC_DECODER_H
+#ifndef DCPOMATIC_DECODER_H
+#define DCPOMATIC_DECODER_H
#include <vector>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include "util.h"
-#include "stream.h"
#include "video_source.h"
#include "audio_source.h"
#include "film.h"
-#include "options.h"
class Image;
class Log;
class Decoder
{
public:
- Decoder (boost::shared_ptr<Film>, DecodeOptions);
+ Decoder (boost::shared_ptr<const Film>);
virtual ~Decoder () {}
virtual bool pass () = 0;
virtual bool seek (double);
- virtual bool seek_to_last ();
- virtual void seek_back () {}
- virtual void seek_forward () {}
+ virtual bool seek_back () {
+ return true;
+ }
+ virtual bool seek_forward () {
+ return true;
+ }
boost::signals2::signal<void()> OutputChanged;
protected:
- /** our Film */
- boost::shared_ptr<Film> _film;
- /** our decode options */
- DecodeOptions _opt;
+ boost::shared_ptr<const Film> _film;
private:
virtual void film_changed (Film::Property) {}
-
+
boost::signals2::scoped_connection _film_connection;
};
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/decoder_factory.cc
- * @brief A method to create an appropriate decoder for some content.
- */
-
-#include <boost/filesystem.hpp>
-#include "ffmpeg_decoder.h"
-#include "imagemagick_decoder.h"
-#include "film.h"
-#include "sndfile_decoder.h"
-#include "decoder_factory.h"
-
-using std::string;
-using std::pair;
-using std::make_pair;
-using boost::shared_ptr;
-using boost::dynamic_pointer_cast;
-
-Decoders
-decoder_factory (
- shared_ptr<Film> f, DecodeOptions o
- )
-{
- if (f->content().empty()) {
- return Decoders ();
- }
-
- if (boost::filesystem::is_directory (f->content_path()) || f->content_type() == STILL) {
- /* A single image file, or a directory of them */
- return Decoders (
- shared_ptr<VideoDecoder> (new ImageMagickDecoder (f, o)),
- shared_ptr<AudioDecoder> (new SndfileDecoder (f, o))
- );
- }
-
- shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (f, o));
- if (f->use_content_audio()) {
- return Decoders (fd, fd);
- }
-
- return Decoders (fd, shared_ptr<AudioDecoder> (new SndfileDecoder (f, o)));
-}
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DVDOMATIC_DECODER_FACTORY_H
-#define DVDOMATIC_DECODER_FACTORY_H
-
-/** @file src/decoder_factory.h
- * @brief A method to create appropriate decoders for some content.
- */
-
-#include "options.h"
-
-class Film;
-class VideoDecoder;
-class AudioDecoder;
-
-struct Decoders {
- Decoders () {}
-
- Decoders (boost::shared_ptr<VideoDecoder> v, boost::shared_ptr<AudioDecoder> a)
- : video (v)
- , audio (a)
- {}
-
- boost::shared_ptr<VideoDecoder> video;
- boost::shared_ptr<AudioDecoder> audio;
-};
-
-extern Decoders decoder_factory (
- boost::shared_ptr<Film>, DecodeOptions
- );
-
-#endif
#include <libdcp/picture_asset.h>
#include "encoder.h"
#include "util.h"
-#include "options.h"
#include "film.h"
#include "log.h"
#include "exceptions.h"
#include "format.h"
#include "cross.h"
#include "writer.h"
+#include "player.h"
+#include "audio_mapping.h"
#include "i18n.h"
using std::list;
using std::cout;
using std::make_pair;
-using namespace boost;
+using boost::shared_ptr;
+using boost::optional;
int const Encoder::_history_size = 25;
void
Encoder::process_begin ()
{
- if (_film->audio_stream() && _film->audio_stream()->sample_rate() != _film->target_audio_sample_rate()) {
+ if (_film->has_audio() && _film->audio_frame_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());
+ s << String::compose (N_("Will resample audio from %1 to %2"), _film->audio_frame_rate(), _film->target_audio_sample_rate());
_film->log()->log (s.str ());
- /* We will be using planar float data when we call the resampler */
+ /* We will be using planar float data when we call the
+ resampler. As far as I can see, the audio channel
+ layout is not necessary for our purposes; it seems
+ only to be used get the number of channels and
+ decide if rematrixing is needed. It won't be, since
+ input and output layouts are the same.
+ */
+
_swr_context = swr_alloc_set_opts (
0,
- _film->audio_stream()->channel_layout(),
+ av_get_default_channel_layout (_film->audio_mapping().dcp_channels ()),
AV_SAMPLE_FMT_FLTP,
_film->target_audio_sample_rate(),
- _film->audio_stream()->channel_layout(),
+ av_get_default_channel_layout (_film->audio_mapping().dcp_channels ()),
AV_SAMPLE_FMT_FLTP,
- _film->audio_stream()->sample_rate(),
+ _film->audio_frame_rate(),
0, 0
);
Encoder::process_end ()
{
#if HAVE_SWRESAMPLE
- if (_film->audio_stream() && _film->audio_stream()->channels() && _swr_context) {
+ if (_film->has_audio() && _swr_context) {
- shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_stream()->channels(), 256));
+ shared_ptr<AudioBuffers> out (new AudioBuffers (_film->audio_mapping().dcp_channels(), 256));
while (1) {
int const frames = swr_convert (_swr_context, (uint8_t **) out->data(), 256, 0, 0);
}
out->set_frames (frames);
- write_audio (out);
+ _writer->write (out);
}
swr_free (&_swr_context);
* 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) {
}
void
-Encoder::process_video (shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub)
+Encoder::process_video (shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub)
{
- FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate());
+ FrameRateConversion frc (_film->video_frame_rate(), _film->dcp_frame_rate());
if (frc.skip && (_video_frames_in % 2)) {
++_video_frames_in;
/* Queue this new frame for encoding */
pair<string, string> const s = Filter::ffmpeg_strings (_film->filters());
TIMING ("adding to queue of %1", _queue.size ());
- _queue.push_back (boost::shared_ptr<DCPVideoFrame> (
+ _queue.push_back (shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_film),
_film->subtitle_offset(), _film->subtitle_scale(),
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;
+ int const max_resampled_frames = ceil ((int64_t) data->frames() * _film->target_audio_sample_rate() / _film->audio_frame_rate()) + 32;
- shared_ptr<AudioBuffers> resampled (new AudioBuffers (_film->audio_stream()->channels(), max_resampled_frames));
+ shared_ptr<AudioBuffers> resampled (new AudioBuffers (_film->audio_mapping().dcp_channels(), max_resampled_frames));
/* Resample audio */
int const resampled_frames = swr_convert (
}
#endif
- write_audio (data);
+ _writer->write (data);
}
void
}
TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _queue.size());
- boost::shared_ptr<DCPVideoFrame> vf = _queue.front ();
+ shared_ptr<DCPVideoFrame> 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 ();
}
if (remote_backoff > 0) {
- dvdomatic_sleep (remote_backoff);
+ dcpomatic_sleep (remote_backoff);
}
lock.lock ();
_condition.notify_all ();
}
}
-
-void
-Encoder::write_audio (shared_ptr<const AudioBuffers> data)
-{
- AudioMapping m (_film->audio_channels ());
- if (m.dcp_channels() != _film->audio_channels()) {
-
- /* Remap (currently just for mono -> 5.1) */
-
- shared_ptr<AudioBuffers> b (new AudioBuffers (m.dcp_channels(), data->frames ()));
- for (int i = 0; i < m.dcp_channels(); ++i) {
- optional<int> s = m.dcp_to_source (static_cast<libdcp::Channel> (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);
-}
*/
-#ifndef DVDOMATIC_ENCODER_H
-#define DVDOMATIC_ENCODER_H
+#ifndef DCPOMATIC_ENCODER_H
+#define DCPOMATIC_ENCODER_H
/** @file src/encoder.h
* @brief Encoder to J2K and WAV for DCP.
/** Called when a processing run has finished */
virtual void process_end ();
- float current_frames_per_second () const;
+ float current_encoding_rate () const;
int video_frames_out () const;
private:
void frame_done ();
- void write_audio (boost::shared_ptr<const AudioBuffers> data);
-
void encoder_thread (ServerDescription *);
void terminate_threads ();
static int const _history_size;
/** Number of video frames received so far */
- SourceFrame _video_frames_in;
+ ContentVideoFrame _video_frames_in;
/** Number of video frames written for the DCP so far */
int _video_frames_out;
*/
-/** @file src/examine_content_job.cc
- * @brief A class to run through content at high speed to find its length.
- */
-
#include <boost/filesystem.hpp>
#include "examine_content_job.h"
-#include "options.h"
-#include "decoder_factory.h"
-#include "decoder.h"
-#include "transcoder.h"
#include "log.h"
-#include "film.h"
-#include "video_decoder.h"
+#include "content.h"
#include "i18n.h"
using std::string;
-using std::vector;
-using std::pair;
using boost::shared_ptr;
-ExamineContentJob::ExamineContentJob (shared_ptr<Film> f)
+ExamineContentJob::ExamineContentJob (shared_ptr<Film> f, shared_ptr<Content> c, bool q)
: Job (f)
+ , _content (c)
+ , _quick (q)
{
}
string
ExamineContentJob::name () const
{
- if (_film->name().empty ()) {
- return _("Examine content");
- }
-
- return String::compose (_("Examine content of %1"), _film->name());
+ return _("Examine content");
}
void
ExamineContentJob::run ()
{
- descend (0.5);
- _film->set_content_digest (md5_digest (_film->content_path ()));
- ascend ();
-
- descend (0.5);
-
- /* Set the film's length to either
- a) a length judged by running through the content or
- b) the length from a decoder's header.
- */
- if (!_film->trust_content_header()) {
- /* Decode the content to get an accurate length */
-
- /* We don't want to use any existing length here, as progress
- will be messed up.
- */
- _film->unset_length ();
- _film->set_crop (Crop ());
-
- DecodeOptions o;
- o.decode_audio = false;
-
- Decoders decoders = decoder_factory (_film, o);
-
- set_progress_unknown ();
- while (!decoders.video->pass()) {
- /* keep going */
- }
-
- _film->set_length (decoders.video->video_frame());
-
- _film->log()->log (String::compose (N_("Video length examined as %1 frames"), _film->length().get()));
-
- } else {
-
- /* Get a quick decoder to get the content's length from its header */
-
- Decoders d = decoder_factory (_film, DecodeOptions());
- _film->set_length (d.video->length());
-
- _film->log()->log (String::compose (N_("Video length obtained from header as %1 frames"), _film->length().get()));
- }
-
- ascend ();
+ _content->examine (_film, shared_from_this (), _quick);
set_progress (1);
set_state (FINISHED_OK);
}
*/
-/** @file src/examine_content_job.h
- * @brief A class to obtain the length and MD5 digest of a content file.
- */
-
+#include <boost/shared_ptr.hpp>
#include "job.h"
-/** @class ExamineContentJob
- * @brief A class to obtain the length and MD5 digest of a content file.
- */
+class Content;
+class Log;
+
class ExamineContentJob : public Job
{
public:
- ExamineContentJob (boost::shared_ptr<Film>);
+ ExamineContentJob (boost::shared_ptr<Film>, boost::shared_ptr<Content>, bool);
~ExamineContentJob ();
std::string name () const;
void run ();
+
+private:
+ boost::shared_ptr<Content> _content;
+ bool _quick;
};
*/
-#ifndef DVDOMATIC_EXCEPTIONS_H
-#define DVDOMATIC_EXCEPTIONS_H
+#ifndef DCPOMATIC_EXCEPTIONS_H
+#define DCPOMATIC_EXCEPTIONS_H
/** @file src/exceptions.h
* @brief Our exceptions.
{
public:
/** @param f File that we were trying to open */
+ /* XXX: should be boost::filesystem::path */
OpenFileError (std::string f);
};
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include "ffmpeg_content.h"
+#include "ffmpeg_decoder.h"
+#include "compose.hpp"
+#include "job.h"
+#include "util.h"
+#include "log.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::stringstream;
+using std::vector;
+using std::list;
+using std::cout;
+using boost::shared_ptr;
+using boost::lexical_cast;
+
+int const FFmpegContentProperty::SUBTITLE_STREAMS = 100;
+int const FFmpegContentProperty::SUBTITLE_STREAM = 101;
+int const FFmpegContentProperty::AUDIO_STREAMS = 102;
+int const FFmpegContentProperty::AUDIO_STREAM = 103;
+
+FFmpegContent::FFmpegContent (boost::filesystem::path f)
+ : Content (f)
+ , VideoContent (f)
+ , AudioContent (f)
+{
+
+}
+
+FFmpegContent::FFmpegContent (shared_ptr<const cxml::Node> node)
+ : Content (node)
+ , VideoContent (node)
+ , AudioContent (node)
+{
+ list<shared_ptr<cxml::Node> > c = node->node_children ("SubtitleStream");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
+ _subtitle_streams.push_back (FFmpegSubtitleStream (*i));
+ if ((*i)->optional_number_child<int> ("Selected")) {
+ _subtitle_stream = _subtitle_streams.back ();
+ }
+ }
+
+ c = node->node_children ("AudioStream");
+ for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) {
+ _audio_streams.push_back (FFmpegAudioStream (*i));
+ if ((*i)->optional_number_child<int> ("Selected")) {
+ _audio_stream = _audio_streams.back ();
+ }
+ }
+}
+
+FFmpegContent::FFmpegContent (FFmpegContent const & o)
+ : Content (o)
+ , VideoContent (o)
+ , AudioContent (o)
+ , _subtitle_streams (o._subtitle_streams)
+ , _subtitle_stream (o._subtitle_stream)
+ , _audio_streams (o._audio_streams)
+ , _audio_stream (o._audio_stream)
+{
+
+}
+
+void
+FFmpegContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("FFmpeg");
+ Content::as_xml (node);
+ VideoContent::as_xml (node);
+
+ boost::mutex::scoped_lock lm (_mutex);
+
+ for (vector<FFmpegSubtitleStream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
+ xmlpp::Node* t = node->add_child("SubtitleStream");
+ if (_subtitle_stream && *i == _subtitle_stream.get()) {
+ t->add_child("Selected")->add_child_text("1");
+ }
+ i->as_xml (t);
+ }
+
+ for (vector<FFmpegAudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
+ xmlpp::Node* t = node->add_child("AudioStream");
+ if (_audio_stream && *i == _audio_stream.get()) {
+ t->add_child("Selected")->add_child_text("1");
+ }
+ i->as_xml (t);
+ }
+}
+
+void
+FFmpegContent::examine (shared_ptr<Film> film, shared_ptr<Job> job, bool quick)
+{
+ job->set_progress_unknown ();
+
+ Content::examine (film, job, quick);
+
+ shared_ptr<FFmpegDecoder> decoder (new FFmpegDecoder (film, shared_from_this (), true, false, false));
+
+ ContentVideoFrame video_length = 0;
+ if (quick) {
+ video_length = decoder->video_length ();
+ film->log()->log (String::compose ("Video length obtained from header as %1 frames", decoder->video_length ()));
+ } else {
+ while (!decoder->pass ()) {
+ /* keep going */
+ }
+
+ video_length = decoder->video_frame ();
+ film->log()->log (String::compose ("Video length examined as %1 frames", decoder->video_frame ()));
+ }
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+
+ _video_length = video_length;
+
+ _subtitle_streams = decoder->subtitle_streams ();
+ if (!_subtitle_streams.empty ()) {
+ _subtitle_stream = _subtitle_streams.front ();
+ }
+
+ _audio_streams = decoder->audio_streams ();
+ if (!_audio_streams.empty ()) {
+ _audio_stream = _audio_streams.front ();
+ }
+ }
+
+ take_from_video_decoder (decoder);
+
+ signal_changed (VideoContentProperty::VIDEO_LENGTH);
+ signal_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
+ signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
+ signal_changed (FFmpegContentProperty::AUDIO_STREAMS);
+ signal_changed (FFmpegContentProperty::AUDIO_STREAM);
+ signal_changed (AudioContentProperty::AUDIO_CHANNELS);
+}
+
+string
+FFmpegContent::summary () const
+{
+ return String::compose (_("Movie: %1"), file().filename().string());
+}
+
+string
+FFmpegContent::information () const
+{
+ if (video_length() == 0 || video_frame_rate() == 0) {
+ return "";
+ }
+
+ stringstream s;
+
+ s << String::compose (_("%1 frames; %2 frames per second"), video_length(), video_frame_rate()) << "\n";
+ s << VideoContent::information ();
+
+ return s.str ();
+}
+
+void
+FFmpegContent::set_subtitle_stream (FFmpegSubtitleStream s)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _subtitle_stream = s;
+ }
+
+ signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
+}
+
+void
+FFmpegContent::set_audio_stream (FFmpegAudioStream s)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _audio_stream = s;
+ }
+
+ signal_changed (FFmpegContentProperty::AUDIO_STREAM);
+}
+
+ContentAudioFrame
+FFmpegContent::audio_length () const
+{
+ if (!_audio_stream) {
+ return 0;
+ }
+
+ return video_frames_to_audio_frames (_video_length, audio_frame_rate(), video_frame_rate());
+}
+
+int
+FFmpegContent::audio_channels () const
+{
+ if (!_audio_stream) {
+ return 0;
+ }
+
+ return _audio_stream->channels;
+}
+
+int
+FFmpegContent::audio_frame_rate () const
+{
+ if (!_audio_stream) {
+ return 0;
+ }
+
+ return _audio_stream->frame_rate;
+}
+
+bool
+operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b)
+{
+ return a.id == b.id;
+}
+
+bool
+operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b)
+{
+ return a.id == b.id;
+}
+
+FFmpegAudioStream::FFmpegAudioStream (shared_ptr<const cxml::Node> node)
+{
+ name = node->string_child ("Name");
+ id = node->number_child<int> ("Id");
+ frame_rate = node->number_child<int> ("FrameRate");
+ channels = node->number_child<int64_t> ("Channels");
+}
+
+void
+FFmpegAudioStream::as_xml (xmlpp::Node* root) const
+{
+ root->add_child("Name")->add_child_text (name);
+ root->add_child("Id")->add_child_text (lexical_cast<string> (id));
+ root->add_child("FrameRate")->add_child_text (lexical_cast<string> (frame_rate));
+ root->add_child("Channels")->add_child_text (lexical_cast<string> (channels));
+}
+
+/** Construct a SubtitleStream from a value returned from to_string().
+ * @param t String returned from to_string().
+ * @param v State file version.
+ */
+FFmpegSubtitleStream::FFmpegSubtitleStream (shared_ptr<const cxml::Node> node)
+{
+ name = node->string_child ("Name");
+ id = node->number_child<int> ("Id");
+}
+
+void
+FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
+{
+ root->add_child("Name")->add_child_text (name);
+ root->add_child("Id")->add_child_text (lexical_cast<string> (id));
+}
+
+shared_ptr<Content>
+FFmpegContent::clone () const
+{
+ return shared_ptr<Content> (new FFmpegContent (*this));
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_FFMPEG_CONTENT_H
+#define DCPOMATIC_FFMPEG_CONTENT_H
+
+#include <boost/enable_shared_from_this.hpp>
+#include "video_content.h"
+#include "audio_content.h"
+
+class FFmpegAudioStream
+{
+public:
+ FFmpegAudioStream (std::string n, int i, int f, int c)
+ : name (n)
+ , id (i)
+ , frame_rate (f)
+ , channels (c)
+ {}
+
+ FFmpegAudioStream (boost::shared_ptr<const cxml::Node>);
+
+ void as_xml (xmlpp::Node *) const;
+
+ std::string name;
+ int id;
+ int frame_rate;
+ int channels;
+};
+
+extern bool operator== (FFmpegAudioStream const & a, FFmpegAudioStream const & b);
+
+class FFmpegSubtitleStream
+{
+public:
+ FFmpegSubtitleStream (std::string n, int i)
+ : name (n)
+ , id (i)
+ {}
+
+ FFmpegSubtitleStream (boost::shared_ptr<const cxml::Node>);
+
+ void as_xml (xmlpp::Node *) const;
+
+ std::string name;
+ int id;
+};
+
+extern bool operator== (FFmpegSubtitleStream const & a, FFmpegSubtitleStream const & b);
+
+class FFmpegContentProperty : public VideoContentProperty
+{
+public:
+ static int const SUBTITLE_STREAMS;
+ static int const SUBTITLE_STREAM;
+ static int const AUDIO_STREAMS;
+ static int const AUDIO_STREAM;
+};
+
+class FFmpegContent : public VideoContent, public AudioContent
+{
+public:
+ FFmpegContent (boost::filesystem::path);
+ FFmpegContent (boost::shared_ptr<const cxml::Node>);
+ FFmpegContent (FFmpegContent const &);
+
+ boost::shared_ptr<FFmpegContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<FFmpegContent> (Content::shared_from_this ());
+ }
+
+ void examine (boost::shared_ptr<Film>, boost::shared_ptr<Job>, bool);
+ std::string summary () const;
+ std::string information () const;
+ void as_xml (xmlpp::Node *) const;
+ boost::shared_ptr<Content> clone () const;
+
+ /* AudioContent */
+ int audio_channels () const;
+ ContentAudioFrame audio_length () const;
+ int audio_frame_rate () const;
+
+ std::vector<FFmpegSubtitleStream> subtitle_streams () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _subtitle_streams;
+ }
+
+ boost::optional<FFmpegSubtitleStream> subtitle_stream () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _subtitle_stream;
+ }
+
+ std::vector<FFmpegAudioStream> audio_streams () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_streams;
+ }
+
+ boost::optional<FFmpegAudioStream> audio_stream () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_stream;
+ }
+
+ void set_subtitle_stream (FFmpegSubtitleStream);
+ void set_audio_stream (FFmpegAudioStream);
+
+private:
+ std::vector<FFmpegSubtitleStream> _subtitle_streams;
+ boost::optional<FFmpegSubtitleStream> _subtitle_stream;
+ std::vector<FFmpegAudioStream> _audio_streams;
+ boost::optional<FFmpegAudioStream> _audio_stream;
+};
+
+#endif
#include "transcoder.h"
#include "job.h"
#include "filter.h"
-#include "options.h"
#include "exceptions.h"
#include "image.h"
#include "util.h"
using boost::dynamic_pointer_cast;
using libdcp::Size;
-FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, DecodeOptions o)
- : Decoder (f, o)
- , VideoDecoder (f, o)
- , AudioDecoder (f, o)
+boost::mutex FFmpegDecoder::_mutex;
+
+FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegContent> c, bool video, bool audio, bool subtitles)
+ : Decoder (f)
+ , VideoDecoder (f)
+ , AudioDecoder (f)
+ , _ffmpeg_content (c)
, _format_context (0)
, _video_stream (-1)
, _frame (0)
, _audio_codec (0)
, _subtitle_codec_context (0)
, _subtitle_codec (0)
+ , _decode_video (video)
+ , _decode_audio (audio)
+ , _decode_subtitles (subtitles)
{
setup_general ();
setup_video ();
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);
}
{
av_register_all ();
- if (avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0) < 0) {
- throw OpenFileError (_film->content_path ());
+ if (avformat_open_input (&_format_context, _ffmpeg_content->file().string().c_str(), 0, 0) < 0) {
+ throw OpenFileError (_ffmpeg_content->file().string ());
}
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 */
+ /* Find video, audio and subtitle streams */
for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
AVStream* s = _format_context->streams[i];
}
_audio_streams.push_back (
- shared_ptr<AudioStream> (
- new FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channel_layout)
- )
+ FFmpegAudioStream (stream_name (s), i, s->codec->sample_rate, s->codec->channels)
);
} else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
- _subtitle_streams.push_back (
- shared_ptr<SubtitleStream> (
- new SubtitleStream (stream_name (s), i)
- )
- );
+ _subtitle_streams.push_back (FFmpegSubtitleStream (stream_name (s), i));
}
}
void
FFmpegDecoder::setup_video ()
{
+ boost::mutex::scoped_lock lm (_mutex);
+
_video_codec_context = _format_context->streams[_video_stream]->codec;
_video_codec = avcodec_find_decoder (_video_codec_context->codec_id);
void
FFmpegDecoder::setup_audio ()
{
- if (!_audio_stream) {
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (!_ffmpeg_content->audio_stream ()) {
return;
}
- shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
- assert (ffa);
-
- _audio_codec_context = _format_context->streams[ffa->id()]->codec;
+ _audio_codec_context = _format_context->streams[_ffmpeg_content->audio_stream()->id]->codec;
_audio_codec = avcodec_find_decoder (_audio_codec_context->codec_id);
if (_audio_codec == 0) {
void
FFmpegDecoder::setup_subtitle ()
{
- if (!_subtitle_stream || _subtitle_stream->id() >= int (_format_context->nb_streams)) {
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (!_ffmpeg_content->subtitle_stream() || _ffmpeg_content->subtitle_stream()->id >= int (_format_context->nb_streams)) {
return;
}
- _subtitle_codec_context = _format_context->streams[_subtitle_stream->id()]->codec;
+ _subtitle_codec_context = _format_context->streams[_ffmpeg_content->subtitle_stream()->id]->codec;
_subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
if (_subtitle_codec == 0) {
/* XXX: should we reset _packet.data and size after each *_decode_* call? */
int frame_finished;
-
- if (_opt.decode_video) {
+
+ if (_decode_video) {
while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
filter_and_emit_video ();
}
}
-
- if (_audio_stream && _opt.decode_audio) {
+
+ if (_ffmpeg_content->audio_stream() && _decode_audio) {
decode_audio_packet ();
}
avcodec_get_frame_defaults (_frame);
- shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
-
- if (_packet.stream_index == _video_stream && _opt.decode_video) {
+ if (_packet.stream_index == _video_stream && _decode_video) {
int frame_finished;
int const r = avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet);
filter_and_emit_video ();
}
- } else if (ffa && _packet.stream_index == ffa->id() && _opt.decode_audio) {
+ } 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 && _decode_subtitles) {
int got_subtitle;
AVSubtitle sub;
shared_ptr<AudioBuffers>
FFmpegDecoder::deinterleave_audio (uint8_t** data, int size)
{
- assert (_film->audio_channels());
+ assert (_ffmpeg_content->audio_channels());
assert (bytes_per_audio_sample());
- shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_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<AudioBuffers> audio (new AudioBuffers (ffa->channels(), frames));
+ int const frames = total_samples / _ffmpeg_content->audio_channels();
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (_ffmpeg_content->audio_channels(), frames));
switch (audio_sample_format()) {
case AV_SAMPLE_FMT_S16:
audio->data(channel)[sample] = float(*p++) / (1 << 15);
++channel;
- if (channel == _film->audio_channels()) {
+ if (channel == _ffmpeg_content->audio_channels()) {
channel = 0;
++sample;
}
case AV_SAMPLE_FMT_S16P:
{
int16_t** p = reinterpret_cast<int16_t **> (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<float>(p[i][j]) / (1 << 15);
}
audio->data(channel)[sample] = static_cast<float>(*p++) / (1 << 31);
++channel;
- if (channel == _film->audio_channels()) {
+ if (channel == _ffmpeg_content->audio_channels()) {
channel = 0;
++sample;
}
audio->data(channel)[sample] = *p++;
++channel;
- if (channel == _film->audio_channels()) {
+ if (channel == _ffmpeg_content->audio_channels()) {
channel = 0;
++sample;
}
case AV_SAMPLE_FMT_FLTP:
{
float** p = reinterpret_cast<float**> (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));
}
}
}
float
-FFmpegDecoder::frames_per_second () const
+FFmpegDecoder::video_frame_rate () const
{
AVStream* s = _format_context->streams[_video_stream];
return av_get_bytes_per_sample (audio_sample_format ());
}
-void
-FFmpegDecoder::set_audio_stream (shared_ptr<AudioStream> s)
-{
- AudioDecoder::set_audio_stream (s);
- setup_audio ();
-}
-
-void
-FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
-{
- VideoDecoder::set_subtitle_stream (s);
- setup_subtitle ();
- OutputChanged ();
-}
-
void
FFmpegDecoder::filter_and_emit_video ()
{
}
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);
+ if (last_content_time() < 2.5) {
+ return true;
+ }
+
+ return do_seek (last_content_time() - 2.5 / video_frame_rate(), true, true);
}
-void
+bool
FFmpegDecoder::seek_forward ()
{
- do_seek (last_source_time() - 0.5 / frames_per_second(), true, true);
+ if (last_content_time() >= (video_length() - video_frame_rate())) {
+ return true;
+ }
+
+ return do_seek (last_content_time() - 0.5 / video_frame_rate(), true, true);
}
bool
return r < 0;
}
-shared_ptr<FFmpegAudioStream>
-FFmpegAudioStream::create (string t, optional<int> v)
-{
- if (!v) {
- /* version < 1; no type in the string, and there's only FFmpeg streams anyway */
- return shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (t, v));
- }
-
- stringstream s (t);
- string type;
- s >> type;
- if (type != N_("ffmpeg")) {
- return shared_ptr<FFmpegAudioStream> ();
- }
-
- return shared_ptr<FFmpegAudioStream> (new FFmpegAudioStream (t, v));
-}
-
-FFmpegAudioStream::FFmpegAudioStream (string t, optional<int> version)
-{
- 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"));
- }
-
- for (int i = 0; i < name_index; ++i) {
- size_t const s = t.find (' ');
- if (s != string::npos) {
- t = t.substr (s + 1);
- }
- }
-
- _name = t;
-}
-
-string
-FFmpegAudioStream::to_string () const
-{
- return String::compose (N_("ffmpeg %1 %2 %3 %4"), _id, _sample_rate, _channel_layout, _name);
-}
-
void
FFmpegDecoder::film_changed (Film::Property p)
{
boost::mutex::scoped_lock lm (_filter_graphs_mutex);
_filter_graphs.clear ();
}
- OutputChanged ();
break;
default:
}
/** @return Length (in video frames) according to our content's header */
-SourceFrame
-FFmpegDecoder::length () const
+ContentVideoFrame
+FFmpegDecoder::video_length () const
{
- return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second();
+ return (double(_format_context->duration) / AV_TIME_BASE) * video_frame_rate();
}
void
FFmpegDecoder::decode_audio_packet ()
{
- shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
- assert (ffa);
-
/* Audio packets can contain multiple frames, so we may have to call avcodec_decode_audio4
several times.
*/
0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
);
- assert (_audio_codec_context->channels == _film->audio_channels());
+ assert (_audio_codec_context->channels == _ffmpeg_content->audio_channels());
Audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds);
}
#include "video_decoder.h"
#include "audio_decoder.h"
#include "film.h"
+#include "ffmpeg_content.h"
struct AVFilterGraph;
struct AVCodecContext;
class Image;
class Log;
-class FFmpegAudioStream : public AudioStream
-{
-public:
- FFmpegAudioStream (std::string n, int i, int s, int64_t c)
- : AudioStream (s, c)
- , _name (n)
- , _id (i)
- {}
-
- std::string to_string () const;
-
- std::string name () const {
- return _name;
- }
-
- int id () const {
- return _id;
- }
-
- static boost::shared_ptr<FFmpegAudioStream> create (std::string t, boost::optional<int> v);
-
-private:
- friend class stream_test;
-
- FFmpegAudioStream (std::string t, boost::optional<int> v);
-
- std::string _name;
- int _id;
-};
-
/** @class FFmpegDecoder
* @brief A decoder using FFmpeg to decode content.
*/
class FFmpegDecoder : public VideoDecoder, public AudioDecoder
{
public:
- FFmpegDecoder (boost::shared_ptr<Film>, DecodeOptions);
+ FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const FFmpegContent>, bool video, bool audio, bool subtitles);
~FFmpegDecoder ();
- float frames_per_second () const;
+ float video_frame_rate () const;
libdcp::Size native_size () const;
- SourceFrame length () const;
+ ContentVideoFrame video_length () const;
int time_base_numerator () const;
int time_base_denominator () const;
int sample_aspect_ratio_numerator () const;
int sample_aspect_ratio_denominator () const;
- void set_audio_stream (boost::shared_ptr<AudioStream>);
- void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
+ std::vector<FFmpegSubtitleStream> subtitle_streams () const {
+ return _subtitle_streams;
+ }
+
+ std::vector<FFmpegAudioStream> audio_streams () const {
+ return _audio_streams;
+ }
bool seek (double);
- bool seek_to_last ();
- void seek_forward ();
- void seek_back ();
+ bool seek_forward ();
+ bool seek_back ();
+ bool pass ();
private:
- bool pass ();
- bool do_seek (double p, bool, bool);
+ /* No copy construction */
+ FFmpegDecoder (FFmpegDecoder const &);
+ FFmpegDecoder& operator= (FFmpegDecoder const &);
+
PixelFormat pixel_format () const;
AVSampleFormat audio_sample_format () const;
int bytes_per_audio_sample () const;
+ bool do_seek (double, bool, bool);
void filter_and_emit_video ();
std::string stream_name (AVStream* s) const;
+ boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
+
AVFormatContext* _format_context;
int _video_stream;
std::list<boost::shared_ptr<FilterGraph> > _filter_graphs;
boost::mutex _filter_graphs_mutex;
+
+ std::vector<FFmpegSubtitleStream> _subtitle_streams;
+ std::vector<FFmpegAudioStream> _audio_streams;
+
+ bool _decode_video;
+ bool _decode_audio;
+ bool _decode_subtitles;
+
+ /* It would appear (though not completely verified) that one must have
+ a mutex around calls to avcodec_open* and avcodec_close... and here
+ it is.
+ */
+ static boost::mutex _mutex;
};
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time.hpp>
+#include <libxml++/libxml++.h>
+#include <libcxml/cxml.h>
#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 "i18n.h"
using std::min;
using std::make_pair;
using std::endl;
+using std::cout;
+using std::list;
using boost::shared_ptr;
using boost::lexical_cast;
using boost::to_upper_copy;
*/
Film::Film (string d, bool must_exist)
- : _use_dci_name (true)
- , _trust_content_header (true)
+ : _playlist (new Playlist)
+ , _use_dci_name (true)
+ , _trust_content_headers (true)
, _dcp_content_type (Config::instance()->default_dcp_content_type ())
, _format (Config::instance()->default_format ())
, _scaler (Scaler::from_id ("bicubic"))
, _trim_start (0)
, _trim_end (0)
, _trim_type (CPL)
- , _dcp_ab (false)
- , _use_content_audio (true)
+ , _ab (false)
, _audio_gain (0)
, _audio_delay (0)
- , _still_duration (10)
, _with_subtitles (false)
, _subtitle_offset (0)
, _subtitle_scale (1)
, _j2k_bandwidth (200000000)
, _dci_metadata (Config::instance()->default_dci_metadata ())
, _dcp_frame_rate (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)
}
}
- _sndfile_stream = SndfileStream::create ();
-
if (must_exist) {
read_metadata ();
} else {
: boost::enable_shared_from_this<Film> (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)
+ , _trust_content_headers (o._trust_content_headers)
, _dcp_content_type (o._dcp_content_type)
, _format (o._format)
, _crop (o._crop)
, _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)
+ , _ab (o._ab)
, _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)
- , _dci_date (o._dci_date)
, _dcp_frame_rate (o._dcp_frame_rate)
- , _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)
+ , _dci_date (o._dci_date)
, _dirty (o._dirty)
{
-
-}
-
-Film::~Film ()
-{
-
+ _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
}
string
stringstream s;
s << format()->id()
- << "_" << content_digest()
+ << "_" << _playlist->video_digest()
<< "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
<< "_" << _dcp_frame_rate
<< "_" << f.first << "_" << f.second
<< "_" << j2k_bandwidth()
<< "_" << boost::lexical_cast<int> (colour_lut());
- if (dcp_ab()) {
+ if (ab()) {
pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
}
{
boost::filesystem::path p;
p /= "analysis";
- p /= content_digest();
+ p /= _playlist->audio_digest();
return file (p.string ());
}
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];
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()));
-#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.");
throw MissingSettingError (_("format"));
}
- if (content().empty ()) {
+ if (_playlist->content().empty ()) {
throw MissingSettingError (_("content"));
}
throw MissingSettingError (_("name"));
}
- DecodeOptions od;
- od.decode_subtitles = with_subtitles ();
-
shared_ptr<Job> r;
- if (dcp_ab()) {
- r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od)));
+ if (ab()) {
+ r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this())));
} else {
- r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od)));
+ r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
}
}
-/** Start a job to analyse the audio of our content file */
+/** Start a job to analyse the audio in our Playlist */
void
Film::analyse_audio ()
{
JobManager::instance()->add (_analyse_audio_job);
}
-/** Start a job to examine our content file */
+/** Start a job to examine a piece of content */
void
-Film::examine_content ()
+Film::examine_content (shared_ptr<Content> c)
{
- 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);
+ shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c, trust_content_headers ()));
+ JobManager::instance()->add (j);
}
void
_analyse_audio_job.reset ();
}
-void
-Film::examine_content_finished ()
-{
- _examine_content_job.reset ();
-}
-
/** Start a job to send our DCP to the configured TMS */
void
Film::send_dcp_to_tms ()
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 (boost::lexical_cast<string> (state_version));
+ root->add_child("Name")->add_child_text (_name);
+ root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
+ root->add_child("TrustContentHeaders")->add_child_text (_trust_content_headers ? "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;
+ root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ());
}
+
if (_format) {
- f << "format " << _format->as_metadata () << endl;
+ root->add_child("Format")->add_child_text (_format->id ());
}
- 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<Filter const *>::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";
+ root->add_child("TrimType")->add_child_text ("CPL");
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;
- }
- for (vector<string>::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 << "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<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
- f << "content_audio_stream " << (*i)->to_string () << endl;
+ root->add_child("TrimType")->add_child_text ("Encode");
}
+
+ root->add_child("LeftCrop")->add_child_text (boost::lexical_cast<string> (_crop.left));
+ root->add_child("RightCrop")->add_child_text (boost::lexical_cast<string> (_crop.right));
+ root->add_child("TopCrop")->add_child_text (boost::lexical_cast<string> (_crop.top));
+ root->add_child("BottomCrop")->add_child_text (boost::lexical_cast<string> (_crop.bottom));
- f << "external_audio_stream " << _sndfile_stream->to_string() << endl;
-
- for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
- f << "subtitle_stream " << (*i)->to_string () << endl;
+ for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
+ root->add_child("Filter")->add_child_text ((*i)->id ());
}
-
- f << "source_frame_rate " << _source_frame_rate << endl;
+
+ root->add_child("Scaler")->add_child_text (_scaler->id ());
+ root->add_child("TrimStart")->add_child_text (boost::lexical_cast<string> (_trim_start));
+ root->add_child("TrimEnd")->add_child_text (boost::lexical_cast<string> (_trim_end));
+ root->add_child("AB")->add_child_text (_ab ? "1" : "0");
+ root->add_child("AudioGain")->add_child_text (boost::lexical_cast<string> (_audio_gain));
+ root->add_child("AudioDelay")->add_child_text (boost::lexical_cast<string> (_audio_delay));
+ root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
+ root->add_child("SubtitleOffset")->add_child_text (boost::lexical_cast<string> (_subtitle_offset));
+ root->add_child("SubtitleScale")->add_child_text (boost::lexical_cast<string> (_subtitle_scale));
+ root->add_child("ColourLUT")->add_child_text (boost::lexical_cast<string> (_colour_lut));
+ root->add_child("J2KBandwidth")->add_child_text (boost::lexical_cast<string> (_j2k_bandwidth));
+ _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
+ root->add_child("DCPFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_frame_rate));
+ root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
+ _audio_mapping.as_xml (root->add_child("AudioMapping"));
+ _playlist->as_xml (root->add_child ("Playlist"));
+
+ doc.write_to_file_formatted (file ("metadata.xml"));
_dirty = false;
}
boost::mutex::scoped_lock lm (_state_mutex);
LocaleGuard lg;
- _external_audio.clear ();
- _content_audio_streams.clear ();
- _subtitle_streams.clear ();
-
- boost::optional<int> version;
-
- /* Backward compatibility things */
- boost::optional<int> audio_sample_rate;
- boost::optional<int> audio_stream_index;
- boost::optional<int> 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<string, string> kv = read_key_value (f);
- /* We need version before anything else */
- multimap<string, string>::iterator v = kv.find ("version");
- if (v != kv.end ()) {
- version = atoi (v->second.c_str());
- }
+ cxml::File f (file ("metadata.xml"), "Metadata");
- for (multimap<string, string>::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");
+ _trust_content_headers = f.bool_child ("TrustContentHeaders");
- 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 ());
+ {
+ optional<string> 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<string> c = f.optional_string_child ("Format");
+ if (c) {
+ _format = Format::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<shared_ptr<AudioStream> >::iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
- (*i)->set_sample_rate (audio_sample_rate.get());
- }
+ {
+ optional<string> c = f.optional_string_child ("TrimType");
+ if (!c || c.get() == "CPL") {
+ _trim_type = CPL;
+ } else if (c && c.get() == "Encode") {
+ _trim_type = ENCODE;
}
+ }
- /* 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()];
- }
+ _crop.left = f.number_child<int> ("LeftCrop");
+ _crop.right = f.number_child<int> ("RightCrop");
+ _crop.top = f.number_child<int> ("TopCrop");
+ _crop.bottom = f.number_child<int> ("BottomCrop");
- /* 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()];
+ {
+ list<shared_ptr<cxml::Node> > c = f.node_children ("Filter");
+ for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
+ _filters.push_back (Filter::from_id ((*i)->content ()));
}
}
-
+
+ _scaler = Scaler::from_id (f.string_child ("Scaler"));
+ _trim_start = f.number_child<int> ("TrimStart");
+ _trim_end = f.number_child<int> ("TrimEnd");
+ _ab = f.bool_child ("AB");
+ _audio_gain = f.number_child<float> ("AudioGain");
+ _audio_delay = f.number_child<int> ("AudioDelay");
+ _with_subtitles = f.bool_child ("WithSubtitles");
+ _subtitle_offset = f.number_child<float> ("SubtitleOffset");
+ _subtitle_scale = f.number_child<float> ("SubtitleScale");
+ _colour_lut = f.number_child<int> ("ColourLUT");
+ _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
+ _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
+ _dcp_frame_rate = f.number_child<int> ("DCPFrameRate");
+ _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
+
+ _playlist->set_from_xml (f.node_child ("Playlist"));
+ _audio_mapping.set_from_xml (_playlist->content(), f.node_child ("AudioMapping"));
+
_dirty = false;
}
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()) {
+ if (!has_audio ()) {
return 0;
}
/* Resample to a DCI-approved sample rate */
- double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
+ double t = dcp_audio_sample_rate (audio_frame_rate());
- FrameRateConversion frc (source_frame_rate(), dcp_frame_rate());
+ FrameRateConversion frc (video_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
*/
if (frc.change_speed) {
- t *= source_frame_rate() * frc.factor() / dcp_frame_rate();
+ t *= video_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
}
}
- switch (audio_channels()) {
+ switch (audio_channels ()) {
case 1:
d << "_10";
break;
}
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;
- }
-
- /* Reset streams here in case the new content doesn't have one or the other */
- _content_audio_stream = shared_ptr<AudioStream> ();
- _subtitle_stream = shared_ptr<SubtitleStream> ();
-
- /* 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 */
- switch (content_type()) {
- case STILL:
- set_format (Format::from_id ("var-185"));
- break;
- case VIDEO:
- set_format (Format::from_id ("185"));
- break;
- }
-
- /* Still image DCPs must use external audio */
- if (content_type() == STILL) {
- set_use_content_audio (false);
- }
-}
-
-void
-Film::set_trust_content_header (bool t)
+Film::set_trust_content_headers (bool t)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _trust_content_header = t;
+ _trust_content_headers = t;
}
- signal_changed (TRUST_CONTENT_HEADER);
+ signal_changed (TRUST_CONTENT_HEADERS);
- if (!_trust_content_header && !content().empty()) {
+
+ ContentList content = _playlist->content ();
+ if (!_trust_content_headers && !content.empty()) {
/* We just said that we don't trust the content's header */
- examine_content ();
+ for (ContentList::iterator i = content.begin(); i != content.end(); ++i) {
+ examine_content (*i);
+ }
}
}
}
void
-Film::set_dcp_ab (bool a)
+Film::set_ab (bool a)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_ab = a;
+ _ab = a;
}
- signal_changed (DCP_AB);
-}
-
-void
-Film::set_content_audio_stream (shared_ptr<AudioStream> s)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content_audio_stream = s;
- }
- signal_changed (CONTENT_AUDIO_STREAM);
-}
-
-void
-Film::set_external_audio (vector<string> a)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _external_audio = a;
- }
-
- shared_ptr<SndfileDecoder> 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);
+ signal_changed (AB);
}
void
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<SubtitleStream> s)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_stream = s;
- }
- signal_changed (SUBTITLE_STREAM);
-}
-
void
Film::set_with_subtitles (bool w)
{
signal_changed (DCP_FRAME_RATE);
}
-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<shared_ptr<AudioStream> > s)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _content_audio_streams = s;
- }
- signal_changed (CONTENT_AUDIO_STREAMS);
-}
-
-void
-Film::set_subtitle_streams (vector<shared_ptr<SubtitleStream> > s)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_streams = s;
- }
- signal_changed (SUBTITLE_STREAMS);
-}
-
-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)
{
_dirty = true;
}
- if (ui_signaller) {
- ui_signaller->emit (boost::bind (boost::ref (Changed), p));
+ switch (p) {
+ case Film::CONTENT:
+ set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
+ set_audio_mapping (_playlist->default_audio_mapping ());
+ break;
+ default:
+ break;
}
-}
-int
-Film::audio_channels () const
-{
- shared_ptr<AudioStream> s = audio_stream ();
- if (!s) {
- return 0;
+ if (ui_signaller) {
+ ui_signaller->emit (boost::bind (boost::ref (Changed), p));
}
-
- return s->channels ();
}
void
_dci_date = boost::gregorian::day_clock::local_day ();
}
-boost::shared_ptr<AudioStream>
-Film::audio_stream () const
-{
- if (use_content_audio()) {
- return _content_audio_stream;
- }
-
- return _sndfile_stream;
-}
-
string
Film::info_path (int f) const
{
return true;
}
+shared_ptr<Player>
+Film::player () const
+{
+ boost::mutex::scoped_lock lm (_state_mutex);
+ return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
+}
+
+ContentList
+Film::content () const
+{
+ return _playlist->content ();
+}
+
+void
+Film::add_content (shared_ptr<Content> c)
+{
+ _playlist->add (c);
+ examine_content (c);
+}
+
+void
+Film::remove_content (shared_ptr<Content> c)
+{
+ _playlist->remove (c);
+}
+
+void
+Film::move_content_earlier (shared_ptr<Content> c)
+{
+ _playlist->move_earlier (c);
+}
+
+void
+Film::move_content_later (shared_ptr<Content> c)
+{
+ _playlist->move_later (c);
+}
+
+ContentAudioFrame
+Film::audio_length () const
+{
+ return _playlist->audio_length ();
+}
+
+int
+Film::audio_channels () const
+{
+ return _playlist->audio_channels ();
+}
+
+int
+Film::audio_frame_rate () const
+{
+ return _playlist->audio_frame_rate ();
+}
+
bool
Film::has_audio () const
{
- if (use_content_audio()) {
- return audio_stream();
+ return _playlist->has_audio ();
+}
+
+float
+Film::video_frame_rate () const
+{
+ return _playlist->video_frame_rate ();
+}
+
+libdcp::Size
+Film::video_size () const
+{
+ return _playlist->video_size ();
+}
+
+ContentVideoFrame
+Film::video_length () const
+{
+ return _playlist->video_length ();
+}
+
+ContentVideoFrame
+Film::content_length () const
+{
+ return _playlist->content_length ();
+}
+
+vector<FFmpegSubtitleStream>
+Film::ffmpeg_subtitle_streams () const
+{
+ shared_ptr<FFmpegContent> f = _playlist->ffmpeg ();
+ if (f) {
+ return f->subtitle_streams ();
}
- vector<string> const e = external_audio ();
- for (vector<string>::const_iterator i = e.begin(); i != e.end(); ++i) {
- if (!i->empty ()) {
- return true;
- }
+ return vector<FFmpegSubtitleStream> ();
+}
+
+boost::optional<FFmpegSubtitleStream>
+Film::ffmpeg_subtitle_stream () const
+{
+ shared_ptr<FFmpegContent> f = _playlist->ffmpeg ();
+ if (f) {
+ return f->subtitle_stream ();
}
- return false;
+ return boost::none;
}
+vector<FFmpegAudioStream>
+Film::ffmpeg_audio_streams () const
+{
+ shared_ptr<FFmpegContent> f = _playlist->ffmpeg ();
+ if (f) {
+ return f->audio_streams ();
+ }
+
+ return vector<FFmpegAudioStream> ();
+}
+
+boost::optional<FFmpegAudioStream>
+Film::ffmpeg_audio_stream () const
+{
+ shared_ptr<FFmpegContent> f = _playlist->ffmpeg ();
+ if (f) {
+ return f->audio_stream ();
+ }
+
+ return boost::none;
+}
+
+void
+Film::set_ffmpeg_subtitle_stream (FFmpegSubtitleStream s)
+{
+ shared_ptr<FFmpegContent> f = _playlist->ffmpeg ();
+ if (f) {
+ f->set_subtitle_stream (s);
+ }
+}
+
+void
+Film::set_ffmpeg_audio_stream (FFmpegAudioStream s)
+{
+ shared_ptr<FFmpegContent> f = _playlist->ffmpeg ();
+ if (f) {
+ f->set_audio_stream (s);
+ }
+}
+
+void
+Film::set_audio_mapping (AudioMapping m)
+{
+ {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _audio_mapping = m;
+ }
+
+ signal_changed (AUDIO_MAPPING);
+}
+
+void
+Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
+{
+ if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
+ set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
+ } else if (p == AudioContentProperty::AUDIO_CHANNELS) {
+ set_audio_mapping (_playlist->default_audio_mapping ());
+ }
+
+ if (ui_signaller) {
+ ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
+ }
+}
+
+void
+Film::playlist_changed ()
+{
+ signal_changed (CONTENT);
+}
+
+int
+Film::loop () const
+{
+ return _playlist->loop ();
+}
+
+void
+Film::set_loop (int c)
+{
+ _playlist->set_loop (c);
+}
*/
/** @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 <string>
#include <vector>
#include <boost/thread.hpp>
#include <boost/signals2.hpp>
#include <boost/enable_shared_from_this.hpp>
-extern "C" {
-#include <libavcodec/avcodec.h>
-}
-#include "dcp_content_type.h"
#include "util.h"
-#include "stream.h"
#include "dci_metadata.h"
+#include "types.h"
+#include "ffmpeg_content.h"
+#include "audio_mapping.h"
+class DCPContentType;
class Format;
class Job;
class Filter;
class ExamineContentJob;
class AnalyseAudioJob;
class ExternalAudioStream;
+class Content;
+class Player;
+class Playlist;
/** @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<Film>
{
public:
Film (std::string d, bool must_exist = true);
Film (Film const &);
- ~Film ();
std::string info_dir () const;
std::string j2c_path (int f, bool t) const;
std::string internal_video_mxf_filename () const;
std::string audio_analysis_path () const;
+ void examine_content (boost::shared_ptr<Content>);
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.
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 ();
libdcp::Size cropped_size (libdcp::Size) const;
std::string dci_name (bool if_created_now) const;
return _dirty;
}
+ bool have_dcp () const;
+
+ boost::shared_ptr<Player> player () const;
+
+ /* Proxies for some Playlist methods */
+
+ ContentList content () const;
+
+ ContentAudioFrame audio_length () const;
int audio_channels () const;
+ int audio_frame_rate () const;
+ bool has_audio () const;
+
+ float video_frame_rate () const;
+ libdcp::Size video_size () const;
+ ContentVideoFrame video_length () const;
- void set_dci_date_today ();
+ ContentVideoFrame content_length () const;
- bool have_dcp () const;
+ std::vector<FFmpegSubtitleStream> ffmpeg_subtitle_streams () const;
+ boost::optional<FFmpegSubtitleStream> ffmpeg_subtitle_stream () const;
+ std::vector<FFmpegAudioStream> ffmpeg_audio_streams () const;
+ boost::optional<FFmpegAudioStream> ffmpeg_audio_stream () const;
+
+ void set_ffmpeg_subtitle_stream (FFmpegSubtitleStream);
+ void set_ffmpeg_audio_stream (FFmpegAudioStream);
+
+ void set_loop (int);
+ int loop () const;
enum TrimType {
CPL,
NONE,
NAME,
USE_DCI_NAME,
+ TRUST_CONTENT_HEADERS,
+ /** 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,
SCALER,
TRIM_START,
TRIM_END,
+ AB,
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_FRAME_RATE,
+ AUDIO_MAPPING
};
return _use_dci_name;
}
- std::string content () const {
+ bool trust_content_headers () 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;
+ return _trust_content_headers;
}
DCPContentType const * dcp_content_type () const {
return _trim_type;
}
- bool dcp_ab () const {
+ bool ab () const {
boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_ab;
+ return _ab;
}
- boost::shared_ptr<AudioStream> content_audio_stream () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content_audio_stream;
- }
-
- std::vector<std::string> 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;
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<SubtitleStream> 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;
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<SourceFrame> length () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _length;
- }
-
- std::string content_digest () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content_digest;
- }
-
- std::vector<boost::shared_ptr<AudioStream> > content_audio_streams () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _content_audio_streams;
- }
- std::vector<boost::shared_ptr<SubtitleStream> > subtitle_streams () const {
+ AudioMapping audio_mapping () const {
boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_streams;
- }
-
- float source_frame_rate () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- if (content_type() == STILL) {
- return 24;
- }
-
- return _source_frame_rate;
+ return _audio_mapping;
}
- boost::shared_ptr<AudioStream> 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 set_trust_content_headers (bool);
+ void add_content (boost::shared_ptr<Content>);
+ void remove_content (boost::shared_ptr<Content>);
+ void move_content_earlier (boost::shared_ptr<Content>);
+ void move_content_later (boost::shared_ptr<Content>);
void set_dcp_content_type (DCPContentType const *);
void set_format (Format const *);
void set_crop (Crop);
void set_scaler (Scaler const *);
void set_trim_start (int);
void set_trim_end (int);
+ void set_ab (bool);
void set_trim_type (TrimType);
- void set_dcp_ab (bool);
- void set_content_audio_stream (boost::shared_ptr<AudioStream>);
- void set_external_audio (std::vector<std::string>);
- 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<SubtitleStream>);
void set_with_subtitles (bool);
void set_subtitle_offset (int);
void set_subtitle_scale (float);
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<boost::shared_ptr<AudioStream> >);
- void set_subtitle_streams (std::vector<boost::shared_ptr<SubtitleStream> >);
- void set_source_frame_rate (float);
-
- /** Emitted when some property has changed */
+ void set_dci_date_today ();
+ void set_audio_mapping (AudioMapping);
+
+ /** Emitted when some property has of the Film has changed */
mutable boost::signals2::signal<void (Property)> Changed;
+ /** Emitted when some property of our content has changed */
+ mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
+
boost::signals2::signal<void ()> AudioAnalysisSucceeded;
/** Current version number of the state file */
private:
- /** Log to write to */
- boost::shared_ptr<Log> _log;
-
- /** Any running ExamineContentJob, or 0 */
- boost::shared_ptr<ExamineContentJob> _examine_content_job;
- /** Any running AnalyseAudioJob, or 0 */
- boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job;
-
void signal_changed (Property);
- void examine_content_finished ();
void analyse_audio_finished ();
std::string video_state_identifier () const;
+ void read_metadata ();
+ void playlist_changed ();
+ void playlist_content_changed (boost::weak_ptr<Content>, int);
+ void setup_default_audio_mapping ();
std::string filename_safe_name () const;
+ /** Log to write to */
+ boost::shared_ptr<Log> _log;
+ /** Any running AnalyseAudioJob, or 0 */
+ boost::shared_ptr<AnalyseAudioJob> _analyse_audio_job;
+ boost::shared_ptr<Playlist> _playlist;
+
/** Complete path to directory containing the film metadata;
* must not be relative.
*/
/** 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;
+ bool _trust_content_headers;
/** 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.) */
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<AudioStream> _content_audio_stream;
- /** List of filenames of external audio files, in channel order
- (L, R, C, Lfe, Ls, Rs)
- */
- std::vector<std::string> _external_audio;
- /** true to use audio from our content file; false to use external audio */
- bool _use_content_audio;
+ bool _ab;
/** 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<SubtitleStream> _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
int _colour_lut;
/** bandwidth for J2K files in bits per second */
int _j2k_bandwidth;
-
/** DCI naming stuff */
DCIMetadata _dci_metadata;
- /** 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;
-
- /* 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<SourceFrame> _length;
- /** MD5 digest of our content file */
- std::string _content_digest;
- /** The audio streams in our content */
- std::vector<boost::shared_ptr<AudioStream> > _content_audio_streams;
- /** A stream to represent possible external audio (will always exist) */
- boost::shared_ptr<AudioStream> _sndfile_stream;
- /** the subtitle streams that we can use */
- std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
- /** Frames per second of the source */
- float _source_frame_rate;
+ /** The date that we should use in a DCI name */
+ boost::gregorian::date _dci_date;
+ AudioMapping _audio_mapping;
/** true if our state has changed since we last saved it */
mutable bool _dirty;
* @brief A class to describe one of FFmpeg's video or post-processing filters.
*/
-#ifndef DVDOMATIC_FILTER_H
-#define DVDOMATIC_FILTER_H
+#ifndef DCPOMATIC_FILTER_H
+#define DCPOMATIC_FILTER_H
#include <string>
#include <vector>
* @param s Size of the images to process.
* @param p Pixel format of the images to process.
*/
-FilterGraph::FilterGraph (shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p)
+FilterGraph::FilterGraph (shared_ptr<const Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p)
: _buffer_src_context (0)
, _buffer_sink_context (0)
, _size (s)
* @brief A graph of FFmpeg filters.
*/
-#ifndef DVDOMATIC_FILTER_GRAPH_H
-#define DVDOMATIC_FILTER_GRAPH_H
+#ifndef DCPOMATIC_FILTER_GRAPH_H
+#define DCPOMATIC_FILTER_GRAPH_H
#include "util.h"
#include "ffmpeg_compatibility.h"
class FilterGraph
{
public:
- FilterGraph (boost::shared_ptr<Film> film, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p);
+ FilterGraph (boost::shared_ptr<const Film>, FFmpegDecoder* decoder, libdcp::Size s, AVPixelFormat p);
bool can_process (libdcp::Size s, AVPixelFormat p) const;
std::list<boost::shared_ptr<Image> > process (AVFrame const * frame);
#include <iostream>
#include "format.h"
#include "film.h"
+#include "playlist.h"
#include "i18n.h"
return s.str ();
}
-/** @return Identifier for this format as metadata for a Film's metadata file */
-string
-Format::as_metadata () const
-{
- return _id;
-}
-
/** Fill our _formats vector with all available formats */
void
Format::setup_formats ()
return *j;
}
-
-/** @param m Metadata, as returned from as_metadata().
- * @return Matching Format, or 0.
- */
-Format const *
-Format::from_metadata (string m)
-{
- return from_id (m);
-}
-
/** @return All available formats */
vector<Format const *>
Format::all ()
float
VariableFormat::ratio (shared_ptr<const Film> f) const
{
- libdcp::Size const c = f->cropped_size (f->size ());
+ libdcp::Size const c = f->cropped_size (f->video_size ());
return float (c.width) / c.height;
}
/** @return the ratio of the container (including any padding) */
float container_ratio () const;
- int dcp_padding (boost::shared_ptr<const Film> f) const;
+ int dcp_padding (boost::shared_ptr<const Film>) const;
/** @return size in pixels of the images that we should
* put in a DCP for this ratio. This size will not correspond
return _dci_name;
}
- std::string as_metadata () const;
-
static Format const * from_nickname (std::string n);
- static Format const * from_metadata (std::string m);
static Format const * from_id (std::string i);
static std::vector<Format const *> all ();
static void setup_formats ();
#include <libintl.h>
-#define _(x) dgettext ("libdvdomatic", x)
+#define _(x) dgettext ("libdcpomatic", x)
#define N_(x) x
* @brief A set of classes to describe video images.
*/
-#ifndef DVDOMATIC_IMAGE_H
-#define DVDOMATIC_IMAGE_H
+#ifndef DCPOMATIC_IMAGE_H
+#define DCPOMATIC_IMAGE_H
#include <string>
#include <boost/shared_ptr.hpp>
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include "imagemagick_content.h"
+#include "imagemagick_decoder.h"
+#include "config.h"
+#include "compose.hpp"
+
+#include "i18n.h"
+
+using std::string;
+using std::stringstream;
+using boost::shared_ptr;
+
+ImageMagickContent::ImageMagickContent (boost::filesystem::path f)
+ : Content (f)
+ , VideoContent (f)
+{
+
+}
+
+ImageMagickContent::ImageMagickContent (shared_ptr<const cxml::Node> node)
+ : Content (node)
+ , VideoContent (node)
+{
+
+}
+
+string
+ImageMagickContent::summary () const
+{
+ return String::compose (_("Image: %1"), file().filename().string());
+}
+
+bool
+ImageMagickContent::valid_file (boost::filesystem::path f)
+{
+ string ext = f.extension().string();
+ transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
+ return (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png" || ext == ".bmp");
+}
+
+void
+ImageMagickContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("ImageMagick");
+ Content::as_xml (node);
+ VideoContent::as_xml (node);
+}
+
+void
+ImageMagickContent::examine (shared_ptr<Film> film, shared_ptr<Job> job, bool quick)
+{
+ Content::examine (film, job, quick);
+ shared_ptr<ImageMagickDecoder> decoder (new ImageMagickDecoder (film, shared_from_this()));
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ /* Initial length */
+ _video_length = Config::instance()->default_still_length() * 24;
+ }
+
+ take_from_video_decoder (decoder);
+
+ signal_changed (VideoContentProperty::VIDEO_LENGTH);
+}
+
+shared_ptr<Content>
+ImageMagickContent::clone () const
+{
+ return shared_ptr<Content> (new ImageMagickContent (*this));
+}
+
+void
+ImageMagickContent::set_video_length (ContentVideoFrame len)
+{
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _video_length = len;
+ }
+
+ signal_changed (VideoContentProperty::VIDEO_LENGTH);
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/enable_shared_from_this.hpp>
+#include "video_content.h"
+
+namespace cxml {
+ class Node;
+}
+
+class ImageMagickContent : public VideoContent
+{
+public:
+ ImageMagickContent (boost::filesystem::path);
+ ImageMagickContent (boost::shared_ptr<const cxml::Node>);
+
+ boost::shared_ptr<ImageMagickContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<ImageMagickContent> (Content::shared_from_this ());
+ };
+
+ void examine (boost::shared_ptr<Film>, boost::shared_ptr<Job>, bool);
+ std::string summary () const;
+ void as_xml (xmlpp::Node *) const;
+ boost::shared_ptr<Content> clone () const;
+
+ void set_video_length (ContentVideoFrame);
+
+ static bool valid_file (boost::filesystem::path);
+};
#include <iostream>
#include <boost/filesystem.hpp>
#include <Magick++.h>
+#include "imagemagick_content.h"
#include "imagemagick_decoder.h"
#include "image.h"
#include "film.h"
using boost::shared_ptr;
using libdcp::Size;
-ImageMagickDecoder::ImageMagickDecoder (
- boost::shared_ptr<Film> f, DecodeOptions o)
- : Decoder (f, o)
- , VideoDecoder (f, o)
+ImageMagickDecoder::ImageMagickDecoder (shared_ptr<const Film> f, shared_ptr<const ImageMagickContent> c)
+ : Decoder (f)
+ , VideoDecoder (f)
+ , _imagemagick_content (c)
+ , _position (0)
{
- if (boost::filesystem::is_directory (_film->content_path())) {
- for (
- boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (_film->content_path());
- i != boost::filesystem::directory_iterator();
- ++i) {
-
- if (still_image_file (i->path().string())) {
- _files.push_back (i->path().string());
- }
- }
- } else {
- _files.push_back (_film->content_path ());
- }
-
- _iter = _files.begin ();
+
}
libdcp::Size
ImageMagickDecoder::native_size () const
{
- if (_files.empty ()) {
- throw DecodeError (_("no still image files found"));
- }
-
- /* Look at the first file and assume its size holds for all */
using namespace MagickCore;
- Magick::Image* image = new Magick::Image (_film->content_path ());
+ Magick::Image* image = new Magick::Image (_imagemagick_content->file().string());
libdcp::Size const s = libdcp::Size (image->columns(), image->rows());
delete image;
return s;
}
+int
+ImageMagickDecoder::video_length () const
+{
+ return _imagemagick_content->video_length ();
+}
+
bool
ImageMagickDecoder::pass ()
{
- if (_iter == _files.end()) {
- if (video_frame() >= _film->still_duration_in_frames()) {
- return true;
- }
+ if (_position < 0 || _position >= _imagemagick_content->video_length ()) {
+ return true;
+ }
- emit_video (_image, true, double (video_frame()) / frames_per_second());
+ if (_image) {
+ emit_video (_image, true, double (_position) / 24);
+ _position++;
return false;
}
- Magick::Image* magick_image = new Magick::Image (_film->content_path ());
+ Magick::Image* magick_image = new Magick::Image (_imagemagick_content->file().string ());
libdcp::Size size = native_size ();
- shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, size, false));
+ _image.reset (new SimpleImage (PIX_FMT_RGB24, size, false));
using namespace MagickCore;
- uint8_t* p = image->data()[0];
+ uint8_t* p = _image->data()[0];
for (int y = 0; y < size.height; ++y) {
for (int x = 0; x < size.width; ++x) {
Magick::Color c = magick_image->pixelColor (x, y);
delete magick_image;
- _image = image->crop (_film->crop(), true);
-
- emit_video (_image, false, double (video_frame()) / frames_per_second());
+ _image = _image->crop (_film->crop(), true);
+ emit_video (_image, false, double (_position) / 24);
- ++_iter;
+ ++_position;
return false;
}
return PIX_FMT_RGB24;
}
-bool
-ImageMagickDecoder::seek_to_last ()
-{
- if (_iter == _files.end()) {
- _iter = _files.begin();
- } else {
- --_iter;
- }
-
- return false;
-}
-
bool
ImageMagickDecoder::seek (double t)
{
- int const f = t * frames_per_second();
-
- _iter = _files.begin ();
- for (int i = 0; i < f; ++i) {
- if (_iter == _files.end()) {
- return true;
- }
- ++_iter;
- }
-
- return false;
-}
+ int const f = t * _imagemagick_content->video_frame_rate ();
-void
-ImageMagickDecoder::film_changed (Film::Property p)
-{
- if (p == Film::CROP) {
- OutputChanged ();
+ if (f >= _imagemagick_content->video_length()) {
+ _position = 0;
+ return true;
}
-}
-float
-ImageMagickDecoder::frames_per_second () const
-{
- return _film->source_frame_rate ();
+ _position = f;
+ return false;
}
class Image;
}
+class ImageMagickContent;
+
class ImageMagickDecoder : public VideoDecoder
{
public:
- ImageMagickDecoder (boost::shared_ptr<Film>, DecodeOptions);
-
- float frames_per_second () const;
+ ImageMagickDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const ImageMagickContent>);
- libdcp::Size native_size () const;
-
- SourceFrame length () const {
- /* We don't know */
- return 0;
+ float video_frame_rate () const {
+ return 24;
}
- int audio_channels () const {
- return 0;
- }
-
- int audio_sample_rate () const {
- return 0;
- }
-
- int64_t audio_channel_layout () const {
- return 0;
- }
+ libdcp::Size native_size () const;
+ ContentVideoFrame video_length () const;
bool seek (double);
- bool seek_to_last ();
+ bool pass ();
protected:
- bool pass ();
PixelFormat pixel_format () const;
int time_base_numerator () const {
}
private:
- void film_changed (Film::Property);
-
- std::list<std::string> _files;
- std::list<std::string>::iterator _iter;
-
+ boost::shared_ptr<const ImageMagickContent> _imagemagick_content;
boost::shared_ptr<Image> _image;
+ ContentVideoFrame _position;
};
using std::stringstream;
using boost::shared_ptr;
-/** @param s Film that we are operating on.
- */
Job::Job (shared_ptr<Film> f)
: _film (f)
, _thread (0)
set_state (FINISHED_ERROR);
set_error (
e.what (),
- _("It is not known what caused this error. The best idea is to report the problem to the DVD-o-matic mailing list (dvdomatic@carlh.net)")
+ _("It is not known what caused this error. The best idea is to report the problem to the DCP-o-matic mailing list (dcpomatic@carlh.net)")
);
} catch (...) {
set_state (FINISHED_ERROR);
set_error (
_("Unknown error"),
- _("It is not known what caused this error. The best idea is to report the problem to the DVD-o-matic mailing list (dvdomatic@carlh.net)")
+ _("It is not known what caused this error. The best idea is to report the problem to the DCP-o-matic mailing list (dcpomatic@carlh.net)")
);
}
* @brief A parent class to represent long-running tasks which are run in their own thread.
*/
-#ifndef DVDOMATIC_JOB_H
-#define DVDOMATIC_JOB_H
+#ifndef DCPOMATIC_JOB_H
+#define DCPOMATIC_JOB_H
#include <string>
#include <boost/thread/mutex.hpp>
class Job : public boost::enable_shared_from_this<Job>
{
public:
- Job (boost::shared_ptr<Film> s);
+ Job (boost::shared_ptr<Film>);
virtual ~Job() {}
/** @return user-readable name of this job */
void set_state (State);
void set_error (std::string s, std::string d);
- /** Film for this job */
boost::shared_ptr<Film> _film;
private:
}
}
- dvdomatic_sleep (1);
+ dcpomatic_sleep (1);
}
}
*/
-#ifndef DVDOMATIC_LOG_H
-#define DVDOMATIC_LOG_H
+#ifndef DCPOMATIC_LOG_H
+#define DCPOMATIC_LOG_H
/** @file src/log.h
* @brief A very simple logging class.
}
void
-Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
+Matcher::process_video (shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
{
_pixel_format = image->pixel_format ();
_size = image->size ();
}
void
-Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t)
+Matcher::process_audio (shared_ptr<const AudioBuffers> b, double t)
{
_channels = b->channels ();
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef DVDOMATIC_OPTIONS_H
-#define DVDOMATIC_OPTIONS_H
-
-/** @file src/options.h
- * @brief Options for a decoding operation.
- */
-
-class DecodeOptions
-{
-public:
- DecodeOptions ()
- : decode_video (true)
- , decode_audio (true)
- , decode_subtitles (false)
- , video_sync (true)
- {}
-
- bool decode_video;
- bool decode_audio;
- bool decode_subtitles;
- bool video_sync;
-};
-
-#endif
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "player.h"
+#include "film.h"
+#include "ffmpeg_decoder.h"
+#include "ffmpeg_content.h"
+#include "imagemagick_decoder.h"
+#include "imagemagick_content.h"
+#include "sndfile_decoder.h"
+#include "sndfile_content.h"
+#include "playlist.h"
+#include "job.h"
+
+using std::list;
+using std::cout;
+using std::vector;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::dynamic_pointer_cast;
+
+Player::Player (shared_ptr<const Film> f, shared_ptr<const Playlist> p)
+ : _film (f)
+ , _playlist (p)
+ , _video (true)
+ , _audio (true)
+ , _subtitles (true)
+ , _have_valid_decoders (false)
+{
+ _playlist->Changed.connect (bind (&Player::playlist_changed, this));
+ _playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2));
+}
+
+void
+Player::disable_video ()
+{
+ _video = false;
+}
+
+void
+Player::disable_audio ()
+{
+ _audio = false;
+}
+
+void
+Player::disable_subtitles ()
+{
+ _subtitles = false;
+}
+
+bool
+Player::pass ()
+{
+ if (!_have_valid_decoders) {
+ setup_decoders ();
+ _have_valid_decoders = true;
+ }
+
+ bool done = true;
+
+ if (_video && _video_decoder < _video_decoders.size ()) {
+
+ /* Run video decoder; this may also produce audio */
+
+ if (_video_decoders[_video_decoder]->pass ()) {
+ _video_decoder++;
+ }
+
+ if (_video_decoder < _video_decoders.size ()) {
+ done = false;
+ }
+
+ }
+
+ if (!_video && _audio && _playlist->audio_from() == Playlist::AUDIO_FFMPEG && _sequential_audio_decoder < _audio_decoders.size ()) {
+
+ /* We're not producing video, so we may need to run FFmpeg content to get the audio */
+
+ if (_audio_decoders[_sequential_audio_decoder]->pass ()) {
+ _sequential_audio_decoder++;
+ }
+
+ if (_sequential_audio_decoder < _audio_decoders.size ()) {
+ done = false;
+ }
+
+ }
+
+ if (_audio && _playlist->audio_from() == Playlist::AUDIO_SNDFILE) {
+
+ /* We're getting audio from SndfileContent */
+
+ for (vector<shared_ptr<AudioDecoder> >::iterator i = _audio_decoders.begin(); i != _audio_decoders.end(); ++i) {
+ if (!(*i)->pass ()) {
+ done = false;
+ }
+ }
+
+ Audio (_audio_buffers, _audio_time.get());
+ _audio_buffers.reset ();
+ _audio_time = boost::none;
+ }
+
+ return done;
+}
+
+void
+Player::set_progress (shared_ptr<Job> job)
+{
+ /* Assume progress can be divined from how far through the video we are */
+
+ if (_video_decoder >= _video_decoders.size() || !_playlist->video_length()) {
+ return;
+ }
+
+ job->set_progress ((_video_start[_video_decoder] + _video_decoders[_video_decoder]->video_frame()) / _playlist->video_length ());
+}
+
+void
+Player::process_video (shared_ptr<const Image> i, bool same, shared_ptr<Subtitle> s, double t)
+{
+ Video (i, same, s, _video_start[_video_decoder] + t);
+}
+
+void
+Player::process_audio (weak_ptr<const AudioContent> c, shared_ptr<const AudioBuffers> b, double t)
+{
+ AudioMapping mapping = _film->audio_mapping ();
+ if (!_audio_buffers) {
+ _audio_buffers.reset (new AudioBuffers (mapping.dcp_channels(), b->frames ()));
+ _audio_buffers->make_silent ();
+ _audio_time = t;
+ if (_playlist->audio_from() == Playlist::AUDIO_FFMPEG) {
+ _audio_time = _audio_time.get() + _audio_start[_sequential_audio_decoder];
+ }
+ }
+
+ for (int i = 0; i < b->channels(); ++i) {
+ list<libdcp::Channel> dcp = mapping.content_to_dcp (AudioMapping::Channel (c, i));
+ for (list<libdcp::Channel>::iterator j = dcp.begin(); j != dcp.end(); ++j) {
+ _audio_buffers->accumulate (b, i, static_cast<int> (*j));
+ }
+ }
+
+ if (_playlist->audio_from() == Playlist::AUDIO_FFMPEG) {
+ /* We can just emit this audio now as it will all be here */
+ Audio (_audio_buffers, t);
+ _audio_buffers.reset ();
+ _audio_time = boost::none;
+ }
+}
+
+/** @return true on error */
+bool
+Player::seek (double t)
+{
+ if (!_have_valid_decoders) {
+ setup_decoders ();
+ _have_valid_decoders = true;
+ }
+
+ if (_video_decoders.empty ()) {
+ return true;
+ }
+
+ /* Find the decoder that contains this position */
+ _video_decoder = 0;
+ while (1) {
+ ++_video_decoder;
+ if (_video_decoder >= _video_decoders.size () || t < _video_start[_video_decoder]) {
+ --_video_decoder;
+ t -= _video_start[_video_decoder];
+ break;
+ }
+ }
+
+ if (_video_decoder < _video_decoders.size()) {
+ _video_decoders[_video_decoder]->seek (t);
+ } else {
+ return true;
+ }
+
+ /* XXX: don't seek audio because we don't need to... */
+
+ return false;
+}
+
+
+void
+Player::seek_back ()
+{
+ /* XXX */
+}
+
+void
+Player::seek_forward ()
+{
+ /* XXX */
+}
+
+
+void
+Player::setup_decoders ()
+{
+ _video_decoders.clear ();
+ _video_decoder = 0;
+ _audio_decoders.clear ();
+ _sequential_audio_decoder = 0;
+
+ _video_start.clear();
+ _audio_start.clear();
+
+ double video_so_far = 0;
+ double audio_so_far = 0;
+
+ for (int l = 0; l < _playlist->loop(); ++l) {
+ list<shared_ptr<const VideoContent> > vc = _playlist->video ();
+ for (list<shared_ptr<const VideoContent> >::iterator i = vc.begin(); i != vc.end(); ++i) {
+
+ shared_ptr<const VideoContent> video_content;
+ shared_ptr<const AudioContent> audio_content;
+ shared_ptr<VideoDecoder> video_decoder;
+ shared_ptr<AudioDecoder> audio_decoder;
+
+ /* XXX: into content? */
+
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+ if (fc) {
+ shared_ptr<FFmpegDecoder> fd (
+ new FFmpegDecoder (
+ _film, fc, _video,
+ _audio && _playlist->audio_from() == Playlist::AUDIO_FFMPEG,
+ _subtitles
+ )
+ );
+
+ video_content = fc;
+ audio_content = fc;
+ video_decoder = fd;
+ audio_decoder = fd;
+ }
+
+ shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
+ if (ic) {
+ video_content = ic;
+ video_decoder.reset (new ImageMagickDecoder (_film, ic));
+ }
+
+ video_decoder->connect_video (shared_from_this ());
+ _video_decoders.push_back (video_decoder);
+ _video_start.push_back (video_so_far);
+ video_so_far += video_content->video_length() / video_content->video_frame_rate();
+
+ if (audio_decoder && _playlist->audio_from() == Playlist::AUDIO_FFMPEG) {
+ audio_decoder->Audio.connect (bind (&Player::process_audio, this, audio_content, _1, _2));
+ _audio_decoders.push_back (audio_decoder);
+ _audio_start.push_back (audio_so_far);
+ audio_so_far += double(audio_content->audio_length()) / audio_content->audio_frame_rate();
+ }
+ }
+
+ _video_decoder = 0;
+ _sequential_audio_decoder = 0;
+
+ if (_playlist->audio_from() == Playlist::AUDIO_SNDFILE) {
+
+ list<shared_ptr<const AudioContent> > ac = _playlist->audio ();
+ for (list<shared_ptr<const AudioContent> >::iterator i = ac.begin(); i != ac.end(); ++i) {
+
+ shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
+ assert (sc);
+
+ shared_ptr<AudioDecoder> d (new SndfileDecoder (_film, sc));
+ d->Audio.connect (bind (&Player::process_audio, this, sc, _1, _2));
+ _audio_decoders.push_back (d);
+ _audio_start.push_back (audio_so_far);
+ }
+ }
+ }
+}
+
+double
+Player::last_video_time () const
+{
+ if (_video_decoder >= _video_decoders.size ()) {
+ return 0;
+ }
+
+ return _video_start[_video_decoder] + _video_decoders[_video_decoder]->last_content_time ();
+}
+
+void
+Player::content_changed (weak_ptr<Content> w, int p)
+{
+ shared_ptr<Content> c = w.lock ();
+ if (!c) {
+ return;
+ }
+
+ if (p == VideoContentProperty::VIDEO_LENGTH) {
+ _have_valid_decoders = false;
+ }
+}
+
+void
+Player::playlist_changed ()
+{
+ _have_valid_decoders = false;
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_PLAYER_H
+#define DCPOMATIC_PLAYER_H
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include "video_source.h"
+#include "audio_source.h"
+#include "video_sink.h"
+#include "audio_sink.h"
+
+class VideoDecoder;
+class AudioDecoder;
+class Job;
+class Film;
+class Playlist;
+class AudioContent;
+
+/** @class Player
+ * @brief A class which can `play' a Playlist; emitting its audio and video.
+ */
+
+class Player : public TimedVideoSource, public TimedAudioSource, public TimedVideoSink, public boost::enable_shared_from_this<Player>
+{
+public:
+ Player (boost::shared_ptr<const Film>, boost::shared_ptr<const Playlist>);
+
+ void disable_video ();
+ void disable_audio ();
+ void disable_subtitles ();
+
+ bool pass ();
+ void set_progress (boost::shared_ptr<Job>);
+ bool seek (double);
+ void seek_back ();
+ void seek_forward ();
+
+ double last_video_time () const;
+
+private:
+ void process_video (boost::shared_ptr<const Image> i, bool same, boost::shared_ptr<Subtitle> s, double);
+ void process_audio (boost::weak_ptr<const AudioContent>, boost::shared_ptr<const AudioBuffers>, double);
+ void setup_decoders ();
+ void playlist_changed ();
+ void content_changed (boost::weak_ptr<Content>, int);
+
+ boost::shared_ptr<const Film> _film;
+ boost::shared_ptr<const Playlist> _playlist;
+
+ bool _video;
+ bool _audio;
+ bool _subtitles;
+
+ /** Our decoders are ready to go; if this is false the decoders must be (re-)created before they are used */
+ bool _have_valid_decoders;
+ /** Video decoders in order of presentation */
+ std::vector<boost::shared_ptr<VideoDecoder> > _video_decoders;
+ /** Start positions of each video decoder in seconds*/
+ std::vector<double> _video_start;
+ /** Index of current video decoder */
+ size_t _video_decoder;
+ /** Audio decoders in order of presentation (if they are from FFmpeg) */
+ std::vector<boost::shared_ptr<AudioDecoder> > _audio_decoders;
+ /** Start positions of each audio decoder (if they are from FFmpeg) in seconds */
+ std::vector<double> _audio_start;
+ /** Current audio decoder index if we are running them sequentially; otherwise undefined */
+ size_t _sequential_audio_decoder;
+
+ boost::shared_ptr<AudioBuffers> _audio_buffers;
+ boost::optional<double> _audio_time;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include "playlist.h"
+#include "sndfile_content.h"
+#include "sndfile_decoder.h"
+#include "video_content.h"
+#include "ffmpeg_decoder.h"
+#include "ffmpeg_content.h"
+#include "imagemagick_decoder.h"
+#include "imagemagick_content.h"
+#include "job.h"
+
+using std::list;
+using std::cout;
+using std::vector;
+using std::min;
+using std::max;
+using std::string;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::dynamic_pointer_cast;
+using boost::lexical_cast;
+
+Playlist::Playlist ()
+ : _audio_from (AUDIO_FFMPEG)
+ , _loop (1)
+{
+
+}
+
+Playlist::Playlist (shared_ptr<const Playlist> other)
+ : _audio_from (other->_audio_from)
+ , _loop (other->_loop)
+{
+ for (ContentList::const_iterator i = other->_content.begin(); i != other->_content.end(); ++i) {
+ _content.push_back ((*i)->clone ());
+ }
+
+ setup ();
+}
+
+void
+Playlist::setup ()
+{
+ _audio_from = AUDIO_FFMPEG;
+
+ _video.clear ();
+ _audio.clear ();
+
+ for (list<boost::signals2::connection>::iterator i = _content_connections.begin(); i != _content_connections.end(); ++i) {
+ i->disconnect ();
+ }
+
+ _content_connections.clear ();
+
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+
+ /* Video is video */
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
+ if (vc) {
+ _video.push_back (vc);
+ }
+
+ /* FFmpegContent is audio if we are doing AUDIO_FFMPEG */
+ shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
+ if (fc && _audio_from == AUDIO_FFMPEG) {
+ _audio.push_back (fc);
+ }
+
+ /* SndfileContent trumps FFmpegContent for audio */
+ shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (*i);
+ if (sc) {
+ if (_audio_from == AUDIO_FFMPEG) {
+ /* This is our fist SndfileContent; clear any FFmpegContent and
+ say that we are using Sndfile.
+ */
+ _audio.clear ();
+ _audio_from = AUDIO_SNDFILE;
+ }
+
+ _audio.push_back (sc);
+ }
+
+ _content_connections.push_back ((*i)->Changed.connect (bind (&Playlist::content_changed, this, _1, _2)));
+ }
+}
+
+/** @return Length of our audio */
+ContentAudioFrame
+Playlist::audio_length () const
+{
+ ContentAudioFrame len = 0;
+
+ switch (_audio_from) {
+ case AUDIO_FFMPEG:
+ /* FFmpeg content is sequential */
+ for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
+ len += (*i)->audio_length ();
+ }
+ break;
+ case AUDIO_SNDFILE:
+ /* Sndfile content is simultaneous */
+ for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
+ len = max (len, (*i)->audio_length ());
+ }
+ break;
+ }
+
+ return len * _loop;
+}
+
+/** @return number of audio channels */
+int
+Playlist::audio_channels () const
+{
+ int channels = 0;
+
+ switch (_audio_from) {
+ case AUDIO_FFMPEG:
+ /* FFmpeg audio is sequential, so use the maximum channel count */
+ for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
+ channels = max (channels, (*i)->audio_channels ());
+ }
+ break;
+ case AUDIO_SNDFILE:
+ /* Sndfile audio is simultaneous, so it's the sum of the channel counts */
+ for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
+ channels += (*i)->audio_channels ();
+ }
+ break;
+ }
+
+ return channels;
+}
+
+int
+Playlist::audio_frame_rate () const
+{
+ if (_audio.empty ()) {
+ return 0;
+ }
+
+ /* XXX: assuming that all content has the same rate */
+ return _audio.front()->audio_frame_rate ();
+}
+
+float
+Playlist::video_frame_rate () const
+{
+ if (_video.empty ()) {
+ return 0;
+ }
+
+ /* XXX: assuming all the same */
+ return _video.front()->video_frame_rate ();
+}
+
+libdcp::Size
+Playlist::video_size () const
+{
+ if (_video.empty ()) {
+ return libdcp::Size ();
+ }
+
+ /* XXX: assuming all the same */
+ return _video.front()->video_size ();
+}
+
+ContentVideoFrame
+Playlist::video_length () const
+{
+ ContentVideoFrame len = 0;
+ for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+ len += (*i)->video_length ();
+ }
+
+ return len * _loop;
+}
+
+bool
+Playlist::has_audio () const
+{
+ return !_audio.empty ();
+}
+
+void
+Playlist::content_changed (weak_ptr<Content> c, int p)
+{
+ ContentChanged (c, p);
+}
+
+AudioMapping
+Playlist::default_audio_mapping () const
+{
+ AudioMapping m;
+ if (_audio.empty ()) {
+ return m;
+ }
+
+ switch (_audio_from) {
+ case AUDIO_FFMPEG:
+ {
+ /* XXX: assumes all the same */
+ if (_audio.front()->audio_channels() == 1) {
+ /* Map mono sources to centre */
+ m.add (AudioMapping::Channel (_audio.front(), 0), libdcp::CENTRE);
+ } else {
+ int const N = min (_audio.front()->audio_channels (), MAX_AUDIO_CHANNELS);
+ /* Otherwise just start with a 1:1 mapping */
+ for (int i = 0; i < N; ++i) {
+ m.add (AudioMapping::Channel (_audio.front(), i), (libdcp::Channel) i);
+ }
+ }
+ break;
+ }
+
+ case AUDIO_SNDFILE:
+ {
+ int n = 0;
+ for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
+ for (int j = 0; j < (*i)->audio_channels(); ++j) {
+ m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
+ ++n;
+ if (n >= MAX_AUDIO_CHANNELS) {
+ break;
+ }
+ }
+ if (n >= MAX_AUDIO_CHANNELS) {
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ return m;
+}
+
+string
+Playlist::audio_digest () const
+{
+ string t;
+
+ for (list<shared_ptr<const AudioContent> >::const_iterator i = _audio.begin(); i != _audio.end(); ++i) {
+ t += (*i)->digest ();
+
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+ if (fc) {
+ t += lexical_cast<string> (fc->audio_stream()->id);
+ }
+ }
+
+ t += lexical_cast<string> (_loop);
+
+ return md5_digest (t.c_str(), t.length());
+}
+
+string
+Playlist::video_digest () const
+{
+ string t;
+
+ for (list<shared_ptr<const VideoContent> >::const_iterator i = _video.begin(); i != _video.end(); ++i) {
+ t += (*i)->digest ();
+ shared_ptr<const FFmpegContent> fc = dynamic_pointer_cast<const FFmpegContent> (*i);
+ if (fc && fc->subtitle_stream()) {
+ t += fc->subtitle_stream()->id;
+ }
+ }
+
+ t += lexical_cast<string> (_loop);
+
+ return md5_digest (t.c_str(), t.length());
+}
+
+ContentVideoFrame
+Playlist::content_length () const
+{
+ float const vfr = video_frame_rate() > 0 ? video_frame_rate() : 24;
+ int const afr = audio_frame_rate() > 0 ? audio_frame_rate() : 48000;
+
+ return max (
+ video_length(),
+ ContentVideoFrame (audio_length() * vfr / afr)
+ );
+}
+
+void
+Playlist::set_from_xml (shared_ptr<const cxml::Node> node)
+{
+ list<shared_ptr<cxml::Node> > c = node->node_children ("Content");
+ for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
+
+ string const type = (*i)->string_child ("Type");
+ boost::shared_ptr<Content> c;
+
+ if (type == "FFmpeg") {
+ c.reset (new FFmpegContent (*i));
+ } else if (type == "ImageMagick") {
+ c.reset (new ImageMagickContent (*i));
+ } else if (type == "Sndfile") {
+ c.reset (new SndfileContent (*i));
+ }
+
+ _content.push_back (c);
+ }
+
+ _loop = node->number_child<int> ("Loop");
+
+ setup ();
+}
+
+void
+Playlist::as_xml (xmlpp::Node* node)
+{
+ for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
+ (*i)->as_xml (node->add_child ("Content"));
+ }
+
+ node->add_child("Loop")->add_child_text(lexical_cast<string> (_loop));
+}
+
+void
+Playlist::add (shared_ptr<Content> c)
+{
+ _content.push_back (c);
+ setup ();
+ Changed ();
+}
+
+void
+Playlist::remove (shared_ptr<Content> c)
+{
+ ContentList::iterator i = find (_content.begin(), _content.end(), c);
+ if (i != _content.end ()) {
+ _content.erase (i);
+ }
+
+ setup ();
+ Changed ();
+}
+
+void
+Playlist::move_earlier (shared_ptr<Content> c)
+{
+ ContentList::iterator i = find (_content.begin(), _content.end(), c);
+ if (i == _content.begin () || i == _content.end()) {
+ return;
+ }
+
+ ContentList::iterator j = i;
+ --j;
+
+ swap (*i, *j);
+
+ setup ();
+ Changed ();
+}
+
+void
+Playlist::move_later (shared_ptr<Content> c)
+{
+ ContentList::iterator i = find (_content.begin(), _content.end(), c);
+ if (i == _content.end()) {
+ return;
+ }
+
+ ContentList::iterator j = i;
+ ++j;
+ if (j == _content.end ()) {
+ return;
+ }
+
+ swap (*i, *j);
+
+ setup ();
+ Changed ();
+}
+
+void
+Playlist::set_loop (int l)
+{
+ _loop = l;
+ Changed ();
+}
+
+shared_ptr<FFmpegContent>
+Playlist::ffmpeg () const
+{
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (*i);
+ if (fc) {
+ return fc;
+ }
+ }
+
+ return shared_ptr<FFmpegContent> ();
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include "video_source.h"
+#include "audio_source.h"
+#include "video_sink.h"
+#include "audio_sink.h"
+#include "ffmpeg_content.h"
+#include "audio_mapping.h"
+
+class Content;
+class FFmpegContent;
+class FFmpegDecoder;
+class ImageMagickContent;
+class ImageMagickDecoder;
+class SndfileContent;
+class SndfileDecoder;
+class Job;
+class Film;
+
+/** @class Playlist
+ * @brief A set of content files (video and audio), with knowledge of how they should be arranged into
+ * a DCP.
+ *
+ * This class holds Content objects, and it knows how they should be arranged. At the moment
+ * the ordering is implicit; video content is placed sequentially, and audio content is taken
+ * from the video unless any sound-only files are present. If sound-only files exist, they
+ * are played simultaneously (i.e. they can be split up into multiple files for different channels)
+ */
+
+class Playlist
+{
+public:
+ Playlist ();
+ Playlist (boost::shared_ptr<const Playlist>);
+
+ void as_xml (xmlpp::Node *);
+ void set_from_xml (boost::shared_ptr<const cxml::Node>);
+
+ void add (boost::shared_ptr<Content>);
+ void remove (boost::shared_ptr<Content>);
+ void move_earlier (boost::shared_ptr<Content>);
+ void move_later (boost::shared_ptr<Content>);
+
+ ContentAudioFrame audio_length () const;
+ int audio_channels () const;
+ int audio_frame_rate () const;
+ bool has_audio () const;
+
+ float video_frame_rate () const;
+ libdcp::Size video_size () const;
+ ContentVideoFrame video_length () const;
+
+ AudioMapping default_audio_mapping () const;
+ ContentVideoFrame content_length () const;
+
+ enum AudioFrom {
+ AUDIO_FFMPEG,
+ AUDIO_SNDFILE
+ };
+
+ AudioFrom audio_from () const {
+ return _audio_from;
+ }
+
+ ContentList content () const {
+ return _content;
+ }
+
+ boost::shared_ptr<FFmpegContent> ffmpeg () const;
+
+ std::list<boost::shared_ptr<const VideoContent> > video () const {
+ return _video;
+ }
+
+ std::list<boost::shared_ptr<const AudioContent> > audio () const {
+ return _audio;
+ }
+
+ std::string audio_digest () const;
+ std::string video_digest () const;
+
+ int loop () const {
+ return _loop;
+ }
+
+ void set_loop (int l);
+
+ mutable boost::signals2::signal<void ()> Changed;
+ mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
+
+private:
+ void setup ();
+ void content_changed (boost::weak_ptr<Content>, int);
+
+ /** where we should get our audio from */
+ AudioFrom _audio_from;
+
+ /** all our content */
+ ContentList _content;
+ /** all our content which contains video */
+ std::list<boost::shared_ptr<const VideoContent> > _video;
+ /** all our content which contains audio. This may contain the same objects
+ * as _video for FFmpegContent.
+ */
+ std::list<boost::shared_ptr<const AudioContent> > _audio;
+
+ int _loop;
+
+ std::list<boost::signals2::connection> _content_connections;
+};
#
msgid ""
msgstr ""
-"Project-Id-Version: LIBDVDOMATIC\n"
+"Project-Id-Version: LIBDCPOMATIC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-04-02 19:10-0500\n"
#: src/lib/job.cc:96 src/lib/job.cc:105
msgid ""
"It is not known what caused this error. The best idea is to report the "
-"problem to the DVD-o-matic mailing list (dvdomatic@carlh.net)"
+"problem to the DCP-o-matic mailing list (dcpomatic@carlh.net)"
msgstr ""
"Error desconocido. La mejor idea es informar del problema a la lista de "
-"correo de DVD-O-matic (dvdomatic@carlh.net)"
+"correo de DCP-o-matic (dcpomatic@carlh.net)"
#: src/lib/filter.cc:82
msgid "Kernel deinterlacer"
#
msgid ""
msgstr ""
-"Project-Id-Version: DVD-o-matic FRENCH\n"
+"Project-Id-Version: DCP-o-matic FRENCH\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-03-20 00:39+0100\n"
#: src/lib/job.cc:96 src/lib/job.cc:105
msgid ""
"It is not known what caused this error. The best idea is to report the "
-"problem to the DVD-o-matic mailing list (dvdomatic@carlh.net)"
+"problem to the DCP-o-matic mailing list (dcpomatic@carlh.net)"
msgstr ""
-"Erreur indéterminée. Merci de rapporter le problème à la liste DVD-o-matic "
-"(dvdomatic@carlh.net)"
+"Erreur indéterminée. Merci de rapporter le problème à la liste DCP-o-matic "
+"(dcpomatic@carlh.net)"
#: src/lib/filter.cc:82
msgid "Kernel deinterlacer"
#: src/lib/job.cc:96 src/lib/job.cc:105
msgid ""
"It is not known what caused this error. The best idea is to report the "
-"problem to the DVD-o-matic mailing list (dvdomatic@carlh.net)"
+"problem to the DCP-o-matic mailing list (dcpomatic@carlh.net)"
msgstr ""
"Non sappiamo cosa ha causato questo errore. La cosa migliore è inviare un "
-"report del problema alla mailing list di DVD-o-matic (dvdomatic@carlh.net)"
+"report del problema alla mailing list di DCP-o-matic (dcpomatic@carlh.net)"
#: src/lib/filter.cc:82
msgid "Kernel deinterlacer"
#
msgid ""
msgstr ""
-"Project-Id-Version: DVD-o-matic\n"
+"Project-Id-Version: DCP-o-matic\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-04-10 15:35+0100\n"
#: src/lib/job.cc:96 src/lib/job.cc:105
msgid ""
"It is not known what caused this error. The best idea is to report the "
-"problem to the DVD-o-matic mailing list (dvdomatic@carlh.net)"
+"problem to the DCP-o-matic mailing list (dcpomatic@carlh.net)"
msgstr ""
"Det är inte känt vad som orsakade detta fel. Bästa sättet att rapportera "
-"problemet är till DVD-o-matics mejl-lista (dvdomatic@carlh.net)"
+"problemet är till DCP-o-matics mejl-lista (dcpomatic@carlh.net)"
#: src/lib/filter.cc:82
msgid "Kernel deinterlacer"
* @brief Parent class for classes which accept and then emit video or audio data.
*/
-#ifndef DVDOMATIC_PROCESSOR_H
-#define DVDOMATIC_PROCESSOR_H
+#ifndef DCPOMATIC_PROCESSOR_H
+#define DCPOMATIC_PROCESSOR_H
#include "video_source.h"
#include "video_sink.h"
* @brief A class to describe one of FFmpeg's software scalers.
*/
-#ifndef DVDOMATIC_SCALER_H
-#define DVDOMATIC_SCALER_H
+#ifndef DCPOMATIC_SCALER_H
+#define DCPOMATIC_SCALER_H
#include <string>
#include <vector>
private:
void set_status (std::string);
-
+
mutable boost::mutex _status_mutex;
std::string _status;
};
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_array.hpp>
+#include <libcxml/cxml.h>
#include "server.h"
#include "util.h"
#include "scaler.h"
using boost::scoped_array;
using libdcp::Size;
+ServerDescription::ServerDescription (shared_ptr<const cxml::Node> node)
+{
+ _host_name = node->string_child ("HostName");
+ _threads = node->number_child<int> ("Threads");
+}
+
+void
+ServerDescription::as_xml (xmlpp::Node* root) const
+{
+ root->add_child("HostName")->add_child_text (_host_name);
+ root->add_child("Threads")->add_child_text (boost::lexical_cast<string> (_threads));
+}
+
/** Create a server description from a string of metadata returned from as_metadata().
* @param v Metadata.
* @return ServerDescription, or 0.
return new ServerDescription (b[0], atoi (b[1].c_str ()));
}
-/** @return Description of this server as text */
-string
-ServerDescription::as_metadata () const
-{
- stringstream s;
- s << _host_name << N_(" ") << _threads;
- return s.str ();
-}
-
Server::Server (shared_ptr<Log> log)
: _log (log)
{
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/thread/condition.hpp>
+#include <libxml++/libxml++.h>
#include "log.h"
class Socket;
+namespace cxml {
+ class Node;
+}
+
/** @class ServerDescription
* @brief Class to describe a server to which we can send encoding work.
*/
, _threads (t)
{}
+ ServerDescription (boost::shared_ptr<const cxml::Node>);
+
/** @return server's host name or IP address in string form */
std::string host_name () const {
return _host_name;
_threads = t;
}
- std::string as_metadata () const;
+ void as_xml (xmlpp::Node *) const;
static ServerDescription * create_from_metadata (std::string v);
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include "sndfile_content.h"
+#include "sndfile_decoder.h"
+#include "compose.hpp"
+#include "job.h"
+
+#include "i18n.h"
+
+using std::string;
+using std::stringstream;
+using std::cout;
+using boost::shared_ptr;
+using boost::lexical_cast;
+
+SndfileContent::SndfileContent (boost::filesystem::path f)
+ : Content (f)
+ , AudioContent (f)
+ , _audio_channels (0)
+ , _audio_length (0)
+ , _audio_frame_rate (0)
+{
+
+}
+
+SndfileContent::SndfileContent (shared_ptr<const cxml::Node> node)
+ : Content (node)
+ , AudioContent (node)
+{
+ _audio_channels = node->number_child<int> ("AudioChannels");
+ _audio_length = node->number_child<ContentAudioFrame> ("AudioLength");
+ _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
+}
+
+string
+SndfileContent::summary () const
+{
+ return String::compose (_("Sound file: %1"), file().filename().string());
+}
+
+string
+SndfileContent::information () const
+{
+ if (_audio_frame_rate == 0) {
+ return "";
+ }
+
+ stringstream s;
+
+ s << String::compose (
+ _("%1 channels, %2kHz, %3 samples"),
+ audio_channels(),
+ audio_frame_rate() / 1000.0,
+ audio_length()
+ );
+
+ return s.str ();
+}
+
+bool
+SndfileContent::valid_file (boost::filesystem::path f)
+{
+ /* XXX: more extensions */
+ string ext = f.extension().string();
+ transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
+ return (ext == ".wav" || ext == ".aif" || ext == ".aiff");
+}
+
+shared_ptr<Content>
+SndfileContent::clone () const
+{
+ return shared_ptr<Content> (new SndfileContent (*this));
+}
+
+void
+SndfileContent::examine (shared_ptr<Film> film, shared_ptr<Job> job, bool quick)
+{
+ job->set_progress_unknown ();
+ Content::examine (film, job, quick);
+
+ SndfileDecoder dec (film, shared_from_this());
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _audio_channels = dec.audio_channels ();
+ _audio_length = dec.audio_length ();
+ _audio_frame_rate = dec.audio_frame_rate ();
+ }
+
+ signal_changed (AudioContentProperty::AUDIO_CHANNELS);
+ signal_changed (AudioContentProperty::AUDIO_LENGTH);
+ signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
+}
+
+void
+SndfileContent::as_xml (xmlpp::Node* node) const
+{
+ node->add_child("Type")->add_child_text ("Sndfile");
+ Content::as_xml (node);
+ node->add_child("AudioChannels")->add_child_text (lexical_cast<string> (_audio_channels));
+ node->add_child("AudioLength")->add_child_text (lexical_cast<string> (_audio_length));
+ node->add_child("AudioFrameRate")->add_child_text (lexical_cast<string> (_audio_frame_rate));
+}
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+extern "C" {
+#include <libavutil/audioconvert.h>
+}
+#include "audio_content.h"
+
+namespace cxml {
+ class Node;
+}
+
+class SndfileContent : public AudioContent
+{
+public:
+ SndfileContent (boost::filesystem::path);
+ SndfileContent (boost::shared_ptr<const cxml::Node>);
+
+ boost::shared_ptr<SndfileContent> shared_from_this () {
+ return boost::dynamic_pointer_cast<SndfileContent> (Content::shared_from_this ());
+ }
+
+ void examine (boost::shared_ptr<Film>, boost::shared_ptr<Job>, bool);
+ std::string summary () const;
+ std::string information () const;
+ void as_xml (xmlpp::Node *) const;
+ boost::shared_ptr<Content> clone () const;
+
+ /* AudioContent */
+ int audio_channels () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_channels;
+ }
+
+ ContentAudioFrame audio_length () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_length;
+ }
+
+ int audio_frame_rate () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _audio_frame_rate;
+ }
+
+ static bool valid_file (boost::filesystem::path);
+
+private:
+ int _audio_channels;
+ ContentAudioFrame _audio_length;
+ int _audio_frame_rate;
+};
#include <iostream>
#include <sndfile.h>
+#include "sndfile_content.h"
#include "sndfile_decoder.h"
#include "film.h"
#include "exceptions.h"
using std::vector;
using std::string;
-using std::stringstream;
using std::min;
using std::cout;
using boost::shared_ptr;
-using boost::optional;
-SndfileDecoder::SndfileDecoder (shared_ptr<Film> f, DecodeOptions o)
- : Decoder (f, o)
- , AudioDecoder (f, o)
- , _done (0)
- , _frames (0)
+SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c)
+ : Decoder (f)
+ , AudioDecoder (f)
+ , _sndfile_content (c)
{
- _done = 0;
- _frames = 0;
-
- vector<string> const files = _film->external_audio ();
-
- int N = 0;
- for (size_t i = 0; i < files.size(); ++i) {
- if (!files[i].empty()) {
- N = i + 1;
- }
- }
-
- if (N == 0) {
- return;
+ _sndfile = sf_open (_sndfile_content->file().string().c_str(), SFM_READ, &_info);
+ if (!_sndfile) {
+ throw DecodeError (_("could not open audio file for reading"));
}
- bool first = true;
-
- for (size_t i = 0; i < (size_t) N; ++i) {
- if (files[i].empty ()) {
- _sndfiles.push_back (0);
- } else {
- SF_INFO info;
- SNDFILE* s = sf_open (files[i].c_str(), SFM_READ, &info);
- if (!s) {
- throw DecodeError (_("could not open external audio file for reading"));
- }
-
- if (info.channels != 1) {
- throw DecodeError (_("external audio files must be mono"));
- }
-
- _sndfiles.push_back (s);
+ _done = 0;
+ _remaining = _info.frames;
+}
- if (first) {
- shared_ptr<SndfileStream> st (
- new SndfileStream (
- info.samplerate, av_get_default_channel_layout (N)
- )
- );
-
- _audio_streams.push_back (st);
- _audio_stream = st;
- _frames = info.frames;
- first = false;
- } else {
- if (info.frames != _frames) {
- throw DecodeError (_("external audio files have differing lengths"));
- }
- }
- }
- }
+SndfileDecoder::~SndfileDecoder ()
+{
+ sf_close (_sndfile);
}
bool
/* Do things in half second blocks as I think there may be limits
to what FFmpeg (and in particular the resampler) can cope with.
*/
- sf_count_t const block = _audio_stream->sample_rate() / 2;
- shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream->channels(), block));
- sf_count_t const this_time = min (block, _frames - _done);
- for (size_t i = 0; i < _sndfiles.size(); ++i) {
- if (!_sndfiles[i]) {
- audio->make_silent (i);
- } else {
- sf_read_float (_sndfiles[i], audio->data(i), this_time);
- }
- }
-
+ sf_count_t const block = _sndfile_content->audio_frame_rate() / 2;
+ sf_count_t const this_time = min (block, _remaining);
+
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (_sndfile_content->audio_channels(), this_time));
+ sf_read_float (_sndfile, audio->data(0), this_time);
audio->set_frames (this_time);
- Audio (audio, double(_done) / _audio_stream->sample_rate());
+ Audio (audio, double(_done) / audio_frame_rate());
_done += this_time;
+ _remaining -= this_time;
- return (_done == _frames);
-}
-
-SndfileDecoder::~SndfileDecoder ()
-{
- for (size_t i = 0; i < _sndfiles.size(); ++i) {
- if (_sndfiles[i]) {
- sf_close (_sndfiles[i]);
- }
- }
-}
-
-shared_ptr<SndfileStream>
-SndfileStream::create ()
-{
- return shared_ptr<SndfileStream> (new SndfileStream);
-}
-
-shared_ptr<SndfileStream>
-SndfileStream::create (string t, optional<int> v)
-{
- if (!v) {
- /* version < 1; no type in the string, and there's only FFmpeg streams anyway */
- return shared_ptr<SndfileStream> ();
- }
-
- stringstream s (t);
- string type;
- s >> type;
- if (type != N_("external")) {
- return shared_ptr<SndfileStream> ();
- }
-
- return shared_ptr<SndfileStream> (new SndfileStream (t, v));
+ return (_remaining == 0);
}
-SndfileStream::SndfileStream (string t, optional<int> v)
+int
+SndfileDecoder::audio_channels () const
{
- assert (v);
-
- stringstream s (t);
- string type;
- s >> type >> _sample_rate >> _channel_layout;
+ return _info.channels;
}
-SndfileStream::SndfileStream ()
+ContentAudioFrame
+SndfileDecoder::audio_length () const
{
-
+ return _info.frames;
}
-string
-SndfileStream::to_string () const
+int
+SndfileDecoder::audio_frame_rate () const
{
- return String::compose (N_("external %1 %2"), _sample_rate, _channel_layout);
+ return _info.samplerate;
}
#include <sndfile.h>
#include "decoder.h"
#include "audio_decoder.h"
-#include "stream.h"
-class SndfileStream : public AudioStream
-{
-public:
- SndfileStream (int sample_rate, int64_t layout)
- : AudioStream (sample_rate, layout)
- {}
-
- std::string to_string () const;
-
- static boost::shared_ptr<SndfileStream> create ();
- static boost::shared_ptr<SndfileStream> create (std::string t, boost::optional<int> v);
-
-private:
- friend class stream_test;
-
- SndfileStream ();
- SndfileStream (std::string t, boost::optional<int> v);
-};
+class SndfileContent;
class SndfileDecoder : public AudioDecoder
{
public:
- SndfileDecoder (boost::shared_ptr<Film>, DecodeOptions);
+ SndfileDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const SndfileContent>);
~SndfileDecoder ();
bool pass ();
+ int audio_channels () const;
+ ContentAudioFrame audio_length () const;
+ int audio_frame_rate () const;
+
private:
- std::vector<SNDFILE*> _sndfiles;
- sf_count_t _done;
- sf_count_t _frames;
+ boost::shared_ptr<const SndfileContent> _sndfile_content;
+ SNDFILE* _sndfile;
+ SF_INFO _info;
+ ContentAudioFrame _done;
+ ContentAudioFrame _remaining;
};
* @brief A class to describe a sound processor.
*/
-#ifndef DVDOMATIC_SOUND_PROCESSOR_H
-#define DVDOMATIC_SOUND_PROCESSOR_H
+#ifndef DCPOMATIC_SOUND_PROCESSOR_H
+#define DCPOMATIC_SOUND_PROCESSOR_H
#include <string>
#include <vector>
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <sstream>
-#include "compose.hpp"
-#include "stream.h"
-#include "ffmpeg_decoder.h"
-#include "sndfile_decoder.h"
-
-#include "i18n.h"
-
-using std::string;
-using std::stringstream;
-using boost::shared_ptr;
-using boost::optional;
-
-/** Construct a SubtitleStream from a value returned from to_string().
- * @param t String returned from to_string().
- * @param v State file version.
- */
-SubtitleStream::SubtitleStream (string t, boost::optional<int>)
-{
- stringstream n (t);
- n >> _id;
-
- size_t const s = t.find (' ');
- if (s != string::npos) {
- _name = t.substr (s + 1);
- }
-}
-
-/** @return A canonical string representation of this stream */
-string
-SubtitleStream::to_string () const
-{
- return String::compose (N_("%1 %2"), _id, _name);
-}
-
-/** Create a SubtitleStream from a value returned from to_string().
- * @param t String returned from to_string().
- * @param v State file version.
- */
-shared_ptr<SubtitleStream>
-SubtitleStream::create (string t, optional<int> v)
-{
- return shared_ptr<SubtitleStream> (new SubtitleStream (t, v));
-}
-
-/** Create an AudioStream from a string returned from to_string().
- * @param t String returned from to_string().
- * @param v State file version.
- * @return AudioStream, or 0.
- */
-shared_ptr<AudioStream>
-audio_stream_factory (string t, optional<int> v)
-{
- shared_ptr<AudioStream> s;
-
- s = FFmpegAudioStream::create (t, v);
- if (!s) {
- s = SndfileStream::create (t, v);
- }
-
- return s;
-}
-
-/** Create a SubtitleStream from a string returned from to_string().
- * @param t String returned from to_string().
- * @param v State file version.
- * @return SubtitleStream, or 0.
- */
-shared_ptr<SubtitleStream>
-subtitle_stream_factory (string t, optional<int> v)
-{
- return SubtitleStream::create (t, v);
-}
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/lib/stream.h
- * @brief Representations of audio and subtitle streams.
- *
- * Some content may have multiple `streams' of audio and/or subtitles; perhaps
- * for multiple languages, or for stereo / surround mixes. These classes represent
- * those streams, and know about their details.
- */
-
-#ifndef DVDOMATIC_STREAM_H
-#define DVDOMATIC_STREAM_H
-
-#include <stdint.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
-extern "C" {
-#include <libavutil/audioconvert.h>
-}
-
-/** @class Stream
- * @brief Parent class for streams.
- */
-class Stream
-{
-public:
- virtual ~Stream () {}
- virtual std::string to_string () const = 0;
-};
-
-/** @class AudioStream
- * @brief A stream of audio data.
- */
-struct AudioStream : public Stream
-{
-public:
- AudioStream (int r, int64_t l)
- : _sample_rate (r)
- , _channel_layout (l)
- {}
-
- /* Only used for backwards compatibility for state file version < 1 */
- void set_sample_rate (int s) {
- _sample_rate = s;
- }
-
- int channels () const {
- return av_get_channel_layout_nb_channels (_channel_layout);
- }
-
- int sample_rate () const {
- return _sample_rate;
- }
-
- int64_t channel_layout () const {
- return _channel_layout;
- }
-
-protected:
- AudioStream ()
- : _sample_rate (0)
- , _channel_layout (0)
- {}
-
- int _sample_rate;
- int64_t _channel_layout;
-};
-
-/** @class SubtitleStream
- * @brief A stream of subtitle data.
- */
-class SubtitleStream : public Stream
-{
-public:
- SubtitleStream (std::string n, int i)
- : _name (n)
- , _id (i)
- {}
-
- std::string to_string () const;
-
- std::string name () const {
- return _name;
- }
-
- int id () const {
- return _id;
- }
-
- static boost::shared_ptr<SubtitleStream> create (std::string t, boost::optional<int> v);
-
-private:
- friend class stream_test;
-
- SubtitleStream (std::string t, boost::optional<int> v);
-
- std::string _name;
- int _id;
-};
-
-boost::shared_ptr<AudioStream> audio_stream_factory (std::string t, boost::optional<int> version);
-boost::shared_ptr<SubtitleStream> subtitle_stream_factory (std::string t, boost::optional<int> version);
-
-#endif
* @brief Some timing classes for debugging and profiling.
*/
-#ifndef DVDOMATIC_TIMER_H
-#define DVDOMATIC_TIMER_H
+#ifndef DCPOMATIC_TIMER_H
+#define DCPOMATIC_TIMER_H
#include <string>
#include <map>
using boost::shared_ptr;
/** @param s Film to use.
- * @param o Decode options.
*/
-TranscodeJob::TranscodeJob (shared_ptr<Film> f, DecodeOptions o)
+TranscodeJob::TranscodeJob (shared_ptr<Film> f)
: Job (f)
- , _decode_opt (o)
{
}
_film->log()->log (N_("Transcode job starting"));
_film->log()->log (String::compose (N_("Audio delay is %1ms"), _film->audio_delay()));
- _encoder.reset (new Encoder (_film));
- Transcoder w (_film, _decode_opt, this, _encoder);
- w.go ();
+ _transcoder.reset (new Transcoder (_film, shared_from_this ()));
+ _transcoder->go ();
set_progress (1);
set_state (FINISHED_OK);
string
TranscodeJob::status () const
{
- if (!_encoder) {
+ if (!_transcoder) {
return _("0%");
}
- float const fps = _encoder->current_frames_per_second ();
+ float const fps = _transcoder->current_encoding_rate ();
if (fps == 0) {
return Job::status ();
}
int
TranscodeJob::remaining_time () const
{
- float fps = _encoder->current_frames_per_second ();
+ if (!_transcoder) {
+ return 0;
+ }
+
+ float fps = _transcoder->current_encoding_rate ();
+
if (fps == 0) {
return 0;
}
- if (!_film->length()) {
+ if (!_film->video_length()) {
return 0;
}
/* Compute approximate proposed length here, as it's only here that we need it */
- int length = _film->length().get();
- FrameRateConversion const frc (_film->source_frame_rate(), _film->dcp_frame_rate());
+ int length = _film->video_length();
+ FrameRateConversion const frc (_film->video_frame_rate(), _film->dcp_frame_rate());
if (frc.skip) {
length /= 2;
}
/* If we are repeating it shouldn't affect transcode time, so don't take it into account */
- /* We assume that dcp_length() is valid, if it is set */
- int const left = length - _encoder->video_frames_out();
+ int const left = length - _transcoder->video_frames_out();
return left / fps;
}
#include <boost/shared_ptr.hpp>
#include "job.h"
-#include "options.h"
-class Encoder;
+class Transcoder;
/** @class TranscodeJob
* @brief A job which transcodes from one format to another.
class TranscodeJob : public Job
{
public:
- TranscodeJob (boost::shared_ptr<Film> f, DecodeOptions od);
+ TranscodeJob (boost::shared_ptr<Film> f);
std::string name () const;
void run ();
std::string status () const;
-protected:
+private:
int remaining_time () const;
-private:
- DecodeOptions _decode_opt;
- boost::shared_ptr<Encoder> _encoder;
+ boost::shared_ptr<Transcoder> _transcoder;
};
#include <boost/signals2.hpp>
#include "transcoder.h"
#include "encoder.h"
-#include "decoder_factory.h"
#include "film.h"
#include "matcher.h"
#include "delay_line.h"
-#include "options.h"
#include "gain.h"
#include "video_decoder.h"
#include "audio_decoder.h"
+#include "player.h"
#include "trimmer.h"
using std::string;
/** Construct a transcoder using a Decoder that we create and a supplied Encoder.
* @param f Film that we are transcoding.
- * @param o Decode options.
* @param j Job that we are running under, or 0.
* @param e Encoder to use.
*/
-Transcoder::Transcoder (shared_ptr<Film> f, DecodeOptions o, Job* j, shared_ptr<Encoder> e)
+Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<Job> j)
: _job (j)
- , _encoder (e)
- , _decoders (decoder_factory (f, o))
+ , _player (f->player ())
+ , _encoder (new Encoder (f))
{
- assert (_encoder);
-
- shared_ptr<AudioStream> st = f->audio_stream();
- if (st) {
- _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate()));
- }
- _delay_line.reset (new DelayLine (f->log(), f->audio_delay() / 1000.0f));
+ _matcher.reset (new Matcher (f->log(), f->audio_frame_rate(), f->video_frame_rate()));
+ _delay_line.reset (new DelayLine (f->log(), f->audio_delay() * f->audio_frame_rate() / 1000));
_gain.reset (new Gain (f->log(), f->audio_gain()));
- int const sr = st ? st->sample_rate() : 0;
int const trim_start = f->trim_type() == Film::ENCODE ? f->trim_start() : 0;
int const trim_end = f->trim_type() == Film::ENCODE ? f->trim_end() : 0;
_trimmer.reset (new Trimmer (
- f->log(), trim_start, trim_end, f->length().get_value_or(0),
- sr, f->source_frame_rate(), f->dcp_frame_rate()
+ f->log(), trim_start, trim_end, f->content_length(),
+ f->audio_frame_rate(), f->video_frame_rate(), f->dcp_frame_rate()
));
-
- /* Set up the decoder to use the film's set streams */
- _decoders.video->set_subtitle_stream (f->subtitle_stream ());
- if (f->audio_stream ()) {
- _decoders.audio->set_audio_stream (f->audio_stream ());
+
+ if (!f->with_subtitles ()) {
+ _player->disable_subtitles ();
}
- _decoders.video->connect_video (_delay_line);
- if (_matcher) {
- _delay_line->connect_video (_matcher);
- _matcher->connect_video (_trimmer);
- } else {
- _delay_line->connect_video (_trimmer);
- }
+ _player->connect_video (_delay_line);
+ _delay_line->connect_video (_matcher);
+ _matcher->connect_video (_trimmer);
_trimmer->connect_video (_encoder);
- _decoders.audio->connect_audio (_delay_line);
- if (_matcher) {
- _delay_line->connect_audio (_matcher);
- _matcher->connect_audio (_gain);
- } else {
- _delay_line->connect_audio (_gain);
- }
+ _player->connect_audio (_delay_line);
+ _delay_line->connect_audio (_matcher);
+ _matcher->connect_audio (_gain);
_gain->connect_audio (_trimmer);
_trimmer->connect_audio (_encoder);
}
-/** Run the decoder, passing its output to the encoder, until the decoder
- * has no more data to present.
- */
void
Transcoder::go ()
{
_encoder->process_begin ();
-
- bool done[2] = { false, false };
-
while (1) {
- if (!done[0]) {
- done[0] = _decoders.video->pass ();
- if (_job) {
- _decoders.video->set_progress (_job);
- }
- }
-
- if (!done[1] && _decoders.audio && dynamic_pointer_cast<Decoder> (_decoders.audio) != dynamic_pointer_cast<Decoder> (_decoders.video)) {
- done[1] = _decoders.audio->pass ();
- } else {
- done[1] = true;
- }
-
- if (done[0] && done[1]) {
+ if (_player->pass ()) {
break;
}
+ _player->set_progress (_job);
}
-
+
_delay_line->process_end ();
if (_matcher) {
_matcher->process_end ();
_gain->process_end ();
_encoder->process_end ();
}
+
+float
+Transcoder::current_encoding_rate () const
+{
+ return _encoder->current_encoding_rate ();
+}
+
+int
+Transcoder::video_frames_out () const
+{
+ return _encoder->video_frames_out ();
+}
*/
/** @file src/transcoder.h
- * @brief A class which takes a Film and some Options, then uses those to transcode the film.
*
* A decoder is selected according to the content type, and the encoder can be specified
* as a parameter to the constructor.
*/
-#include "decoder_factory.h"
-
class Film;
class Job;
class Encoder;
class Matcher;
class VideoFilter;
class Gain;
-class VideoDecoder;
-class AudioDecoder;
class DelayLine;
+class Player;
class Trimmer;
/** @class Transcoder
- * @brief A class which takes a Film and some Options, then uses those to transcode the film.
*
* A decoder is selected according to the content type, and the encoder can be specified
* as a parameter to the constructor.
public:
Transcoder (
boost::shared_ptr<Film> f,
- DecodeOptions o,
- Job* j,
- boost::shared_ptr<Encoder> e
+ boost::shared_ptr<Job> j
);
void go ();
- boost::shared_ptr<VideoDecoder> video_decoder () const {
- return _decoders.video;
- }
+ float current_encoding_rate () const;
+ int video_frames_out () const;
protected:
/** A Job that is running this Transcoder, or 0 */
- Job* _job;
- /** The encoder that we will use */
+ boost::shared_ptr<Job> _job;
+ boost::shared_ptr<Player> _player;
boost::shared_ptr<Encoder> _encoder;
- /** The decoders that we will use */
- Decoders _decoders;
boost::shared_ptr<Matcher> _matcher;
boost::shared_ptr<DelayLine> _delay_line;
boost::shared_ptr<Gain> _gain;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "types.h"
+
+bool operator== (Crop const & a, Crop const & b)
+{
+ return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
+}
+
+bool operator!= (Crop const & a, Crop const & b)
+{
+ return !(a == b);
+}
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_TYPES_H
+#define DCPOMATIC_TYPES_H
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <libdcp/util.h>
+
+class Content;
+
+typedef std::vector<boost::shared_ptr<Content> > ContentList;
+typedef int64_t ContentAudioFrame;
+typedef int ContentVideoFrame;
+
+/** @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;
+};
+
+/** @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;
+};
+
+#endif
*/
-#ifndef DVDOMATIC_UI_SIGNALLER_H
-#define DVDOMATIC_UI_SIGNALLER_H
+#ifndef DCPOMATIC_UI_SIGNALLER_H
+#define DCPOMATIC_UI_SIGNALLER_H
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <fstream>
#include <climits>
-#ifdef DVDOMATIC_POSIX
+#ifdef DCPOMATIC_POSIX
#include <execinfo.h>
#include <cxxabi.h>
#endif
#include "i18n.h"
-using std::cout;
using std::string;
using std::stringstream;
-using std::list;
+using std::setfill;
using std::ostream;
+using std::endl;
using std::vector;
+using std::hex;
+using std::setw;
using std::ifstream;
-using std::istream;
+using std::ios;
using std::min;
using std::max;
+using std::list;
using std::multimap;
+using std::istream;
+using std::numeric_limits;
using std::pair;
using boost::shared_ptr;
+using boost::thread;
using boost::lexical_cast;
using boost::optional;
using libdcp::Size;
return ap.str ();
}
-#ifdef DVDOMATIC_POSIX
+#ifdef DCPOMATIC_POSIX
/** @param l Mangled C++ identifier.
* @return Demangled version.
*/
return t.tv_sec + (double (t.tv_usec) / 1e6);
}
-/** Call the required functions to set up DVD-o-matic's static arrays, etc.
+/** Call the required functions to set up DCP-o-matic's static arrays, etc.
* Must be called from the UI thread, if there is one.
*/
void
-dvdomatic_setup ()
+dcpomatic_setup ()
{
avfilter_register_all ();
ui_thread = boost::this_thread::get_id ();
}
-#ifdef DVDOMATIC_WINDOWS
+#ifdef DCPOMATIC_WINDOWS
boost::filesystem::path
mo_path ()
{
#endif
void
-dvdomatic_setup_i18n (string lang)
+dcpomatic_setup_i18n (string lang)
{
-#ifdef DVDOMATIC_POSIX
+#ifdef DCPOMATIC_POSIX
lang += ".UTF8";
#endif
}
setlocale (LC_ALL, "");
- textdomain ("libdvdomatic");
+ textdomain ("libdcpomatic");
-#ifdef DVDOMATIC_WINDOWS
- bindtextdomain ("libdvdomatic", mo_path().string().c_str());
- bind_textdomain_codeset ("libdvdomatic", "UTF8");
+#ifdef DCPOMATIC_WINDOWS
+ bindtextdomain ("libdcpomatic", mo_path().string().c_str());
+ bind_textdomain_codeset ("libdcpomatic", "UTF8");
#endif
-#ifdef DVDOMATIC_POSIX
- bindtextdomain ("libdvdomatic", POSIX_LOCALE_PREFIX);
+#ifdef DCPOMATIC_POSIX
+ bindtextdomain ("libdcpomatic", POSIX_LOCALE_PREFIX);
#endif
}
* @return MD5 digest of file's contents.
*/
string
-md5_digest (string file)
+md5_digest (boost::filesystem::path file)
{
- ifstream f (file.c_str(), std::ios::binary);
+ ifstream f (file.string().c_str(), std::ios::binary);
if (!f.good ()) {
- throw OpenFileError (file);
+ throw OpenFileError (file.string());
}
f.seekg (0, std::ios::end);
return 96000;
}
-bool operator== (Crop const & a, Crop const & b)
-{
- return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom);
-}
-
-bool operator!= (Crop const & a, Crop const & b)
-{
- return !(a == b);
-}
-
/** @param index Colour LUT index.
* @return Human-readable name.
*/
}
}
+/** Add data from from `from', `from_channel' to our channel `to_channel' */
+void
+AudioBuffers::accumulate (shared_ptr<const AudioBuffers> from, int from_channel, int to_channel)
+{
+ int const N = frames ();
+ assert (from->frames() == N);
+
+ float* s = from->data (from_channel);
+ float* d = _data[to_channel];
+
+ for (int i = 0; i < N; ++i) {
+ *d++ += *s++;
+ }
+}
+
/** Trip an assert if the caller is not in the UI thread */
void
ensure_ui_thread ()
assert (boost::this_thread::get_id() == ui_thread);
}
-/** @param v Source video frame.
+/** @param v Content video frame.
* @param audio_sample_rate Source audio sample rate.
* @param frames_per_second Number of video frames per second.
* @return Equivalent number of audio frames for `v'.
*/
int64_t
-video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float frames_per_second)
+video_frames_to_audio_frames (ContentVideoFrame v, float audio_sample_rate, float frames_per_second)
{
return ((int64_t) v * audio_sample_rate / frames_per_second);
}
-/** @param f Filename.
- * @return true if this file is a still image, false if it is something else.
- */
-bool
-still_image_file (string f)
-{
- string ext = boost::filesystem::path(f).extension().string();
-
- transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
-
- return (ext == N_(".tif") || ext == N_(".tiff") || ext == N_(".jpg") || ext == N_(".jpeg") || ext == N_(".png") || ext == N_(".bmp"));
-}
-
/** @return A pair containing CPU model name and the number of processors */
pair<string, int>
cpu_info ()
pair<string, int> info;
info.second = 0;
-#ifdef DVDOMATIC_POSIX
+#ifdef DCPOMATIC_POSIX
ifstream f (N_("/proc/cpuinfo"));
while (f.good ()) {
string l;
return channels[c];
}
-AudioMapping::AudioMapping (int c)
- : _source_channels (c)
-{
-
-}
-
-optional<libdcp::Channel>
-AudioMapping::source_to_dcp (int c) const
-{
- if (c >= _source_channels) {
- return optional<libdcp::Channel> ();
- }
-
- if (_source_channels == 1) {
- /* mono sources to centre */
- return libdcp::CENTRE;
- }
-
- return static_cast<libdcp::Channel> (c);
-}
-
-optional<int>
-AudioMapping::dcp_to_source (libdcp::Channel c) const
-{
- if (_source_channels == 1) {
- if (c == libdcp::CENTRE) {
- return 0;
- } else {
- return optional<int> ();
- }
- }
-
- if (static_cast<int> (c) >= _source_channels) {
- return optional<int> ();
- }
-
- return static_cast<int> (c);
-}
-
-int
-AudioMapping::dcp_channels () const
-{
- if (_source_channels == 1) {
- /* The source is mono, so to put the mono channel into
- the centre we need to generate a 5.1 soundtrack.
- */
- return 6;
- }
-
- return _source_channels;
-}
-
FrameRateConversion::FrameRateConversion (float source, int dcp)
: skip (false)
, repeat (false)
* @brief Some utility functions and classes.
*/
-#ifndef DVDOMATIC_UTIL_H
-#define DVDOMATIC_UTIL_H
+#ifndef DCPOMATIC_UTIL_H
+#define DCPOMATIC_UTIL_H
#include <string>
#include <vector>
#include <libavfilter/avfilter.h>
}
#include "compose.hpp"
+#include "types.h"
-#ifdef DVDOMATIC_DEBUG
+#ifdef DCPOMATIC_DEBUG
#define TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TIMING);
#else
#define TIMING(...)
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_i18n (std::string);
+extern void dcpomatic_setup ();
+extern void dcpomatic_setup_i18n (std::string);
extern std::vector<std::string> 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);
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;
-};
-
-/** @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 std::string colour_lut_index_to_name (int index);
/** @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.
void copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset);
void move (int from, int to, int frames);
+ void accumulate (boost::shared_ptr<const AudioBuffers>, int, int);
private:
/** Number of channels */
float** _data;
};
-class AudioMapping
-{
-public:
- AudioMapping (int);
-
- boost::optional<libdcp::Channel> source_to_dcp (int c) const;
- boost::optional<int> dcp_to_source (libdcp::Channel c) const;
- int dcp_channels () const;
-
-private:
- int _source_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 (ContentVideoFrame v, float audio_sample_rate, float frames_per_second);
extern std::pair<std::string, int> cpu_info ();
class LocaleGuard
-extern char const * dvdomatic_version;
-extern char const * dvdomatic_git_commit;
+extern char const * dcpomatic_version;
+extern char const * dcpomatic_git_commit;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <libcxml/cxml.h>
+#include "video_content.h"
+#include "video_decoder.h"
+
+#include "i18n.h"
+
+int const VideoContentProperty::VIDEO_LENGTH = 0;
+int const VideoContentProperty::VIDEO_SIZE = 1;
+int const VideoContentProperty::VIDEO_FRAME_RATE = 2;
+
+using std::string;
+using std::stringstream;
+using std::setprecision;
+using boost::shared_ptr;
+using boost::lexical_cast;
+
+VideoContent::VideoContent (boost::filesystem::path f)
+ : Content (f)
+ , _video_length (0)
+{
+
+}
+
+VideoContent::VideoContent (shared_ptr<const cxml::Node> node)
+ : Content (node)
+{
+ _video_length = node->number_child<ContentVideoFrame> ("VideoLength");
+ _video_size.width = node->number_child<int> ("VideoWidth");
+ _video_size.height = node->number_child<int> ("VideoHeight");
+ _video_frame_rate = node->number_child<float> ("VideoFrameRate");
+}
+
+VideoContent::VideoContent (VideoContent const & o)
+ : Content (o)
+ , _video_length (o._video_length)
+ , _video_size (o._video_size)
+ , _video_frame_rate (o._video_frame_rate)
+{
+
+}
+
+void
+VideoContent::as_xml (xmlpp::Node* node) const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ node->add_child("VideoLength")->add_child_text (lexical_cast<string> (_video_length));
+ node->add_child("VideoWidth")->add_child_text (lexical_cast<string> (_video_size.width));
+ node->add_child("VideoHeight")->add_child_text (lexical_cast<string> (_video_size.height));
+ node->add_child("VideoFrameRate")->add_child_text (lexical_cast<string> (_video_frame_rate));
+}
+
+void
+VideoContent::take_from_video_decoder (shared_ptr<VideoDecoder> d)
+{
+ /* These decoder calls could call other content methods which take a lock on the mutex */
+ libdcp::Size const vs = d->native_size ();
+ float const vfr = d->video_frame_rate ();
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _video_size = vs;
+ _video_frame_rate = vfr;
+ }
+
+ signal_changed (VideoContentProperty::VIDEO_SIZE);
+ signal_changed (VideoContentProperty::VIDEO_FRAME_RATE);
+}
+
+
+string
+VideoContent::information () const
+{
+ if (video_size().width == 0 || video_size().height == 0) {
+ return "";
+ }
+
+ stringstream s;
+
+ s << String::compose (
+ _("%1x%2 pixels (%3:1)"),
+ video_size().width,
+ video_size().height,
+ setprecision (3), float (video_size().width) / video_size().height
+ );
+
+ return s.str ();
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef DCPOMATIC_VIDEO_CONTENT_H
+#define DCPOMATIC_VIDEO_CONTENT_H
+
+#include "content.h"
+#include "util.h"
+
+class VideoDecoder;
+
+class VideoContentProperty
+{
+public:
+ static int const VIDEO_LENGTH;
+ static int const VIDEO_SIZE;
+ static int const VIDEO_FRAME_RATE;
+};
+
+class VideoContent : public virtual Content
+{
+public:
+ VideoContent (boost::filesystem::path);
+ VideoContent (boost::shared_ptr<const cxml::Node>);
+ VideoContent (VideoContent const &);
+
+ void as_xml (xmlpp::Node *) const;
+ virtual std::string information () const;
+
+ ContentVideoFrame video_length () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _video_length;
+ }
+
+ libdcp::Size video_size () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _video_size;
+ }
+
+ float video_frame_rate () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _video_frame_rate;
+ }
+
+protected:
+ void take_from_video_decoder (boost::shared_ptr<VideoDecoder>);
+
+ ContentVideoFrame _video_length;
+
+private:
+ libdcp::Size _video_size;
+ float _video_frame_rate;
+};
+
+#endif
#include "film.h"
#include "image.h"
#include "log.h"
-#include "options.h"
#include "job.h"
#include "i18n.h"
using boost::shared_ptr;
using boost::optional;
-VideoDecoder::VideoDecoder (shared_ptr<Film> f, DecodeOptions o)
- : Decoder (f, o)
+VideoDecoder::VideoDecoder (shared_ptr<const Film> f)
+ : Decoder (f)
, _video_frame (0)
- , _last_source_time (0)
+ , _last_content_time (0)
{
}
sub = _timed_subtitle->subtitle ();
}
+ TIMING (N_("Decoder emits %1"), _video_frame);
Video (image, same, sub, t);
++_video_frame;
-
- _last_source_time = t;
+
+ _last_content_time = t;
}
/** Set up the current subtitle. This will be put onto frames that
}
}
-/** Set which stream of subtitles we should use from our source.
- * @param s Stream to use.
- */
-void
-VideoDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
-{
- _subtitle_stream = s;
-}
-
void
VideoDecoder::set_progress (Job* j) const
{
assert (j);
-
- if (_film->length()) {
- j->set_progress (float (_video_frame) / _film->length().get());
+
+ if (_film->video_length()) {
+ j->set_progress (float (_video_frame) / _film->video_length());
}
}
*/
-#ifndef DVDOMATIC_VIDEO_DECODER_H
-#define DVDOMATIC_VIDEO_DECODER_H
+#ifndef DCPOMATIC_VIDEO_DECODER_H
+#define DCPOMATIC_VIDEO_DECODER_H
#include "video_source.h"
-#include "stream.h"
#include "decoder.h"
+class VideoContent;
+
class VideoDecoder : public TimedVideoSource, public virtual Decoder
{
public:
- VideoDecoder (boost::shared_ptr<Film>, DecodeOptions);
+ VideoDecoder (boost::shared_ptr<const Film>);
- /** @return video frames per second, or 0 if unknown */
- virtual float frames_per_second () const = 0;
+ /** @return video frame rate second, or 0 if unknown */
+ virtual float video_frame_rate () const = 0;
/** @return native size in pixels */
virtual libdcp::Size native_size () const = 0;
- /** @return length (in source video frames), according to our content's header */
- virtual SourceFrame length () const = 0;
+ /** @return length according to our content's header */
+ virtual ContentVideoFrame video_length () const = 0;
virtual int time_base_numerator () const = 0;
virtual int time_base_denominator () const = 0;
virtual int sample_aspect_ratio_numerator () const = 0;
virtual int sample_aspect_ratio_denominator () const = 0;
- virtual void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
-
void set_progress (Job *) const;
int video_frame () const {
return _video_frame;
}
- boost::shared_ptr<SubtitleStream> subtitle_stream () const {
- return _subtitle_stream;
- }
-
- std::vector<boost::shared_ptr<SubtitleStream> > subtitle_streams () const {
- return _subtitle_streams;
- }
-
- double last_source_time () const {
- return _last_source_time;
+ double last_content_time () const {
+ return _last_content_time;
}
protected:
void emit_video (boost::shared_ptr<Image>, bool, double);
void emit_subtitle (boost::shared_ptr<TimedSubtitle>);
- /** Subtitle stream to use when decoding */
- boost::shared_ptr<SubtitleStream> _subtitle_stream;
- /** Subtitle streams that this decoder's content has */
- std::vector<boost::shared_ptr<SubtitleStream> > _subtitle_streams;
-
private:
int _video_frame;
- double _last_source_time;
+ double _last_content_time;
boost::shared_ptr<TimedSubtitle> _timed_subtitle;
};
*/
-#ifndef DVDOMATIC_VIDEO_SINK_H
-#define DVDOMATIC_VIDEO_SINK_H
+#ifndef DCPOMATIC_VIDEO_SINK_H
+#define DCPOMATIC_VIDEO_SINK_H
#include <boost/shared_ptr.hpp>
#include "util.h"
#include "video_sink.h"
using boost::shared_ptr;
+using boost::weak_ptr;
using boost::bind;
-void
-VideoSource::connect_video (shared_ptr<VideoSink> s)
+static void
+process_video_proxy (weak_ptr<VideoSink> sink, shared_ptr<const Image> i, bool same, shared_ptr<Subtitle> s)
{
- Video.connect (bind (&VideoSink::process_video, s, _1, _2, _3));
+ shared_ptr<VideoSink> p = sink.lock ();
+ if (p) {
+ p->process_video (i, same, s);
+ }
}
void
-TimedVideoSource::connect_video (shared_ptr<TimedVideoSink> s)
+VideoSource::connect_video (shared_ptr<VideoSink> s)
{
- Video.connect (bind (&TimedVideoSink::process_video, s, _1, _2, _3, _4));
+ /* If we bind, say, a Player (as the VideoSink) to a Decoder (which is owned
+ by the Player) we create a cycle. Use a weak_ptr to break it.
+ */
+ Video.connect (bind (process_video_proxy, weak_ptr<VideoSink> (s), _1, _2, _3));
}
void
TimedVideoSource::connect_video (shared_ptr<VideoSink> s)
{
- Video.connect (bind (&VideoSink::process_video, s, _1, _2, _3));
+ Video.connect (bind (process_video_proxy, weak_ptr<VideoSink> (s), _1, _2, _3));
}
-
+static void
+timed_process_video_proxy (weak_ptr<TimedVideoSink> sink, shared_ptr<const Image> i, bool same, shared_ptr<Subtitle> s, double t)
+{
+ shared_ptr<TimedVideoSink> p = sink.lock ();
+ if (p) {
+ p->process_video (i, same, s, t);
+ }
+}
+
+void
+TimedVideoSource::connect_video (shared_ptr<TimedVideoSink> s)
+{
+ Video.connect (bind (timed_process_video_proxy, weak_ptr<TimedVideoSink> (s), _1, _2, _3, _4));
+}
* @brief Parent class for classes which emit video data.
*/
-#ifndef DVDOMATIC_VIDEO_SOURCE_H
-#define DVDOMATIC_VIDEO_SOURCE_H
+#ifndef DCPOMATIC_VIDEO_SOURCE_H
+#define DCPOMATIC_VIDEO_SOURCE_H
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <libdcp/sound_asset.h>
#include <libdcp/picture_frame.h>
#include <libdcp/reel.h>
+#include <libdcp/dcp.h>
#include <libdcp/cpl.h>
#include "writer.h"
#include "compose.hpp"
#include "format.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 "i18n.h"
_picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
- AudioMapping m (_film->audio_channels ());
-
- if (m.dcp_channels() > 0) {
+ if (_film->audio_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())
+ _film->audio_mapping().dcp_channels (),
+ dcp_audio_sample_rate (_film->audio_frame_rate())
)
);
ab_transcoder.cc
analyse_audio_job.cc
audio_analysis.cc
+ audio_content.cc
audio_decoder.cc
+ audio_mapping.cc
audio_source.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
exceptions.cc
filter_graph.cc
ffmpeg_compatibility.cc
+ ffmpeg_content.cc
ffmpeg_decoder.cc
film.cc
filter.cc
format.cc
gain.cc
image.cc
+ imagemagick_content.cc
imagemagick_decoder.cc
job.cc
job_manager.cc
log.cc
lut.cc
matcher.cc
+ player.cc
+ playlist.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
+ types.cc
trimmer.cc
ui_signaller.cc
util.cc
+ video_content.cc
video_decoder.cc
video_source.cc
writer.cc
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
"""
if bld.env.TARGET_WINDOWS:
obj.uselib += ' WINSOCK2'
+ if bld.env.STATIC:
+ obj.uselib += ' XML++'
obj.source = sources + " version.cc"
- obj.target = 'dvdomatic'
+ obj.target = 'dcpomatic'
- 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')
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <fstream>
+#include <boost/filesystem.hpp>
+#ifdef __WXMSW__
+#include <shellapi.h>
+#endif
+#include <wx/aboutdlg.h>
+#include <wx/stdpaths.h>
+#include <wx/cmdline.h>
+#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 "lib/film.h"
+#include "lib/format.h"
+#include "lib/config.h"
+#include "lib/filter.h"
+#include "lib/util.h"
+#include "lib/scaler.h"
+#include "lib/exceptions.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> film;
+static std::string log_level;
+static std::string film_to_load;
+static std::string film_to_create;
+static wxMenu* jobs_menu = 0;
+static wxLocale* locale = 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<wxMenuItem*, Sensitivity> 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<wxMenuItem*, Sensitivity>::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_file_quit,
+ ID_edit_preferences,
+ ID_jobs_make_dcp,
+ ID_jobs_send_dcp_to_tms,
+ ID_jobs_show_dcp,
+ ID_jobs_analyse_audio,
+ ID_help_about
+};
+
+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);
+ file->AppendSeparator ();
+ add_item (file, _("&Quit"), ID_file_quit, ALWAYS);
+
+ wxMenu* edit = new wxMenu;
+ add_item (edit, _("&Preferences..."), ID_edit_preferences, ALWAYS);
+
+ 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);
+ jobs_menu->AppendSeparator ();
+ add_item (jobs_menu, _("&Analyse audio"), ID_jobs_analyse_audio, NEEDS_FILM);
+
+ wxMenu* help = new wxMenu;
+ add_item (help, _("About"), ID_help_about, ALWAYS);
+
+ m->Append (file, _("&File"));
+ m->Append (edit, _("&Edit"));
+ 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 (ID_file_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_quit));
+ Connect (ID_edit_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 (ID_jobs_analyse_audio, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_analyse_audio));
+ Connect (ID_help_about, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about));
+
+ Connect (wxID_ANY, wxEVT_MENU_OPEN, wxMenuEventHandler (Frame::menu_opened));
+
+ wxPanel* panel = new wxPanel (this);
+ wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ s->Add (panel, 1, wxEXPAND);
+ SetSizer (s);
+
+ film_editor = new FilmEditor (film, panel);
+ film_viewer = new FilmViewer (film, panel);
+ JobManagerView* job_manager_view = new JobManagerView (panel);
+
+ _top_sizer = new wxBoxSizer (wxHORIZONTAL);
+ _top_sizer->Add (film_editor, 0, wxALL, 6);
+ _top_sizer->Add (film_viewer, 1, wxEXPAND | wxALL, 6);
+
+ wxBoxSizer* main_sizer = new wxBoxSizer (wxVERTICAL);
+ main_sizer->Add (_top_sizer, 2, wxEXPAND | wxALL, 6);
+ main_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
+ panel->SetSizer (main_sizer);
+
+ set_menu_sensitivity ();
+
+ film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
+ if (film) {
+ file_changed (film->directory ());
+ } else {
+ file_changed ("");
+ }
+
+ set_film ();
+
+ film_editor->Connect (wxID_ANY, wxEVT_SIZE, wxSizeEventHandler (Frame::film_editor_sized), 0, this);
+ }
+
+private:
+
+ void film_editor_sized (wxSizeEvent &)
+ {
+ static bool in_layout = false;
+ if (!in_layout) {
+ in_layout = true;
+ _top_sizer->Layout ();
+ in_layout = false;
+ }
+ }
+
+ 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 (), false));
+ 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_quit (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 jobs_analyse_audio (wxCommandEvent &)
+ {
+ film->analyse_audio ();
+ }
+
+ void help_about (wxCommandEvent &)
+ {
+ wxAboutDialogInfo info;
+ info.SetName (_("DCP-o-matic"));
+ if (strcmp (dcpomatic_git_commit, "release") == 0) {
+ info.SetVersion (std_to_wx (String::compose ("version %1", dcpomatic_version)));
+ } else {
+ info.SetVersion (std_to_wx (String::compose ("version %1 git %2", dcpomatic_version, dcpomatic_git_commit)));
+ }
+ info.SetDescription (_("Free, open-source DCP generation from almost anything."));
+ info.SetCopyright (_("(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"));
+
+ wxArrayString authors;
+ authors.Add (wxT ("Carl Hetherington"));
+ authors.Add (wxT ("Terrence Meiczinger"));
+ authors.Add (wxT ("Paul Davis"));
+ authors.Add (wxT ("Ole Laursen"));
+ info.SetDevelopers (authors);
+
+ wxArrayString translators;
+ translators.Add (wxT ("Olivier Perriere"));
+ translators.Add (wxT ("Lilian Lefranc"));
+ translators.Add (wxT ("Thierry Journet"));
+ translators.Add (wxT ("Massimiliano Broggi"));
+ translators.Add (wxT ("Manuel AC"));
+ translators.Add (wxT ("Adam Klotblixt"));
+ info.SetTranslators (translators);
+
+ info.SetWebSite (wxT ("http://carlh.net/software/dcpomatic"));
+ wxAboutBox (info);
+ }
+
+ wxSizer* _top_sizer;
+};
+
+#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
+
+void
+setup_i18n ()
+{
+ int language = wxLANGUAGE_DEFAULT;
+
+ ofstream f ("c:/users/carl hetherington/foo", std::ios::app);
+ f << "Hello.\n";
+
+ boost::optional<string> config_lang = Config::instance()->language ();
+ if (config_lang && !config_lang->empty ()) {
+ f << "Configured language " << config_lang.get() << "\n";
+ wxLanguageInfo const * li = wxLocale::FindLanguageInfo (std_to_wx (config_lang.get ()));
+ f << "LanguageInfo " << li << "\n";
+ if (li) {
+ language = li->Language;
+ f << "language=" << language << " cf " << wxLANGUAGE_DEFAULT << " " << wxLANGUAGE_ENGLISH << "\n";
+ }
+ }
+
+ if (wxLocale::IsAvailable (language)) {
+ f << "Language is available.\n";
+ locale = new wxLocale (language, wxLOCALE_LOAD_DEFAULT);
+
+#ifdef DCPOMATIC_WINDOWS
+ locale->AddCatalogLookupPathPrefix (std_to_wx (mo_path().string()));
+#endif
+
+ locale->AddCatalog (wxT ("libdcpomatic-wx"));
+ locale->AddCatalog (wxT ("dcpomatic"));
+
+ if (!locale->IsOk()) {
+ f << "Locale is not ok.\n";
+ delete locale;
+ locale = new wxLocale (wxLANGUAGE_ENGLISH);
+ language = wxLANGUAGE_ENGLISH;
+ }
+ }
+
+ if (locale) {
+ dcpomatic_setup_i18n (wx_to_std (locale->GetCanonicalName ()));
+ }
+}
+
+class App : public wxApp
+{
+ bool OnInit ()
+ {
+ if (!wxApp::OnInit()) {
+ return false;
+ }
+
+#ifdef DCPOMATIC_POSIX
+ unsetenv ("UBUNTU_MENUPROXY");
+#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.
+ */
+ 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->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, false));
+ 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)
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <iomanip>
+#include <getopt.h>
+#include <libdcp/version.h>
+#include "format.h"
+#include "film.h"
+#include "filter.h"
+#include "transcode_job.h"
+#include "job_manager.h"
+#include "ab_transcode_job.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] <FILM>\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"
+ << " -n, --no-progress do not print progress to stdout\n"
+ << " -r, --no-remote do not use any remote servers\n"
+ << "\n"
+ << "<FILM> 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'},
+ { "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);
+
+ 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 '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<ServerDescription*> ());
+ }
+
+ 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> film;
+ try {
+ film.reset (new Film (film_dir, true));
+ } 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 ";
+ if (film->ab()) {
+ cout << "A/B ";
+ }
+ cout << "DCP for " << film->name() << "\n";
+// cout << "Content: " << film->content() << "\n";
+ pair<string, string> 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<shared_ptr<Job> > 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<shared_ptr<Job> >::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;
+}
+
+
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/thread.hpp>
+#include <wx/taskbar.h>
+#include <wx/icon.h>
+#include "wx_util.h"
+#include "lib/util.h"
+#include "lib/server.h"
+#include "lib/config.h"
+
+using std::cout;
+using std::string;
+using boost::shared_ptr;
+using boost::thread;
+using boost::bind;
+
+enum {
+ ID_status = 1,
+ ID_quit,
+ ID_timer
+};
+
+class MemoryLog : public Log
+{
+public:
+
+ string get () const {
+ boost::mutex::scoped_lock (_mutex);
+ return _log;
+ }
+
+private:
+ void do_log (string m)
+ {
+ _log = m;
+ }
+
+ string _log;
+};
+
+static shared_ptr<MemoryLog> memory_log (new MemoryLog);
+
+class StatusDialog : public wxDialog
+{
+public:
+ StatusDialog ()
+ : wxDialog (0, wxID_ANY, _("DCP-o-matic encode server"), wxDefaultPosition, wxSize (600, 80), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+ , _timer (this, ID_timer)
+ {
+ _sizer = new wxFlexGridSizer (1, 6, 6);
+ _sizer->AddGrowableCol (0, 1);
+
+ _text = new wxTextCtrl (this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
+ _sizer->Add (_text, 1, wxEXPAND);
+
+ SetSizer (_sizer);
+ _sizer->Layout ();
+
+ Connect (ID_timer, wxEVT_TIMER, wxTimerEventHandler (StatusDialog::update));
+ _timer.Start (1000);
+ }
+
+private:
+ void update (wxTimerEvent &)
+ {
+ _text->ChangeValue (std_to_wx (memory_log->get ()));
+ _sizer->Layout ();
+ }
+
+ wxFlexGridSizer* _sizer;
+ wxTextCtrl* _text;
+ wxTimer _timer;
+};
+
+class TaskBarIcon : public wxTaskBarIcon
+{
+public:
+ TaskBarIcon ()
+ {
+#ifdef __WXMSW__
+ wxIcon icon (std_to_wx ("taskbar_icon"));
+#endif
+#ifdef __WXGTK__
+ wxInitAllImageHandlers();
+ wxBitmap bitmap (wxString::Format (wxT ("%s/taskbar_icon.png"), POSIX_ICON_PREFIX), wxBITMAP_TYPE_PNG);
+ wxIcon icon;
+ icon.CopyFromBitmap (bitmap);
+#endif
+ SetIcon (icon, std_to_wx ("DCP-o-matic encode server"));
+
+ Connect (ID_status, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (TaskBarIcon::status));
+ Connect (ID_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (TaskBarIcon::quit));
+ }
+
+ wxMenu* CreatePopupMenu ()
+ {
+ wxMenu* menu = new wxMenu;
+ menu->Append (ID_status, std_to_wx ("Status..."));
+ menu->Append (ID_quit, std_to_wx ("Quit"));
+ return menu;
+ }
+
+private:
+ void status (wxCommandEvent &)
+ {
+ StatusDialog* d = new StatusDialog;
+ d->Show ();
+ }
+
+ void quit (wxCommandEvent &)
+ {
+ wxTheApp->ExitMainLoop ();
+ }
+};
+
+class App : public wxApp
+{
+public:
+ App ()
+ : wxApp ()
+ , _thread (0)
+ , _icon (0)
+ {}
+
+private:
+
+ bool OnInit ()
+ {
+ if (!wxApp::OnInit ()) {
+ return false;
+ }
+
+ dcpomatic_setup ();
+
+ _icon = new TaskBarIcon;
+ _thread = new thread (bind (&App::main_thread, this));
+
+ return true;
+ }
+
+ int OnExit ()
+ {
+ delete _icon;
+ return wxApp::OnExit ();
+ }
+
+ void main_thread ()
+ {
+ Server server (memory_log);
+ server.run (Config::instance()->num_local_encoding_threads ());
+ }
+
+ boost::thread* _thread;
+ TaskBarIcon* _icon;
+};
+
+IMPLEMENT_APP (App)
--- /dev/null
+/*
+ Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "lib/server.h"
+#include <iostream>
+#include <stdexcept>
+#include <sstream>
+#include <cstring>
+#include <vector>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <boost/array.hpp>
+#include <boost/asio.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/condition.hpp>
+#include "config.h"
+#include "dcp_video_frame.h"
+#include "exceptions.h"
+#include "util.h"
+#include "config.h"
+#include "scaler.h"
+#include "image.h"
+#include "log.h"
+#include "version.h"
+
+using std::cerr;
+using std::string;
+using std::cout;
+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"
+ << " -t, --threads number of parallel encoding threads to use\n";
+}
+
+int
+main (int argc, char* argv[])
+{
+ int num_threads = Config::instance()->num_local_encoding_threads ();
+
+ int option_index = 0;
+ while (1) {
+ static struct option long_options[] = {
+ { "version", no_argument, 0, 'v'},
+ { "help", no_argument, 0, 'h'},
+ { "threads", required_argument, 0, 't'},
+ { 0, 0, 0, 0 }
+ };
+
+ int c = getopt_long (argc, argv, "vht:", 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 't':
+ num_threads = atoi (optarg);
+ break;
+ }
+ }
+
+ Scaler::setup_scalers ();
+ shared_ptr<FileLog> log (new FileLog ("servomatic.log"));
+ Server server (log);
+ server.run (num_threads);
+ return 0;
+}
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iostream>
-#include <fstream>
-#include <boost/filesystem.hpp>
-#ifdef __WXMSW__
-#include <shellapi.h>
-#endif
-#include <wx/aboutdlg.h>
-#include <wx/stdpaths.h>
-#include <wx/cmdline.h>
-#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 "lib/film.h"
-#include "lib/format.h"
-#include "lib/config.h"
-#include "lib/filter.h"
-#include "lib/util.h"
-#include "lib/scaler.h"
-#include "lib/exceptions.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> film;
-static std::string log_level;
-static std::string film_to_load;
-static wxMenu* jobs_menu = 0;
-static wxLocale* locale = 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<wxMenuItem*, Sensitivity> 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<wxMenuItem*, Sensitivity>::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_file_quit,
- ID_edit_preferences,
- ID_jobs_make_dcp,
- ID_jobs_send_dcp_to_tms,
- ID_jobs_show_dcp,
- ID_jobs_analyse_audio,
- ID_help_about
-};
-
-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);
- file->AppendSeparator ();
- add_item (file, _("&Quit"), ID_file_quit, ALWAYS);
-
- wxMenu* edit = new wxMenu;
- add_item (edit, _("&Preferences..."), ID_edit_preferences, ALWAYS);
-
- 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);
- jobs_menu->AppendSeparator ();
- add_item (jobs_menu, _("&Analyse audio"), ID_jobs_analyse_audio, NEEDS_FILM);
-
- wxMenu* help = new wxMenu;
- add_item (help, _("About"), ID_help_about, ALWAYS);
-
- m->Append (file, _("&File"));
- m->Append (edit, _("&Edit"));
- 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 (ID_file_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_quit));
- Connect (ID_edit_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 (ID_jobs_analyse_audio, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::jobs_analyse_audio));
- Connect (ID_help_about, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about));
-
- Connect (wxID_ANY, wxEVT_MENU_OPEN, wxMenuEventHandler (Frame::menu_opened));
-
- wxPanel* panel = new wxPanel (this);
- wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- s->Add (panel, 1, wxEXPAND);
- SetSizer (s);
-
- film_editor = new FilmEditor (film, panel);
- film_viewer = new FilmViewer (film, panel);
- JobManagerView* job_manager_view = new JobManagerView (panel);
-
- _top_sizer = new wxBoxSizer (wxHORIZONTAL);
- _top_sizer->Add (film_editor, 0, wxALL, 6);
- _top_sizer->Add (film_viewer, 1, wxEXPAND | wxALL, 6);
-
- wxBoxSizer* main_sizer = new wxBoxSizer (wxVERTICAL);
- main_sizer->Add (_top_sizer, 2, wxEXPAND | wxALL, 6);
- main_sizer->Add (job_manager_view, 1, wxEXPAND | wxALL, 6);
- panel->SetSizer (main_sizer);
-
- set_menu_sensitivity ();
-
- /* XXX: calling these here is a bit of a hack */
- film_editor->setup_visibility ();
-
- film_editor->FileChanged.connect (bind (&Frame::file_changed, this, _1));
- if (film) {
- file_changed (film->directory ());
- } else {
- file_changed ("");
- }
-
- set_film ();
-
- film_editor->Connect (wxID_ANY, wxEVT_SIZE, wxSizeEventHandler (Frame::film_editor_sized), 0, this);
- }
-
-private:
-
- void film_editor_sized (wxSizeEvent &)
- {
- static bool in_layout = false;
- if (!in_layout) {
- in_layout = true;
- _top_sizer->Layout ();
- in_layout = false;
- }
- }
-
- 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 (_("DVD-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 (), false));
- 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_quit (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 jobs_analyse_audio (wxCommandEvent &)
- {
- film->analyse_audio ();
- }
-
- void help_about (wxCommandEvent &)
- {
- wxAboutDialogInfo info;
- info.SetName (_("DVD-o-matic"));
- if (strcmp (dvdomatic_git_commit, "release") == 0) {
- info.SetVersion (std_to_wx (String::compose ("version %1", dvdomatic_version)));
- } else {
- info.SetVersion (std_to_wx (String::compose ("version %1 git %2", dvdomatic_version, dvdomatic_git_commit)));
- }
- info.SetDescription (_("Free, open-source DCP generation from almost anything."));
- info.SetCopyright (_("(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"));
-
- wxArrayString authors;
- authors.Add (wxT ("Carl Hetherington"));
- authors.Add (wxT ("Terrence Meiczinger"));
- authors.Add (wxT ("Paul Davis"));
- authors.Add (wxT ("Ole Laursen"));
- info.SetDevelopers (authors);
-
- wxArrayString translators;
- translators.Add (wxT ("Olivier Perriere"));
- translators.Add (wxT ("Lilian Lefranc"));
- translators.Add (wxT ("Thierry Journet"));
- translators.Add (wxT ("Massimiliano Broggi"));
- translators.Add (wxT ("Manuel AC"));
- translators.Add (wxT ("Adam Klotblixt"));
- info.SetTranslators (translators);
-
- info.SetWebSite (wxT ("http://carlh.net/software/dvdomatic"));
- wxAboutBox (info);
- }
-
- wxSizer* _top_sizer;
-};
-
-#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_PARAM, 0, 0, "film to load", 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_PARAM, 0, 0, wxT("film to load"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | wxCMD_LINE_PARAM_OPTIONAL },
- { wxCMD_LINE_NONE, wxT(""), wxT(""), wxT(""), wxCmdLineParamType (0), 0 }
-};
-#endif
-
-void
-setup_i18n ()
-{
- int language = wxLANGUAGE_DEFAULT;
-
- ofstream f ("c:/users/carl hetherington/foo", std::ios::app);
- f << "Hello.\n";
-
- boost::optional<string> config_lang = Config::instance()->language ();
- if (config_lang && !config_lang->empty ()) {
- f << "Configured language " << config_lang.get() << "\n";
- wxLanguageInfo const * li = wxLocale::FindLanguageInfo (std_to_wx (config_lang.get ()));
- f << "LanguageInfo " << li << "\n";
- if (li) {
- language = li->Language;
- f << "language=" << language << " cf " << wxLANGUAGE_DEFAULT << " " << wxLANGUAGE_ENGLISH << "\n";
- }
- }
-
- if (wxLocale::IsAvailable (language)) {
- f << "Language is available.\n";
- locale = new wxLocale (language, wxLOCALE_LOAD_DEFAULT);
-
-#ifdef DVDOMATIC_WINDOWS
- locale->AddCatalogLookupPathPrefix (std_to_wx (mo_path().string()));
-#endif
-
- locale->AddCatalog (wxT ("libdvdomatic-wx"));
- locale->AddCatalog (wxT ("dvdomatic"));
-
- if (!locale->IsOk()) {
- f << "Locale is not ok.\n";
- delete locale;
- locale = new wxLocale (wxLANGUAGE_ENGLISH);
- language = wxLANGUAGE_ENGLISH;
- }
- }
-
- if (locale) {
- dvdomatic_setup_i18n (wx_to_std (locale->GetCanonicalName ()));
- }
-}
-
-class App : public wxApp
-{
- bool OnInit ()
- {
- if (!wxApp::OnInit()) {
- return false;
- }
-
-#ifdef DVDOMATIC_POSIX
- unsetenv ("UBUNTU_MENUPROXY");
-#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 dvdomatic_setup
- hasn't yet been called and there aren't any scalers, filters etc.
- set up yet.
- */
- setup_i18n ();
-
- /* Set things up, including scalers / filters etc.
- which will now be internationalised correctly.
- */
- dvdomatic_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->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())));
- }
- }
-
- Frame* f = new Frame (_("DVD-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) {
- 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)
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <iostream>
-#include <iomanip>
-#include <getopt.h>
-#include <libdcp/version.h>
-#include "format.h"
-#include "film.h"
-#include "filter.h"
-#include "transcode_job.h"
-#include "job_manager.h"
-#include "ab_transcode_job.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] <FILM>\n"
- << " -v, --version show DVD-o-matic version\n"
- << " -h, --help show this help\n"
- << " -d, --deps list DVD-o-matic dependency details and quit\n"
- << " -n, --no-progress do not print progress to stdout\n"
- << " -r, --no-remote do not use any remote servers\n"
- << "\n"
- << "<FILM> 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'},
- { "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);
-
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'v':
- cout << "dvdomatic version " << dvdomatic_version << " " << dvdomatic_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 '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];
-
- dvdomatic_setup ();
-
- if (no_remote) {
- Config::instance()->set_servers (vector<ServerDescription*> ());
- }
-
- cout << "DVD-o-matic " << dvdomatic_version << " git " << dvdomatic_git_commit;
- char buf[256];
- if (gethostname (buf, 256) == 0) {
- cout << " on " << buf;
- }
- cout << "\n";
-
- shared_ptr<Film> film;
- try {
- film.reset (new Film (film_dir, true));
- } 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 ";
- if (film->dcp_ab()) {
- cout << "A/B ";
- }
- cout << "DCP for " << film->name() << "\n";
- cout << "Content: " << film->content() << "\n";
- pair<string, string> 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) {
-
- dvdomatic_sleep (5);
-
- list<shared_ptr<Job> > 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<shared_ptr<Job> >::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;
-}
-
-
#
msgid ""
msgstr ""
-"Project-Id-Version: DVDOMATIC\n"
+"Project-Id-Version: DCPOMATIC\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-03-23 21:08-0500\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.5\n"
-#: src/tools/dvdomatic.cc:179
+#: src/tools/dcpomatic.cc:177
msgid "&Analyse audio"
msgstr "&Analizar audio"
-#: src/tools/dvdomatic.cc:185
+#: src/tools/dcpomatic.cc:183
msgid "&Edit"
msgstr "&Editar"
-#: src/tools/dvdomatic.cc:184
+#: src/tools/dcpomatic.cc:182
msgid "&File"
msgstr "&Archivo"
-#: src/tools/dvdomatic.cc:187
+#: src/tools/dcpomatic.cc:185
msgid "&Help"
msgstr "&Ayuda"
-#: src/tools/dvdomatic.cc:186
+#: src/tools/dcpomatic.cc:184
msgid "&Jobs"
msgstr "&Tareas"
-#: src/tools/dvdomatic.cc:175
+#: src/tools/dcpomatic.cc:173
msgid "&Make DCP"
msgstr "&Crear DCP"
-#: src/tools/dvdomatic.cc:163
+#: src/tools/dcpomatic.cc:161
msgid "&Open..."
msgstr "&Abrir..."
-#: src/tools/dvdomatic.cc:172
+#: src/tools/dcpomatic.cc:170
msgid "&Preferences..."
msgstr "&Preferencias..."
-#: src/tools/dvdomatic.cc:167
+#: src/tools/dcpomatic.cc:165
msgid "&Properties..."
msgstr "&Propiedades..."
-#: src/tools/dvdomatic.cc:169
+#: src/tools/dcpomatic.cc:167
msgid "&Quit"
msgstr "&Salir"
-#: src/tools/dvdomatic.cc:165
+#: src/tools/dcpomatic.cc:163
msgid "&Save"
msgstr "&Guardar"
-#: src/tools/dvdomatic.cc:176
+#: src/tools/dcpomatic.cc:174
msgid "&Send DCP to TMS"
msgstr "&Enviar DCP al TMS"
-#: src/tools/dvdomatic.cc:419
+#: src/tools/dcpomatic.cc:417
msgid ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/tools/dvdomatic.cc:182
+#: src/tools/dcpomatic.cc:180
msgid "About"
msgstr "Acerca de"
-#: src/tools/dvdomatic.cc:538
+#: src/tools/dcpomatic.cc:527
#, fuzzy
msgid "Could not load film %1 (%2)"
msgstr "No se pudo cargar la pelÃcula %s (%s)"
-#: src/tools/dvdomatic.cc:341
+#: src/tools/dcpomatic.cc:339
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "No se pudo cargar la pelÃcula en %s (%s)"
-#: src/tools/dvdomatic.cc:289 src/tools/dvdomatic.cc:412
-#: src/tools/dvdomatic.cc:542
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+#: src/tools/dcpomatic.cc:287 src/tools/dcpomatic.cc:410
+#: src/tools/dcpomatic.cc:531
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
-#: src/tools/dvdomatic.cc:77
+#: src/tools/dcpomatic.cc:75
msgid "Film changed"
msgstr "PelÃcula cambiada"
-#: src/tools/dvdomatic.cc:418
+#: src/tools/dcpomatic.cc:416
msgid "Free, open-source DCP generation from almost anything."
msgstr ""
"Generación de DCP a partir de casi cualquier fuente, libre y de código "
"abierto."
-#: src/tools/dvdomatic.cc:162
+#: src/tools/dcpomatic.cc:160
msgid "New..."
msgstr "Nuevo..."
-#: src/tools/dvdomatic.cc:177
+#: src/tools/dcpomatic.cc:175
msgid "S&how DCP"
msgstr "&Mostrar DCP"
-#: src/tools/dvdomatic.cc:76
-#, c-format
+#: src/tools/dcpomatic.cc:74
msgid "Save changes to film \"%s\" before closing?"
msgstr ""
-#: src/tools/dvdomatic.cc:321
+#: src/tools/dcpomatic.cc:319
msgid "Select film to open"
msgstr "Selecciona la pelÃcula a abrir"
-#: src/tools/dvdomatic.cc:305
+#: src/tools/dcpomatic.cc:303
#, fuzzy
msgid "The directory %1 already exists."
msgstr "La carpeta %s ya existe."
-#: src/tools/dvdomatic.cc:326
+#: src/tools/dcpomatic.cc:324
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
#
msgid ""
msgstr ""
-"Project-Id-Version: DVD-o-matic FRENCH\n"
+"Project-Id-Version: DCP-o-matic FRENCH\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-03-13 22:33+0100\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: src/tools/dvdomatic.cc:179
+#: src/tools/dcpomatic.cc:177
msgid "&Analyse audio"
msgstr "&Analyser le son"
-#: src/tools/dvdomatic.cc:185
+#: src/tools/dcpomatic.cc:183
msgid "&Edit"
msgstr "&Edition"
-#: src/tools/dvdomatic.cc:184
+#: src/tools/dcpomatic.cc:182
msgid "&File"
msgstr "&Fichier"
-#: src/tools/dvdomatic.cc:187
+#: src/tools/dcpomatic.cc:185
msgid "&Help"
msgstr "&Aide"
-#: src/tools/dvdomatic.cc:186
+#: src/tools/dcpomatic.cc:184
msgid "&Jobs"
msgstr "&Travaux"
-#: src/tools/dvdomatic.cc:175
+#: src/tools/dcpomatic.cc:173
msgid "&Make DCP"
msgstr "&Créer le DCP"
-#: src/tools/dvdomatic.cc:163
+#: src/tools/dcpomatic.cc:161
msgid "&Open..."
msgstr "&Ouvrir..."
-#: src/tools/dvdomatic.cc:172
+#: src/tools/dcpomatic.cc:170
msgid "&Preferences..."
msgstr "&Préférences..."
-#: src/tools/dvdomatic.cc:167
+#: src/tools/dcpomatic.cc:165
msgid "&Properties..."
msgstr "&Propriétés..."
-#: src/tools/dvdomatic.cc:169
+#: src/tools/dcpomatic.cc:167
msgid "&Quit"
msgstr "&Quitter"
-#: src/tools/dvdomatic.cc:165
+#: src/tools/dcpomatic.cc:163
msgid "&Save"
msgstr "&Enregistrer"
-#: src/tools/dvdomatic.cc:176
+#: src/tools/dcpomatic.cc:174
msgid "&Send DCP to TMS"
msgstr "&Envoyer le DCP dans le TMS"
-#: src/tools/dvdomatic.cc:419
+#: src/tools/dcpomatic.cc:417
msgid ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/tools/dvdomatic.cc:182
+#: src/tools/dcpomatic.cc:180
msgid "About"
msgstr "A Propos"
-#: src/tools/dvdomatic.cc:538
+#: src/tools/dcpomatic.cc:527
#, fuzzy
msgid "Could not load film %1 (%2)"
msgstr "Impossible de charger le film %s (%s)"
-#: src/tools/dvdomatic.cc:341
+#: src/tools/dcpomatic.cc:339
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Impossible d'ouvrir le film à %s (%s)"
-#: src/tools/dvdomatic.cc:289 src/tools/dvdomatic.cc:412
-#: src/tools/dvdomatic.cc:542
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+#: src/tools/dcpomatic.cc:287 src/tools/dcpomatic.cc:410
+#: src/tools/dcpomatic.cc:531
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
-#: src/tools/dvdomatic.cc:77
+#: src/tools/dcpomatic.cc:75
msgid "Film changed"
msgstr "Film changé"
-#: src/tools/dvdomatic.cc:418
+#: src/tools/dcpomatic.cc:416
msgid "Free, open-source DCP generation from almost anything."
msgstr "Création de DCP libre et open-source à partir de presque tout."
-#: src/tools/dvdomatic.cc:162
+#: src/tools/dcpomatic.cc:160
msgid "New..."
msgstr "Nouveau..."
-#: src/tools/dvdomatic.cc:177
+#: src/tools/dcpomatic.cc:175
msgid "S&how DCP"
msgstr "Voir le DCP"
-#: src/tools/dvdomatic.cc:76
-#, c-format
+#: src/tools/dcpomatic.cc:74
msgid "Save changes to film \"%s\" before closing?"
msgstr ""
-#: src/tools/dvdomatic.cc:321
+#: src/tools/dcpomatic.cc:319
msgid "Select film to open"
msgstr "Sélectionner le film à ouvrir"
-#: src/tools/dvdomatic.cc:305
+#: src/tools/dcpomatic.cc:303
#, fuzzy
msgid "The directory %1 already exists."
msgstr "Le dossier %s existe déjà ."
-#: src/tools/dvdomatic.cc:326
+#: src/tools/dcpomatic.cc:324
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.5\n"
-#: src/tools/dvdomatic.cc:179
+#: src/tools/dcpomatic.cc:177
msgid "&Analyse audio"
msgstr "&Analizza audio"
-#: src/tools/dvdomatic.cc:185
+#: src/tools/dcpomatic.cc:183
msgid "&Edit"
msgstr "&Modifica"
-#: src/tools/dvdomatic.cc:184
+#: src/tools/dcpomatic.cc:182
msgid "&File"
msgstr "&File"
-#: src/tools/dvdomatic.cc:187
+#: src/tools/dcpomatic.cc:185
msgid "&Help"
msgstr "&Aiuto"
-#: src/tools/dvdomatic.cc:186
+#: src/tools/dcpomatic.cc:184
msgid "&Jobs"
msgstr "&Lavori"
-#: src/tools/dvdomatic.cc:175
+#: src/tools/dcpomatic.cc:173
msgid "&Make DCP"
msgstr "&Crea DCP"
-#: src/tools/dvdomatic.cc:163
+#: src/tools/dcpomatic.cc:161
msgid "&Open..."
msgstr "&Apri..."
-#: src/tools/dvdomatic.cc:172
+#: src/tools/dcpomatic.cc:170
msgid "&Preferences..."
msgstr "&Preferenze..."
-#: src/tools/dvdomatic.cc:167
+#: src/tools/dcpomatic.cc:165
msgid "&Properties..."
msgstr "&Proprieta'..."
-#: src/tools/dvdomatic.cc:169
+#: src/tools/dcpomatic.cc:167
msgid "&Quit"
msgstr "&Esci"
-#: src/tools/dvdomatic.cc:165
+#: src/tools/dcpomatic.cc:163
msgid "&Save"
msgstr "&Salva"
-#: src/tools/dvdomatic.cc:176
+#: src/tools/dcpomatic.cc:174
msgid "&Send DCP to TMS"
msgstr "&Invia DCP a TMS"
-#: src/tools/dvdomatic.cc:419
+#: src/tools/dcpomatic.cc:417
msgid ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/tools/dvdomatic.cc:182
+#: src/tools/dcpomatic.cc:180
msgid "About"
msgstr "Informazioni"
-#: src/tools/dvdomatic.cc:538
+#: src/tools/dcpomatic.cc:527
msgid "Could not load film %1 (%2)"
msgstr "Non posso caricare il film %s (%s)"
-#: src/tools/dvdomatic.cc:341
+#: src/tools/dcpomatic.cc:339
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Non posso aprire il film in %s (%s)"
-#: src/tools/dvdomatic.cc:289 src/tools/dvdomatic.cc:412
-#: src/tools/dvdomatic.cc:542
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+#: src/tools/dcpomatic.cc:287 src/tools/dcpomatic.cc:410
+#: src/tools/dcpomatic.cc:531
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
-#: src/tools/dvdomatic.cc:77
+#: src/tools/dcpomatic.cc:75
msgid "Film changed"
msgstr "Film modificato"
-#: src/tools/dvdomatic.cc:418
+#: src/tools/dcpomatic.cc:416
msgid "Free, open-source DCP generation from almost anything."
msgstr "Genera DCP da quasi tutto, free e open-source."
-#: src/tools/dvdomatic.cc:162
+#: src/tools/dcpomatic.cc:160
msgid "New..."
msgstr "Nuovo"
-#: src/tools/dvdomatic.cc:177
+#: src/tools/dcpomatic.cc:175
msgid "S&how DCP"
msgstr "&Mostra DCP"
-#: src/tools/dvdomatic.cc:76
-#, c-format
+#: src/tools/dcpomatic.cc:74
msgid "Save changes to film \"%s\" before closing?"
msgstr "Salvare i cambiamenti del film \"%s\" prima di chiudere?"
-#: src/tools/dvdomatic.cc:321
+#: src/tools/dcpomatic.cc:319
msgid "Select film to open"
msgstr "Seleziona il film da aprire"
-#: src/tools/dvdomatic.cc:305
+#: src/tools/dcpomatic.cc:303
msgid "The directory %1 already exists."
msgstr "La directory %s esiste gia'."
-#: src/tools/dvdomatic.cc:326
+#: src/tools/dcpomatic.cc:324
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
#
msgid ""
msgstr ""
-"Project-Id-Version: DVD-o-matic\n"
+"Project-Id-Version: DCP-o-matic\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-04-09 10:12+0100\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.5\n"
-#: src/tools/dvdomatic.cc:179
+#: src/tools/dcpomatic.cc:177
msgid "&Analyse audio"
msgstr "&Analysera audio"
-#: src/tools/dvdomatic.cc:185
+#: src/tools/dcpomatic.cc:183
msgid "&Edit"
msgstr "&Redigera"
-#: src/tools/dvdomatic.cc:184
+#: src/tools/dcpomatic.cc:182
msgid "&File"
msgstr "&Fil"
-#: src/tools/dvdomatic.cc:187
+#: src/tools/dcpomatic.cc:185
msgid "&Help"
msgstr "&Hjälp"
-#: src/tools/dvdomatic.cc:186
+#: src/tools/dcpomatic.cc:184
msgid "&Jobs"
msgstr "&Jobb"
-#: src/tools/dvdomatic.cc:175
+#: src/tools/dcpomatic.cc:173
msgid "&Make DCP"
msgstr "&Skapa DCP"
-#: src/tools/dvdomatic.cc:163
+#: src/tools/dcpomatic.cc:161
msgid "&Open..."
msgstr "&Öppna"
-#: src/tools/dvdomatic.cc:172
+#: src/tools/dcpomatic.cc:170
msgid "&Preferences..."
msgstr "&Inställningar"
-#: src/tools/dvdomatic.cc:167
+#: src/tools/dcpomatic.cc:165
msgid "&Properties..."
msgstr "&Egenskaper"
-#: src/tools/dvdomatic.cc:169
+#: src/tools/dcpomatic.cc:167
msgid "&Quit"
msgstr "&Avsluta"
-#: src/tools/dvdomatic.cc:165
+#: src/tools/dcpomatic.cc:163
msgid "&Save"
msgstr "&Spara"
-#: src/tools/dvdomatic.cc:176
+#: src/tools/dcpomatic.cc:174
msgid "&Send DCP to TMS"
msgstr "&Skicka DCP till TMS"
-#: src/tools/dvdomatic.cc:419
+#: src/tools/dcpomatic.cc:417
msgid ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
msgstr ""
"(C) 2012-2013 Carl Hetherington, Terrence Meiczinger, Paul Davis, Ole Laursen"
-#: src/tools/dvdomatic.cc:182
+#: src/tools/dcpomatic.cc:180
msgid "About"
msgstr "Om"
-#: src/tools/dvdomatic.cc:538
+#: src/tools/dcpomatic.cc:527
msgid "Could not load film %1 (%2)"
msgstr "Kunde inte öppna filmen %1 (%2)"
-#: src/tools/dvdomatic.cc:341
+#: src/tools/dcpomatic.cc:339
#, c-format
msgid "Could not open film at %s (%s)"
msgstr "Kunde inte öppna filmen vid %s (%s)"
-#: src/tools/dvdomatic.cc:289 src/tools/dvdomatic.cc:412
-#: src/tools/dvdomatic.cc:542
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+#: src/tools/dcpomatic.cc:287 src/tools/dcpomatic.cc:410
+#: src/tools/dcpomatic.cc:531
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
-#: src/tools/dvdomatic.cc:77
+#: src/tools/dcpomatic.cc:75
msgid "Film changed"
msgstr "Film ändrad"
-#: src/tools/dvdomatic.cc:418
+#: src/tools/dcpomatic.cc:416
msgid "Free, open-source DCP generation from almost anything."
msgstr ""
"Fri, öppen-källkodsprogramvara för DCP-generering från nästan vad som helst."
-#: src/tools/dvdomatic.cc:162
+#: src/tools/dcpomatic.cc:160
msgid "New..."
msgstr "Ny..."
-#: src/tools/dvdomatic.cc:177
+#: src/tools/dcpomatic.cc:175
msgid "S&how DCP"
msgstr "&Visa DCP"
-#: src/tools/dvdomatic.cc:76
-#, fuzzy, c-format
+#: src/tools/dcpomatic.cc:74
msgid "Save changes to film \"%s\" before closing?"
-msgstr "Spara ändringarna till filmen \"%1\" före avslut?"
+msgstr "Spara ändringarna till filmen \"%s\" före avslut?"
-#: src/tools/dvdomatic.cc:321
+#: src/tools/dcpomatic.cc:319
msgid "Select film to open"
msgstr "Välj film att öppna"
-#: src/tools/dvdomatic.cc:305
+#: src/tools/dcpomatic.cc:303
msgid "The directory %1 already exists."
msgstr "Katalogen %1 finns redan."
-#: src/tools/dvdomatic.cc:326
+#: src/tools/dcpomatic.cc:324
msgid ""
"You did not select a folder. Make sure that you select a folder before "
"clicking Open."
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "lib/server.h"
-#include <iostream>
-#include <stdexcept>
-#include <sstream>
-#include <cstring>
-#include <vector>
-#include <unistd.h>
-#include <errno.h>
-#include <getopt.h>
-#include <boost/array.hpp>
-#include <boost/asio.hpp>
-#include <boost/algorithm/string.hpp>
-#include <boost/thread.hpp>
-#include <boost/thread/mutex.hpp>
-#include <boost/thread/condition.hpp>
-#include "config.h"
-#include "dcp_video_frame.h"
-#include "exceptions.h"
-#include "util.h"
-#include "config.h"
-#include "scaler.h"
-#include "image.h"
-#include "log.h"
-#include "version.h"
-
-using std::cerr;
-using std::string;
-using std::cout;
-using boost::shared_ptr;
-
-static void
-help (string n)
-{
- cerr << "Syntax: " << n << " [OPTION]\n"
- << " -v, --version show DVD-o-matic version\n"
- << " -h, --help show this help\n"
- << " -t, --threads number of parallel encoding threads to use\n";
-}
-
-int
-main (int argc, char* argv[])
-{
- int num_threads = Config::instance()->num_local_encoding_threads ();
-
- int option_index = 0;
- while (1) {
- static struct option long_options[] = {
- { "version", no_argument, 0, 'v'},
- { "help", no_argument, 0, 'h'},
- { "threads", required_argument, 0, 't'},
- { 0, 0, 0, 0 }
- };
-
- int c = getopt_long (argc, argv, "vht:", long_options, &option_index);
-
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'v':
- cout << "dvdomatic version " << dvdomatic_version << " " << dvdomatic_git_commit << "\n";
- exit (EXIT_SUCCESS);
- case 'h':
- help (argv[0]);
- exit (EXIT_SUCCESS);
- case 't':
- num_threads = atoi (optarg);
- break;
- }
- }
-
- Scaler::setup_scalers ();
- shared_ptr<FileLog> log (new FileLog ("servomatic.log"));
- Server server (log);
- server.run (num_threads);
- return 0;
-}
+++ /dev/null
-/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <boost/thread.hpp>
-#include <wx/taskbar.h>
-#include <wx/icon.h>
-#include "wx_util.h"
-#include "lib/util.h"
-#include "lib/server.h"
-#include "lib/config.h"
-
-using std::cout;
-using std::string;
-using boost::shared_ptr;
-using boost::thread;
-using boost::bind;
-
-enum {
- ID_status = 1,
- ID_quit,
- ID_timer
-};
-
-class MemoryLog : public Log
-{
-public:
-
- string get () const {
- boost::mutex::scoped_lock (_mutex);
- return _log;
- }
-
-private:
- void do_log (string m)
- {
- _log = m;
- }
-
- string _log;
-};
-
-static shared_ptr<MemoryLog> memory_log (new MemoryLog);
-
-class StatusDialog : public wxDialog
-{
-public:
- StatusDialog ()
- : wxDialog (0, wxID_ANY, _("DVD-o-matic encode server"), wxDefaultPosition, wxSize (600, 80), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
- , _timer (this, ID_timer)
- {
- _sizer = new wxFlexGridSizer (1, 6, 6);
- _sizer->AddGrowableCol (0, 1);
-
- _text = new wxTextCtrl (this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize, wxTE_READONLY);
- _sizer->Add (_text, 1, wxEXPAND);
-
- SetSizer (_sizer);
- _sizer->Layout ();
-
- Connect (ID_timer, wxEVT_TIMER, wxTimerEventHandler (StatusDialog::update));
- _timer.Start (1000);
- }
-
-private:
- void update (wxTimerEvent &)
- {
- _text->ChangeValue (std_to_wx (memory_log->get ()));
- _sizer->Layout ();
- }
-
- wxFlexGridSizer* _sizer;
- wxTextCtrl* _text;
- wxTimer _timer;
-};
-
-class TaskBarIcon : public wxTaskBarIcon
-{
-public:
- TaskBarIcon ()
- {
-#ifdef __WXMSW__
- wxIcon icon (std_to_wx ("taskbar_icon"));
-#endif
-#ifdef __WXGTK__
- wxInitAllImageHandlers();
- wxBitmap bitmap (wxString::Format (wxT ("%s/taskbar_icon.png"), POSIX_ICON_PREFIX), wxBITMAP_TYPE_PNG);
- wxIcon icon;
- icon.CopyFromBitmap (bitmap);
-#endif
- SetIcon (icon, std_to_wx ("DVD-o-matic encode server"));
-
- Connect (ID_status, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (TaskBarIcon::status));
- Connect (ID_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (TaskBarIcon::quit));
- }
-
- wxMenu* CreatePopupMenu ()
- {
- wxMenu* menu = new wxMenu;
- menu->Append (ID_status, std_to_wx ("Status..."));
- menu->Append (ID_quit, std_to_wx ("Quit"));
- return menu;
- }
-
-private:
- void status (wxCommandEvent &)
- {
- StatusDialog* d = new StatusDialog;
- d->Show ();
- }
-
- void quit (wxCommandEvent &)
- {
- wxTheApp->ExitMainLoop ();
- }
-};
-
-class App : public wxApp
-{
-public:
- App ()
- : wxApp ()
- , _thread (0)
- , _icon (0)
- {}
-
-private:
-
- bool OnInit ()
- {
- if (!wxApp::OnInit ()) {
- return false;
- }
-
- dvdomatic_setup ();
-
- _icon = new TaskBarIcon;
- _thread = new thread (bind (&App::main_thread, this));
-
- return true;
- }
-
- int OnExit ()
- {
- delete _icon;
- return wxApp::OnExit ();
- }
-
- void main_thread ()
- {
- Server server (memory_log);
- server.run (Config::instance()->num_local_encoding_threads ());
- }
-
- boost::thread* _thread;
- TaskBarIcon* _icon;
-};
-
-IMPLEMENT_APP (App)
#include "scaler.h"
#include "server.h"
#include "dcp_video_frame.h"
-#include "options.h"
#include "decoder.h"
#include "exceptions.h"
#include "scaler.h"
#include "log.h"
-#include "decoder_factory.h"
#include "video_decoder.h"
+#include "player.h"
using std::cout;
using std::cerr;
exit (EXIT_FAILURE);
}
- dvdomatic_setup ();
+ dcpomatic_setup ();
server = new ServerDescription (server_host, 1);
shared_ptr<Film> film (new Film (film_dir, true));
- DecodeOptions opt;
- opt.decode_audio = false;
- opt.decode_subtitles = true;
- opt.video_sync = true;
+ shared_ptr<Player> player = film->player ();
+ player->disable_audio ();
- Decoders decoders = decoder_factory (film, opt);
try {
- decoders.video->Video.connect (boost::bind (process_video, _1, _2, _3));
+ player->Video.connect (boost::bind (process_video, _1, _2, _3));
bool done = false;
while (!done) {
- done = decoders.video->pass ();
+ done = player->pass ();
}
} catch (std::exception& e) {
cerr << "Error: " << e.what() << "\n";
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 AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC'
+ obj.uselib = 'BOOST_THREAD OPENJPEG DCP CXML AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC'
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', 'servomatic_gui']:
+ for t in ['dcpomatic', 'dcpomatic_server']:
obj = bld(features = 'cxx cxxprogram')
- obj.uselib = 'DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC'
+ obj.uselib = 'DCP CXML OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC'
obj.includes = ['..']
- obj.use = ['libdvdomatic', 'libdvdomatic-wx']
+ obj.use = ['libdcpomatic', 'libdcpomatic-wx']
obj.source = '%s.cc' % t
if bld.env.TARGET_WINDOWS:
- obj.source += ' ../../windows/dvdomatic.rc'
+ obj.source += ' ../../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')
*/
#include <boost/filesystem.hpp>
+#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;
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)));
}
void
-AudioDialog::set_film (boost::shared_ptr<Film> f)
+AudioDialog::set_film (shared_ptr<Film> f)
{
_film_changed_connection.disconnect ();
_film_audio_analysis_succeeded_connection.disconnect ();
_film = f;
+ for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
+ _channel_checkbox[i]->Show (!_film->audio_mapping().dcp_to_content (static_cast<libdcp::Channel> (i)).empty());
+ }
+
try_to_load_analysis ();
- setup_channels ();
_plot->set_gain (_film->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));
- 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(_film->name()).data()));
}
-void
-AudioDialog::setup_channels ()
-{
- if (!_film->audio_stream()) {
- return;
- }
-
- AudioMapping m (_film->audio_stream()->channels ());
-
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- if (m.dcp_to_source(static_cast<libdcp::Channel>(i))) {
- _channel_checkbox[i]->Show ();
- } else {
- _channel_checkbox[i]->Hide ();
- }
- }
-}
void
AudioDialog::try_to_load_analysis ()
_plot->set_analysis (a);
- AudioMapping m (_film->audio_stream()->channels ());
- optional<libdcp::Channel> 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);
assert (c < MAX_AUDIO_CHANNELS);
- AudioMapping m (_film->audio_stream()->channels ());
- optional<int> s = m.dcp_to_source (static_cast<libdcp::Channel> (c));
- if (s) {
- _plot->set_channel_visible (s.get(), _channel_checkbox[c]->GetValue ());
- }
+ _plot->set_channel_visible (c, _channel_checkbox[c]->GetValue ());
}
void
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;
}
void type_clicked (wxCommandEvent &);
void smoothing_changed (wxScrollEvent &);
void try_to_load_analysis ();
- void setup_channels ();
boost::shared_ptr<Film> _film;
AudioPlot* _plot;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/wx.h>
+#include <wx/renderer.h>
+#include <wx/grid.h>
+#include <libdcp/types.h>
+#include "lib/audio_mapping.h"
+#include "audio_mapping_view.h"
+#include "wx_util.h"
+
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+
+/* This could go away with wxWidgets 2.9, which has an API call
+ to find these values.
+*/
+
+#ifdef __WXMSW__
+#define CHECKBOX_WIDTH 16
+#define CHECKBOX_HEIGHT 16
+#endif
+
+#ifdef __WXGTK__
+#define CHECKBOX_WIDTH 20
+#define CHECKBOX_HEIGHT 20
+#endif
+
+
+class NoSelectionStringRenderer : public wxGridCellStringRenderer
+{
+public:
+ void Draw (wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool)
+ {
+ wxGridCellStringRenderer::Draw (grid, attr, dc, rect, row, col, false);
+ }
+};
+
+class CheckBoxRenderer : public wxGridCellRenderer
+{
+public:
+
+ void Draw (wxGrid& grid, wxGridCellAttr &, wxDC& dc, const wxRect& rect, int row, int col, bool)
+ {
+ wxRendererNative::Get().DrawCheckBox (
+ &grid,
+ dc, rect,
+ grid.GetCellValue (row, col) == wxT("1") ? static_cast<int>(wxCONTROL_CHECKED) : 0
+ );
+ }
+
+ wxSize GetBestSize (wxGrid &, wxGridCellAttr &, wxDC &, int, int)
+ {
+ return wxSize (CHECKBOX_WIDTH + 4, CHECKBOX_HEIGHT + 4);
+ }
+
+ wxGridCellRenderer* Clone () const
+ {
+ return new CheckBoxRenderer;
+ }
+};
+
+
+AudioMappingView::AudioMappingView (wxWindow* parent)
+ : wxPanel (parent, wxID_ANY)
+{
+ _grid = new wxGrid (this, wxID_ANY);
+
+ _grid->CreateGrid (0, 7);
+#if wxMINOR_VERSION == 9
+ _grid->HideRowLabels ();
+#else
+ _grid->SetRowLabelSize (0);
+#endif
+ _grid->DisableDragRowSize ();
+ _grid->DisableDragColSize ();
+ _grid->EnableEditing (false);
+ _grid->SetCellHighlightPenWidth (0);
+ _grid->SetDefaultRenderer (new NoSelectionStringRenderer);
+
+ _grid->SetColLabelValue (0, _("Content channel"));
+ _grid->SetColLabelValue (1, _("L"));
+ _grid->SetColLabelValue (2, _("R"));
+ _grid->SetColLabelValue (3, _("C"));
+ _grid->SetColLabelValue (4, _("Lfe"));
+ _grid->SetColLabelValue (5, _("Ls"));
+ _grid->SetColLabelValue (6, _("Rs"));
+
+ _grid->AutoSize ();
+
+ wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
+ s->Add (_grid, 1, wxEXPAND);
+ SetSizerAndFit (s);
+
+ Connect (wxID_ANY, wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler (AudioMappingView::left_click), 0, this);
+}
+
+void
+AudioMappingView::left_click (wxGridEvent& ev)
+{
+ if (ev.GetCol() == 0) {
+ return;
+ }
+
+ if (_grid->GetCellValue (ev.GetRow(), ev.GetCol()) == wxT("1")) {
+ _grid->SetCellValue (ev.GetRow(), ev.GetCol(), wxT("0"));
+ } else {
+ _grid->SetCellValue (ev.GetRow(), ev.GetCol(), wxT("1"));
+ }
+}
+
+void
+AudioMappingView::set_mapping (AudioMapping map)
+{
+ if (_grid->GetNumberRows ()) {
+ _grid->DeleteRows (0, _grid->GetNumberRows ());
+ }
+
+ list<AudioMapping::Channel> content_channels = map.content_channels ();
+ _grid->InsertRows (0, content_channels.size ());
+
+ for (size_t r = 0; r < content_channels.size(); ++r) {
+ for (int c = 1; c < 7; ++c) {
+ _grid->SetCellRenderer (r, c, new CheckBoxRenderer);
+ }
+ }
+
+ int n = 0;
+ for (list<AudioMapping::Channel>::iterator i = content_channels.begin(); i != content_channels.end(); ++i) {
+ shared_ptr<const AudioContent> ac = i->content.lock ();
+ assert (ac);
+ _grid->SetCellValue (n, 0, wxString::Format (wxT("%s %d"), std_to_wx (ac->file().filename().string()).data(), i->index + 1));
+
+ list<libdcp::Channel> const d = map.content_to_dcp (*i);
+ for (list<libdcp::Channel>::const_iterator j = d.begin(); j != d.end(); ++j) {
+ _grid->SetCellValue (n, static_cast<int> (*j) + 1, wxT("1"));
+ }
+ ++n;
+ }
+
+ _grid->AutoSize ();
+}
+
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/signals2.hpp>
+#include <wx/wx.h>
+#include <wx/grid.h>
+
+class AudioMappingView : public wxPanel
+{
+public:
+ AudioMappingView (wxWindow *);
+
+ void set_mapping (AudioMapping);
+
+private:
+ void left_click (wxGridEvent &);
+
+ wxGrid* _grid;
+};
#include <boost/bind.hpp>
#include <wx/graphics.h>
#include "audio_plot.h"
-#include "lib/decoder_factory.h"
#include "lib/audio_decoder.h"
#include "lib/audio_analysis.h"
#include "wx/wx_util.h"
table->Add (_num_local_encoding_threads, 1, wxEXPAND);
table->AddSpacer (0);
+ add_label_to_sizer (table, _misc_panel, _("Default duration of still images"));
+ _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, _("Default directory for new films"));
#ifdef __WXMSW__
_default_directory = new DirPickerCtrl (_misc_panel);
_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);
_tms_password->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ConfigDialog::tms_password_changed), 0, this);
}
-void
-ConfigDialog::make_metadata_panel ()
-{
- _metadata_panel = new wxPanel (_notebook);
- wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
- _metadata_panel->SetSizer (s);
-
- wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
- table->AddGrowableCol (1, 1);
- s->Add (table, 1, wxALL | wxEXPAND, 8);
-
- add_label_to_sizer (table, _metadata_panel, _("Issuer"));
- _issuer = new wxTextCtrl (_metadata_panel, wxID_ANY);
- table->Add (_issuer, 1, wxEXPAND);
-
- add_label_to_sizer (table, _metadata_panel, _("Creator"));
- _creator = new wxTextCtrl (_metadata_panel, wxID_ANY);
- table->Add (_creator, 1, wxEXPAND);
-
- Config* config = Config::instance ();
-
- _issuer->SetValue (std_to_wx (config->dcp_metadata().issuer));
- _issuer->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ConfigDialog::issuer_changed), 0, this);
- _creator->SetValue (std_to_wx (config->dcp_metadata().creator));
- _creator->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ConfigDialog::creator_changed), 0, this);
-}
-
void
ConfigDialog::make_ab_panel ()
{
add_label_to_sizer (table, _ab_panel, _("Reference filters"));
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);
+ s->Add (_reference_filters, 1, wxEXPAND);
_reference_filters_button = new wxButton (_ab_panel, wxID_ANY, _("Edit..."));
s->Add (_reference_filters_button, 0);
table->Add (s, 1, wxEXPAND);
table->AddSpacer (0);
}
+}
+
+void
+ConfigDialog::make_metadata_panel ()
+{
+ _metadata_panel = new wxPanel (_notebook);
+ wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
+ _metadata_panel->SetSizer (s);
+
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
+ table->AddGrowableCol (1, 1);
+ s->Add (table, 1, wxALL | wxEXPAND, 8);
+
+ add_label_to_sizer (table, _metadata_panel, _("Issuer"));
+ _issuer = new wxTextCtrl (_metadata_panel, wxID_ANY);
+ table->Add (_issuer, 1, wxEXPAND);
+
+ add_label_to_sizer (table, _metadata_panel, _("Creator"));
+ _creator = new wxTextCtrl (_metadata_panel, wxID_ANY);
+ table->Add (_creator, 1, wxEXPAND);
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<string, string> 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);
+ _issuer->SetValue (std_to_wx (config->dcp_metadata().issuer));
+ _issuer->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ConfigDialog::issuer_changed), 0, this);
+ _creator->SetValue (std_to_wx (config->dcp_metadata().creator));
+ _creator->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (ConfigDialog::creator_changed), 0, this);
}
void
_language->Enable (_set_language->GetValue ());
}
+void
+ConfigDialog::default_still_length_changed (wxCommandEvent &)
+{
+ Config::instance()->set_default_still_length (_default_still_length->GetValue ());
+}
+
void
ConfigDialog::default_format_changed (wxCommandEvent &)
{
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 &);
wxTextCtrl* _tms_user;
wxTextCtrl* _tms_password;
wxSpinCtrl* _num_local_encoding_threads;
+ wxSpinCtrl* _default_still_length;
#ifdef __WXMSW__
DirPickerCtrl* _default_directory;
#else
#include <iomanip>
#include <wx/wx.h>
#include <wx/notebook.h>
+#include <wx/listctrl.h>
#include <boost/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include "lib/filter.h"
#include "lib/config.h"
#include "lib/ffmpeg_decoder.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"
#include "dci_metadata_dialog.h"
#include "scaler.h"
#include "audio_dialog.h"
+#include "imagemagick_content_dialog.h"
+#include "audio_mapping_view.h"
using std::string;
using std::cout;
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<Film> f, wxWindow* parent)
: wxPanel (parent)
- , _film (f)
, _generally_sensitive (true)
, _audio_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_content_panel ();
+ _notebook->AddPage (_content_panel, _("Content"), false);
make_video_panel ();
_notebook->AddPage (_video_panel, _("Video"), false);
make_audio_panel ();
make_subtitle_panel ();
_notebook->AddPage (_subtitle_panel, _("Subtitles"), false);
- 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
grid->Add (_edit_dci_button, wxGBPosition (r, 1), wxDefaultSpan);
++r;
- add_label_to_grid_bag_sizer (grid, _film_panel, _("Content"), 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));
+ _trust_content_headers = new wxCheckBox (_film_panel, wxID_ANY, _("Trust content's header"));
+ grid->Add (_trust_content_headers, wxGBPosition (r, 0), wxGBSpan(1, 2));
++r;
add_label_to_grid_bag_sizer (grid, _film_panel, _("Content Type"), wxGBPosition (r, 0));
grid->Add (_dcp_content_type, wxGBPosition (r, 1));
++r;
- video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Original Frame Rate"), 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, _film_panel, _("DCP Frame Rate"), wxGBPosition (r, 0));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
++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);
+ grid->Add (_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"), wxGBPosition (r, 0)));
+ add_label_to_grid_bag_sizer (grid, _film_panel, _("Length"), wxGBPosition (r, 0));
_length = new wxStaticText (_film_panel, wxID_ANY, wxT (""));
- grid->Add (video_control (_length), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ grid->Add (_length, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
++r;
{
- video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim frames"), wxGBPosition (r, 0)));
+ add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim frames"), wxGBPosition (r, 0));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- video_control (add_label_to_sizer (s, _film_panel, _("Start")));
+ add_label_to_sizer (s, _film_panel, _("Start"));
_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")));
+ s->Add (_trim_start);
+ add_label_to_sizer (s, _film_panel, _("End"));
_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_trim_end));
+ s->Add (_trim_end);
grid->Add (s, wxGBPosition (r, 1));
}
++r;
- video_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), wxGBPosition (r, 0)));
+ add_label_to_grid_bag_sizer (grid, _film_panel, _("Trim method"), wxGBPosition (r, 0));
_trim_type = new wxChoice (_film_panel, wxID_ANY);
- grid->Add (video_control (_trim_type), wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ grid->Add (_trim_type, 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;
-
- /* STILL-only stuff */
- {
- still_control (add_label_to_grid_bag_sizer (grid, _film_panel, _("Duration"), 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")));
- grid->Add (s, wxGBPosition (r, 1));
- }
+ _ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B"));
+ grid->Add (_ab, wxGBPosition (r, 0));
++r;
vector<DCPContentType const *> const ct = DCPContentType::all ();
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);
- _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);
+ _format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
+ _trust_content_headers->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_headers_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->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler (FilmEditor::content_activated), 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_edit->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_edit_clicked), 0, this);
+ _content_earlier->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_earlier_clicked), 0, this);
+ _content_later->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_later_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);
+ _ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::ab_toggled), 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);
+ _ffmpeg_subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::ffmpeg_subtitle_stream_changed), 0, this);
+ _ffmpeg_audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::ffmpeg_audio_stream_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);
}
void
/* VIDEO-only stuff */
{
- video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), wxGBPosition (r, 0)));
+ add_label_to_grid_bag_sizer (grid, _video_panel, _("Filters"), 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..."));
- video_control (_filters_button);
s->Add (_filters_button, 0);
grid->Add (s, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
}
++r;
- video_control (add_label_to_grid_bag_sizer (grid, _video_panel, _("Scaler"), wxGBPosition (r, 0)));
+ add_label_to_grid_bag_sizer (grid, _video_panel, _("Scaler"), wxGBPosition (r, 0));
_scaler = new wxChoice (_video_panel, wxID_ANY);
- grid->Add (video_control (_scaler), wxGBPosition (r, 1));
+ grid->Add (_scaler, wxGBPosition (r, 1));
++r;
vector<Scaler const *> const sc = Scaler::all ();
_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, 100);
_trim_end->SetRange (0, 100);
_j2k_bandwidth->SetRange (50, 250);
}
+void
+FilmEditor::make_content_panel ()
+{
+ _content_panel = new wxPanel (_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);
+ _content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
+ b->Add (_content_remove);
+ _content_edit = new wxButton (_content_panel, wxID_ANY, _("Edit..."));
+ b->Add (_content_edit);
+ _content_earlier = new wxButton (_content_panel, wxID_ANY, _("Earlier"));
+ b->Add (_content_earlier);
+ _content_later = new wxButton (_content_panel, wxID_ANY, _("Later"));
+ b->Add (_content_later);
+
+ s->Add (b, 0, wxALL, 4);
+
+ _content_sizer->Add (s, 1, 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"));
+ _content_sizer->Add (h, 0, wxALL, 6);
+
+ _content_information = new wxTextCtrl (_content_panel, wxID_ANY, wxT ("\n\n\n\n"), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE);
+ _content_sizer->Add (_content_information, 1, wxEXPAND | wxALL, 6);
+
+ _loop_count->SetRange (2, 1024);
+}
+
void
FilmEditor::make_audio_panel ()
{
grid->AddSpacer (0);
{
- video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Gain")));
+ add_label_to_sizer (grid, _audio_panel, _("Audio Gain"));
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")));
+ s->Add (_audio_gain, 1);
+ add_label_to_sizer (s, _audio_panel, _("dB"));
_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);
}
{
- video_control (add_label_to_sizer (grid, _audio_panel, _("Audio Delay")));
+ add_label_to_sizer (grid, _audio_panel, _("Audio Delay"));
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
- video_control (add_label_to_sizer (s, _audio_panel, _("ms")));
+ add_label_to_sizer (s, _audio_panel, _("ms"));
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, 1, wxEXPAND);
- }
-
- _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)));
- _external_audio[i] = new wxFilePickerCtrl (_audio_panel, wxID_ANY, wxT (""), _("Select Audio File"), wxT ("*.wav"));
- grid->Add (_external_audio[i], 1, wxEXPAND);
- }
+ {
+ add_label_to_sizer (grid, _audio_panel, _("Audio Stream"));
+ wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _ffmpeg_audio_stream = new wxChoice (_audio_panel, wxID_ANY);
+ s->Add (_ffmpeg_audio_stream, 1);
+ _audio = new wxStaticText (_audio_panel, wxID_ANY, wxT (""));
+ s->Add (_audio, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8);
+ grid->Add (s, 1, wxEXPAND);
+ }
+ _audio_mapping = new AudioMappingView (_audio_panel);
+ _audio_sizer->Add (_audio_mapping, 1, wxEXPAND | wxALL, 6);
+
_audio_gain->SetRange (-60, 60);
_audio_delay->SetRange (-1000, 1000);
}
_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);
- _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY);
- grid->Add (video_control (_subtitle_stream));
+ _ffmpeg_subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY);
+ grid->Add (_ffmpeg_subtitle_stream);
{
- video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset")));
+ add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Offset"));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
_subtitle_offset = new wxSpinCtrl (_subtitle_panel);
s->Add (_subtitle_offset);
- video_control (add_label_to_sizer (s, _subtitle_panel, _("pixels")));
+ add_label_to_sizer (s, _subtitle_panel, _("pixels"));
grid->Add (s);
}
{
- video_control (add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale")));
+ add_label_to_sizer (grid, _subtitle_panel, _("Subtitle Scale"));
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, _("%")));
+ s->Add (_subtitle_scale);
+ add_label_to_sizer (s, _subtitle_panel, _("%"));
grid->Add (s);
}
_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 &)
+FilmEditor::trust_content_headers_changed (wxCommandEvent &)
{
if (!_film) {
return;
}
- _film->set_trust_content_header (_trust_content_header->GetValue ());
+ _film->set_trust_content_headers (_trust_content_headers->GetValue ());
}
/** Called when the DCP A/B switch has been toggled */
void
-FilmEditor::dcp_ab_toggled (wxCommandEvent &)
+FilmEditor::ab_toggled (wxCommandEvent &)
{
if (!_film) {
return;
}
- _film->set_dcp_ab (_dcp_ab->GetValue ());
+ _film->set_ab (_ab->GetValue ());
}
/** Called when the name widget has been changed */
case Film::NONE:
break;
case Film::CONTENT:
- checked_set (_content, _film->content ());
- setup_visibility ();
+ setup_content ();
setup_formats ();
+ setup_format ();
setup_subtitle_control_sensitivity ();
setup_streams ();
setup_show_audio_sensitivity ();
- setup_frame_rate_description ();
+ setup_length ();
break;
- case Film::TRUST_CONTENT_HEADER:
- checked_set (_trust_content_header, _film->trust_content_header ());
+ case Film::LOOP:
+ checked_set (_loop_content, _film->loop() > 1);
+ checked_set (_loop_count, _film->loop());
+ setup_loop_sensitivity ();
break;
- case Film::SUBTITLE_STREAMS:
- setup_subtitle_control_sensitivity ();
- setup_streams ();
- break;
- case Film::CONTENT_AUDIO_STREAMS:
- setup_streams ();
- setup_show_audio_sensitivity ();
- setup_frame_rate_description ();
+ case Film::TRUST_CONTENT_HEADERS:
+ checked_set (_trust_content_headers, _film->trust_content_headers ());
break;
case Film::FORMAT:
- {
- int n = 0;
- vector<Format const *>::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 ();
+ setup_format ();
break;
- }
case Film::CROP:
checked_set (_left_crop, _film->crop().left);
checked_set (_right_crop, _film->crop().right);
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 ());
+ case Film::AB:
+ checked_set (_ab, _film->ab ());
break;
case Film::SCALER:
checked_set (_scaler, Scaler::as_index (_film->scaler ()));
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 ();
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 ();
- break;
- case 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 ();
- break;
- case Film::SUBTITLE_STREAM:
- if (_film->subtitle_stream()) {
- checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
- }
- break;
- case Film::EXTERNAL_AUDIO:
- {
- vector<string> 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 ();
- 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<string> (_film->dcp_frame_rate())) {
- if (_dcp_frame_rate->GetSelection() != int(i)) {
- _dcp_frame_rate->SetSelection (i);
- break;
- }
+ 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 ());
+ if (!done) {
+ checked_set (_dcp_frame_rate, -1);
+ }
+
+ if (_film->video_frame_rate()) {
+ _best_dcp_frame_rate->Enable (best_dcp_frame_rate (_film->video_frame_rate ()) != _film->dcp_frame_rate ());
} else {
_best_dcp_frame_rate->Disable ();
}
-
setup_frame_rate_description ();
+ break;
+ }
+ case Film::AUDIO_MAPPING:
+ _audio_mapping->set_mapping (_film->audio_mapping ());
+ break;
+ }
+}
+
+void
+FilmEditor::film_content_changed (weak_ptr<Content> content, int property)
+{
+ if (!_film) {
+ /* We call this method ourselves (as well as using it as a signal handler)
+ so _film can be 0.
+ */
+ return;
+ }
+
+ if (property == FFmpegContentProperty::SUBTITLE_STREAMS) {
+ setup_subtitle_control_sensitivity ();
+ setup_streams ();
+ } else if (property == FFmpegContentProperty::AUDIO_STREAMS) {
+ setup_streams ();
+ setup_show_audio_sensitivity ();
+ } else if (property == VideoContentProperty::VIDEO_LENGTH || property == AudioContentProperty::AUDIO_LENGTH) {
+ setup_length ();
+ boost::shared_ptr<Content> c = content.lock ();
+ if (c && c == selected_content()) {
+ setup_content_information ();
+ }
+ } else if (property == FFmpegContentProperty::AUDIO_STREAM) {
+ if (_film->ffmpeg_audio_stream()) {
+ checked_set (_ffmpeg_audio_stream, boost::lexical_cast<string> (_film->ffmpeg_audio_stream()->id));
+ }
+ setup_dcp_name ();
+ setup_audio_details ();
+ setup_show_audio_sensitivity ();
+ } else if (property == FFmpegContentProperty::SUBTITLE_STREAM) {
+ if (_film->ffmpeg_subtitle_stream()) {
+ checked_set (_ffmpeg_subtitle_stream, boost::lexical_cast<string> (_film->ffmpeg_subtitle_stream()->id));
+ }
}
}
+void
+FilmEditor::setup_format ()
+{
+ int n = 0;
+ vector<Format const *>::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 ();
+}
+
+void
+FilmEditor::setup_length ()
+{
+ stringstream s;
+ ContentVideoFrame const frames = _film->content_length ();
+
+ if (frames && _film->video_frame_rate()) {
+ s << frames << " " << wx_to_std (_("frames")) << "; " << seconds_to_hms (frames / _film->video_frame_rate());
+ } else if (frames) {
+ s << frames << " " << wx_to_std (_("frames"));
+ }
+
+ _length->SetLabel (std_to_wx (s.str ()));
+
+ if (frames) {
+ _trim_start->SetRange (0, frames);
+ _trim_end->SetRange (0, frames);
+ }
+}
+
void
FilmEditor::setup_frame_rate_description ()
{
wxString d;
int lines = 0;
- if (_film->source_frame_rate()) {
- d << std_to_wx (FrameRateConversion (_film->source_frame_rate(), _film->dcp_frame_rate()).description);
+ if (_film->video_frame_rate()) {
+ d << std_to_wx (FrameRateConversion (_film->video_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 ()) {
+ if (_film->audio_frame_rate() && _film->audio_frame_rate() != _film->target_audio_sample_rate ()) {
d << wxString::Format (
_("Audio will be resampled from %dHz to %dHz\n"),
- _film->audio_stream()->sample_rate(),
+ _film->audio_frame_rate(),
_film->target_audio_sample_rate()
);
++lines;
void
FilmEditor::set_film (shared_ptr<Film> 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) {
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::TRUST_CONTENT_HEADERS);
film_changed (Film::DCP_CONTENT_TYPE);
film_changed (Film::FORMAT);
film_changed (Film::CROP);
film_changed (Film::SCALER);
film_changed (Film::TRIM_START);
film_changed (Film::TRIM_END);
+ film_changed (Film::AB);
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::AUDIO_MAPPING);
+
+ film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::SUBTITLE_STREAMS);
+ film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::SUBTITLE_STREAM);
+ film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::AUDIO_STREAMS);
+ film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::AUDIO_STREAM);
+
+ setup_content_information ();
}
/** Updates the sensitivity of lots of widgets to a given value.
_edit_dci_button->Enable (s);
_format->Enable (s);
_content->Enable (s);
- _trust_content_header->Enable (s);
+ _trust_content_headers->Enable (s);
+ _content->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);
+ _ffmpeg_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);
+ _ab->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);
setup_subtitle_control_sensitivity ();
- setup_audio_control_sensitivity ();
setup_show_audio_sensitivity ();
+ setup_content_button_sensitivity ();
}
/** Called when the `Edit filters' button has been clicked */
_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;
-}
-
-void
-FilmEditor::setup_visibility ()
-{
- ContentType c = VIDEO;
-
- if (_film) {
- c = _film->content_type ();
- }
-
- for (list<wxControl*>::iterator i = _video_controls.begin(); i != _video_controls.end(); ++i) {
- (*i)->Show (c == VIDEO);
- }
-
- for (list<wxControl*>::iterator i = _still_controls.begin(); i != _still_controls.end(); ++i) {
- (*i)->Show (c == STILL);
- }
-
- setup_notebook_size ();
-}
-
void
FilmEditor::setup_notebook_size ()
{
Fit ();
}
-void
-FilmEditor::still_duration_changed (wxCommandEvent &)
-{
- if (!_film) {
- return;
- }
-
- _film->set_still_duration (_still_duration->GetValue ());
-}
-
void
FilmEditor::trim_start_changed (wxCommandEvent &)
{
void
FilmEditor::setup_formats ()
{
- ContentType c = VIDEO;
-
- if (_film) {
- c = _film->content_type ();
- }
-
- _formats.clear ();
-
- vector<Format const *> fmt = Format::all ();
- for (vector<Format const *>::iterator i = fmt.begin(); i != fmt.end(); ++i) {
- if (c == VIDEO || (c == STILL && dynamic_cast<VariableFormat const *> (*i))) {
- _formats.push_back (*i);
- }
- }
+ _formats = Format::all ();
_format->Clear ();
for (vector<Format const *>::iterator i = _formats.begin(); i != _formats.end(); ++i) {
{
bool h = false;
if (_generally_sensitive && _film) {
- h = !_film->subtitle_streams().empty();
+ h = !_film->ffmpeg_subtitle_streams().empty();
}
_with_subtitles->Enable (h);
j = _film->with_subtitles ();
}
- _subtitle_stream->Enable (j);
+ _ffmpeg_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);
- }
-}
-
void
FilmEditor::use_dci_name_toggled (wxCommandEvent &)
{
void
FilmEditor::setup_streams ()
{
- _audio_stream->Clear ();
- vector<shared_ptr<AudioStream> > a = _film->content_audio_streams ();
- for (vector<shared_ptr<AudioStream> >::iterator i = a.begin(); i != a.end(); ++i) {
- shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (*i);
- assert (ffa);
- _audio_stream->Append (std_to_wx (ffa->name()), new wxStringClientData (std_to_wx (ffa->to_string ())));
+ if (!_film) {
+ return;
+ }
+
+ _ffmpeg_audio_stream->Clear ();
+ vector<FFmpegAudioStream> a = _film->ffmpeg_audio_streams ();
+ for (vector<FFmpegAudioStream>::iterator i = a.begin(); i != a.end(); ++i) {
+ _ffmpeg_audio_stream->Append (std_to_wx (i->name), new wxStringClientData (std_to_wx (boost::lexical_cast<string> (i->id))));
}
- if (_film->use_content_audio() && _film->audio_stream()) {
- checked_set (_audio_stream, _film->audio_stream()->to_string());
+ if (_film->ffmpeg_audio_stream()) {
+ checked_set (_ffmpeg_audio_stream, boost::lexical_cast<string> (_film->ffmpeg_audio_stream()->id));
}
- _subtitle_stream->Clear ();
- vector<shared_ptr<SubtitleStream> > s = _film->subtitle_streams ();
- for (vector<shared_ptr<SubtitleStream> >::iterator i = s.begin(); i != s.end(); ++i) {
- _subtitle_stream->Append (std_to_wx ((*i)->name()), new wxStringClientData (std_to_wx ((*i)->to_string ())));
+ _ffmpeg_subtitle_stream->Clear ();
+ vector<FFmpegSubtitleStream> s = _film->ffmpeg_subtitle_streams ();
+ for (vector<FFmpegSubtitleStream>::iterator i = s.begin(); i != s.end(); ++i) {
+ _ffmpeg_subtitle_stream->Append (std_to_wx (i->name), new wxStringClientData (std_to_wx (boost::lexical_cast<string> (i->id))));
}
- if (_film->subtitle_stream()) {
- checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
+
+ if (_film->ffmpeg_subtitle_stream()) {
+ checked_set (_ffmpeg_subtitle_stream, boost::lexical_cast<string> (_film->ffmpeg_subtitle_stream()->id));
} else {
- _subtitle_stream->SetSelection (wxNOT_FOUND);
+ _ffmpeg_subtitle_stream->SetSelection (wxNOT_FOUND);
}
}
void
-FilmEditor::audio_stream_changed (wxCommandEvent &)
+FilmEditor::ffmpeg_audio_stream_changed (wxCommandEvent &)
{
if (!_film) {
return;
}
- _film->set_content_audio_stream (
- audio_stream_factory (
- string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())),
- Film::state_version
- )
- );
+ vector<FFmpegAudioStream> a = _film->ffmpeg_audio_streams ();
+ vector<FFmpegAudioStream>::iterator i = a.begin ();
+ string const s = string_client_data (_ffmpeg_audio_stream->GetClientObject (_ffmpeg_audio_stream->GetSelection ()));
+ while (i != a.end() && lexical_cast<string> (i->id) != s) {
+ ++i;
+ }
+
+ if (i != a.end ()) {
+ _film->set_ffmpeg_audio_stream (*i);
+ }
}
void
-FilmEditor::subtitle_stream_changed (wxCommandEvent &)
+FilmEditor::ffmpeg_subtitle_stream_changed (wxCommandEvent &)
{
if (!_film) {
return;
}
- _film->set_subtitle_stream (
- subtitle_stream_factory (
- string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())),
- Film::state_version
- )
- );
+ vector<FFmpegSubtitleStream> a = _film->ffmpeg_subtitle_streams ();
+ vector<FFmpegSubtitleStream>::iterator i = a.begin ();
+ string const s = string_client_data (_ffmpeg_subtitle_stream->GetClientObject (_ffmpeg_subtitle_stream->GetSelection ()));
+ while (i != a.end() && lexical_cast<string> (i->id) != s) {
+ ++i;
+ }
+
+ if (i != a.end ()) {
+ _film->set_ffmpeg_subtitle_stream (*i);
+ }
}
void
FilmEditor::setup_audio_details ()
{
- if (!_film->content_audio_stream()) {
+ if (!_film->ffmpeg_audio_stream()) {
_audio->SetLabel (wxT (""));
} else {
wxString s;
- if (_film->audio_stream()->channels() == 1) {
+ if (_film->audio_channels() == 1) {
s << _("1 channel");
} else {
- s << _film->audio_stream()->channels () << wxT (" ") << _("channels");
+ s << _film->audio_channels() << wxT (" ") << _("channels");
}
- s << wxT (", ") << _film->audio_stream()->sample_rate() << _("Hz");
+ s << wxT (", ") << _film->audio_frame_rate() << _("Hz");
_audio->SetLabel (s);
}
set_things_sensitive (!a);
}
-void
-FilmEditor::use_audio_changed (wxCommandEvent &)
-{
- _film->set_use_content_audio (_use_content_audio->GetValue());
-}
-
-void
-FilmEditor::external_audio_changed (wxCommandEvent &)
-{
- vector<string> a;
- for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
- a.push_back (wx_to_std (_external_audio[i]->GetPath()));
- }
-
- _film->set_external_audio (a);
-}
-
void
FilmEditor::setup_dcp_name ()
{
return;
}
- _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->source_frame_rate ()));
+ _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->video_frame_rate ()));
}
void
_show_audio->Enable (_film && _film->has_audio ());
}
+void
+FilmEditor::setup_content ()
+{
+ string selected_summary;
+ int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (s != -1) {
+ selected_summary = wx_to_std (_content->GetItemText (s));
+ }
+
+ _content->DeleteAllItems ();
+
+ ContentList content = _film->content ();
+ for (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);
+ }
+ }
+
+ if (selected_summary.empty () && !content.empty ()) {
+ /* Select the first item of content if non was selected before */
+ _content->SetItemState (0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+ }
+}
+
+void
+FilmEditor::content_add_clicked (wxCommandEvent &)
+{
+ 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]));
+
+ if (ImageMagickContent::valid_file (p)) {
+ _film->add_content (shared_ptr<ImageMagickContent> (new ImageMagickContent (p)));
+ } else if (SndfileContent::valid_file (p)) {
+ _film->add_content (shared_ptr<SndfileContent> (new SndfileContent (p)));
+ } else {
+ _film->add_content (shared_ptr<FFmpegContent> (new FFmpegContent (p)));
+ }
+ }
+}
+
+void
+FilmEditor::content_remove_clicked (wxCommandEvent &)
+{
+ shared_ptr<Content> c = selected_content ();
+ if (c) {
+ _film->remove_content (c);
+ }
+}
+
+void
+FilmEditor::content_activated (wxListEvent& ev)
+{
+ ContentList c = _film->content ();
+ assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < c.size ());
+
+ edit_content (c[ev.GetIndex()]);
+}
+
+void
+FilmEditor::content_edit_clicked (wxCommandEvent &)
+{
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
+ return;
+ }
+
+ edit_content (c);
+}
+
+void
+FilmEditor::edit_content (shared_ptr<Content> c)
+{
+ shared_ptr<ImageMagickContent> im = dynamic_pointer_cast<ImageMagickContent> (c);
+ if (im) {
+ ImageMagickContentDialog* d = new ImageMagickContentDialog (this, im);
+ d->ShowModal ();
+ d->Destroy ();
+
+ im->set_video_length (d->video_length() * 24);
+ }
+}
+
+void
+FilmEditor::content_earlier_clicked (wxCommandEvent &)
+{
+ shared_ptr<Content> c = selected_content ();
+ if (c) {
+ _film->move_content_earlier (c);
+ }
+}
+
+void
+FilmEditor::content_later_clicked (wxCommandEvent &)
+{
+ shared_ptr<Content> c = selected_content ();
+ if (c) {
+ _film->move_content_later (c);
+ }
+}
+
+void
+FilmEditor::content_selection_changed (wxListEvent &)
+{
+ setup_content_button_sensitivity ();
+ setup_content_information ();
+}
+
+void
+FilmEditor::setup_content_information ()
+{
+ shared_ptr<Content> c = selected_content ();
+ if (!c) {
+ _content_information->SetValue (wxT (""));
+ return;
+ }
+
+ _content_information->SetValue (std_to_wx (c->information ()));
+}
+
+void
+FilmEditor::setup_content_button_sensitivity ()
+{
+ _content_add->Enable (_generally_sensitive);
+
+ shared_ptr<Content> selection = selected_content ();
+
+ _content_edit->Enable (selection && _generally_sensitive && dynamic_pointer_cast<ImageMagickContent> (selection));
+ _content_remove->Enable (selection && _generally_sensitive);
+ _content_earlier->Enable (selection && _generally_sensitive);
+ _content_later->Enable (selection && _generally_sensitive);
+}
+
+shared_ptr<Content>
+FilmEditor::selected_content ()
+{
+ int const s = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ if (s == -1) {
+ return shared_ptr<Content> ();
+ }
+
+ ContentList c = _film->content ();
+ if (s < 0 || size_t (s) >= c.size ()) {
+ return shared_ptr<Content> ();
+ }
+
+ return c[s];
+}
+
void
FilmEditor::setup_scaling_description ()
{
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->size ());
+ libdcp::Size const cropped = _film->cropped_size (_film->video_size ());
d << wxString::Format (
_("Cropped to %dx%d (%.2f:1)\n"),
cropped.width, cropped.height,
{
_film->set_trim_type (_trim_type->GetSelection () == 0 ? Film::CPL : Film::ENCODE);
}
+
+void
+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_loop (_loop_count->GetValue ());
+}
+
+void
+FilmEditor::setup_loop_sensitivity ()
+{
+ _loop_count->Enable (_loop_content->GetValue ());
+}
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.
*/
#include "lib/film.h"
class wxNotebook;
+class wxListCtrl;
+class wxListEvent;
class Film;
class AudioDialog;
+class AudioMappingView;
/** @class FilmEditor
* @brief A wx widget to edit a film's metadata, and perform various functions.
FilmEditor (boost::shared_ptr<Film>, wxWindow *);
void set_film (boost::shared_ptr<Film>);
- void setup_visibility ();
boost::signals2::signal<void (std::string)> FileChanged;
private:
void make_film_panel ();
+ void make_content_panel ();
void make_video_panel ();
void make_audio_panel ();
void make_subtitle_panel ();
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 trust_content_headers_changed (wxCommandEvent &);
+ void content_selection_changed (wxListEvent &);
+ void content_activated (wxListEvent &);
+ void content_add_clicked (wxCommandEvent &);
+ void content_remove_clicked (wxCommandEvent &);
+ void content_edit_clicked (wxCommandEvent &);
+ void content_earlier_clicked (wxCommandEvent &);
+ void content_later_clicked (wxCommandEvent &);
+ void imagemagick_video_length_changed (wxCommandEvent &);
void format_changed (wxCommandEvent &);
void trim_start_changed (wxCommandEvent &);
void trim_end_changed (wxCommandEvent &);
void trim_type_changed (wxCommandEvent &);
void dcp_content_type_changed (wxCommandEvent &);
- void dcp_ab_toggled (wxCommandEvent &);
+ void ab_toggled (wxCommandEvent &);
void scaler_changed (wxCommandEvent &);
void audio_gain_changed (wxCommandEvent &);
void audio_gain_calculate_button_clicked (wxCommandEvent &);
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 ffmpeg_audio_stream_changed (wxCommandEvent &);
+ void ffmpeg_subtitle_stream_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 &);
/* Handle changes to the model */
void film_changed (Film::Property);
-
- /* Button clicks */
- void edit_filters_clicked (wxCommandEvent &);
+ void film_content_changed (boost::weak_ptr<Content>, int);
void set_things_sensitive (bool);
void setup_formats ();
void setup_subtitle_control_sensitivity ();
- void setup_audio_control_sensitivity ();
void setup_streams ();
void setup_audio_details ();
void setup_dcp_name ();
void setup_scaling_description ();
void setup_notebook_size ();
void setup_frame_rate_description ();
+ void setup_content ();
+ void setup_format ();
+ void setup_length ();
+ void setup_content_information ();
+ void setup_content_button_sensitivity ();
+ void setup_loop_sensitivity ();
- wxControl* video_control (wxControl *);
- wxControl* still_control (wxControl *);
-
void active_jobs_changed (bool);
+ boost::shared_ptr<Content> selected_content ();
+ void edit_content (boost::shared_ptr<Content>);
wxNotebook* _notebook;
wxPanel* _film_panel;
wxSizer* _film_sizer;
+ wxPanel* _content_panel;
+ wxSizer* _content_sizer;
wxPanel* _video_panel;
wxSizer* _video_sizer;
wxPanel* _audio_panel;
wxTextCtrl* _name;
wxStaticText* _dcp_name;
wxCheckBox* _use_dci_name;
+ wxListCtrl* _content;
+ wxButton* _content_add;
+ wxButton* _content_remove;
+ wxButton* _content_edit;
+ wxButton* _content_earlier;
+ wxButton* _content_later;
+ wxTextCtrl* _content_information;
+ wxCheckBox* _loop_content;
+ wxSpinCtrl* _loop_count;
wxButton* _edit_dci_button;
- /** The Film's format */
wxChoice* _format;
+ wxStaticText* _format_description;
+ wxCheckBox* _trust_content_headers;
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;
+ wxChoice* _ffmpeg_audio_stream;
+ AudioMappingView* _audio_mapping;
wxCheckBox* _with_subtitles;
- wxChoice* _subtitle_stream;
+ wxChoice* _ffmpeg_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;
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<wxControl*> _video_controls;
- std::list<wxControl*> _still_controls;
+ wxCheckBox* _ab;
std::vector<Format const *> _formats;
#include "lib/format.h"
#include "lib/util.h"
#include "lib/job_manager.h"
-#include "lib/options.h"
#include "lib/subtitle.h"
#include "lib/image.h"
#include "lib/scaler.h"
#include "lib/exceptions.h"
#include "lib/examine_content_job.h"
#include "lib/filter.h"
+#include "lib/player.h"
+#include "lib/video_content.h"
+#include "lib/ffmpeg_content.h"
+#include "lib/imagemagick_content.h"
#include "film_viewer.h"
#include "wx_util.h"
#include "video_decoder.h"
using std::cout;
using std::list;
using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
+using boost::weak_ptr;
using libdcp::Size;
FilmViewer::FilmViewer (shared_ptr<Film> f, wxWindow* p)
break;
case Film::CONTENT:
{
- DecodeOptions o;
- o.decode_audio = false;
- o.decode_subtitles = true;
- o.video_sync = false;
-
- try {
- _decoders = decoder_factory (_film, o);
- } catch (StringError& e) {
- error_dialog (this, wxString::Format (_("Could not open content file (%s)"), std_to_wx(e.what()).data()));
- return;
- }
-
- if (_decoders.video == 0) {
- break;
- }
- _decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2, _3, _4));
- _decoders.video->OutputChanged.connect (boost::bind (&FilmViewer::decoder_changed, this));
- _decoders.video->set_subtitle_stream (_film->subtitle_stream());
calculate_sizes ();
- get_frame ();
- _panel->Refresh ();
- _slider->Show (_film->content_type() == VIDEO);
- _play_button->Show (_film->content_type() == VIDEO);
- _v_sizer->Layout ();
+ wxScrollEvent ev;
+ slider_moved (ev);
break;
}
case Film::WITH_SUBTITLES:
case Film::SUBTITLE_OFFSET:
case Film::SUBTITLE_SCALE:
+ raw_to_display ();
+ _panel->Refresh ();
+ _panel->Update ();
+ break;
case Film::SCALER:
case Film::FILTERS:
- update_from_raw ();
- break;
- case Film::SUBTITLE_STREAM:
- if (_decoders.video) {
- _decoders.video->set_subtitle_stream (_film->subtitle_stream ());
- }
+ case Film::CROP:
+ update_from_decoder ();
break;
default:
break;
if (_film == f) {
return;
}
-
+
_film = f;
_raw_frame.reset ();
return;
}
+ _player = f->player ();
+ _player->disable_audio ();
+ /* Don't disable subtitles here as we may need them, and it's nice to be able to turn them
+ on and off without needing obtain a new Player.
+ */
+
+ _player->Video.connect (bind (&FilmViewer::process_video, this, _1, _2, _3, _4));
+
_film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
+ _film->ContentChanged.connect (boost::bind (&FilmViewer::film_content_changed, this, _1, _2));
film_changed (Film::CONTENT);
film_changed (Film::FORMAT);
film_changed (Film::WITH_SUBTITLES);
film_changed (Film::SUBTITLE_OFFSET);
film_changed (Film::SUBTITLE_SCALE);
- film_changed (Film::SUBTITLE_STREAM);
}
void
-FilmViewer::decoder_changed ()
+FilmViewer::update_from_decoder ()
{
- if (_decoders.video == 0 || _decoders.video->seek_to_last ()) {
+ if (!_player || _player->seek (_player->last_video_time ())) {
return;
}
void
FilmViewer::timer (wxTimerEvent &)
{
- if (!_film || !_decoders.video) {
+ if (!_player) {
return;
}
get_frame ();
- if (_film->length()) {
- int const new_slider_position = 4096 * _decoders.video->last_source_time() / (_film->length().get() / _film->source_frame_rate());
+ if (_film->video_length()) {
+ int const new_slider_position = 4096 * _player->last_video_time() / (_film->video_length() / _film->video_frame_rate());
if (new_slider_position != _slider->GetValue()) {
_slider->SetValue (new_slider_position);
}
void
FilmViewer::slider_moved (wxScrollEvent &)
{
- if (!_film || !_film->length() || !_decoders.video) {
+ if (!_film || !_player) {
return;
}
-
- if (_decoders.video->seek (_slider->GetValue() * _film->length().get() / (4096 * _film->source_frame_rate()))) {
+
+ if (_player->seek (_slider->GetValue() * _film->video_length() / (4096 * _film->video_frame_rate()))) {
return;
}
return;
}
- boost::shared_ptr<const Image> input = _raw_frame;
+ shared_ptr<const Image> input = _raw_frame;
pair<string, string> const s = Filter::ffmpeg_strings (_film->filters());
if (!s.second.empty ()) {
when working out the scale that we are applying.
*/
- Size const cropped_size = _film->cropped_size (_film->size ());
+ Size const cropped_size = _film->cropped_size (_film->video_size ());
Rect tx = subtitle_transformed_area (
float (_film_size.width) / cropped_size.width,
void
FilmViewer::calculate_sizes ()
{
- if (!_film) {
+ if (!_film || !_player) {
return;
}
}
if (_play_button->GetValue()) {
- _timer.Start (1000 / _film->source_frame_rate());
+ _timer.Start (1000 / _film->video_frame_rate());
} else {
_timer.Stop ();
}
_got_frame = true;
- double const fps = _decoders.video->frames_per_second ();
+ double const fps = _film->video_frame_rate ();
_frame->SetLabel (wxString::Format (wxT("%d"), int (rint (t * fps))));
double w = t;
_timecode->SetLabel (wxString::Format (wxT("%02d:%02d:%02d:%02d"), h, m, s, f));
}
+/** Get a new _raw_frame from the decoder and then do
+ * raw_to_display ().
+ */
void
FilmViewer::get_frame ()
{
/* Clear our raw frame in case we don't get a new one */
_raw_frame.reset ();
- if (_decoders.video == 0) {
+ if (!_player) {
_display_frame.reset ();
return;
}
-
+
try {
_got_frame = false;
while (!_got_frame) {
- if (_decoders.video->pass ()) {
+ if (_player->pass ()) {
/* We didn't get a frame before the decoder gave up,
so clear our display frame.
*/
_play_button->Enable (!a);
}
+void
+FilmViewer::film_content_changed (weak_ptr<Content>, int p)
+{
+ if (p == VideoContentProperty::VIDEO_LENGTH) {
+ /* Force an update to our frame */
+ wxScrollEvent ev;
+ slider_moved (ev);
+ }
+}
+
void
FilmViewer::back_clicked (wxCommandEvent &)
{
- if (!_decoders.video) {
+ if (!_player) {
return;
}
- _decoders.video->seek_back ();
+ _player->seek_back ();
get_frame ();
_panel->Refresh ();
_panel->Update ();
void
FilmViewer::forward_clicked (wxCommandEvent &)
{
- if (!_decoders.video) {
+ if (!_player) {
return;
}
- _decoders.video->seek_forward ();
+ _player->seek_forward ();
get_frame ();
_panel->Refresh ();
_panel->Update ();
#include <wx/wx.h>
#include "lib/film.h"
-#include "lib/decoder_factory.h"
class wxToggleButton;
class FFmpegPlayer;
/** @class FilmViewer
* @brief A wx widget to view a preview of a Film.
+ *
+ * The film takes the following path through the viewer:
+ *
+ * 1. get_frame() asks our _player to decode some data. If it does, process_video()
+ * will be called.
+ *
+ * 2. process_video() takes the image and subtitle from the decoder (_raw_frame and _raw_sub)
+ * and calls raw_to_display().
+ *
+ * 3. raw_to_display() copies _raw_frame to _display_frame, processing it and scaling it.
+ *
+ * 4. calling _panel->Refresh() and _panel->Update() results in paint_panel() being called;
+ * this creates frame_bitmap from _display_frame and blits it to the display. It also
+ * blits the subtitle, if required.
+ *
+ * update_from_decoder() asks the player to re-emit its current frame on the next pass(), and then
+ * starts from step #1.
+ *
+ * update_from_raw() starts at step #3, then calls _panel->Refresh and _panel->Update.
*/
class FilmViewer : public wxPanel
{
private:
void film_changed (Film::Property);
+ void film_content_changed (boost::weak_ptr<Content>, int);
void paint_panel (wxPaintEvent &);
void panel_sized (wxSizeEvent &);
void slider_moved (wxScrollEvent &);
void calculate_sizes ();
void check_play_state ();
void update_from_raw ();
- void decoder_changed ();
+ void update_from_decoder ();
void raw_to_display ();
void get_frame ();
void active_jobs_changed (bool);
void forward_clicked (wxCommandEvent &);
boost::shared_ptr<Film> _film;
+ boost::shared_ptr<Player> _player;
wxSizer* _v_sizer;
wxPanel* _panel;
wxToggleButton* _play_button;
wxTimer _timer;
- Decoders _decoders;
boost::shared_ptr<const Image> _raw_frame;
boost::shared_ptr<Subtitle> _raw_sub;
boost::shared_ptr<const Image> _display_frame;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/spinctrl.h>
+#include "lib/imagemagick_content.h"
+#include "imagemagick_content_dialog.h"
+#include "wx_util.h"
+
+using boost::shared_ptr;
+
+ImageMagickContentDialog::ImageMagickContentDialog (wxWindow* parent, shared_ptr<ImageMagickContent> content)
+ : wxDialog (parent, wxID_ANY, _("Image"))
+{
+ wxFlexGridSizer* grid = new wxFlexGridSizer (3, 6, 6);
+ grid->AddGrowableCol (1, 1);
+
+ {
+ add_label_to_sizer (grid, this, (_("Duration")));
+ 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"));
+ 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);
+}
+
+int
+ImageMagickContentDialog::video_length () const
+{
+ return _video_length->GetValue ();
+}
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/wx.h>
+
+class wxSpinCtrl;
+class ImageMagickContent;
+
+class ImageMagickContentDialog : public wxDialog
+{
+public:
+ ImageMagickContentDialog (wxWindow *, boost::shared_ptr<ImageMagickContent>);
+
+ int video_length () const;
+
+private:
+ wxSpinCtrl* _video_length;
+};
{
wxObject* o = ev.GetEventObject ();
- for (map<boost::shared_ptr<Job>, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) {
+ for (map<shared_ptr<Job>, 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]);
{
wxObject* o = ev.GetEventObject ();
- for (map<boost::shared_ptr<Job>, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) {
+ for (map<shared_ptr<Job>, JobRecord>::iterator i = _job_records.begin(); i != _job_records.end(); ++i) {
if (i->second.cancel == o) {
i->first->cancel ();
}
#
msgid ""
msgstr ""
-"Project-Id-Version: libdvdomatic-wx\n"
+"Project-Id-Version: libdcpomatic-wx\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-04-02 19:08-0500\n"
msgstr "%"
#: src/wx/config_dialog.cc:61
-msgid "(restart DVD-o-matic to see language changes)"
+msgid "(restart DCP-o-matic to see language changes)"
msgstr ""
#: src/wx/film_editor.cc:1276
msgstr "Nombre DCP"
#: src/wx/wx_util.cc:61
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
#: src/wx/config_dialog.cc:44
-msgid "DVD-o-matic Preferences"
-msgstr "Preferencias DVD-o-matic"
+msgid "DCP-o-matic Preferences"
+msgstr "Preferencias DCP-o-matic"
#: src/wx/audio_dialog.cc:101
#, fuzzy, c-format
-msgid "DVD-o-matic audio - %s"
-msgstr "Audio DVD-o-matic - %1"
+msgid "DCP-o-matic audio - %s"
+msgstr "Audio DCP-o-matic - %1"
#: src/wx/config_dialog.cc:102
msgid "Default DCI name details"
#
msgid ""
msgstr ""
-"Project-Id-Version: DVD-o-matic FRENCH\n"
+"Project-Id-Version: DCP-o-matic FRENCH\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-03-20 00:34+0100\n"
msgstr "%"
#: src/wx/config_dialog.cc:61
-msgid "(restart DVD-o-matic to see language changes)"
+msgid "(restart DCP-o-matic to see language changes)"
msgstr ""
#: src/wx/film_editor.cc:1276
msgstr "Nom du DCP"
#: src/wx/wx_util.cc:61
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
#: src/wx/config_dialog.cc:44
-msgid "DVD-o-matic Preferences"
-msgstr "Préférences DVD-o-matic"
+msgid "DCP-o-matic Preferences"
+msgstr "Préférences DCP-o-matic"
#: src/wx/audio_dialog.cc:101
#, c-format
-msgid "DVD-o-matic audio - %s"
-msgstr "Son DVD-o-matic - %s"
+msgid "DCP-o-matic audio - %s"
+msgstr "Son DCP-o-matic - %s"
#: src/wx/config_dialog.cc:102
msgid "Default DCI name details"
msgstr "%"
#: src/wx/config_dialog.cc:61
-msgid "(restart DVD-o-matic to see language changes)"
-msgstr "(riavviare DVD-o-matic per vedere i cambiamenti di lingua)"
+msgid "(restart DCP-o-matic to see language changes)"
+msgstr "(riavviare DCP-o-matic per vedere i cambiamenti di lingua)"
#: src/wx/film_editor.cc:1276
msgid "1 channel"
msgstr "Nome del DCP"
#: src/wx/wx_util.cc:61
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
#: src/wx/config_dialog.cc:44
-msgid "DVD-o-matic Preferences"
-msgstr "Preferenze DVD-o-matic"
+msgid "DCP-o-matic Preferences"
+msgstr "Preferenze DCP-o-matic"
#: src/wx/audio_dialog.cc:101
#, c-format
-msgid "DVD-o-matic audio - %s"
-msgstr "Audio DVD-o-matic - %s"
+msgid "DCP-o-matic audio - %s"
+msgstr "Audio DCP-o-matic - %s"
#: src/wx/config_dialog.cc:102
msgid "Default DCI name details"
#
msgid ""
msgstr ""
-"Project-Id-Version: DVD-o-matic\n"
+"Project-Id-Version: DCP-o-matic\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-22 15:06+0100\n"
"PO-Revision-Date: 2013-04-09 10:13+0100\n"
msgstr "%"
#: src/wx/config_dialog.cc:61
-msgid "(restart DVD-o-matic to see language changes)"
-msgstr "(starta om DVD-o-matic för att se språkändringar)"
+msgid "(restart DCP-o-matic to see language changes)"
+msgstr "(starta om DCP-o-matic för att se språkändringar)"
#: src/wx/film_editor.cc:1276
msgid "1 channel"
msgstr "DCP Namn"
#: src/wx/wx_util.cc:61
-msgid "DVD-o-matic"
-msgstr "DVD-o-matic"
+msgid "DCP-o-matic"
+msgstr "DCP-o-matic"
#: src/wx/config_dialog.cc:44
-msgid "DVD-o-matic Preferences"
-msgstr "DVD-o-matic Inställningar"
+msgid "DCP-o-matic Preferences"
+msgstr "DCP-o-matic Inställningar"
#: src/wx/audio_dialog.cc:101
#, c-format
-msgid "DVD-o-matic audio - %s"
-msgstr "DVD-o-matic audio - %s"
+msgid "DCP-o-matic audio - %s"
+msgstr "DCP-o-matic audio - %s"
#: src/wx/config_dialog.cc:102
msgid "Default DCI name details"
_encoded = new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this));
table->Add (_encoded, 1, wxALIGN_CENTER_VERTICAL);
+#if 0
if (_film->length()) {
_frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().get())));
FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate());
_frames->SetLabel (_("unknown"));
_disk->SetLabel (_("unknown"));
}
+#endif
wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
overall_sizer->Add (table, 0, wxALL, 6);
return "";
}
- if (_film->length()) {
- /* XXX: encoded_frames() should check which frames have been encoded */
- u << " (" << (_film->encoded_frames() * 100 / _film->length().get()) << "%)";
- }
+// if (_film->length()) {
+// /* XXX: encoded_frames() should check which frames have been encoded */
+// u << " (" << (_film->encoded_frames() * 100 / _film->length().get()) << "%)";
+// }
return u.str ();
}
sources = """
audio_dialog.cc
+ audio_mapping_view.cc
audio_plot.cc
config_dialog.cc
dci_metadata_dialog.cc
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
else:
obj = bld(features = 'cxx cxxshlib')
- obj.name = 'libdvdomatic-wx'
+ obj.name = 'libdcpomatic-wx'
obj.includes = [ '..' ]
obj.export_includes = ['.']
obj.uselib = 'WXWIDGETS'
- 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')
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 ();
}
#include "scaler.h"
#include "ffmpeg_decoder.h"
#include "sndfile_decoder.h"
+#include "dcp_content_type.h"
#include "trimmer.h"
#define BOOST_TEST_DYN_LINK
-#define BOOST_TEST_MODULE dvdomatic_test
+#define BOOST_TEST_MODULE dcpomatic_test
#include <boost/test/unit_test.hpp>
using std::string;
BOOST_AUTO_TEST_CASE (make_black_test)
{
/* This needs to happen in the first test */
- dvdomatic_setup ();
+ dcpomatic_setup ();
libdcp::Size in_size (512, 512);
libdcp::Size out_size (1024, 1024);
BOOST_CHECK (f->filters ().empty());
f->set_name ("fred");
- BOOST_CHECK_THROW (f->set_content ("jim"), OpenFileError);
+// BOOST_CHECK_THROW (f->set_content ("jim"), OpenFileError);
f->set_dcp_content_type (DCPContentType::from_pretty_name ("Short"));
f->set_format (Format::from_nickname ("Flat"));
f->set_left_crop (1);
f->set_filters (f_filters);
f->set_trim_start (42);
f->set_trim_end (99);
- f->set_dcp_ab (true);
+ f->set_ab (true);
f->write_metadata ();
stringstream s;
BOOST_CHECK_EQUAL (g_filters.back(), Filter::from_id ("unsharp"));
BOOST_CHECK_EQUAL (g->trim_start(), 42);
BOOST_CHECK_EQUAL (g->trim_end(), 99);
- BOOST_CHECK_EQUAL (g->dcp_ab(), true);
+ BOOST_CHECK_EQUAL (g->ab(), true);
g->write_metadata ();
BOOST_CHECK_EQUAL (::system (s.str().c_str ()), 0);
}
-BOOST_AUTO_TEST_CASE (stream_test)
-{
- FFmpegAudioStream a ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (a.id(), 4);
- BOOST_CHECK_EQUAL (a.sample_rate(), 44100);
- BOOST_CHECK_EQUAL (a.channel_layout(), 1);
- BOOST_CHECK_EQUAL (a.name(), "hello there world");
- BOOST_CHECK_EQUAL (a.to_string(), "ffmpeg 4 44100 1 hello there world");
-
- SndfileStream e ("external 44100 1", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (e.sample_rate(), 44100);
- BOOST_CHECK_EQUAL (e.channel_layout(), 1);
- BOOST_CHECK_EQUAL (e.to_string(), "external 44100 1");
-
- SubtitleStream s ("5 a b c", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (s.id(), 5);
- BOOST_CHECK_EQUAL (s.name(), "a b c");
-
- shared_ptr<AudioStream> ff = audio_stream_factory ("ffmpeg 4 44100 1 hello there world", boost::optional<int> (1));
- shared_ptr<FFmpegAudioStream> cff = dynamic_pointer_cast<FFmpegAudioStream> (ff);
- BOOST_CHECK (cff);
- BOOST_CHECK_EQUAL (cff->id(), 4);
- BOOST_CHECK_EQUAL (cff->sample_rate(), 44100);
- BOOST_CHECK_EQUAL (cff->channel_layout(), 1);
- BOOST_CHECK_EQUAL (cff->name(), "hello there world");
- BOOST_CHECK_EQUAL (cff->to_string(), "ffmpeg 4 44100 1 hello there world");
-
- shared_ptr<AudioStream> fe = audio_stream_factory ("external 44100 1", boost::optional<int> (1));
- BOOST_CHECK_EQUAL (fe->sample_rate(), 44100);
- BOOST_CHECK_EQUAL (fe->channel_layout(), 1);
- BOOST_CHECK_EQUAL (fe->to_string(), "external 44100 1");
-}
-
BOOST_AUTO_TEST_CASE (format_test)
{
Format::setup_formats ();
BOOST_CHECK_EQUAL (f->dcp_size().height, 858);
}
-/* Test VariableFormat-based scaling of content */
-BOOST_AUTO_TEST_CASE (scaling_test)
-{
- shared_ptr<Film> film (new Film (test_film_dir ("scaling_test").string(), false));
-
- /* 4:3 ratio */
- film->set_size (libdcp::Size (320, 240));
-
- /* This format should preserve aspect ratio of the source */
- Format const * format = Format::from_id ("var-185");
-
- /* We should have enough padding that the result is 4:3,
- which would be 1440 pixels.
- */
- BOOST_CHECK_EQUAL (format->dcp_padding (film), (1998 - 1440) / 2);
-
- /* This crops it to 1.291666667 */
- film->set_left_crop (5);
- film->set_right_crop (5);
-
- /* We should now have enough padding that the result is 1.29166667,
- which would be 1395 pixels.
- */
- BOOST_CHECK_EQUAL (format->dcp_padding (film), rint ((1998 - 1395) / 2.0));
-}
-
BOOST_AUTO_TEST_CASE (util_test)
{
string t = "Hello this is a string \"with quotes\" and indeed without them";
BOOST_CHECK_THROW (md5_digest ("foobar"), OpenFileError);
}
-BOOST_AUTO_TEST_CASE (paths_test)
-{
- shared_ptr<Film> f = new_test_film ("paths_test");
- f->set_directory ("build/test/a/b/c/d/e");
-
- f->_content = "/foo/bar/baz";
- BOOST_CHECK_EQUAL (f->content_path(), "/foo/bar/baz");
- f->_content = "foo/bar/baz";
- BOOST_CHECK_EQUAL (f->content_path(), "build/test/a/b/c/d/e/foo/bar/baz");
-}
-
void
do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* description, shared_ptr<EncodedData> locally_encoded)
{
new thread (boost::bind (&Server::run, server, 2));
/* Let the server get itself ready */
- dvdomatic_sleep (1);
+ dcpomatic_sleep (1);
ServerDescription description ("localhost", 2);
{
shared_ptr<Film> film = new_test_film ("make_dcp_test");
film->set_name ("test_film2");
- film->set_content ("../../../test/test.mp4");
+// film->set_content ("../../../test/test.mp4");
film->set_format (Format::from_nickname ("Flat"));
film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
film->make_dcp ();
film->write_metadata ();
while (JobManager::instance()->work_to_do ()) {
- dvdomatic_sleep (1);
+ dcpomatic_sleep (1);
}
BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
{
shared_ptr<Film> film = new_test_film ("make_dcp_with_range_test");
film->set_name ("test_film3");
- film->set_content ("../../../test/test.mp4");
- film->examine_content ();
+// film->set_content ("../../../test/test.mp4");
+// film->examine_content ();
film->set_format (Format::from_nickname ("Flat"));
film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test"));
film->set_trim_end (42);
film->make_dcp ();
while (JobManager::instance()->work_to_do() && !JobManager::instance()->errors()) {
- dvdomatic_sleep (1);
+ dcpomatic_sleep (1);
}
BOOST_CHECK_EQUAL (JobManager::instance()->errors(), false);
Config::instance()->set_allowed_dcp_frame_rates (afr);
shared_ptr<Film> f = new_test_film ("audio_sampling_rate_test");
- f->set_source_frame_rate (24);
+// f->set_source_frame_rate (24);
f->set_dcp_frame_rate (24);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 48000);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 80000, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 96000);
- f->set_source_frame_rate (23.976);
+// f->set_source_frame_rate (23.976);
f->set_dcp_frame_rate (best_dcp_frame_rate (23.976));
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
- f->set_source_frame_rate (29.97);
+// f->set_source_frame_rate (29.97);
f->set_dcp_frame_rate (best_dcp_frame_rate (29.97));
BOOST_CHECK_EQUAL (f->dcp_frame_rate (), 30);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 47952);
- f->set_source_frame_rate (25);
+// f->set_source_frame_rate (25);
f->set_dcp_frame_rate (24);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 48000, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
- f->set_source_frame_rate (25);
+// f->set_source_frame_rate (25);
f->set_dcp_frame_rate (24);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 44100, 0)));
BOOST_CHECK_EQUAL (f->target_audio_sample_rate(), 50000);
/* Check some out-there conversions (not the best) */
- f->set_source_frame_rate (14.99);
+// f->set_source_frame_rate (14.99);
f->set_dcp_frame_rate (25);
- f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
+// f->set_content_audio_stream (shared_ptr<AudioStream> (new FFmpegAudioStream ("a", 42, 16000, 0)));
/* The FrameRateConversion within target_audio_sample_rate should choose to double-up
the 14.99 fps video to 30 and then run it slow at 25.
*/
shared_ptr<TestJob> a (new TestJob (f));
JobManager::instance()->add (a);
- dvdomatic_sleep (1);
+ dcpomatic_sleep (1);
BOOST_CHECK_EQUAL (a->running (), true);
a->set_finished_ok ();
- dvdomatic_sleep (2);
+ dcpomatic_sleep (2);
BOOST_CHECK_EQUAL (a->finished_ok(), true);
}
def build(bld):
obj = bld(features = 'cxx cxxprogram')
obj.name = 'unit-tests'
- obj.uselib = 'BOOST_TEST DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC'
- obj.use = 'libdvdomatic'
+ obj.uselib = 'BOOST_TEST CXML DCP OPENJPEG AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC'
+ obj.use = 'libdcpomatic'
obj.source = 'test.cc'
obj.target = 'unit-tests'
obj.install_path = ''
--- /dev/null
+id ICON "dcpomatic.ico"
+taskbar_icon ICON "dcpomatic_taskbar.ico"
+#include "wx-2.9/wx/msw/wx.rc"
+++ /dev/null
-id ICON "dvdomatic.ico"
-taskbar_icon ICON "dvdomatic_taskbar.ico"
-#include "wx-2.9/wx/msw/wx.rc"
!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"
File "%deps%/bin/libfontconfig-1.dll"
File "%deps%/bin/libexpat-1.dll"
File "%deps%/bin/libbz2.dll"
+File "%deps%/bin/cxml.dll"
-File "%binaries%/src/wx/dvdomatic-wx.dll"
-File "%binaries%/src/lib/dvdomatic.dll"
-File "%binaries%/src/tools/dvdomatic.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_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
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 encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" ""
+CreateShortCut "$DESKTOP\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.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 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 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"
RMDir /r "$INSTDIR\*.*"
RMDir "$INSTDIR"
-Delete "$DESKTOP\DVD-o-matic.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 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
!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"
; 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"
File "%deps%/bin/libfontconfig-1.dll"
File "%deps%/bin/libexpat-1.dll"
File "%deps%/bin/libbz2.dll"
+File "%deps%/bin/cxml.dll"
-File "%binaries%/src/wx/dvdomatic-wx.dll"
-File "%binaries%/src/lib/dvdomatic.dll"
-File "%binaries%/src/tools/dvdomatic.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_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
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 encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" ""
+CreateShortCut "$DESKTOP\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.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 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 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"
RMDir /r "$INSTDIR\*.*"
RMDir "$INSTDIR"
-Delete "$DESKTOP\DVD-o-matic.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 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
import os
import sys
-APPNAME = 'dvdomatic'
-VERSION = '0.89pre'
+APPNAME = 'dcpomatic'
+VERSION = '1.00pre'
def options(opt):
opt.load('compiler_cxx')
'-Wall', '-Wno-attributes', '-Wextra'])
if conf.options.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:
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')
conf.env.VERSION = VERSION
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.45', 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.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.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.env.HAVE_AVFORMAT = 1
conf.env.STLIB_AVFORMAT = ['avformat']
conf.env.HAVE_AVFILTER = 1
d = { 'PREFIX' : '${PREFIX' }
obj = bld(features = 'subst')
- obj.source = 'dvdomatic.desktop.in'
- obj.target = 'dvdomatic.desktop'
+ obj.source = 'dcpomatic.desktop.in'
+ obj.target = 'dcpomatic.desktop'
obj.dict = d
- bld.install_files('${PREFIX}/share/applications', 'dvdomatic.desktop')
+ bld.install_files('${PREFIX}/share/applications', 'dcpomatic.desktop')
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)
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
print('Writing version information to src/lib/version.cc')
o = open('src/lib/version.cc', 'w')
o.write(text)