diff options
188 files changed, 5874 insertions, 4973 deletions
diff --git a/.gitignore b/.gitignore index cc3351558..70738a18f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ sync doc/manual/html doc/manual/pdf doc/manual/extensions.ent +doc/design/*.pdf +doc/design/*.log +doc/design/*.aux .be/id-cache *.pyc GPATH @@ -26,7 +26,7 @@ DOXYFILE_ENCODING = UTF-8 # 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 @@ -1,4 +1,4 @@ -dvd-o-matic +DCP-o-matic ----------- Hello! @@ -33,27 +33,22 @@ You will need these libraries: 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.) @@ -76,7 +71,7 @@ Server/client ------------- 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. @@ -1,137 +0,0 @@ -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 - - diff --git a/branch-notes b/branch-notes new file mode 100644 index 000000000..dd90ed91f --- /dev/null +++ b/branch-notes @@ -0,0 +1,3 @@ +audio map view is screwed up on windows, apparently not extended to full height +still image stuff pretty slow + diff --git a/builds/control-12.04-32 b/builds/control-12.04-32 index 0f52d03ae..dc104958a 100644 --- a/builds/control-12.04-32 +++ b/builds/control-12.04-32 @@ -1,24 +1,24 @@ -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. diff --git a/builds/control-12.04-64 b/builds/control-12.04-64 index fa4b4476e..09c636e4a 100644 --- a/builds/control-12.04-64 +++ b/builds/control-12.04-64 @@ -1,24 +1,24 @@ -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. diff --git a/builds/control-12.10-32 b/builds/control-12.10-32 index 0e5fc1f46..1330b3e5f 100644 --- a/builds/control-12.10-32 +++ b/builds/control-12.10-32 @@ -1,23 +1,23 @@ -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. diff --git a/builds/control-12.10-64 b/builds/control-12.10-64 index 24e16b4b5..ea1c491ed 100644 --- a/builds/control-12.10-64 +++ b/builds/control-12.10-64 @@ -1,24 +1,24 @@ -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. @@ -7,8 +7,9 @@ def dependencies(target): return () else: return (('openjpeg-cdist', None), + ('libcxml', None), ('ffmpeg-cdist', '488d5d4496af5e3a3b9d31d6b221e8eeada6b77e'), - ('libdcp', 'v0.49')) + ('libdcp', None)) def build(env, target): cmd = './waf configure --prefix=%s' % env.work_dir_cscript() @@ -42,14 +43,14 @@ def package(env, target, version): 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')) @@ -64,9 +65,9 @@ def package(env, target, version): 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') diff --git a/dvdomatic.desktop.in b/dcpomatic.desktop.in index 65067eb3b..aabd992f5 100644 --- a/dvdomatic.desktop.in +++ b/dcpomatic.desktop.in @@ -3,8 +3,8 @@ Encoding=UTF-8 Version=1.0 Type=Application Terminal=false -Exec=@PREFIX@/bin/dvdomatic -Name=DVD-o-matic -Icon=dvdomatic +Exec=@PREFIX@/bin/dcpomatic +Name=DCP-o-matic +Icon=dcpomatic Comment=DCP generator Categories=AudioVideo;Video diff --git a/debian/changelog b/debian/changelog index a10ba5d92..1cb0a7d31 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,370 +1,358 @@ -dvdomatic (0.89-1) UNRELEASED; urgency=low - - * New upstream release. - - -- Carl Hetherington <carl@houllier.lan> Mon, 06 May 2013 02:03:25 +0100 - -dvdomatic (0.89beta1-1) UNRELEASED; urgency=low - - * New upstream release. - - -- Carl Hetherington <carl@houllier.lan> Sat, 04 May 2013 21:15:34 +0100 - -dvdomatic (0.88-1) UNRELEASED; urgency=low +dcpomatic (0.88-1) UNRELEASED; urgency=low * New upstream release. -- Carl Hetherington <carl@houllier.lan> Sun, 28 Apr 2013 16:28:17 +0100 -dvdomatic (0.87-1) UNRELEASED; urgency=low +dcpomatic (0.87-1) UNRELEASED; urgency=low * New upstream release. -- Carl Hetherington <carl@houllier.lan> Fri, 26 Apr 2013 09:53:27 +0100 -dvdomatic (0.86-1) UNRELEASED; urgency=low +dcpomatic (0.86-1) UNRELEASED; urgency=low * New upstream release. -- Carl Hetherington <carl@houllier.lan> Tue, 23 Apr 2013 08:13:13 +0100 -dvdomatic (0.85-1) UNRELEASED; urgency=low +dcpomatic (0.85-1) UNRELEASED; urgency=low * New upstream release. -- Carl Hetherington <carl@houllier.lan> Tue, 23 Apr 2013 00:08:20 +0100 -dvdomatic (0.84-1) UNRELEASED; urgency=low +dcpomatic (0.84-1) UNRELEASED; urgency=low * New upstream release. -- 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. @@ -372,7 +360,7 @@ dvdomatic (0.70-1) UNRELEASED; urgency=low -- 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. @@ -381,13 +369,13 @@ dvdomatic (0.70beta3-1) UNRELEASED; urgency=low -- 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. @@ -397,91 +385,91 @@ dvdomatic (0.68beta10-1) UNRELEASED; urgency=low -- 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. diff --git a/debian/copyright b/debian/copyright index 2579947e4..0cf23aacd 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,6 +1,6 @@ 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> diff --git a/debian/files b/debian/files index 7639f05ac..ca46cf438 100644 --- a/debian/files +++ b/debian/files @@ -1 +1 @@ -dvdomatic_0.59beta1-1_i386.deb video extra +dcpomatic_0.59beta1-1_i386.deb video extra diff --git a/debian/rules b/debian/rules index f2b2219be..29f926c31 100755 --- a/debian/rules +++ b/debian/rules @@ -20,8 +20,8 @@ override_dh_auto_build: ./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 diff --git a/doc/design/content.tex b/doc/design/content.tex new file mode 100644 index 000000000..0f5f17025 --- /dev/null +++ b/doc/design/content.tex @@ -0,0 +1,195 @@ +\documentclass{article} +\begin{document} + +\section{Status quo} + +As at 0.78 there is an unfortunate mish-mash of code to handle the +input `content' the goes into a DCP. + +The Film has a `content' file name. This is guessed to be either a +movie (for FFmpeg) or a still-image (for ImageMagick) based on its +extension. We also have `external audio', which is a set of WAV files +for libsndfile, and a flag to enable that. + +The `content' file is badly named and limiting. We can't have +multiple content files, and it's not really the `content' as such (it +used to be, but increasingly it's only a part of the content, on equal +footing with `external' audio). + +The choice of sources for sound is expressed clumsily by the +AudioStream class hierarchy. + + +\section{Targets} + +We want to be able to implement the following: + +\begin{itemize} +\item Immediately: +\begin{itemize} +\item Multiple still images, each with their own duration, made into a `slide-show' +\item Lack of bugs in adding WAV-file audio to still images. +\item External subtitle files (either XML or SRT) to be converted to XML subtitles in the DCP. +\end{itemize} + +\item In the future: +\begin{itemize} +\item Playlist-style multiple video / audio (perhaps). +\end{itemize} +\end{itemize} + + +\section{Content hierarchy} + +One idea is to have a hierarchy of Content classes (\texttt{Content}, +\texttt{\{Video/Audio\}Content}, \texttt{FFmpegContent}, \texttt{ImageMagickContent}, +\texttt{SndfileContent}). + +Then the Film has a list of these, and decides what to put into the +DCP based on some rules. These rules would probably be fixed (for +now), with the possibility to expand later into some kind of playlist. + + +\section{Immediate questions} + +\subsection{What Film attributes are video-content specific, and which are general?} + +Questionable attributes: + +\begin{itemize} +\item Trust content header +\item Crop +\item Filters + +Post-processing (held as part of the filters description) is done in +the encoder, by which time all knowledge of the source is lost. + +\item Scaler +\item Trim start/end + +Messily tied in with the encoding side. We want to implement this +using start/end point specifications in the DCP reel, otherwise +modifying the trim points requires a complete re-encode. + +\item Audio gain +\item Audio delay +\item With subtitles +\item Subtitle offset/scale +\item Colour LUT +\end{itemize} + +Attributes that I think must remain in Film: +\begin{itemize} +\item DCP content type +\item Format +\item A/B +\item J2K bandwidth +\end{itemize} + +Part of the consideration here is that per-content attributes need to +be represented in the GUI differently to how things are represented +now. + +Bear in mind also that, as it stands, the only options for video are: + +\begin{enumerate} +\item An FFmpeg video +\item A set of stills +\end{enumerate} + +and so the need for multiple scalers, crop and filters is +questionable. Also, there is one set of audio (either from WAVs or +from the FFMpeg file), so per-content audio gain/delay is also +questionable. Trust content header is only applicable for FFmpeg +content, really. Similarly trim, with-subtitles, subtitle details, +colour LUT; basically none of it is really important right now. + +Hence it may be sensible to keep everything in Film and move it later +along YAGNI lines. + + +\subsection{Who answers questions like: ``what is the length of video?''?} + +If we have FFmpeg video, the question is easy to answer. For a set of +stills, it is less easy. Who knows that we are sticking them all +together end-to-end, with different durations for each? + +If we have one-content-object equalling one file, the content objects +will presumably know how long their file should be displayed for. +There would appear to be two options following this: + +\begin{enumerate} +\item There is one \texttt{ImageMagickDecoder} which is fed all the + files, and outputs them in order. The magic knowledge is then + within this class, really. +\item There are multiple \texttt{ImageMagickDecoder} classes, one per + \texttt{..Content}, and some controlling (`playlist') class to manage + them. The `playlist' is then itself a + \texttt{\{Video/Audio\}Source}, and has the magic knowledge. +\end{enumerate} + + +\section{Playlist approach} + +Let's try the playlist approach. We define a hierarchy of content classes: + +\begin{verbatim} + +class Content +{ +public: + boost::filesystem::path file () const; +}; + +class VideoContent : virtual public Content +{ +public: + VideoContentFrame video_length () const; + float video_frame_rate () const; + libdcp::Size size () const; + +}; + +class AudioContent : virtual public Content +{ + +}; + +class FFmpegContent : public VideoContent, public AudioContent +{ +public: + .. stream stuff .. +}; + +class ImageMagickContent : public VideoContent +{ + +}; + +class SndfileContent : public AudioContent +{ +public: + .. channel allocation for this file .. +}; +\end{verbatim} + +Then Film has a \texttt{Playlist} which has a +\texttt{vector<shared\_ptr<Content> >}. It can answer questions +about audio/video length, frame rate, audio channels and so on. + +\texttt{Playlist} can also be a source of video and audio, so clients can do: + +\begin{verbatim} +shared_ptr<Playlist> p = film->playlist (); +p->Video.connect (foo); +p->Audio.connect (foo); +while (!p->pass ()) { + /* carry on */ +} +\end{verbatim} + +Playlist could be created on-demand for all the difference it would +make. And perhaps it should, since it will hold Decoders which are +probably run-once. + +\end{document} diff --git a/doc/mainpage.txt b/doc/mainpage.txt index 59c578899..649c9c609 100644 --- a/doc/mainpage.txt +++ b/doc/mainpage.txt @@ -1,37 +1,37 @@ -/** @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 */ diff --git a/doc/manual/Makefile b/doc/manual/Makefile index 94abc8516..115d7c3c8 100644 --- a/doc/manual/Makefile +++ b/doc/manual/Makefile @@ -1,4 +1,4 @@ -# DVD-o-matic manual makefile +# DCP-o-matic manual makefile all: html pdf @@ -8,7 +8,7 @@ SCREENSHOTS := file-new.png video-new-film.png still-new-film.png click-content- 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 := @@ -70,7 +70,7 @@ diagrams/%.pdf: diagrams/%.svg # 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))) \ @@ -80,19 +80,19 @@ html: $(XML) dvdomatic-html.xsl extensions-html.ent dvdomatic.css \ 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. @@ -100,14 +100,14 @@ pdf: $(XML) dvdomatic-pdf.xsl extensions-pdf.ent screenshots/*.png $(subst .svg, 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. @@ -116,8 +116,8 @@ tex: $(XML) dvdomatic-pdf.xsl extensions-pdf.ent 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 diff --git a/doc/manual/dvdomatic-html.xsl b/doc/manual/dcpomatic-html.xsl index 059d7ead7..144675d47 100644 --- a/doc/manual/dvdomatic-html.xsl +++ b/doc/manual/dcpomatic-html.xsl @@ -4,7 +4,7 @@ version="1.0"> <!-- Our CSS --> -<xsl:param name="html.stylesheet" select="'dvdomatic.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"/> diff --git a/doc/manual/dvdomatic-pdf.xsl b/doc/manual/dcpomatic-pdf.xml index 414fb64b8..414fb64b8 100644 --- a/doc/manual/dvdomatic-pdf.xsl +++ b/doc/manual/dcpomatic-pdf.xml diff --git a/doc/manual/dvdomatic.css b/doc/manual/dcpomatic.css index 0e4982f20..0e4982f20 100644 --- a/doc/manual/dvdomatic.css +++ b/doc/manual/dcpomatic.css diff --git a/doc/manual/dvdomatic.sty b/doc/manual/dcpomatic.sty index 834e581fc..834e581fc 100644 --- a/doc/manual/dvdomatic.sty +++ b/doc/manual/dcpomatic.sty diff --git a/doc/manual/dvdomatic.xml b/doc/manual/dcpomatic.xml index 58315eca6..ee7b96083 100644 --- a/doc/manual/dvdomatic.xml +++ b/doc/manual/dcpomatic.xml @@ -11,7 +11,7 @@ <book xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en"> <bookinfo> -<title>DVD-o-matic</title> +<title>DCP-o-matic</title> <author><firstname>Carl</firstname><surname>Hetherington</surname></author> </bookinfo> @@ -19,14 +19,14 @@ <title>Introduction</title> <para> -Hello, and welcome to DVD-o-matic! +Hello, and welcome to DCP-o-matic! </para> <section> -<title>What is DVD-o-matic?</title> +<title>What is DCP-o-matic?</title> <para> -DVD-o-matic is a program to generate <ulink +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 @@ -45,7 +45,7 @@ your cinema. <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>. +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> @@ -59,17 +59,17 @@ DVD-o-matic is licensed under the <ulink url="http://www.gnu.org/licenses/old-li <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> +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 -DVD-o-matic will be installed onto your machine. +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 DVD-o-matic to -use more memory. You may find that DVD-o-matic crashes if you run +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> @@ -80,13 +80,13 @@ version. <title>Ubuntu Linux</title> <para> -You can install DVD-o-matic on Ubuntu 12.04 (‘Precise +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/dvdomatic">http://carlh.net</ulink> and +url="http://carlh.net/software/dcpomatic">http://carlh.net</ulink> and double-click it. Ubuntu will install the necessary bits and pieces -and set DVD-o-matic up for you. +and set DCP-o-matic up for you. </para> </section> @@ -98,7 +98,7 @@ and set DVD-o-matic up for you. 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 +know via the <ulink url="mailto:dcpomatic@carlh.net">mailing list</ulink> and I will see about building some packages. </para> @@ -121,7 +121,7 @@ The following dependencies are required: <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>, +url="http://carlh.net/software/dcpomatic">http://carlh.net</ulink>, unpack it and run the following commands from inside the source directory: </para> @@ -133,11 +133,11 @@ sudo ./waf install </programlisting> <para> -With any luck, this will build and install DVD-o-matic on your system. To run it, enter: +With any luck, this will build and install DCP-o-matic on your system. To run it, enter: </para> <programlisting> -dvdomatic +dcpomatic </programlisting> <para> @@ -152,7 +152,7 @@ in a shell. <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 +DCP-o-matic. We will gloss over some of the finer details, which are explained in later chapters. </para> @@ -160,7 +160,7 @@ explained in later chapters. <title>Creating a new film</title> <para> -Let's make a very simple DCP to see how DVD-o-matic works. First, we +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 @@ -170,10 +170,10 @@ 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 +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. DVD-o-matic stores its data in a folder on your disk while it +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"/>. @@ -206,15 +206,15 @@ linkend="fig-video-new-film"/>. 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 +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 -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. +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> @@ -255,7 +255,7 @@ 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 +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"/>. @@ -331,10 +331,10 @@ full name by hovering the mouse pointer over the partial name. <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 +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 DVD-o-matic will run through the content +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> @@ -370,7 +370,7 @@ This tab contains settings related to the picture in your DCP, as shown in <xref <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 +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 @@ -440,7 +440,7 @@ 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 +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. @@ -460,7 +460,7 @@ linkend="fig-calculate-audio-gain"/> will open. <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 +<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 @@ -468,7 +468,7 @@ your sound-rack fader. </para> <para> -Current versions of DVD-o-matic only know about the Dolby CP750. If +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>. @@ -488,7 +488,7 @@ 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 +Note that if your content's audio is mono, DCP-o-matic will place it in the centre channel in the DCP. </para> @@ -518,7 +518,7 @@ This tab contains settings related to subtitles in your DCP, as shown in <xref l </figure> <para> -DVD-o-matic will extract subtitles from the content, if present, and +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 @@ -530,7 +530,7 @@ control changes their size. </para> <para> -Future versions of DVD-o-matic will hopefully include the option to +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> @@ -543,9 +543,9 @@ DCPs). <para> Now that we have set everything up, choose <guilabel>Make -DCP</guilabel> from the <guilabel>Jobs</guilabel> menu. DVD-o-matic +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, DVD-o-matic will update you on +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> @@ -567,7 +567,7 @@ stick, hard-drive or network connection. <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"/>. +DCP-o-matic. See <xref linkend="sec-tms-upload"/>. </para> </section> @@ -578,7 +578,7 @@ DVD-o-matic. See <xref linkend="sec-tms-upload"/>. <title>Creating a still-image DCP</title> <para> -DVD-o-matic can also be used to create DCPs of a still image, perhaps +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> @@ -626,7 +626,7 @@ 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 +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> @@ -641,7 +641,7 @@ longer, silence will be added after your audio. <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 +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> @@ -652,7 +652,7 @@ to encode a single frame which it can then repeat. <title>Preferences</title> <para> -DVD-o-matic provides a few preferences which can be used to modify its +DCP-o-matic provides a few preferences which can be used to modify its behaviour. This chapter explains those options. </para> @@ -680,7 +680,7 @@ menu. The dialogue is shown in <xref linkend="fig-prefs"/>. <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. +connections, you can upload DCPs directly from DCP-o-matic to the TMS. This is discussed in <xref linkend="sec-tms-upload"/>. </para> @@ -697,9 +697,9 @@ credentials required to log into the TMS via SSH. <title>Threads</title> <para> -When DVD-o-matic is encoding DCPs it can use multiple parallel threads +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 -DVD-o-matic should use. This would typically be set to the number of +DCP-o-matic should use. This would typically be set to the number of processors (or processor cores) in your machine. </para> @@ -709,7 +709,7 @@ processors (or processor cores) in your machine. <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. +This is the directory which DCP-o-matic will suggest initially as a place to put new films. </para> </section> @@ -718,7 +718,7 @@ This is the directory which DVD-o-matic will suggest initially as a place to put <title>A/B options</title> <para> -These options are for DVD-o-matic's special mode of making A/B +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> @@ -732,7 +732,7 @@ use is described in <xref linkend="sec-ab"/>. 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 +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. @@ -746,7 +746,7 @@ used. <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 +<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> @@ -755,10 +755,10 @@ circumstances. <title>Filtering</title> <para> -DVD-o-matic offers a variety of filters that can be applied to your +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 DVD-o-matic window; this opens the filters selector +setup area of the DCP-o-matic window; this opens the filters selector as shown in <xref linkend="fig-filters"/>. </para> @@ -773,7 +773,7 @@ as shown in <xref linkend="fig-filters"/>. <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 +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> @@ -785,7 +785,7 @@ image is much smaller and of lower resolution than a projected image! <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 +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. @@ -811,7 +811,7 @@ from the <guilabel>Jobs</guilabel> menu. <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 +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. @@ -821,7 +821,7 @@ evaluated. <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 +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. @@ -834,10 +834,10 @@ half will use the filters and scaler specified in the film setup. <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 +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 DVD-o-matic, and the ‘server’ machines run +machine runs DCP-o-matic, and the ‘server’ machines run a small program called ‘servomatic’. </para> @@ -876,7 +876,7 @@ to run 4 threads in parallel. </para> <para> -To run the GUI version on windows, run the ‘DVD-o-matic encode +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. @@ -884,16 +884,16 @@ server or open a window to show its status. </section> <section> -<title>Setting up DVD-o-matic</title> +<title>Setting up DCP-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 +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 DVD-o-matic knows how many +threads that it is running, so that DCP-o-matic knows how many parallel encode jobs to send to the server. </para> @@ -907,8 +907,8 @@ up between the master machine and the servers. <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 +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> @@ -918,7 +918,7 @@ 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 +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> diff --git a/hacks/python-playback/config.py b/hacks/python-playback/config.py deleted file mode 100644 index fecf261f5..000000000 --- a/hacks/python-playback/config.py +++ /dev/null @@ -1,2 +0,0 @@ - -LEFT_SCREEN_WIDTH = 1366 diff --git a/hacks/python-playback/dvdomatic b/hacks/python-playback/dvdomatic deleted file mode 100755 index ce405f37e..000000000 --- a/hacks/python-playback/dvdomatic +++ /dev/null @@ -1,209 +0,0 @@ -#!/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() diff --git a/hacks/python-playback/film.py b/hacks/python-playback/film.py deleted file mode 100644 index 3ad128027..000000000 --- a/hacks/python-playback/film.py +++ /dev/null @@ -1,188 +0,0 @@ -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]) diff --git a/hacks/python-playback/film_view.py b/hacks/python-playback/film_view.py deleted file mode 100644 index c11b2e657..000000000 --- a/hacks/python-playback/film_view.py +++ /dev/null @@ -1,212 +0,0 @@ -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) - diff --git a/hacks/python-playback/player.py b/hacks/python-playback/player.py deleted file mode 100644 index 5cc8da711..000000000 --- a/hacks/python-playback/player.py +++ /dev/null @@ -1,112 +0,0 @@ -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)) - diff --git a/hacks/python-playback/ratio.py b/hacks/python-playback/ratio.py deleted file mode 100644 index 62320dc8a..000000000 --- a/hacks/python-playback/ratio.py +++ /dev/null @@ -1,56 +0,0 @@ -# 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 diff --git a/hacks/python-playback/screens b/hacks/python-playback/screens deleted file mode 100644 index f389cb1c4..000000000 --- a/hacks/python-playback/screens +++ /dev/null @@ -1,62 +0,0 @@ -# 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 diff --git a/hacks/python-playback/screens.py b/hacks/python-playback/screens.py deleted file mode 100644 index 4230a4cf8..000000000 --- a/hacks/python-playback/screens.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/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 diff --git a/hacks/python-playback/thumbs.py b/hacks/python-playback/thumbs.py deleted file mode 100644 index 921f82f5d..000000000 --- a/hacks/python-playback/thumbs.py +++ /dev/null @@ -1,76 +0,0 @@ -# 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()) diff --git a/hacks/python-playback/util.py b/hacks/python-playback/util.py deleted file mode 100644 index d78abdd00..000000000 --- a/hacks/python-playback/util.py +++ /dev/null @@ -1,7 +0,0 @@ - -def s_to_hms(s): - m = int(s / 60) - s -= (m * 60) - h = int(m / 60) - m -= (h * 60) - return (h, m, s) diff --git a/hacks/python-playback/xrandr-notes b/hacks/python-playback/xrandr-notes deleted file mode 100644 index eeabf1423..000000000 --- a/hacks/python-playback/xrandr-notes +++ /dev/null @@ -1,17 +0,0 @@ -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 - diff --git a/icons/128x128/dvdomatic.png b/icons/128x128/dcpomatic.png Binary files differindex 9936b39af..9936b39af 100644 --- a/icons/128x128/dvdomatic.png +++ b/icons/128x128/dcpomatic.png diff --git a/icons/16x16/dvdomatic.png b/icons/16x16/dcpomatic.png Binary files differindex 3c5a10f2d..3c5a10f2d 100644 --- a/icons/16x16/dvdomatic.png +++ b/icons/16x16/dcpomatic.png diff --git a/icons/22x22/dvdomatic.png b/icons/22x22/dcpomatic.png Binary files differindex dddb86298..dddb86298 100644 --- a/icons/22x22/dvdomatic.png +++ b/icons/22x22/dcpomatic.png diff --git a/icons/32x32/dvdomatic.png b/icons/32x32/dcpomatic.png Binary files differindex 8cecf08f8..8cecf08f8 100644 --- a/icons/32x32/dvdomatic.png +++ b/icons/32x32/dcpomatic.png diff --git a/icons/48x48/dvdomatic.png b/icons/48x48/dcpomatic.png Binary files differindex 07bf2d10b..07bf2d10b 100644 --- a/icons/48x48/dvdomatic.png +++ b/icons/48x48/dcpomatic.png diff --git a/icons/64x64/dvdomatic.png b/icons/64x64/dcpomatic.png Binary files differindex 35564a8a2..35564a8a2 100644 --- a/icons/64x64/dvdomatic.png +++ b/icons/64x64/dcpomatic.png diff --git a/run/dvdomatic b/run/dcpomatic index 147c001cd..7ea08778c 100755 --- a/run/dvdomatic +++ b/run/dcpomatic @@ -3,13 +3,13 @@ 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 "$*" + gdb --args build/src/tools/dcpomatic $* elif [ "$1" == "--valgrind" ]; then shift - valgrind --tool="memcheck" build/src/tools/dvdomatic $* + valgrind --tool="memcheck" build/src/tools/dcpomatic $* elif [ "$1" == "--i18n" ]; then shift - LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 build/src/tools/dvdomatic "$*" + LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 build/src/tools/dcpomatic "$*" else - build/src/tools/dvdomatic "$*" + build/src/tools/dcpomatic $* fi diff --git a/run/makedcp b/run/dcpomatic_cli index f71345b66..bf2f08015 100755 --- a/run/makedcp +++ b/run/dcpomatic_cli @@ -3,10 +3,10 @@ export LD_LIBRARY_PATH=build/src/lib:$LD_LIBRARY_PATH:build/src if [ "$1" == "--debug" ]; then shift - gdb --args build/src/tools/makedcp "$@" + gdb --args build/src/tools/dcpomatic_cli "$@" elif [ "$1" == "--valgrind" ]; then shift - valgrind --tool="memcheck" --leak-check=full --show-reachable=yes build/src/tools/makedcp "$@" + valgrind --tool="memcheck" --leak-check=full --show-reachable=yes build/src/tools/dcpomatic_cli "$@" else - build/src/tools/makedcp "$@" + build/src/tools/dcpomatic_cli "$@" fi diff --git a/src/lib/ab_transcode_job.cc b/src/lib/ab_transcode_job.cc index 4ffdd9af6..2bdff47de 100644 --- a/src/lib/ab_transcode_job.cc +++ b/src/lib/ab_transcode_job.cc @@ -32,11 +32,9 @@ using std::string; 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 ()); @@ -54,7 +52,7 @@ ABTranscodeJob::run () { 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); diff --git a/src/lib/ab_transcode_job.h b/src/lib/ab_transcode_job.h index 8e3cbe2d8..cd82d4247 100644 --- a/src/lib/ab_transcode_job.h +++ b/src/lib/ab_transcode_job.h @@ -23,7 +23,6 @@ #include <boost/shared_ptr.hpp> #include "job.h" -#include "options.h" class Film; @@ -38,16 +37,13 @@ class ABTranscodeJob : public Job { 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; }; diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index d8f13dae4..f75491dce 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -21,13 +21,10 @@ #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" @@ -50,58 +47,36 @@ using boost::dynamic_pointer_cast; * @param e Encoder to use. */ -ABTranscoder::ABTranscoder ( - shared_ptr<Film> a, shared_ptr<Film> b, DecodeOptions o, Job* j, shared_ptr<Encoder> e) - : _film_a (a) - , _film_b (b) +ABTranscoder::ABTranscoder (shared_ptr<Film> film_a, shared_ptr<Film> film_b, shared_ptr<Job> j) + : _player_a (film_a->player ()) + , _player_b (film_b->player ()) , _job (j) - , _encoder (e) - , _combiner (new Combiner (a->log())) + , _encoder (new Encoder (film_a, j)) + , _combiner (new Combiner (film_a->log())) { - _da = decoder_factory (_film_a, o); - _db = decoder_factory (_film_b, o); + _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())); - 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)); - _gain.reset (new Gain (_film_a->log(), _film_a->audio_gain())); + _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 sr = st ? st->sample_rate() : 0; - 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; + 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); } @@ -111,32 +86,12 @@ ABTranscoder::go () { _encoder->process_begin (); - bool done[3] = { false, 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; - } - - if (_job) { - _da.video->set_progress (_job); - } - - if (done[0] && done[1] && done[2]) { - break; - } - } + while (!_player_a->pass () || !_player_b->pass ()) {} _delay_line->process_end (); - if (_matcher) { - _matcher->process_end (); - } + _matcher->process_end (); _gain->process_end (); + _trimmer->process_end (); _encoder->process_end (); } diff --git a/src/lib/ab_transcoder.h b/src/lib/ab_transcoder.h index 4f1b14e48..134bce3e4 100644 --- a/src/lib/ab_transcoder.h +++ b/src/lib/ab_transcoder.h @@ -25,20 +25,17 @@ #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 @@ -51,20 +48,16 @@ public: 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; diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc index 88cd65fee..f3c55b208 100644 --- a/src/lib/analyse_audio_job.cc +++ b/src/lib/analyse_audio_job.cc @@ -21,9 +21,7 @@ #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" @@ -52,29 +50,18 @@ AnalyseAudioJob::name () const 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 ()); diff --git a/src/lib/audio_analysis.h b/src/lib/audio_analysis.h index 6e0e2b78a..ec6905105 100644 --- a/src/lib/audio_analysis.h +++ b/src/lib/audio_analysis.h @@ -17,8 +17,8 @@ */ -#ifndef DVDOMATIC_AUDIO_ANALYSIS_H -#define DVDOMATIC_AUDIO_ANALYSIS_H +#ifndef DCPOMATIC_AUDIO_ANALYSIS_H +#define DCPOMATIC_AUDIO_ANALYSIS_H #include <iostream> #include <vector> diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc new file mode 100644 index 000000000..dfa48d97e --- /dev/null +++ b/src/lib/audio_content.cc @@ -0,0 +1,51 @@ +/* + 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) +{ + +} + +Time +AudioContent::temporal_length () const +{ + return audio_length() / audio_frame_rate(); +} diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h new file mode 100644 index 000000000..18107843c --- /dev/null +++ b/src/lib/audio_content.h @@ -0,0 +1,52 @@ +/* + 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; + + Time temporal_length () const; +}; + +#endif diff --git a/src/lib/audio_decoder.cc b/src/lib/audio_decoder.cc index a54c14843..df13a984a 100644 --- a/src/lib/audio_decoder.cc +++ b/src/lib/audio_decoder.cc @@ -18,19 +18,12 @@ */ #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; -} diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index cfe94b528..c393e95f1 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -21,38 +21,21 @@ * @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 diff --git a/src/lib/audio_mapping.cc b/src/lib/audio_mapping.cc new file mode 100644 index 000000000..7e28aa5c4 --- /dev/null +++ b/src/lib/audio_mapping.cc @@ -0,0 +1,131 @@ +/* + 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; +} diff --git a/src/lib/audio_mapping.h b/src/lib/audio_mapping.h new file mode 100644 index 000000000..248d2570e --- /dev/null +++ b/src/lib/audio_mapping.h @@ -0,0 +1,62 @@ +/* + 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 diff --git a/src/lib/audio_sink.h b/src/lib/audio_sink.h index 69b3a4b75..ee39f9ee7 100644 --- a/src/lib/audio_sink.h +++ b/src/lib/audio_sink.h @@ -17,8 +17,8 @@ */ -#ifndef DVDOMATIC_AUDIO_SINK_H -#define DVDOMATIC_AUDIO_SINK_H +#ifndef DCPOMATIC_AUDIO_SINK_H +#define DCPOMATIC_AUDIO_SINK_H class AudioSink { diff --git a/src/lib/audio_source.cc b/src/lib/audio_source.cc index d77e89367..2867bcc24 100644 --- a/src/lib/audio_source.cc +++ b/src/lib/audio_source.cc @@ -21,22 +21,42 @@ #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)); } + diff --git a/src/lib/audio_source.h b/src/lib/audio_source.h index c13f1636b..c7f0a09ed 100644 --- a/src/lib/audio_source.h +++ b/src/lib/audio_source.h @@ -21,8 +21,8 @@ * @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> diff --git a/src/lib/config.cc b/src/lib/config.cc index 8c65e371a..3beb0aea6 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -22,6 +22,7 @@ #include <fstream> #include <glib.h> #include <boost/filesystem.hpp> +#include <libcxml/cxml.h> #include "config.h" #include "server.h" #include "scaler.h" @@ -36,7 +37,10 @@ using std::vector; 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; @@ -47,6 +51,7 @@ Config::Config () , _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) { @@ -56,8 +61,70 @@ Config::Config () _allowed_dcp_frame_rates.push_back (48); _allowed_dcp_frame_rates.push_back (50); _allowed_dcp_frame_rates.push_back (60); +} + +void +Config::read () +{ + if (!boost::filesystem::exists (file (false))) { + read_old_metadata (); + return; + } + + cxml::File f (file (false), "Config"); + optional<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 ()) { @@ -101,7 +168,7 @@ Config::Config () } 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") { @@ -112,17 +179,21 @@ Config::Config () _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 (); } @@ -132,6 +203,13 @@ Config::instance () { if (_instance == 0) { _instance = new Config; + try { + _instance->read (); + } catch (...) { + /* configuration load failed; never mind, just + stick with the default. + */ + } } return _instance; @@ -141,44 +219,48 @@ Config::instance () 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 diff --git a/src/lib/config.h b/src/lib/config.h index a59cdcae0..96aa4e449 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -21,8 +21,8 @@ * @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> @@ -110,6 +110,10 @@ public: return _language; } + int default_still_length () const { + return _default_still_length; + } + Format const * default_format () const { return _default_format; } @@ -185,6 +189,10 @@ public: _language = boost::none; } + void set_default_still_length (int s) { + _default_still_length = s; + } + void set_default_format (Format const * f) { _default_format = f; } @@ -204,7 +212,9 @@ public: private: Config (); - std::string file () const; + std::string file (bool) const; + void read (); + void read_old_metadata (); /** number of threads to use for J2K encoding on the local machine */ int _num_local_encoding_threads; @@ -233,6 +243,7 @@ private: /** 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; diff --git a/src/lib/content.cc b/src/lib/content.cc new file mode 100644 index 000000000..9c3bcd39d --- /dev/null +++ b/src/lib/content.cc @@ -0,0 +1,69 @@ +/* + 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); +} diff --git a/src/lib/content.h b/src/lib/content.h new file mode 100644 index 000000000..e1cf41df0 --- /dev/null +++ b/src/lib/content.h @@ -0,0 +1,73 @@ +/* + 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> +#include "types.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; + virtual Time temporal_length () 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 diff --git a/src/lib/cross.cc b/src/lib/cross.cc index 2c66ab53a..f232f1779 100644 --- a/src/lib/cross.cc +++ b/src/lib/cross.cc @@ -18,20 +18,20 @@ */ #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 } diff --git a/src/lib/cross.h b/src/lib/cross.h index 110660b16..00457c968 100644 --- a/src/lib/cross.h +++ b/src/lib/cross.h @@ -17,8 +17,8 @@ */ -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS #define WEXITSTATUS(w) (w) #endif -void dvdomatic_sleep (int); +void dcpomatic_sleep (int); diff --git a/src/lib/dci_metadata.cc b/src/lib/dci_metadata.cc index 758886db4..f25b3ddb0 100644 --- a/src/lib/dci_metadata.cc +++ b/src/lib/dci_metadata.cc @@ -18,26 +18,39 @@ */ #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; diff --git a/src/lib/dci_metadata.h b/src/lib/dci_metadata.h index eecdc7655..b87609ed0 100644 --- a/src/lib/dci_metadata.h +++ b/src/lib/dci_metadata.h @@ -17,16 +17,24 @@ */ -#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; diff --git a/src/lib/dcp_content_type.h b/src/lib/dcp_content_type.h index 960bb0129..14204bd72 100644 --- a/src/lib/dcp_content_type.h +++ b/src/lib/dcp_content_type.h @@ -17,8 +17,8 @@ */ -#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.) diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index d674393a9..da51665d1 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -47,7 +47,6 @@ #include "dcp_video_frame.h" #include "lut.h" #include "config.h" -#include "options.h" #include "exceptions.h" #include "server.h" #include "util.h" @@ -267,7 +266,7 @@ DCPVideoFrame::encode_locally () _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 */ diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 52b22fa06..082ad5076 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -22,41 +22,26 @@ */ #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 @@ -64,12 +49,3 @@ Decoder::seek (double) { 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")); -} diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 2bc462c33..02ccaa42b 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -21,8 +21,8 @@ * @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> @@ -30,11 +30,9 @@ #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; @@ -53,26 +51,26 @@ class FilterGraph; 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; }; diff --git a/src/lib/decoder_factory.cc b/src/lib/decoder_factory.cc deleted file mode 100644 index f7f9f4074..000000000 --- a/src/lib/decoder_factory.cc +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/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))); -} diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index cff9899ac..d25e0d0f8 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -27,7 +27,6 @@ #include <libdcp/picture_asset.h> #include "encoder.h" #include "util.h" -#include "options.h" #include "film.h" #include "log.h" #include "exceptions.h" @@ -38,6 +37,8 @@ #include "format.h" #include "cross.h" #include "writer.h" +#include "player.h" +#include "audio_mapping.h" #include "i18n.h" @@ -48,13 +49,15 @@ using std::vector; 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; /** @param f Film that we are encoding */ -Encoder::Encoder (shared_ptr<Film> f) +Encoder::Encoder (shared_ptr<Film> f, shared_ptr<Job> j) : _film (f) + , _job (j) , _video_frames_in (0) , _video_frames_out (0) #ifdef HAVE_SWRESAMPLE @@ -77,22 +80,29 @@ Encoder::~Encoder () 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 ); @@ -118,7 +128,7 @@ Encoder::process_begin () } } - _writer.reset (new Writer (_film)); + _writer.reset (new Writer (_film, _job)); } @@ -126,9 +136,9 @@ void 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); @@ -142,7 +152,7 @@ Encoder::process_end () } out->set_frames (frames); - write_audio (out); + _writer->write (out); } swr_free (&_swr_context); @@ -193,7 +203,7 @@ Encoder::process_end () * 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) { @@ -231,9 +241,9 @@ Encoder::frame_done () } 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; @@ -269,7 +279,7 @@ Encoder::process_video (shared_ptr<const Image> image, bool same, boost::shared_ /* 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(), @@ -301,9 +311,9 @@ Encoder::process_audio (shared_ptr<const AudioBuffers> data) 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 ( @@ -321,7 +331,7 @@ Encoder::process_audio (shared_ptr<const AudioBuffers> data) } #endif - write_audio (data); + _writer->write (data); } void @@ -362,7 +372,7 @@ Encoder::encoder_thread (ServerDescription* server) } 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 (); @@ -416,34 +426,10 @@ Encoder::encoder_thread (ServerDescription* server) } 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); -} diff --git a/src/lib/encoder.h b/src/lib/encoder.h index 70e81a7e0..6cf5540c5 100644 --- a/src/lib/encoder.h +++ b/src/lib/encoder.h @@ -17,8 +17,8 @@ */ -#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. @@ -51,6 +51,7 @@ class ServerDescription; class DCPVideoFrame; class EncodedData; class Writer; +class Job; /** @class Encoder * @brief Encoder to J2K and WAV for DCP. @@ -62,11 +63,11 @@ class Writer; class Encoder : public VideoSink, public AudioSink { public: - Encoder (boost::shared_ptr<Film> f); + Encoder (boost::shared_ptr<Film> f, boost::shared_ptr<Job>); virtual ~Encoder (); /** Called to indicate that a processing run is about to begin */ - virtual void process_begin (); + void process_begin (); /** Call with a frame of video. * @param i Video frame image. @@ -79,22 +80,21 @@ public: void process_audio (boost::shared_ptr<const AudioBuffers>); /** Called when a processing run has finished */ - virtual void process_end (); + 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 (); /** Film that we are encoding */ boost::shared_ptr<Film> _film; + boost::shared_ptr<Job> _job; /** Mutex for _time_history and _last_frame */ mutable boost::mutex _history_mutex; @@ -106,7 +106,7 @@ private: 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; diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc index 4b30c9431..aad7f265e 100644 --- a/src/lib/examine_content_job.cc +++ b/src/lib/examine_content_job.cc @@ -17,29 +17,20 @@ */ -/** @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) { } @@ -51,60 +42,13 @@ ExamineContentJob::~ExamineContentJob () 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); } diff --git a/src/lib/examine_content_job.h b/src/lib/examine_content_job.h index 8ee4f0d60..dc0d53fff 100644 --- a/src/lib/examine_content_job.h +++ b/src/lib/examine_content_job.h @@ -17,22 +17,23 @@ */ -/** @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; }; diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index e45a62353..6bad7c924 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -17,8 +17,8 @@ */ -#ifndef DVDOMATIC_EXCEPTIONS_H -#define DVDOMATIC_EXCEPTIONS_H +#ifndef DCPOMATIC_EXCEPTIONS_H +#define DCPOMATIC_EXCEPTIONS_H /** @file src/exceptions.h * @brief Our exceptions. @@ -112,6 +112,7 @@ class OpenFileError : public FileError { public: /** @param f File that we were trying to open */ + /* XXX: should be boost::filesystem::path */ OpenFileError (std::string f); }; diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc new file mode 100644 index 000000000..a61f777c8 --- /dev/null +++ b/src/lib/ffmpeg_content.cc @@ -0,0 +1,288 @@ +/* + 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)); +} + +double +FFmpegContent::temporal_length () const +{ + return video_length() / video_frame_rate(); +} diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h new file mode 100644 index 000000000..6d7151498 --- /dev/null +++ b/src/lib/ffmpeg_content.h @@ -0,0 +1,129 @@ +/* + 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; + double temporal_length () 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 diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 8e09810cb..d5285b73a 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -41,7 +41,6 @@ extern "C" { #include "transcoder.h" #include "job.h" #include "filter.h" -#include "options.h" #include "exceptions.h" #include "image.h" #include "util.h" @@ -62,10 +61,13 @@ using boost::optional; 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) @@ -75,6 +77,9 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, DecodeOptions o) , _audio_codec (0) , _subtitle_codec_context (0) , _subtitle_codec (0) + , _decode_video (video) + , _decode_audio (audio) + , _decode_subtitles (subtitles) { setup_general (); setup_video (); @@ -84,10 +89,12 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, DecodeOptions o) 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); } @@ -106,15 +113,15 @@ FFmpegDecoder::setup_general () { av_register_all (); - if (avformat_open_input (&_format_context, _film->content_path().c_str(), 0, 0) < 0) { - throw OpenFileError (_film->content_path ()); + if (avformat_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]; @@ -131,17 +138,11 @@ FFmpegDecoder::setup_general () } _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)); } } @@ -158,6 +159,8 @@ FFmpegDecoder::setup_general () 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); @@ -173,14 +176,13 @@ FFmpegDecoder::setup_video () 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) { @@ -195,11 +197,13 @@ FFmpegDecoder::setup_audio () 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) { @@ -233,14 +237,14 @@ FFmpegDecoder::pass () /* 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 (); } @@ -249,9 +253,7 @@ FFmpegDecoder::pass () 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); @@ -264,9 +266,9 @@ FFmpegDecoder::pass () 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; @@ -298,19 +300,16 @@ FFmpegDecoder::pass () 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: @@ -322,7 +321,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) audio->data(channel)[sample] = float(*p++) / (1 << 15); ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@ -333,7 +332,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) 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); } @@ -350,7 +349,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) 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; } @@ -367,7 +366,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) audio->data(channel)[sample] = *p++; ++channel; - if (channel == _film->audio_channels()) { + if (channel == _ffmpeg_content->audio_channels()) { channel = 0; ++sample; } @@ -378,7 +377,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) 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)); } } @@ -392,7 +391,7 @@ FFmpegDecoder::deinterleave_audio (uint8_t** data, int size) } float -FFmpegDecoder::frames_per_second () const +FFmpegDecoder::video_frame_rate () const { AVStream* s = _format_context->streams[_video_stream]; @@ -483,21 +482,6 @@ FFmpegDecoder::bytes_per_audio_sample () const } 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 () { boost::mutex::scoped_lock lm (_filter_graphs_mutex); @@ -536,25 +520,23 @@ FFmpegDecoder::seek (double p) } 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 @@ -596,58 +578,6 @@ FFmpegDecoder::do_seek (double p, bool backwards, bool accurate) 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) { @@ -658,7 +588,6 @@ FFmpegDecoder::film_changed (Film::Property p) boost::mutex::scoped_lock lm (_filter_graphs_mutex); _filter_graphs.clear (); } - OutputChanged (); break; default: @@ -667,18 +596,15 @@ FFmpegDecoder::film_changed (Film::Property p) } /** @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. */ @@ -700,7 +626,7 @@ FFmpegDecoder::decode_audio_packet () 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); } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 0c89b973d..1e273752a 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -36,6 +36,7 @@ extern "C" { #include "video_decoder.h" #include "audio_decoder.h" #include "film.h" +#include "ffmpeg_content.h" struct AVFilterGraph; struct AVCodecContext; @@ -50,68 +51,46 @@ class Options; 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 (); @@ -129,6 +108,8 @@ private: std::string stream_name (AVStream* s) const; + boost::shared_ptr<const FFmpegContent> _ffmpeg_content; + AVFormatContext* _format_context; int _video_stream; @@ -145,4 +126,17 @@ private: 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; }; diff --git a/src/lib/film.cc b/src/lib/film.cc index 0ca374604..b8102d315 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -29,29 +29,31 @@ #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" @@ -67,6 +69,8 @@ using std::setfill; using std::min; using std::make_pair; using std::endl; +using std::cout; +using std::list; using boost::shared_ptr; using boost::lexical_cast; using boost::to_upper_copy; @@ -86,19 +90,18 @@ int const Film::state_version = 4; */ 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) @@ -106,10 +109,12 @@ Film::Film (string d, bool must_exist) , _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) @@ -139,8 +144,6 @@ Film::Film (string d, bool must_exist) } } - _sndfile_stream = SndfileStream::create (); - if (must_exist) { read_metadata (); } else { @@ -154,11 +157,11 @@ Film::Film (Film const & o) : 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) @@ -167,37 +170,20 @@ Film::Film (Film const & o) , _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 @@ -210,7 +196,7 @@ Film::video_state_identifier () const 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 @@ -218,7 +204,7 @@ Film::video_state_identifier () const << "_" << 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; } @@ -282,7 +268,7 @@ Film::audio_analysis_path () const { boost::filesystem::path p; p /= "analysis"; - p /= content_digest(); + p /= _playlist->audio_digest(); return file (p.string ()); } @@ -296,7 +282,7 @@ Film::make_dcp () throw BadSettingError (_("name"), _("cannot contain slashes")); } - log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary())); + log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary())); { char buffer[128]; @@ -304,18 +290,18 @@ Film::make_dcp () 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."); @@ -329,7 +315,7 @@ Film::make_dcp () throw MissingSettingError (_("format")); } - if (content().empty ()) { + if (_playlist->content().empty ()) { throw MissingSettingError (_("content")); } @@ -341,19 +327,16 @@ Film::make_dcp () 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 () { @@ -366,17 +349,12 @@ 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 @@ -391,12 +369,6 @@ Film::analyse_audio_finished () _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 () @@ -433,81 +405,57 @@ Film::write_metadata () const 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; - } - 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; + root->add_child("Format")->add_child_text (_format->id ()); } - 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; } @@ -519,165 +467,69 @@ Film::read_metadata () 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; } @@ -725,47 +577,18 @@ Film::file (string f) const 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 @@ -774,18 +597,12 @@ Film::target_audio_sample_rate () const */ 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 @@ -832,7 +649,7 @@ Film::dci_name (bool if_created_now) const } } - switch (audio_channels()) { + switch (audio_channels ()) { case 1: d << "_10"; break; @@ -911,110 +728,22 @@ Film::set_use_dci_name (bool u) } 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); + } } } @@ -1156,50 +885,13 @@ Film::set_trim_type (TrimType t) } 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 @@ -1223,26 +915,6 @@ Film::set_audio_delay (int d) } 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) { { @@ -1314,76 +986,6 @@ Film::set_dcp_frame_rate (int f) } 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) { { @@ -1391,20 +993,18 @@ 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 @@ -1413,16 +1013,6 @@ Film::set_dci_date_today () _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 { @@ -1478,20 +1068,144 @@ Film::have_dcp () 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)); +} + +shared_ptr<Playlist> +Film::playlist () const +{ + boost::mutex::scoped_lock lm (_state_mutex); + return _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 (); +} + +bool +Film::has_subtitles () const +{ + return _playlist->has_subtitles (); +} + +void +Film::set_audio_mapping (AudioMapping m) +{ + { + boost::mutex::scoped_lock lm (_state_mutex); + _audio_mapping = m; } - vector<string> const e = external_audio (); - for (vector<string>::const_iterator i = e.begin(); i != e.end(); ++i) { - if (!i->empty ()) { - return true; - } + 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)); } +} - return false; +void +Film::playlist_changed () +{ + signal_changed (CONTENT); +} + +int +Film::loop () const +{ + return _playlist->loop (); } +void +Film::set_loop (int c) +{ + _playlist->set_loop (c); +} diff --git a/src/lib/film.h b/src/lib/film.h index dd0a83d94..18255a15e 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -18,12 +18,12 @@ */ /** @file src/film.h - * @brief A representation of a piece of video (with sound), including naming, - * the source content file, and how it should be presented in a DCP. + * @brief A representation of some audio and video content, and details of + * how they should be presented in a DCP. */ -#ifndef DVDOMATIC_FILM_H -#define DVDOMATIC_FILM_H +#ifndef DCPOMATIC_FILM_H +#define DCPOMATIC_FILM_H #include <string> #include <vector> @@ -32,14 +32,13 @@ #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; @@ -47,19 +46,19 @@ class Log; 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; @@ -68,13 +67,12 @@ public: 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. @@ -89,13 +87,9 @@ public: 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; @@ -106,11 +100,31 @@ public: return _dirty; } + bool have_dcp () const; + + boost::shared_ptr<Player> player () const; + boost::shared_ptr<Playlist> playlist () 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; - void set_dci_date_today (); + bool has_subtitles () const; + + float video_frame_rate () const; + libdcp::Size video_size () const; + ContentVideoFrame video_length () const; + + ContentVideoFrame content_length () const; + + void set_loop (int); + int loop () const; - bool have_dcp () const; enum TrimType { CPL, @@ -124,8 +138,10 @@ public: 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, @@ -133,27 +149,18 @@ public: 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 }; @@ -174,14 +181,9 @@ public: 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 { @@ -224,26 +226,11 @@ public: 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; @@ -254,18 +241,6 @@ public: 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; @@ -300,51 +275,22 @@ public: 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); @@ -356,15 +302,10 @@ public: 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); @@ -372,17 +313,15 @@ public: 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 */ @@ -390,20 +329,21 @@ public: 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. */ @@ -411,19 +351,11 @@ private: /** 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.) */ @@ -443,22 +375,11 @@ private: 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 @@ -474,30 +395,13 @@ private: 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; diff --git a/src/lib/filter.h b/src/lib/filter.h index 205d92482..7587312c2 100644 --- a/src/lib/filter.h +++ b/src/lib/filter.h @@ -21,8 +21,8 @@ * @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> diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index 045cbaa6a..a52c030fe 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -57,7 +57,7 @@ using libdcp::Size; * @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) diff --git a/src/lib/filter_graph.h b/src/lib/filter_graph.h index 7e4e8422b..1ff5527ab 100644 --- a/src/lib/filter_graph.h +++ b/src/lib/filter_graph.h @@ -21,8 +21,8 @@ * @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" @@ -37,7 +37,7 @@ class FFmpegDecoder; 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); diff --git a/src/lib/format.cc b/src/lib/format.cc index 8c3d0d8ad..f5026c0da 100644 --- a/src/lib/format.cc +++ b/src/lib/format.cc @@ -29,6 +29,7 @@ #include <iostream> #include "format.h" #include "film.h" +#include "playlist.h" #include "i18n.h" @@ -59,13 +60,6 @@ FixedFormat::name () const 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 () @@ -164,16 +158,6 @@ Format::from_id (string i) 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 () @@ -224,7 +208,7 @@ VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d) 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; } diff --git a/src/lib/format.h b/src/lib/format.h index e95306232..d45a3a10a 100644 --- a/src/lib/format.h +++ b/src/lib/format.h @@ -41,7 +41,7 @@ public: /** @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 @@ -67,10 +67,7 @@ public: 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 (); diff --git a/src/lib/i18n.h b/src/lib/i18n.h index 46bb1d565..890313bc6 100644 --- a/src/lib/i18n.h +++ b/src/lib/i18n.h @@ -19,5 +19,5 @@ #include <libintl.h> -#define _(x) dgettext ("libdvdomatic", x) +#define _(x) dgettext ("libdcpomatic", x) #define N_(x) x diff --git a/src/lib/image.h b/src/lib/image.h index 62961a92e..de03d0e3f 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -21,8 +21,8 @@ * @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> diff --git a/src/lib/imagemagick_content.cc b/src/lib/imagemagick_content.cc new file mode 100644 index 000000000..9e5f00ba0 --- /dev/null +++ b/src/lib/imagemagick_content.cc @@ -0,0 +1,100 @@ +/* + 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); +} diff --git a/src/lib/imagemagick_content.h b/src/lib/imagemagick_content.h new file mode 100644 index 000000000..b1e7f9495 --- /dev/null +++ b/src/lib/imagemagick_content.h @@ -0,0 +1,45 @@ +/* + 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); +}; diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index 5ce22c296..dd18ad64e 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -20,6 +20,7 @@ #include <iostream> #include <boost/filesystem.hpp> #include <Magick++.h> +#include "imagemagick_content.h" #include "imagemagick_decoder.h" #include "image.h" #include "film.h" @@ -31,66 +32,57 @@ using std::cout; 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")); + if (!_native_size) { + using namespace MagickCore; + Magick::Image* image = new Magick::Image (_imagemagick_content->file().string()); + _native_size = libdcp::Size (image->columns(), image->rows()); + delete image; } - /* Look at the first file and assume its size holds for all */ - using namespace MagickCore; - Magick::Image* image = new Magick::Image (_film->content_path ()); - libdcp::Size const s = libdcp::Size (image->columns(), image->rows()); - delete image; + return _native_size.get (); +} - 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 (_imagemagick_content->file().string ()); + _native_size = libdcp::Size (magick_image->columns(), magick_image->rows()); - Magick::Image* magick_image = new Magick::Image (_film->content_path ()); - - libdcp::Size size = native_size (); - shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGB24, size, false)); + _image.reset (new SimpleImage (PIX_FMT_RGB24, _native_size.get(), false)); using namespace MagickCore; - uint8_t* p = image->data()[0]; - for (int y = 0; y < size.height; ++y) { - for (int x = 0; x < size.width; ++x) { + uint8_t* p = _image->data()[0]; + for (int y = 0; y < _native_size->height; ++y) { + for (int x = 0; x < _native_size->width; ++x) { Magick::Color c = magick_image->pixelColor (x, y); *p++ = c.redQuantum() * 255 / QuantumRange; *p++ = c.greenQuantum() * 255 / QuantumRange; @@ -100,11 +92,10 @@ ImageMagickDecoder::pass () delete magick_image; - _image = image->crop (_film->crop(), true); + _image = _image->crop (_film->crop(), true); + emit_video (_image, false, double (_position) / 24); - emit_video (_image, false, double (video_frame()) / frames_per_second()); - - ++_iter; + ++_position; return false; } @@ -116,43 +107,15 @@ ImageMagickDecoder::pixel_format () const } 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; } diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index 80a08f81f..cb68655ce 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -23,37 +23,28 @@ namespace Magick { 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; - } + libdcp::Size native_size () const; + ContentVideoFrame video_length () const; - int audio_sample_rate () const { - return 0; - } + bool seek (double); + bool pass (); - int64_t audio_channel_layout () const { - return 0; + boost::shared_ptr<const ImageMagickContent> content () const { + return _imagemagick_content; } - bool seek (double); - bool seek_to_last (); - protected: - bool pass (); PixelFormat pixel_format () const; int time_base_numerator () const { @@ -75,10 +66,8 @@ protected: } 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; + mutable boost::optional<libdcp::Size> _native_size; }; diff --git a/src/lib/job.cc b/src/lib/job.cc index 9a5812fa7..2e6385d62 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -35,8 +35,6 @@ using std::list; 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) @@ -94,7 +92,7 @@ Job::run_wrapper () 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 (...) { @@ -103,7 +101,7 @@ Job::run_wrapper () 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)") ); } @@ -200,7 +198,7 @@ Job::set_progress (float p) boost::this_thread::interruption_point (); if (paused ()) { - dvdomatic_sleep (1); + dcpomatic_sleep (1); } } diff --git a/src/lib/job.h b/src/lib/job.h index 37fa56d20..40e90b73c 100644 --- a/src/lib/job.h +++ b/src/lib/job.h @@ -21,8 +21,8 @@ * @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> @@ -38,7 +38,7 @@ class Film; 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 */ @@ -91,7 +91,6 @@ protected: void set_state (State); void set_error (std::string s, std::string d); - /** Film for this job */ boost::shared_ptr<Film> _film; private: diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc index 910597628..f96275467 100644 --- a/src/lib/job_manager.cc +++ b/src/lib/job_manager.cc @@ -126,7 +126,7 @@ JobManager::scheduler () } } - dvdomatic_sleep (1); + dcpomatic_sleep (1); } } diff --git a/src/lib/log.h b/src/lib/log.h index 3a2cfcbfd..3ad6516c1 100644 --- a/src/lib/log.h +++ b/src/lib/log.h @@ -17,8 +17,8 @@ */ -#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. diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 9924c003a..c56a56301 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -41,7 +41,7 @@ Matcher::Matcher (shared_ptr<Log> log, int sample_rate, float frames_per_second) } 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 (); @@ -90,7 +90,7 @@ Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost:: } 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 (); diff --git a/src/lib/player.cc b/src/lib/player.cc new file mode 100644 index 000000000..95036cfe0 --- /dev/null +++ b/src/lib/player.cc @@ -0,0 +1,329 @@ +/* + 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::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 () +{ + vector<shared_ptr<VideoDecoder> > old_video_decoders = _video_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; + + video_decoder->connect_video (shared_from_this ()); + } + + shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i); + if (ic) { + video_content = ic; + + /* See if we can re-use an old ImageMagickDecoder */ + for (vector<shared_ptr<VideoDecoder> >::const_iterator i = old_video_decoders.begin(); i != old_video_decoders.end(); ++i) { + shared_ptr<ImageMagickDecoder> imd = dynamic_pointer_cast<ImageMagickDecoder> (*i); + if (imd && imd->content() == ic) { + video_decoder = *i; + } + } + + if (!video_decoder) { + 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; +} diff --git a/src/lib/player.h b/src/lib/player.h new file mode 100644 index 000000000..b6fb41f6e --- /dev/null +++ b/src/lib/player.h @@ -0,0 +1,91 @@ +/* + 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 (); + 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 diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc new file mode 100644 index 000000000..f1dd881b3 --- /dev/null +++ b/src/lib/playlist.cc @@ -0,0 +1,432 @@ +/* + 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" + +#include "i18n.h" + +using std::list; +using std::cout; +using std::vector; +using std::min; +using std::max; +using std::string; +using std::stringstream; +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> (); +} + +bool +Playlist::has_subtitles () const +{ + shared_ptr<FFmpegContent> fc = ffmpeg (); + if (!fc) { + return false; + } + + return !fc->subtitle_streams().empty(); +} diff --git a/src/lib/playlist.h b/src/lib/playlist.h new file mode 100644 index 000000000..e6acff694 --- /dev/null +++ b/src/lib/playlist.h @@ -0,0 +1,132 @@ +/* + 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; + } + + bool has_subtitles () const; + + 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; +}; diff --git a/src/lib/po/es_ES.po b/src/lib/po/es_ES.po index 1608f3b0c..7d2f8511e 100644 --- a/src/lib/po/es_ES.po +++ b/src/lib/po/es_ES.po @@ -5,7 +5,7 @@ # 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" @@ -250,10 +250,10 @@ msgstr "Horizontal deblocking filter A" #: 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" diff --git a/src/lib/po/fr_FR.po b/src/lib/po/fr_FR.po index d1123d84b..7f3da788b 100644 --- a/src/lib/po/fr_FR.po +++ b/src/lib/po/fr_FR.po @@ -5,7 +5,7 @@ # 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" @@ -248,10 +248,10 @@ msgstr "Filtre dé-bloc horizontal" #: 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" diff --git a/src/lib/po/it_IT.po b/src/lib/po/it_IT.po index a3d35dec9..bd22412fc 100644 --- a/src/lib/po/it_IT.po +++ b/src/lib/po/it_IT.po @@ -247,10 +247,10 @@ msgstr "Filtro A sblocco orizzontale" #: 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" diff --git a/src/lib/po/sv_SE.po b/src/lib/po/sv_SE.po index 11aeff987..ff86e23af 100644 --- a/src/lib/po/sv_SE.po +++ b/src/lib/po/sv_SE.po @@ -5,7 +5,7 @@ # 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" @@ -248,10 +248,10 @@ msgstr "Filter för horisontal kantighetsutjämning A" #: 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" diff --git a/src/lib/processor.h b/src/lib/processor.h index 603239f8f..7b7735faa 100644 --- a/src/lib/processor.h +++ b/src/lib/processor.h @@ -21,8 +21,8 @@ * @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" diff --git a/src/lib/scaler.h b/src/lib/scaler.h index c80f4b7db..a736e92de 100644 --- a/src/lib/scaler.h +++ b/src/lib/scaler.h @@ -21,8 +21,8 @@ * @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> diff --git a/src/lib/scp_dcp_job.h b/src/lib/scp_dcp_job.h index 08d8e2c78..8c16d53fb 100644 --- a/src/lib/scp_dcp_job.h +++ b/src/lib/scp_dcp_job.h @@ -34,7 +34,7 @@ public: private: void set_status (std::string); - + mutable boost::mutex _status_mutex; std::string _status; }; diff --git a/src/lib/server.cc b/src/lib/server.cc index 9c5a77f68..ca0bec580 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -29,6 +29,7 @@ #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" @@ -51,6 +52,19 @@ using boost::bind; 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. @@ -68,15 +82,6 @@ ServerDescription::create_from_metadata (string v) 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) { diff --git a/src/lib/server.h b/src/lib/server.h index 89aeca626..398401a55 100644 --- a/src/lib/server.h +++ b/src/lib/server.h @@ -26,10 +26,15 @@ #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. */ @@ -44,6 +49,8 @@ public: , _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; @@ -62,7 +69,7 @@ public: _threads = t; } - std::string as_metadata () const; + void as_xml (xmlpp::Node *) const; static ServerDescription * create_from_metadata (std::string v); diff --git a/src/lib/sndfile_content.cc b/src/lib/sndfile_content.cc new file mode 100644 index 000000000..539b0dfb5 --- /dev/null +++ b/src/lib/sndfile_content.cc @@ -0,0 +1,122 @@ +/* + 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)); +} + diff --git a/src/lib/sndfile_content.h b/src/lib/sndfile_content.h new file mode 100644 index 000000000..e8e86b603 --- /dev/null +++ b/src/lib/sndfile_content.h @@ -0,0 +1,67 @@ +/* + 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; +}; diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index fdaf2eeaa..dd9e654c7 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -19,6 +19,7 @@ #include <iostream> #include <sndfile.h> +#include "sndfile_content.h" #include "sndfile_decoder.h" #include "film.h" #include "exceptions.h" @@ -27,70 +28,29 @@ 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) + , _deinterleave_buffer (0) { - _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")); - } + _done = 0; + _remaining = _info.frames; +} - if (info.channels != 1) { - throw DecodeError (_("external audio files must be mono")); - } - - _sndfiles.push_back (s); - - 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); + delete[] _deinterleave_buffer; } bool @@ -99,73 +59,56 @@ SndfileDecoder::pass () /* 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); + + int const channels = _sndfile_content->audio_channels (); + + shared_ptr<AudioBuffers> audio (new AudioBuffers (channels, this_time)); + + if (_sndfile_content->audio_channels() == 1) { + /* No de-interleaving required */ + sf_read_float (_sndfile, audio->data(0), this_time); + } else { + /* Deinterleave */ + if (!_deinterleave_buffer) { + _deinterleave_buffer = new float[block * channels]; + } + sf_readf_float (_sndfile, _deinterleave_buffer, this_time); + vector<float*> out_ptr (channels); + for (int i = 0; i < channels; ++i) { + out_ptr[i] = audio->data(i); + } + float* in_ptr = _deinterleave_buffer; + for (int i = 0; i < this_time; ++i) { + for (int j = 0; j < channels; ++j) { + *out_ptr[j]++ = *in_ptr++; + } } } - + 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; } diff --git a/src/lib/sndfile_decoder.h b/src/lib/sndfile_decoder.h index 9489cb5ec..64bd2f7f5 100644 --- a/src/lib/sndfile_decoder.h +++ b/src/lib/sndfile_decoder.h @@ -20,37 +20,26 @@ #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; + float* _deinterleave_buffer; }; diff --git a/src/lib/sound_processor.h b/src/lib/sound_processor.h index 2edf38840..bdbe72ba2 100644 --- a/src/lib/sound_processor.h +++ b/src/lib/sound_processor.h @@ -21,8 +21,8 @@ * @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> diff --git a/src/lib/stream.cc b/src/lib/stream.cc deleted file mode 100644 index bfe7b5eb4..000000000 --- a/src/lib/stream.cc +++ /dev/null @@ -1,92 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include <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); -} diff --git a/src/lib/stream.h b/src/lib/stream.h deleted file mode 100644 index 16b06e4bc..000000000 --- a/src/lib/stream.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -/** @file src/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 diff --git a/src/lib/timer.h b/src/lib/timer.h index f509a7492..173d0d961 100644 --- a/src/lib/timer.h +++ b/src/lib/timer.h @@ -22,8 +22,8 @@ * @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> diff --git a/src/lib/transcode_job.cc b/src/lib/transcode_job.cc index 234ebe051..0c3b8c37b 100644 --- a/src/lib/transcode_job.cc +++ b/src/lib/transcode_job.cc @@ -39,11 +39,9 @@ using std::setprecision; 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) { } @@ -62,9 +60,8 @@ TranscodeJob::run () _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); @@ -83,11 +80,11 @@ TranscodeJob::run () 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 (); } @@ -106,24 +103,28 @@ TranscodeJob::status () const 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; } diff --git a/src/lib/transcode_job.h b/src/lib/transcode_job.h index 9b69e4e65..7880a925e 100644 --- a/src/lib/transcode_job.h +++ b/src/lib/transcode_job.h @@ -23,9 +23,8 @@ #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. @@ -33,16 +32,14 @@ class Encoder; 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; }; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index faafcaf8b..f8fe0c8d5 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -28,15 +28,15 @@ #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" +#include "job.h" using std::string; using boost::shared_ptr; @@ -44,86 +44,47 @@ using boost::dynamic_pointer_cast; /** 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, j)) { - 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]) { - break; - } - } + while (!_player->pass ()) {} _delay_line->process_end (); if (_matcher) { @@ -132,3 +93,15 @@ Transcoder::go () _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 (); +} diff --git a/src/lib/transcoder.h b/src/lib/transcoder.h index f5b8ae6e3..48b453fe4 100644 --- a/src/lib/transcoder.h +++ b/src/lib/transcoder.h @@ -17,28 +17,25 @@ */ +#include "types.h" + /** @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. @@ -48,24 +45,19 @@ class Transcoder 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: +private: /** 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; diff --git a/src/lib/types.cc b/src/lib/types.cc new file mode 100644 index 000000000..1e0f48327 --- /dev/null +++ b/src/lib/types.cc @@ -0,0 +1,31 @@ +/* + 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); +} + diff --git a/src/lib/types.h b/src/lib/types.h new file mode 100644 index 000000000..f9e9b2f4b --- /dev/null +++ b/src/lib/types.h @@ -0,0 +1,110 @@ +/* + 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; +typedef double Time; + +/** @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 diff --git a/src/lib/ui_signaller.h b/src/lib/ui_signaller.h index 221bcbe95..428ab698f 100644 --- a/src/lib/ui_signaller.h +++ b/src/lib/ui_signaller.h @@ -17,8 +17,8 @@ */ -#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> diff --git a/src/lib/util.cc b/src/lib/util.cc index be078a95f..6c8166143 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -27,7 +27,7 @@ #include <iostream> #include <fstream> #include <climits> -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX #include <execinfo.h> #include <cxxabi.h> #endif @@ -63,19 +63,25 @@ extern "C" { #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; @@ -143,7 +149,7 @@ seconds_to_approximate_hms (int s) return ap.str (); } -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX /** @param l Mangled C++ identifier. * @return Demangled version. */ @@ -242,11 +248,11 @@ seconds (struct timeval t) 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 (); @@ -259,7 +265,7 @@ dvdomatic_setup () ui_thread = boost::this_thread::get_id (); } -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS boost::filesystem::path mo_path () { @@ -274,9 +280,9 @@ mo_path () #endif void -dvdomatic_setup_gettext_i18n (string lang) +dcpomatic_setup_gettext_i18n (string lang) { -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX lang += ".UTF8"; #endif @@ -292,15 +298,15 @@ dvdomatic_setup_gettext_i18n (string lang) } 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 } @@ -361,11 +367,11 @@ md5_digest (void const * data, int size) * @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); @@ -490,16 +496,6 @@ dcp_audio_sample_rate (int fs) 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. */ @@ -889,6 +885,21 @@ AudioBuffers::move (int from, int to, int frames) } } +/** 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 () @@ -896,30 +907,17 @@ 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 () @@ -927,7 +925,7 @@ 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; @@ -966,58 +964,6 @@ audio_channel_name (int c) 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) diff --git a/src/lib/util.h b/src/lib/util.h index d0c350a56..51ccbba99 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -22,8 +22,8 @@ * @brief Some utility functions and classes. */ -#ifndef DVDOMATIC_UTIL_H -#define DVDOMATIC_UTIL_H +#ifndef DCPOMATIC_UTIL_H +#define DCPOMATIC_UTIL_H #include <string> #include <vector> @@ -37,8 +37,9 @@ extern "C" { #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(...) @@ -54,19 +55,17 @@ extern std::string seconds_to_approximate_hms (int); extern void stacktrace (std::ostream &, int); extern std::string dependency_version_summary (); extern double seconds (struct timeval); -extern void dvdomatic_setup (); -extern void dvdomatic_setup_gettext_i18n (std::string); +extern void dcpomatic_setup (); +extern void dcpomatic_setup_gettext_i18n (std::string); extern std::vector<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); @@ -104,87 +103,6 @@ struct FrameRateConversion 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); @@ -199,7 +117,7 @@ extern std::string get_optional_string (std::multimap<std::string, std::string> /** @class Socket * @brief A class to wrap a boost::asio::ip::tcp::socket with some things - * that are useful for DVD-o-matic. + * that are useful for DCP-o-matic. * * This class wraps some things that I could not work out how to do with boost; * most notably, sync read/write calls with timeouts. @@ -265,6 +183,7 @@ public: 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 */ @@ -277,21 +196,7 @@ private: 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 diff --git a/src/lib/version.h b/src/lib/version.h index 71639e3bc..518862fc4 100644 --- a/src/lib/version.h +++ b/src/lib/version.h @@ -1,3 +1,3 @@ -extern char const * dvdomatic_version; -extern char const * dvdomatic_git_commit; +extern char const * dcpomatic_version; +extern char const * dcpomatic_git_commit; diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc new file mode 100644 index 000000000..2af6ba908 --- /dev/null +++ b/src/lib/video_content.cc @@ -0,0 +1,112 @@ +/* + 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 (); +} + +Time +VideoContent::temporal_length () const +{ + return video_length() / video_frame_rate(); +} diff --git a/src/lib/video_content.h b/src/lib/video_content.h new file mode 100644 index 000000000..b2ec87e2b --- /dev/null +++ b/src/lib/video_content.h @@ -0,0 +1,72 @@ +/* + 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; + Time temporal_length () 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 diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 16a076698..fd8238441 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -22,7 +22,6 @@ #include "film.h" #include "image.h" #include "log.h" -#include "options.h" #include "job.h" #include "i18n.h" @@ -31,10 +30,10 @@ using std::cout; 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) { } @@ -52,10 +51,11 @@ VideoDecoder::emit_video (shared_ptr<Image> image, bool same, double t) 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 @@ -74,21 +74,12 @@ VideoDecoder::emit_subtitle (shared_ptr<TimedSubtitle> s) } } -/** 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()); } } diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 6e4fd48c0..0b05b2f71 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -17,48 +17,39 @@ */ -#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: @@ -68,14 +59,9 @@ 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; }; diff --git a/src/lib/video_sink.h b/src/lib/video_sink.h index 0170c7350..6239bc557 100644 --- a/src/lib/video_sink.h +++ b/src/lib/video_sink.h @@ -17,8 +17,8 @@ */ -#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" diff --git a/src/lib/video_source.cc b/src/lib/video_source.cc index 539243402..4d505f9fe 100644 --- a/src/lib/video_source.cc +++ b/src/lib/video_source.cc @@ -21,24 +21,44 @@ #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)); +} diff --git a/src/lib/video_source.h b/src/lib/video_source.h index 748cb6fe9..9b4c9b4a2 100644 --- a/src/lib/video_source.h +++ b/src/lib/video_source.h @@ -21,8 +21,8 @@ * @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> diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 177e929ae..c7d2cf8b4 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -22,6 +22,7 @@ #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" @@ -29,7 +30,11 @@ #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 "job.h" #include "i18n.h" @@ -43,8 +48,9 @@ using boost::shared_ptr; int const Writer::_maximum_frames_in_memory = 8; -Writer::Writer (shared_ptr<Film> f) +Writer::Writer (shared_ptr<Film> f, shared_ptr<Job> j) : _film (f) + , _job (j) , _first_nonexistant_frame (0) , _thread (0) , _finish (false) @@ -76,16 +82,14 @@ Writer::Writer (shared_ptr<Film> f) _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()) ) ); @@ -200,6 +204,10 @@ try } } lock.lock (); + + if (_film->video_length ()) { + _job->set_progress (float(_full_written + _fake_written + _repeat_written) / _film->video_length()); + } ++_last_written_frame; } diff --git a/src/lib/writer.h b/src/lib/writer.h index beb16c7b9..62714edf3 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -26,6 +26,7 @@ class Film; class EncodedData; class AudioBuffers; +class Job; namespace libdcp { class MonoPictureAsset; @@ -63,7 +64,7 @@ bool operator== (QueueItem const & a, QueueItem const & b); class Writer : public ExceptionStore { public: - Writer (boost::shared_ptr<Film>); + Writer (boost::shared_ptr<Film>, boost::shared_ptr<Job>); bool can_fake_write (int) const; @@ -80,6 +81,7 @@ private: /** our Film */ boost::shared_ptr<Film> _film; + boost::shared_ptr<Job> _job; /** the first frame index that does not already exist in our MXF */ int _first_nonexistant_frame; diff --git a/src/lib/wscript b/src/lib/wscript index 51b103afd..e53ac5a84 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -6,16 +6,18 @@ sources = """ 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 @@ -23,31 +25,37 @@ sources = """ 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 @@ -59,22 +67,24 @@ def build(bld): else: obj = bld(features = 'cxx cxxshlib') - obj.name = 'libdvdomatic' + obj.name = 'libdcpomatic' obj.export_includes = ['.'] obj.uselib = """ AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 - SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP GLIB LZMA + SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA """ 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') diff --git a/src/tools/dvdomatic.cc b/src/tools/dcpomatic.cc index ff1560f0e..71c29072f 100644 --- a/src/tools/dvdomatic.cc +++ b/src/tools/dcpomatic.cc @@ -61,6 +61,7 @@ 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 void set_menu_sensitivity (); @@ -237,9 +238,6 @@ public: 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 ()); @@ -285,7 +283,7 @@ private: void file_changed (string f) { stringstream s; - s << wx_to_std (_("DVD-o-matic")); + s << wx_to_std (_("DCP-o-matic")); if (!f.empty ()) { s << " - " << f; } @@ -416,11 +414,11 @@ private: 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))); + 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", dvdomatic_version, dvdomatic_git_commit))); + 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")); @@ -441,7 +439,7 @@ private: translators.Add (wxT ("Adam Klotblixt")); info.SetTranslators (translators); - info.SetWebSite (wxT ("http://carlh.net/software/dvdomatic")); + info.SetWebSite (wxT ("http://carlh.net/software/dcpomatic")); wxAboutBox (info); } @@ -451,13 +449,15 @@ private: #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_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_PARAM, 0, 0, wxT("film to load"), wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_MULTIPLE | 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 @@ -470,7 +470,7 @@ class App : public wxApp return false; } -#ifdef DVDOMATIC_POSIX +#ifdef DCPOMATIC_POSIX unsetenv ("UBUNTU_MENUPROXY"); #endif @@ -478,16 +478,16 @@ class App : public wxApp /* 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 + object will be wrong, however, because dcpomatic_setup hasn't yet been called and there aren't any scalers, filters etc. set up yet. */ - dvdomatic_setup_i18n (); + dcpomatic_setup_i18n (); /* Set things up, including scalers / filters etc. which will now be internationalised correctly. */ - dvdomatic_setup (); + dcpomatic_setup (); /* Force the configuration to be re-loaded correctly next time it is needed. @@ -503,7 +503,13 @@ class App : public wxApp } } - Frame* f = new Frame (_("DVD-o-matic")); + 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 (); @@ -523,11 +529,15 @@ class App : public wxApp bool OnCmdLineParsed (wxCmdLineParser& parser) { if (parser.GetParamCount() > 0) { - film_to_load = wx_to_std (parser.GetParam(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)) { + if (parser.Found (wxT ("log"), &log)) { log_level = wx_to_std (log); } diff --git a/src/tools/makedcp.cc b/src/tools/dcpomatic_cli.cc index e73930d3c..d4a4210de 100644 --- a/src/tools/makedcp.cc +++ b/src/tools/dcpomatic_cli.cc @@ -46,9 +46,9 @@ static void help (string n) { cerr << "Syntax: " << n << " [OPTION] <FILM>\n" - << " -v, --version show DVD-o-matic version\n" + << " -v, --version show DCP-o-matic version\n" << " -h, --help show this help\n" - << " -d, --deps list DVD-o-matic dependency details and quit\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" @@ -83,7 +83,7 @@ main (int argc, char* argv[]) switch (c) { case 'v': - cout << "dvdomatic version " << dvdomatic_version << " " << dvdomatic_git_commit << "\n"; + cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; exit (EXIT_SUCCESS); case 'h': help (argv[0]); @@ -110,13 +110,13 @@ main (int argc, char* argv[]) film_dir = argv[optind]; - dvdomatic_setup (); + dcpomatic_setup (); if (no_remote) { Config::instance()->set_servers (vector<ServerDescription*> ()); } - cout << "DVD-o-matic " << dvdomatic_version << " git " << dvdomatic_git_commit; + cout << "DCP-o-matic " << dcpomatic_version << " git " << dcpomatic_git_commit; char buf[256]; if (gethostname (buf, 256) == 0) { cout << " on " << buf; @@ -134,11 +134,11 @@ main (int argc, char* argv[]) film->log()->set_level ((Log::Level) log_level); cout << "\nMaking "; - if (film->dcp_ab()) { + if (film->ab()) { cout << "A/B "; } cout << "DCP for " << film->name() << "\n"; - cout << "Content: " << film->content() << "\n"; +// cout << "Content: " << film->content() << "\n"; pair<string, string> const f = Filter::ffmpeg_strings (film->filters ()); cout << "Filters: " << f.first << " " << f.second << "\n"; @@ -149,7 +149,7 @@ main (int argc, char* argv[]) bool error = false; while (!should_stop) { - dvdomatic_sleep (5); + dcpomatic_sleep (5); list<shared_ptr<Job> > jobs = JobManager::instance()->get (); diff --git a/src/tools/servomatic_gui.cc b/src/tools/dcpomatic_server.cc index 5e36660eb..152e063c1 100644 --- a/src/tools/servomatic_gui.cc +++ b/src/tools/dcpomatic_server.cc @@ -61,7 +61,7 @@ class StatusDialog : public wxDialog { public: StatusDialog () - : wxDialog (0, wxID_ANY, _("DVD-o-matic encode server"), wxDefaultPosition, wxSize (600, 80), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + : 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); @@ -103,7 +103,7 @@ public: wxIcon icon; icon.CopyFromBitmap (bitmap); #endif - SetIcon (icon, std_to_wx ("DVD-o-matic encode server")); + 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)); @@ -147,7 +147,7 @@ private: return false; } - dvdomatic_setup (); + dcpomatic_setup (); _icon = new TaskBarIcon; _thread = new thread (bind (&App::main_thread, this)); diff --git a/src/tools/servomatic_cli.cc b/src/tools/dcpomatic_server_cli.cc index 6626d45b9..76d085034 100644 --- a/src/tools/servomatic_cli.cc +++ b/src/tools/dcpomatic_server_cli.cc @@ -51,7 +51,7 @@ static void help (string n) { cerr << "Syntax: " << n << " [OPTION]\n" - << " -v, --version show DVD-o-matic version\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"; } @@ -78,7 +78,7 @@ main (int argc, char* argv[]) switch (c) { case 'v': - cout << "dvdomatic version " << dvdomatic_version << " " << dvdomatic_git_commit << "\n"; + cout << "dcpomatic version " << dcpomatic_version << " " << dcpomatic_git_commit << "\n"; exit (EXIT_SUCCESS); case 'h': help (argv[0]); diff --git a/src/tools/dvdomatic_batch.cc b/src/tools/dvdomatic_batch.cc deleted file mode 100644 index 7a3d38d9c..000000000 --- a/src/tools/dvdomatic_batch.cc +++ /dev/null @@ -1,239 +0,0 @@ -/* - Copyright (C) 2013 Carl Hetherington <cth@carlh.net> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include <wx/aboutdlg.h> -#include <wx/stdpaths.h> -#include <wx/wx.h> -#include "lib/version.h" -#include "lib/compose.hpp" -#include "lib/config.h" -#include "lib/util.h" -#include "lib/film.h" -#include "lib/job_manager.h" -#include "wx/wx_util.h" -#include "wx/wx_ui_signaller.h" -#include "wx/job_manager_view.h" - -using boost::shared_ptr; - -enum { - ID_file_add_film = 1, - ID_file_quit, - ID_help_about -}; - -void -setup_menu (wxMenuBar* m) -{ - wxMenu* file = new wxMenu; - file->Append (ID_file_add_film, _("&Add Film...")); - file->Append (ID_file_quit, _("&Quit")); - - wxMenu* help = new wxMenu; - help->Append (ID_help_about, _("About")); - - m->Append (file, _("&File")); - m->Append (help, _("&Help")); -} - -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_add_film, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_add_film)); - Connect (ID_file_quit, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::file_quit)); - Connect (ID_help_about, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler (Frame::help_about)); - - wxPanel* panel = new wxPanel (this); - wxSizer* s = new wxBoxSizer (wxHORIZONTAL); - s->Add (panel, 1, wxEXPAND); - SetSizer (s); - - wxSizer* sizer = new wxBoxSizer (wxVERTICAL); - - JobManagerView* job_manager_view = new JobManagerView (panel, JobManagerView::PAUSE); - sizer->Add (job_manager_view, 1, wxALL | wxEXPAND, 6); - - wxSizer* buttons = new wxBoxSizer (wxHORIZONTAL); - wxButton* add = new wxButton (panel, wxID_ANY, _("Add Film...")); - add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (Frame::add_film)); - buttons->Add (add, 1, wxALL, 6); - - sizer->Add (buttons, 0, wxALL, 6); - - panel->SetSizer (sizer); - - Connect (wxID_ANY, wxEVT_CLOSE_WINDOW, wxCloseEventHandler (Frame::close)); - } - -private: - bool should_close () - { - if (!JobManager::instance()->work_to_do ()) { - return true; - } - - wxMessageDialog* d = new wxMessageDialog ( - 0, - _("There are unfinished jobs; are you sure you want to quit?"), - _("Unfinished jobs"), - wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION - ); - - bool const r = d->ShowModal() == wxID_YES; - d->Destroy (); - return r; - } - - void close (wxCloseEvent& ev) - { - if (!should_close ()) { - ev.Veto (); - return; - } - - ev.Skip (); - } - - void file_add_film (wxCommandEvent& ev) - { - add_film (ev); - } - - void file_quit (wxCommandEvent &) - { - if (should_close ()) { - Close (true); - } - } - - void help_about (wxCommandEvent &) - { - wxAboutDialogInfo info; - info.SetName (_("DVD-o-matic Batch Converter")); - 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); - } - - void add_film (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) { - try { - shared_ptr<Film> film (new Film (wx_to_std (c->GetPath ()))); - film->make_dcp (); - } 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 (); - } -}; - -class App : public wxApp -{ - bool OnInit () - { - if (!wxApp::OnInit()) { - return false; - } - -#ifdef DVDOMATIC_POSIX - unsetenv ("UBUNTU_MENUPROXY"); -#endif - - /* 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. - */ - dvdomatic_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 (); - - Frame* f = new Frame (_("DVD-o-matic Batch Converter")); - SetTopWindow (f); - f->Maximize (); - f->Show (); - - ui_signaller = new wxUISignaller (this); - this->Connect (-1, wxEVT_IDLE, wxIdleEventHandler (App::idle)); - - return true; - } - - void idle (wxIdleEvent &) - { - ui_signaller->ui_idle (); - } -}; - -IMPLEMENT_APP (App) diff --git a/src/tools/po/es_ES.po b/src/tools/po/es_ES.po index 1739f97cd..346aa2b39 100644 --- a/src/tools/po/es_ES.po +++ b/src/tools/po/es_ES.po @@ -5,7 +5,7 @@ # 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" @@ -17,112 +17,111 @@ msgstr "" "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." diff --git a/src/tools/po/fr_FR.po b/src/tools/po/fr_FR.po index c1447f7e6..8ce305ccf 100644 --- a/src/tools/po/fr_FR.po +++ b/src/tools/po/fr_FR.po @@ -5,7 +5,7 @@ # 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" @@ -16,110 +16,109 @@ msgstr "" "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." diff --git a/src/tools/po/it_IT.po b/src/tools/po/it_IT.po index f0984946d..b4c1a6e66 100644 --- a/src/tools/po/it_IT.po +++ b/src/tools/po/it_IT.po @@ -17,108 +17,107 @@ msgstr "" "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." diff --git a/src/tools/po/sv_SE.po b/src/tools/po/sv_SE.po index 8ae68853f..69706d647 100644 --- a/src/tools/po/sv_SE.po +++ b/src/tools/po/sv_SE.po @@ -5,7 +5,7 @@ # 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" @@ -17,109 +17,108 @@ msgstr "" "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." diff --git a/src/tools/servomatictest.cc b/src/tools/servomatictest.cc index 5e1cf49b4..af176ac18 100644 --- a/src/tools/servomatictest.cc +++ b/src/tools/servomatictest.cc @@ -28,13 +28,12 @@ #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; @@ -146,22 +145,19 @@ main (int argc, char* argv[]) 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"; diff --git a/src/tools/wscript b/src/tools/wscript index f36f0abef..cddd07b7d 100644 --- a/src/tools/wscript +++ b/src/tools/wscript @@ -4,29 +4,29 @@ from waflib import Logs 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', 'dvdomatic_batch', 'servomatic_gui']: + for t in ['dcpomatic', 'dcpomatic_batch', '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') diff --git a/src/wx/audio_dialog.cc b/src/wx/audio_dialog.cc index d12b5516f..d1d13ab78 100644 --- a/src/wx/audio_dialog.cc +++ b/src/wx/audio_dialog.cc @@ -18,10 +18,10 @@ */ #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; @@ -43,7 +43,6 @@ AudioDialog::AudioDialog (wxWindow* parent) 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))); @@ -84,40 +83,26 @@ AudioDialog::AudioDialog (wxWindow* parent) } 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 () @@ -134,12 +119,10 @@ 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); @@ -157,11 +140,7 @@ AudioDialog::channel_clicked (wxCommandEvent& ev) 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 @@ -171,11 +150,6 @@ AudioDialog::film_changed (Film::Property p) case Film::AUDIO_GAIN: _plot->set_gain (_film->audio_gain ()); break; - case Film::CONTENT_AUDIO_STREAM: - case Film::EXTERNAL_AUDIO: - case Film::USE_CONTENT_AUDIO: - setup_channels (); - break; default: break; } diff --git a/src/wx/audio_dialog.h b/src/wx/audio_dialog.h index 514faeea0..db1d74f30 100644 --- a/src/wx/audio_dialog.h +++ b/src/wx/audio_dialog.h @@ -39,7 +39,6 @@ private: void type_clicked (wxCommandEvent &); void smoothing_changed (wxScrollEvent &); void try_to_load_analysis (); - void setup_channels (); boost::shared_ptr<Film> _film; AudioPlot* _plot; diff --git a/src/wx/audio_mapping_view.cc b/src/wx/audio_mapping_view.cc new file mode 100644 index 000000000..093d25b7c --- /dev/null +++ b/src/wx/audio_mapping_view.cc @@ -0,0 +1,167 @@ +/* + 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) + { +#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9 + dc.SetPen (*wxThePenList->FindOrCreatePen (wxColour (255, 255, 255), 0, wxPENSTYLE_SOLID)); +#else + dc.SetPen (*wxThePenList->FindOrCreatePen (wxColour (255, 255, 255), 0, wxSOLID)); +#endif + dc.DrawRectangle (rect); + + 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 (); +} + diff --git a/src/lib/options.h b/src/wx/audio_mapping_view.h index 0d2c07fd5..36429412f 100644 --- a/src/lib/options.h +++ b/src/wx/audio_mapping_view.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + 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 @@ -17,27 +17,19 @@ */ -#ifndef DVDOMATIC_OPTIONS_H -#define DVDOMATIC_OPTIONS_H +#include <boost/signals2.hpp> +#include <wx/wx.h> +#include <wx/grid.h> -/** @file src/options.h - * @brief Options for a decoding operation. - */ - -class DecodeOptions +class AudioMappingView : public wxPanel { 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; -}; + AudioMappingView (wxWindow *); + + void set_mapping (AudioMapping); -#endif +private: + void left_click (wxGridEvent &); + + wxGrid* _grid; +}; diff --git a/src/wx/audio_plot.cc b/src/wx/audio_plot.cc index cf44eb69f..2a6210164 100644 --- a/src/wx/audio_plot.cc +++ b/src/wx/audio_plot.cc @@ -21,7 +21,6 @@ #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" diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index 98657b666..50b8990b1 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -108,6 +108,11 @@ ConfigDialog::make_misc_panel () 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); @@ -157,6 +162,10 @@ ConfigDialog::make_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); @@ -227,33 +236,6 @@ ConfigDialog::make_tms_panel () } 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 () { _ab_panel = new wxPanel (_notebook); @@ -278,21 +260,39 @@ 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 @@ -527,6 +527,12 @@ ConfigDialog::setup_language_sensitivity () } void +ConfigDialog::default_still_length_changed (wxCommandEvent &) +{ + Config::instance()->set_default_still_length (_default_still_length->GetValue ()); +} + +void ConfigDialog::default_format_changed (wxCommandEvent &) { vector<Format const *> fmt = Format::all (); diff --git a/src/wx/config_dialog.h b/src/wx/config_dialog.h index 526480912..5df1a634f 100644 --- a/src/wx/config_dialog.h +++ b/src/wx/config_dialog.h @@ -47,6 +47,7 @@ private: void tms_user_changed (wxCommandEvent &); void tms_password_changed (wxCommandEvent &); void num_local_encoding_threads_changed (wxCommandEvent &); + void default_still_length_changed (wxCommandEvent &); void default_directory_changed (wxCommandEvent &); void edit_default_dci_metadata_clicked (wxCommandEvent &); void reference_scaler_changed (wxCommandEvent &); @@ -85,6 +86,7 @@ private: wxTextCtrl* _tms_user; wxTextCtrl* _tms_password; wxSpinCtrl* _num_local_encoding_threads; + wxSpinCtrl* _default_still_length; #ifdef __WXMSW__ DirPickerCtrl* _default_directory; #else diff --git a/src/wx/ffmpeg_content_dialog.cc b/src/wx/ffmpeg_content_dialog.cc new file mode 100644 index 000000000..8adff59f4 --- /dev/null +++ b/src/wx/ffmpeg_content_dialog.cc @@ -0,0 +1,141 @@ +/* + 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 "lib/ffmpeg_content.h" +#include "ffmpeg_content_dialog.h" +#include "wx_util.h" + +using std::vector; +using std::string; +using boost::shared_ptr; +using boost::lexical_cast; + +FFmpegContentDialog::FFmpegContentDialog (wxWindow* parent, shared_ptr<FFmpegContent> content) + : wxDialog (parent, wxID_ANY, _("Video")) +{ + wxFlexGridSizer* grid = new wxFlexGridSizer (3, 6, 6); + grid->AddGrowableCol (1, 1); + + add_label_to_sizer (grid, this, _("Audio Stream")); + _audio_stream = new wxChoice (this, wxID_ANY); + grid->Add (_audio_stream, 1, wxEXPAND | wxALL, 6); + _audio_description = new wxStaticText (this, wxID_ANY, wxT ("")); + grid->Add (_audio_description, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8); + + add_label_to_sizer (grid, this, _("Subtitle stream")); + _subtitle_stream = new wxChoice (this, wxID_ANY); + grid->Add (_subtitle_stream, 1, wxEXPAND | wxALL, 6); + grid->AddSpacer (0); + + _audio_stream->Clear (); + vector<FFmpegAudioStream> a = content->audio_streams (); + for (vector<FFmpegAudioStream>::iterator i = a.begin(); i != a.end(); ++i) { + _audio_stream->Append (std_to_wx (i->name), new wxStringClientData (std_to_wx (lexical_cast<string> (i->id)))); + } + + if (content->audio_stream()) { + checked_set (_audio_stream, lexical_cast<string> (content->audio_stream()->id)); + } + + _subtitle_stream->Clear (); + vector<FFmpegSubtitleStream> s = content->subtitle_streams (); + if (s.empty ()) { + _subtitle_stream->Enable (false); + } + for (vector<FFmpegSubtitleStream>::iterator i = s.begin(); i != s.end(); ++i) { + _subtitle_stream->Append (std_to_wx (i->name), new wxStringClientData (std_to_wx (lexical_cast<string> (i->id)))); + } + + if (content->subtitle_stream()) { + checked_set (_subtitle_stream, lexical_cast<string> (content->subtitle_stream()->id)); + } else { + _subtitle_stream->SetSelection (wxNOT_FOUND); + } + + 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); + + _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FFmpegContentDialog::audio_stream_changed), 0, this); + _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FFmpegContentDialog::subtitle_stream_changed), 0, this); +} + +void +FFmpegContentDialog::audio_stream_changed (wxCommandEvent &) +{ + shared_ptr<FFmpegContent> c = _content.lock (); + if (!c) { + return; + } + + vector<FFmpegAudioStream> a = c->audio_streams (); + vector<FFmpegAudioStream>::iterator i = a.begin (); + string const s = string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())); + while (i != a.end() && lexical_cast<string> (i->id) != s) { + ++i; + } + + if (i != a.end ()) { + c->set_audio_stream (*i); + } + + if (!c->audio_stream ()) { + _audio_description->SetLabel (wxT ("")); + } else { + wxString s; + if (c->audio_channels() == 1) { + s << _("1 channel"); + } else { + s << c->audio_channels() << wxT (" ") << _("channels"); + } + s << wxT (", ") << c->audio_frame_rate() << _("Hz"); + _audio_description->SetLabel (s); + } +} + + + +void +FFmpegContentDialog::subtitle_stream_changed (wxCommandEvent &) +{ + shared_ptr<FFmpegContent> c = _content.lock (); + if (!c) { + return; + } + + vector<FFmpegSubtitleStream> a = c->subtitle_streams (); + vector<FFmpegSubtitleStream>::iterator i = a.begin (); + string const s = string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())); + while (i != a.end() && lexical_cast<string> (i->id) != s) { + ++i; + } + + if (i != a.end ()) { + c->set_subtitle_stream (*i); + } +} diff --git a/src/wx/ffmpeg_content_dialog.h b/src/wx/ffmpeg_content_dialog.h new file mode 100644 index 000000000..5251ad5da --- /dev/null +++ b/src/wx/ffmpeg_content_dialog.h @@ -0,0 +1,40 @@ +/* + 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 <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> + +class wxSpinCtrl; +class FFmpegContent; + +class FFmpegContentDialog : public wxDialog +{ +public: + FFmpegContentDialog (wxWindow *, boost::shared_ptr<FFmpegContent>); + +private: + void audio_stream_changed (wxCommandEvent &); + void subtitle_stream_changed (wxCommandEvent &); + + boost::weak_ptr<FFmpegContent> _content; + wxChoice* _audio_stream; + wxStaticText* _audio_description; + wxChoice* _subtitle_stream; +}; diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index a21782a6f..884904e51 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -25,6 +25,7 @@ #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> @@ -37,6 +38,9 @@ #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" @@ -45,6 +49,10 @@ #include "dci_metadata_dialog.h" #include "scaler.h" #include "audio_dialog.h" +#include "imagemagick_content_dialog.h" +#include "ffmpeg_content_dialog.h" +#include "audio_mapping_view.h" +#include "timeline_dialog.h" using std::string; using std::cout; @@ -54,23 +62,27 @@ using std::fixed; using std::setprecision; using std::list; using std::vector; +using std::max; using boost::shared_ptr; +using boost::weak_ptr; using boost::dynamic_pointer_cast; +using boost::lexical_cast; /** @param f Film to edit */ FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent) : wxPanel (parent) - , _film (f) , _generally_sensitive (true) , _audio_dialog (0) + , _timeline_dialog (0) { wxBoxSizer* s = new wxBoxSizer (wxVERTICAL); - SetSizer (s); _notebook = new wxNotebook (this, wxID_ANY); s->Add (_notebook, 1); make_film_panel (); _notebook->AddPage (_film_panel, _("Film"), true); + make_content_panel (); + _notebook->AddPage (_content_panel, _("Content"), false); make_video_panel (); _notebook->AddPage (_video_panel, _("Video"), false); make_audio_panel (); @@ -78,15 +90,16 @@ FilmEditor::FilmEditor (shared_ptr<Film> f, wxWindow* parent) make_subtitle_panel (); _notebook->AddPage (_subtitle_panel, _("Subtitles"), false); - set_film (_film); + setup_formats (); + + set_film (f); connect_to_widgets (); JobManager::instance()->ActiveJobsChanged.connect ( bind (&FilmEditor::active_jobs_changed, this, _1) ); - setup_visibility (); - setup_formats (); + SetSizerAndFit (s); } void @@ -117,14 +130,8 @@ FilmEditor::make_film_panel () 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)); @@ -132,11 +139,6 @@ FilmEditor::make_film_panel () 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); @@ -149,54 +151,40 @@ FilmEditor::make_film_panel () ++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); - ++r; - - _dcp_ab = new wxCheckBox (_film_panel, wxID_ANY, _("A/B")); - video_control (_dcp_ab); - grid->Add (_dcp_ab, wxGBPosition (r, 0)); + grid->Add (_trim_type, wxGBPosition (r, 1), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); ++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 (); @@ -216,46 +204,46 @@ FilmEditor::make_film_panel () 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); + _timeline_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::timeline_clicked), 0, this); + _loop_content->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::loop_content_toggled), 0, this); + _loop_count->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::loop_count_changed), 0, this); + _left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this); + _right_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::right_crop_changed), 0, this); + _top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this); + _bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this); + _filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this); + _scaler->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this); + _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this); + _dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_frame_rate_changed), 0, this); + _best_dcp_frame_rate->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::best_dcp_frame_rate_clicked), 0, this); + _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); + _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 @@ -304,21 +292,19 @@ FilmEditor::make_video_panel () /* 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 (); @@ -349,13 +335,62 @@ FilmEditor::make_video_panel () _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, 0.75, wxEXPAND | wxALL, 6); + } + + _content_information = new wxTextCtrl (_content_panel, wxID_ANY, wxT ("\n \n "), wxDefaultPosition, wxDefaultSize, wxTE_READONLY | wxTE_MULTILINE); + _content_sizer->Add (_content_information, 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); + + _timeline_button = new wxButton (_content_panel, wxID_ANY, _("Timeline...")); + _content_sizer->Add (_timeline_button, 0, wxALL, 6); + + _loop_count->SetRange (2, 1024); +} + +void FilmEditor::make_audio_panel () { _audio_panel = new wxPanel (_notebook); @@ -370,48 +405,29 @@ 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); - } - + _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); } @@ -426,27 +442,24 @@ FilmEditor::make_subtitle_panel () _subtitle_sizer->Add (grid, 0, wxALL, 8); _with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, _("With Subtitles")); - video_control (_with_subtitles); grid->Add (_with_subtitles, 1); + grid->AddSpacer (0); - _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY); - grid->Add (video_control (_subtitle_stream)); - { - 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); } @@ -498,41 +511,25 @@ FilmEditor::bottom_crop_changed (wxCommandEvent &) _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 */ @@ -620,43 +617,24 @@ FilmEditor::film_changed (Film::Property p) 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); @@ -680,34 +658,12 @@ FilmEditor::film_changed (Film::Property p) 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 ())); @@ -727,9 +683,6 @@ FilmEditor::film_changed (Film::Property p) 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 (); @@ -754,75 +707,115 @@ FilmEditor::film_changed (Film::Property p) 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 (); + } else if (property == FFmpegContentProperty::AUDIO_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) { + setup_dcp_name (); + setup_show_audio_sensitivity (); } } 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; @@ -870,12 +863,17 @@ FilmEditor::dcp_content_type_changed (wxCommandEvent &) 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) { @@ -891,7 +889,8 @@ FilmEditor::set_film (shared_ptr<Film> f) 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); @@ -899,26 +898,25 @@ FilmEditor::set_film (shared_ptr<Film> f) 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. @@ -934,31 +932,31 @@ FilmEditor::set_things_sensitive (bool s) _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); _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 */ @@ -1005,40 +1003,6 @@ FilmEditor::audio_delay_changed (wxCommandEvent &) _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 () { @@ -1058,16 +1022,6 @@ FilmEditor::setup_notebook_size () } void -FilmEditor::still_duration_changed (wxCommandEvent &) -{ - if (!_film) { - return; - } - - _film->set_still_duration (_still_duration->GetValue ()); -} - -void FilmEditor::trim_start_changed (wxCommandEvent &) { if (!_film) { @@ -1117,20 +1071,7 @@ FilmEditor::audio_gain_calculate_button_clicked (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) { @@ -1155,7 +1096,7 @@ FilmEditor::setup_subtitle_control_sensitivity () { bool h = false; if (_generally_sensitive && _film) { - h = !_film->subtitle_streams().empty(); + h = _film->has_subtitles (); } _with_subtitles->Enable (h); @@ -1165,27 +1106,11 @@ FilmEditor::setup_subtitle_control_sensitivity () j = _film->with_subtitles (); } - _subtitle_stream->Enable (j); _subtitle_offset->Enable (j); _subtitle_scale->Enable (j); } void -FilmEditor::setup_audio_control_sensitivity () -{ - _use_content_audio->Enable (_generally_sensitive && _film && !_film->content_audio_streams().empty()); - _use_external_audio->Enable (_generally_sensitive); - - bool const source = _generally_sensitive && _use_content_audio->GetValue(); - bool const external = _generally_sensitive && _use_external_audio->GetValue(); - - _audio_stream->Enable (source); - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - _external_audio[i]->Enable (external); - } -} - -void FilmEditor::use_dci_name_toggled (wxCommandEvent &) { if (!_film) { @@ -1209,143 +1134,222 @@ FilmEditor::edit_dci_button_clicked (wxCommandEvent &) } void -FilmEditor::setup_streams () +FilmEditor::active_jobs_changed (bool a) { - _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->use_content_audio() && _film->audio_stream()) { - checked_set (_audio_stream, _film->audio_stream()->to_string()); - } + set_things_sensitive (!a); +} - _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 ()))); - } - if (_film->subtitle_stream()) { - checked_set (_subtitle_stream, _film->subtitle_stream()->to_string()); +void +FilmEditor::setup_dcp_name () +{ + string s = _film->dcp_name (true); + if (s.length() > 28) { + _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("...")); + _dcp_name->SetToolTip (std_to_wx (s)); } else { - _subtitle_stream->SetSelection (wxNOT_FOUND); + _dcp_name->SetLabel (std_to_wx (s)); + } +} + +void +FilmEditor::show_audio_clicked (wxCommandEvent &) +{ + if (_audio_dialog) { + _audio_dialog->Destroy (); + _audio_dialog = 0; } + + _audio_dialog = new AudioDialog (this); + _audio_dialog->Show (); + _audio_dialog->set_film (_film); } void -FilmEditor::audio_stream_changed (wxCommandEvent &) +FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &) { if (!_film) { return; } + + _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->video_frame_rate ())); +} - _film->set_content_audio_stream ( - audio_stream_factory ( - string_client_data (_audio_stream->GetClientObject (_audio_stream->GetSelection ())), - Film::state_version - ) - ); +void +FilmEditor::setup_show_audio_sensitivity () +{ + _show_audio->Enable (_film && _film->has_audio ()); } void -FilmEditor::subtitle_stream_changed (wxCommandEvent &) +FilmEditor::setup_content () { - if (!_film) { - return; + 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); + } } - _film->set_subtitle_stream ( - subtitle_stream_factory ( - string_client_data (_subtitle_stream->GetClientObject (_subtitle_stream->GetSelection ())), - Film::state_version - ) - ); + 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); + } + + setup_content_information (); } void -FilmEditor::setup_audio_details () +FilmEditor::content_add_clicked (wxCommandEvent &) { - if (!_film->content_audio_stream()) { - _audio->SetLabel (wxT ("")); - } else { - wxString s; - if (_film->audio_stream()->channels() == 1) { - s << _("1 channel"); + 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 { - s << _film->audio_stream()->channels () << wxT (" ") << _("channels"); + _film->add_content (shared_ptr<FFmpegContent> (new FFmpegContent (p))); } - s << wxT (", ") << _film->audio_stream()->sample_rate() << _("Hz"); - _audio->SetLabel (s); } +} - setup_notebook_size (); +void +FilmEditor::content_remove_clicked (wxCommandEvent &) +{ + shared_ptr<Content> c = selected_content (); + if (c) { + _film->remove_content (c); + } } void -FilmEditor::active_jobs_changed (bool a) +FilmEditor::content_activated (wxListEvent& ev) { - set_things_sensitive (!a); + ContentList c = _film->content (); + assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < c.size ()); + + edit_content (c[ev.GetIndex()]); } void -FilmEditor::use_audio_changed (wxCommandEvent &) +FilmEditor::content_edit_clicked (wxCommandEvent &) { - _film->set_use_content_audio (_use_content_audio->GetValue()); + shared_ptr<Content> c = selected_content (); + if (!c) { + return; + } + + edit_content (c); } void -FilmEditor::external_audio_changed (wxCommandEvent &) +FilmEditor::edit_content (shared_ptr<Content> c) { - vector<string> a; - for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) { - a.push_back (wx_to_std (_external_audio[i]->GetPath())); + shared_ptr<ImageMagickContent> im = dynamic_pointer_cast<ImageMagickContent> (c); + if (im) { + ImageMagickContentDialog* d = new ImageMagickContentDialog (this, im); + d->ShowModal (); + d->Destroy (); } - _film->set_external_audio (a); + shared_ptr<FFmpegContent> ff = dynamic_pointer_cast<FFmpegContent> (c); + if (ff) { + FFmpegContentDialog* d = new FFmpegContentDialog (this, ff); + d->ShowModal (); + d->Destroy (); + } } void -FilmEditor::setup_dcp_name () +FilmEditor::content_earlier_clicked (wxCommandEvent &) { - string s = _film->dcp_name (true); - if (s.length() > 28) { - _dcp_name->SetLabel (std_to_wx (s.substr (0, 28)) + N_("...")); - _dcp_name->SetToolTip (std_to_wx (s)); - } else { - _dcp_name->SetLabel (std_to_wx (s)); + shared_ptr<Content> c = selected_content (); + if (c) { + _film->move_content_earlier (c); } } void -FilmEditor::show_audio_clicked (wxCommandEvent &) +FilmEditor::content_later_clicked (wxCommandEvent &) { - if (_audio_dialog) { - _audio_dialog->Destroy (); - _audio_dialog = 0; + shared_ptr<Content> c = selected_content (); + if (c) { + _film->move_content_later (c); } - - _audio_dialog = new AudioDialog (this); - _audio_dialog->Show (); - _audio_dialog->set_film (_film); } void -FilmEditor::best_dcp_frame_rate_clicked (wxCommandEvent &) +FilmEditor::content_selection_changed (wxListEvent &) { - if (!_film) { + 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; } - - _film->set_dcp_frame_rate (best_dcp_frame_rate (_film->source_frame_rate ())); + + _content_information->SetValue (std_to_wx (c->information ())); } void -FilmEditor::setup_show_audio_sensitivity () +FilmEditor::setup_content_button_sensitivity () { - _show_audio->Enable (_film && _film->has_audio ()); + _content_add->Enable (_generally_sensitive); + + shared_ptr<Content> selection = selected_content (); + + _content_edit->Enable ( + selection && _generally_sensitive && + (dynamic_pointer_cast<ImageMagickContent> (selection) || dynamic_pointer_cast<FFmpegContent> (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 @@ -1355,18 +1359,18 @@ 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, @@ -1409,3 +1413,39 @@ FilmEditor::trim_type_changed (wxCommandEvent &) { _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 ()); +} + +void +FilmEditor::timeline_clicked (wxCommandEvent &) +{ + if (_timeline_dialog) { + _timeline_dialog->Destroy (); + _timeline_dialog = 0; + } + + _timeline_dialog = new TimelineDialog (this, _film->playlist ()); + _timeline_dialog->Show (); +} diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index e2a4d5836..685e7864f 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -16,7 +16,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - + /** @file src/film_editor.h * @brief A wx widget to edit a film's metadata, and perform various functions. */ @@ -29,8 +29,12 @@ #include "lib/film.h" class wxNotebook; +class wxListCtrl; +class wxListEvent; class Film; class AudioDialog; +class AudioMappingView; +class TimelineDialog; /** @class FilmEditor * @brief A wx widget to edit a film's metadata, and perform various functions. @@ -41,12 +45,12 @@ public: 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 (); @@ -60,14 +64,21 @@ private: 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 &); @@ -78,40 +89,41 @@ private: void subtitle_scale_changed (wxCommandEvent &); void colour_lut_changed (wxCommandEvent &); void j2k_bandwidth_changed (wxCommandEvent &); - void still_duration_changed (wxCommandEvent &); - void audio_stream_changed (wxCommandEvent &); - void subtitle_stream_changed (wxCommandEvent &); - void use_audio_changed (wxCommandEvent &); - void external_audio_changed (wxCommandEvent &); void dcp_frame_rate_changed (wxCommandEvent &); void best_dcp_frame_rate_clicked (wxCommandEvent &); + void edit_filters_clicked (wxCommandEvent &); + void loop_content_toggled (wxCommandEvent &); + void loop_count_changed (wxCommandEvent &); + void timeline_clicked (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_show_audio_sensitivity (); 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; @@ -125,69 +137,55 @@ private: 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* _timeline_button; 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; + AudioMappingView* _audio_mapping; wxCheckBox* _with_subtitles; - wxChoice* _subtitle_stream; wxSpinCtrl* _subtitle_offset; wxSpinCtrl* _subtitle_scale; wxChoice* _colour_lut; wxSpinCtrl* _j2k_bandwidth; - /** The Film's DCP content type */ wxChoice* _dcp_content_type; - /** The Film's source frame rate */ - wxStaticText* _source_frame_rate; wxChoice* _dcp_frame_rate; wxButton* _best_dcp_frame_rate; 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; bool _generally_sensitive; AudioDialog* _audio_dialog; + TimelineDialog* _timeline_dialog; }; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 4f2985a06..03f0ca646 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -28,13 +28,16 @@ #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" @@ -45,6 +48,8 @@ using std::max; 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) @@ -114,43 +119,22 @@ FilmViewer::film_changed (Film::Property 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; @@ -163,7 +147,7 @@ FilmViewer::set_film (shared_ptr<Film> f) if (_film == f) { return; } - + _film = f; _raw_frame.reset (); @@ -175,20 +159,28 @@ FilmViewer::set_film (shared_ptr<Film> f) 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; } @@ -200,7 +192,7 @@ FilmViewer::decoder_changed () void FilmViewer::timer (wxTimerEvent &) { - if (!_film || !_decoders.video) { + if (!_player) { return; } @@ -209,8 +201,8 @@ FilmViewer::timer (wxTimerEvent &) 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); } @@ -266,12 +258,8 @@ FilmViewer::paint_panel (wxPaintEvent &) void FilmViewer::slider_moved (wxScrollEvent &) { - if (!_film || !_film->length() || !_decoders.video) { - return; - } - - if (_decoders.video->seek (_slider->GetValue() * _film->length().get() / (4096 * _film->source_frame_rate()))) { - return; + if (_film && _player) { + _player->seek (_slider->GetValue() * _film->video_length() / (4096 * _film->video_frame_rate())); } get_frame (); @@ -308,7 +296,7 @@ FilmViewer::raw_to_display () 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 ()) { @@ -324,7 +312,7 @@ FilmViewer::raw_to_display () 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, @@ -343,7 +331,7 @@ FilmViewer::raw_to_display () void FilmViewer::calculate_sizes () { - if (!_film) { + if (!_film || !_player) { return; } @@ -393,7 +381,7 @@ FilmViewer::check_play_state () } if (_play_button->GetValue()) { - _timer.Start (1000 / _film->source_frame_rate()); + _timer.Start (1000 / _film->video_frame_rate()); } else { _timer.Stop (); } @@ -409,7 +397,7 @@ FilmViewer::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subti _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; @@ -423,21 +411,24 @@ FilmViewer::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subti _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. */ @@ -473,13 +464,23 @@ FilmViewer::active_jobs_changed (bool 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 (); @@ -488,11 +489,11 @@ FilmViewer::back_clicked (wxCommandEvent &) void FilmViewer::forward_clicked (wxCommandEvent &) { - if (!_decoders.video) { + if (!_player) { return; } - _decoders.video->seek_forward (); + _player->seek_forward (); get_frame (); _panel->Refresh (); _panel->Update (); diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index ed5874fbc..02d862ca0 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -23,7 +23,6 @@ #include <wx/wx.h> #include "lib/film.h" -#include "lib/decoder_factory.h" class wxToggleButton; class FFmpegPlayer; @@ -33,6 +32,25 @@ class Subtitle; /** @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 { @@ -43,6 +61,7 @@ public: 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 &); @@ -52,7 +71,7 @@ private: 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); @@ -60,6 +79,7 @@ private: void forward_clicked (wxCommandEvent &); boost::shared_ptr<Film> _film; + boost::shared_ptr<Player> _player; wxSizer* _v_sizer; wxPanel* _panel; @@ -71,7 +91,6 @@ private: 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; diff --git a/src/wx/imagemagick_content_dialog.cc b/src/wx/imagemagick_content_dialog.cc new file mode 100644 index 000000000..24b92fdaf --- /dev/null +++ b/src/wx/imagemagick_content_dialog.cc @@ -0,0 +1,69 @@ +/* + 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")) + , _content (content) +{ + 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); + _video_length->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (ImageMagickContentDialog::video_length_changed), 0, this); +} + +void +ImageMagickContentDialog::video_length_changed (wxCommandEvent &) +{ + shared_ptr<ImageMagickContent> c = _content.lock (); + if (!c) { + return; + } + + c->set_video_length (_video_length->GetValue() * 24); +} diff --git a/src/wx/imagemagick_content_dialog.h b/src/wx/imagemagick_content_dialog.h new file mode 100644 index 000000000..b22ff6817 --- /dev/null +++ b/src/wx/imagemagick_content_dialog.h @@ -0,0 +1,35 @@ +/* + 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>); + +private: + void video_length_changed (wxCommandEvent &); + + boost::weak_ptr<ImageMagickContent> _content; + wxSpinCtrl* _video_length; +}; diff --git a/src/wx/job_manager_view.cc b/src/wx/job_manager_view.cc index 5cd9f2e15..1594dfc91 100644 --- a/src/wx/job_manager_view.cc +++ b/src/wx/job_manager_view.cc @@ -86,6 +86,7 @@ JobManagerView::update () JobRecord r; int n = 1; r.finalised = false; + r.scroll_nudged = false; r.gauge = new wxGauge (_panel, wxID_ANY, 100); _table->Insert (index + n, r.gauge, 1, wxEXPAND | wxLEFT | wxRIGHT); ++n; @@ -113,6 +114,7 @@ JobManagerView::update () ++n; _job_records[*i] = r; + } string const st = (*i)->status (); @@ -126,8 +128,25 @@ JobManagerView::update () checked_set (_job_records[*i].message, wx_to_std (_("Running"))); _job_records[*i].gauge->Pulse (); } + } + if (!_job_records[*i].scroll_nudged && ((*i)->running () || (*i)->finished())) { + int x, y; + _job_records[*i].gauge->GetPosition (&x, &y); + int px, py; + GetScrollPixelsPerUnit (&px, &py); + int vx, vy; + GetViewStart (&vx, &vy); + int sx, sy; + GetClientSize (&sx, &sy); + + if (y > (vy * py + sy / 2)) { + Scroll (-1, y / py); + _job_records[*i].scroll_nudged = true; + } + } + if ((*i)->finished() && !_job_records[*i].finalised) { checked_set (_job_records[*i].message, st); if (!(*i)->finished_cancelled()) { @@ -156,7 +175,7 @@ JobManagerView::details_clicked (wxCommandEvent& ev) { 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]); @@ -170,7 +189,7 @@ JobManagerView::cancel_clicked (wxCommandEvent& ev) { 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 (); } diff --git a/src/wx/job_manager_view.h b/src/wx/job_manager_view.h index fc29eadb4..3d1ad30c0 100644 --- a/src/wx/job_manager_view.h +++ b/src/wx/job_manager_view.h @@ -57,6 +57,7 @@ private: wxButton* pause; wxButton* details; bool finalised; + bool scroll_nudged; }; std::map<boost::shared_ptr<Job>, JobRecord> _job_records; diff --git a/src/wx/po/es_ES.po b/src/wx/po/es_ES.po index 56c0856bd..a193325e6 100644 --- a/src/wx/po/es_ES.po +++ b/src/wx/po/es_ES.po @@ -5,7 +5,7 @@ # 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" @@ -22,7 +22,7 @@ msgid "%" 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 @@ -141,17 +141,17 @@ msgid "DCP Name" 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" diff --git a/src/wx/po/fr_FR.po b/src/wx/po/fr_FR.po index c7ef31f5a..36ae4a925 100644 --- a/src/wx/po/fr_FR.po +++ b/src/wx/po/fr_FR.po @@ -5,7 +5,7 @@ # 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" @@ -21,7 +21,7 @@ msgid "%" 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 @@ -140,17 +140,17 @@ msgid "DCP Name" 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" diff --git a/src/wx/po/it_IT.po b/src/wx/po/it_IT.po index 2d4ee7fd7..2c6c67868 100644 --- a/src/wx/po/it_IT.po +++ b/src/wx/po/it_IT.po @@ -22,8 +22,8 @@ msgid "%" 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" @@ -141,17 +141,17 @@ msgid "DCP Name" 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" diff --git a/src/wx/po/sv_SE.po b/src/wx/po/sv_SE.po index 4127d77f8..9ed7ee2be 100644 --- a/src/wx/po/sv_SE.po +++ b/src/wx/po/sv_SE.po @@ -5,7 +5,7 @@ # 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" @@ -22,8 +22,8 @@ msgid "%" 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" @@ -141,17 +141,17 @@ msgid "DCP Name" 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" diff --git a/src/wx/properties_dialog.cc b/src/wx/properties_dialog.cc index 44a713dc3..d20a58ca8 100644 --- a/src/wx/properties_dialog.cc +++ b/src/wx/properties_dialog.cc @@ -50,18 +50,13 @@ PropertiesDialog::PropertiesDialog (wxWindow* parent, shared_ptr<Film> film) _encoded = new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this)); table->Add (_encoded, 1, wxALIGN_CENTER_VERTICAL); - if (_film->length()) { - _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().get()))); - FrameRateConversion frc (_film->source_frame_rate(), _film->dcp_frame_rate()); - int const dcp_length = _film->length().get() * frc.factor(); - double const disk = ((double) _film->j2k_bandwidth() / 8) * dcp_length / (_film->dcp_frame_rate() * 1073741824.0f); - stringstream s; - s << fixed << setprecision (1) << disk << wx_to_std (_("Gb")); - _disk->SetLabel (std_to_wx (s.str ())); - } else { - _frames->SetLabel (_("unknown")); - _disk->SetLabel (_("unknown")); - } + _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->video_length()))); + FrameRateConversion frc (_film->video_frame_rate(), _film->dcp_frame_rate()); + int const dcp_length = _film->video_length() * frc.factor(); + double const disk = ((double) _film->j2k_bandwidth() / 8) * dcp_length / (_film->dcp_frame_rate() * 1073741824.0f); + stringstream s; + s << fixed << setprecision (1) << disk << wx_to_std (_("Gb")); + _disk->SetLabel (std_to_wx (s.str ())); wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); overall_sizer->Add (table, 0, wxALL, 6); @@ -85,9 +80,9 @@ PropertiesDialog::frames_already_encoded () const return ""; } - if (_film->length()) { + if (_film->video_length()) { /* XXX: encoded_frames() should check which frames have been encoded */ - u << " (" << (_film->encoded_frames() * 100 / _film->length().get()) << "%)"; + u << " (" << (_film->encoded_frames() * 100 / _film->video_length()) << "%)"; } return u.str (); } diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc new file mode 100644 index 000000000..253903888 --- /dev/null +++ b/src/wx/timeline.cc @@ -0,0 +1,200 @@ +/* + 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 <wx/graphics.h> +#include "timeline.h" +#include "wx_util.h" +#include "playlist.h" + +using std::list; +using std::cout; +using std::max; +using boost::shared_ptr; +using boost::bind; + +int const Timeline::_track_height = 64; + +Timeline::Timeline (wxWindow* parent, shared_ptr<Playlist> pl) + : wxPanel (parent) + , _playlist (pl) +{ + SetDoubleBuffered (true); + + Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (Timeline::paint), 0, this); + + if (pl->audio_from() == Playlist::AUDIO_FFMPEG) { + SetMinSize (wxSize (640, _track_height * 2 + 96)); + } else { + SetMinSize (wxSize (640, _track_height * (max (size_t (1), pl->audio().size()) + 1) + 96)); + } + + pl->Changed.connect (bind (&Timeline::playlist_changed, this)); + pl->ContentChanged.connect (bind (&Timeline::playlist_changed, this)); +} + +template <class T> +int +plot_content_list ( + list<shared_ptr<const T> > content, wxGraphicsContext* gc, int x, int y, double pixels_per_second, int track_height, wxString type, bool consecutive + ) +{ + Time t = 0; + for (typename list<shared_ptr<const T> >::iterator i = content.begin(); i != content.end(); ++i) { + Time const len = (*i)->temporal_length (); + wxGraphicsPath path = gc->CreatePath (); + path.MoveToPoint (x + t * pixels_per_second, y); + path.AddLineToPoint (x + (t + len) * pixels_per_second, y); + path.AddLineToPoint (x + (t + len) * pixels_per_second, y + track_height); + path.AddLineToPoint (x + t * pixels_per_second, y + track_height); + path.AddLineToPoint (x + t * pixels_per_second, y); + gc->StrokePath (path); + gc->FillPath (path); + + wxString name = wxString::Format (wxT ("%s [%s]"), std_to_wx ((*i)->file().filename().string()).data(), type.data()); + wxDouble name_width; + wxDouble name_height; + wxDouble name_descent; + wxDouble name_leading; + gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading); + + gc->Clip (wxRegion (x + t * pixels_per_second, y, len * pixels_per_second, track_height)); + gc->DrawText (name, t * pixels_per_second + 12, y + track_height - name_height - 4); + gc->ResetClip (); + + if (consecutive) { + t += len; + } else { + y += track_height; + } + } + + if (consecutive) { + y += track_height; + } + + return y; +} + +void +Timeline::paint (wxPaintEvent &) +{ + wxPaintDC dc (this); + + shared_ptr<Playlist> pl = _playlist.lock (); + if (!pl) { + return; + } + + wxGraphicsContext* gc = wxGraphicsContext::Create (dc); + if (!gc) { + return; + } + + int const x_offset = 8; + int y = 8; + int const width = GetSize().GetWidth(); + double const pixels_per_second = (width - x_offset * 2) / (pl->content_length() / pl->video_frame_rate()); + + gc->SetFont (gc->CreateFont (*wxNORMAL_FONT)); + + gc->SetPen (*wxBLACK_PEN); +#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9 + gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, wxPENSTYLE_SOLID)); + gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (149, 121, 232, 255), wxBRUSHSTYLE_SOLID)); +#else + gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 4, wxSOLID)); + gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (149, 121, 232, 255), wxSOLID)); +#endif + y = plot_content_list (pl->video (), gc, x_offset, y, pixels_per_second, _track_height, _("video"), true); + +#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9 + gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (242, 92, 120, 255), wxBRUSHSTYLE_SOLID)); +#else + gc->SetBrush (*wxTheBrushList->FindOrCreateBrush (wxColour (242, 92, 120, 255), wxSOLID)); +#endif + y = plot_content_list (pl->audio (), gc, x_offset, y, pixels_per_second, _track_height, _("audio"), pl->audio_from() == Playlist::AUDIO_FFMPEG); + + /* Time axis */ + +#if wxMAJOR_VERSION == 2 && wxMINOR_VERSION >= 9 + gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID)); +#else + gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxSOLID)); +#endif + + int mark_interval = rint (128 / pixels_per_second); + if (mark_interval > 5) { + mark_interval -= mark_interval % 5; + } + if (mark_interval > 10) { + mark_interval -= mark_interval % 10; + } + if (mark_interval > 60) { + mark_interval -= mark_interval % 60; + } + if (mark_interval > 3600) { + mark_interval -= mark_interval % 3600; + } + + if (mark_interval < 1) { + mark_interval = 1; + } + + wxGraphicsPath path = gc->CreatePath (); + path.MoveToPoint (x_offset, y + 40); + path.AddLineToPoint (width, y + 40); + gc->StrokePath (path); + + double t = 0; + while ((t * pixels_per_second) < width) { + wxGraphicsPath path = gc->CreatePath (); + path.MoveToPoint (x_offset + t * pixels_per_second, y + 36); + path.AddLineToPoint (x_offset + t * pixels_per_second, y + 44); + gc->StrokePath (path); + + int tc = t; + int const h = tc / 3600; + tc -= h * 3600; + int const m = tc / 60; + tc -= m * 60; + int const s = tc; + + wxString str = wxString::Format (wxT ("%02d:%02d:%02d"), h, m, s); + wxDouble str_width; + wxDouble str_height; + wxDouble str_descent; + wxDouble str_leading; + gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading); + + int const tx = x_offset + t * pixels_per_second; + if ((tx + str_width) < width) { + gc->DrawText (str, x_offset + t * pixels_per_second, y + 60); + } + t += mark_interval; + } + + delete gc; +} + +void +Timeline::playlist_changed () +{ + Refresh (); +} diff --git a/src/lib/decoder_factory.h b/src/wx/timeline.h index 8076b01c7..1993eb9c2 100644 --- a/src/lib/decoder_factory.h +++ b/src/wx/timeline.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + 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 @@ -17,33 +17,22 @@ */ -#ifndef DVDOMATIC_DECODER_FACTORY_H -#define DVDOMATIC_DECODER_FACTORY_H +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <wx/wx.h> -/** @file src/decoder_factory.h - * @brief A method to create appropriate decoders for some content. - */ +class Playlist; -#include "options.h" +class Timeline : public wxPanel +{ +public: + Timeline (wxWindow *, boost::shared_ptr<Playlist>); -class Film; -class VideoDecoder; -class AudioDecoder; +private: + void paint (wxPaintEvent &); + void playlist_changed (); -struct Decoders { - Decoders () {} + static int const _track_height; - 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; + boost::weak_ptr<Playlist> _playlist; }; - -extern Decoders decoder_factory ( - boost::shared_ptr<Film>, DecodeOptions - ); - -#endif diff --git a/src/wx/timeline_dialog.cc b/src/wx/timeline_dialog.cc new file mode 100644 index 000000000..7a75044c9 --- /dev/null +++ b/src/wx/timeline_dialog.cc @@ -0,0 +1,41 @@ +/* + 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 <wx/graphics.h> +#include "timeline_dialog.h" +#include "wx_util.h" +#include "playlist.h" + +using std::list; +using std::cout; +using boost::shared_ptr; + +TimelineDialog::TimelineDialog (wxWindow* parent, shared_ptr<Playlist> pl) + : wxDialog (parent, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE) + , _timeline (this, pl) +{ + wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL); + + sizer->Add (&_timeline, 1, wxEXPAND | wxALL, 12); + + SetSizer (sizer); + sizer->Layout (); + sizer->SetSizeHints (this); +} diff --git a/src/wx/timeline_dialog.h b/src/wx/timeline_dialog.h new file mode 100644 index 000000000..e58de5540 --- /dev/null +++ b/src/wx/timeline_dialog.h @@ -0,0 +1,34 @@ +/* + 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/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include <wx/wx.h> +#include "timeline.h" + +class Playlist; + +class TimelineDialog : public wxDialog +{ +public: + TimelineDialog (wxWindow *, boost::shared_ptr<Playlist>); + +private: + Timeline _timeline; +}; diff --git a/src/wx/wscript b/src/wx/wscript index 42bb8ca88..c63ef128d 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -5,20 +5,25 @@ import i18n sources = """ audio_dialog.cc + audio_mapping_view.cc audio_plot.cc config_dialog.cc dci_metadata_dialog.cc dir_picker_ctrl.cc + ffmpeg_content_dialog.cc film_editor.cc film_viewer.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 properties_dialog.cc server_dialog.cc + timeline.cc + timeline_dialog.cc wx_util.cc wx_ui_signaller.cc """ @@ -32,18 +37,18 @@ def build(bld): 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') diff --git a/src/wx/wx_util.cc b/src/wx/wx_util.cc index 1a7b73faf..5691d341a 100644 --- a/src/wx/wx_util.cc +++ b/src/wx/wx_util.cc @@ -60,7 +60,7 @@ add_label_to_grid_bag_sizer (wxGridBagSizer* s, wxWindow* p, wxString t, wxGBPos void error_dialog (wxWindow* parent, wxString m) { - wxMessageDialog* d = new wxMessageDialog (parent, m, _("DVD-o-matic"), wxOK); + wxMessageDialog* d = new wxMessageDialog (parent, m, _("DCP-o-matic"), wxOK); d->ShowModal (); d->Destroy (); } @@ -68,7 +68,7 @@ error_dialog (wxWindow* parent, wxString m) bool confirm_dialog (wxWindow* parent, wxString m) { - wxMessageDialog* d = new wxMessageDialog (parent, m, _("DVD-o-matic"), wxYES_NO | wxICON_QUESTION); + wxMessageDialog* d = new wxMessageDialog (parent, m, _("DCP-o-matic"), wxYES_NO | wxICON_QUESTION); int const r = d->ShowModal (); d->Destroy (); return r == wxID_YES; @@ -215,7 +215,7 @@ checked_set (wxRadioButton* widget, bool value) } void -dvdomatic_setup_i18n () +dcpomatic_setup_i18n () { int language = wxLANGUAGE_DEFAULT; @@ -231,12 +231,12 @@ dvdomatic_setup_i18n () if (wxLocale::IsAvailable (language)) { locale = new wxLocale (language, wxLOCALE_LOAD_DEFAULT); -#ifdef DVDOMATIC_WINDOWS +#ifdef DCPOMATIC_WINDOWS locale->AddCatalogLookupPathPrefix (std_to_wx (mo_path().string())); #endif - locale->AddCatalog (wxT ("libdvdomatic-wx")); - locale->AddCatalog (wxT ("dvdomatic")); + locale->AddCatalog (wxT ("libdcpomatic-wx")); + locale->AddCatalog (wxT ("dcpomatic")); if (!locale->IsOk()) { delete locale; @@ -246,6 +246,6 @@ dvdomatic_setup_i18n () } if (locale) { - dvdomatic_setup_gettext_i18n (wx_to_std (locale->GetCanonicalName ())); + dcpomatic_setup_gettext_i18n (wx_to_std (locale->GetCanonicalName ())); } } diff --git a/src/wx/wx_util.h b/src/wx/wx_util.h index 00a625e1c..bff11647e 100644 --- a/src/wx/wx_util.h +++ b/src/wx/wx_util.h @@ -36,7 +36,7 @@ extern wxStaticText* add_label_to_sizer (wxSizer *, wxWindow *, wxString, int pr extern wxStaticText* add_label_to_grid_bag_sizer (wxGridBagSizer *, wxWindow *, wxString, wxGBPosition, wxGBSpan span = wxDefaultSpan); extern std::string wx_to_std (wxString); extern wxString std_to_wx (std::string); -extern void dvdomatic_setup_i18n (); +extern void dcpomatic_setup_i18n (); /** @class ThreadedStaticText * diff --git a/test/test.cc b/test/test.cc index 496c91519..1cf651405 100644 --- a/test/test.cc +++ b/test/test.cc @@ -39,9 +39,10 @@ #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; @@ -89,7 +90,7 @@ new_test_film (string name) 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); @@ -216,7 +217,7 @@ BOOST_AUTO_TEST_CASE (film_metadata_test) 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); @@ -229,7 +230,7 @@ BOOST_AUTO_TEST_CASE (film_metadata_test) 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; @@ -251,45 +252,12 @@ BOOST_AUTO_TEST_CASE (film_metadata_test) 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 (); @@ -305,32 +273,6 @@ BOOST_AUTO_TEST_CASE (format_test) 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"; @@ -362,17 +304,6 @@ BOOST_AUTO_TEST_CASE (md5_digest_test) 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) { @@ -442,7 +373,7 @@ BOOST_AUTO_TEST_CASE (client_server_test) new thread (boost::bind (&Server::run, server, 2)); /* Let the server get itself ready */ - dvdomatic_sleep (1); + dcpomatic_sleep (1); ServerDescription description ("localhost", 2); @@ -464,14 +395,14 @@ BOOST_AUTO_TEST_CASE (make_dcp_test) { 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); @@ -494,15 +425,15 @@ BOOST_AUTO_TEST_CASE (make_dcp_with_range_test) { 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); @@ -656,44 +587,44 @@ BOOST_AUTO_TEST_CASE (audio_sampling_rate_test) 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. */ @@ -739,10 +670,10 @@ BOOST_AUTO_TEST_CASE (job_manager_test) 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); } diff --git a/test/wscript b/test/wscript index 15d5410b3..61c391663 100644 --- a/test/wscript +++ b/test/wscript @@ -8,8 +8,8 @@ def configure(conf): 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 = '' diff --git a/windows/dvdomatic.bmp b/windows/dcpomatic.bmp Binary files differindex 0a196f7a0..0a196f7a0 100644 --- a/windows/dvdomatic.bmp +++ b/windows/dcpomatic.bmp diff --git a/windows/dvdomatic.ico b/windows/dcpomatic.ico Binary files differindex 225008cfe..225008cfe 100644 --- a/windows/dvdomatic.ico +++ b/windows/dcpomatic.ico diff --git a/windows/dcpomatic.rc b/windows/dcpomatic.rc new file mode 100644 index 000000000..3963873bc --- /dev/null +++ b/windows/dcpomatic.rc @@ -0,0 +1,3 @@ +id ICON "dcpomatic.ico" +taskbar_icon ICON "dcpomatic_taskbar.ico" +#include "wx-2.9/wx/msw/wx.rc" diff --git a/windows/dvdomatic_taskbar.ico b/windows/dcpomatic_taskbar.ico Binary files differindex f4489fa14..f4489fa14 100644 --- a/windows/dvdomatic_taskbar.ico +++ b/windows/dcpomatic_taskbar.ico diff --git a/windows/dvdomatic.rc b/windows/dvdomatic.rc deleted file mode 100644 index 17790cf0d..000000000 --- a/windows/dvdomatic.rc +++ /dev/null @@ -1,3 +0,0 @@ -id ICON "dvdomatic.ico" -taskbar_icon ICON "dvdomatic_taskbar.ico" -#include "wx-2.9/wx/msw/wx.rc" diff --git a/windows/installer.nsi.32.in b/windows/installer.nsi.32.in index b73e4f345..a7867c618 100644 --- a/windows/installer.nsi.32.in +++ b/windows/installer.nsi.32.in @@ -1,14 +1,14 @@ !include "MUI2.nsh" -Name "DVD-o-matic" +Name "DCP-o-matic" RequestExecutionLevel admin -outFile "DVD-o-matic @version@ 32-bit Installer.exe" -!define MUI_ICON "%resources%/dvdomatic.ico" -!define MUI_UNICON "%resources%/dvdomatic.ico" -!define MUI_SPECIALBITMAP "%resources%/dvdomatic.bmp" +outFile "DCP-o-matic @version@ 32-bit Installer.exe" +!define MUI_ICON "%resources%/dcpomatic.ico" +!define MUI_UNICON "%resources%/dcpomatic.ico" +!define MUI_SPECIALBITMAP "%resources%/dcpomatic.bmp" -InstallDir "$PROGRAMFILES\DVD-o-matic" +InstallDir "$PROGRAMFILES\DCP-o-matic" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "../../COPYING" @@ -79,13 +79,14 @@ File "%deps%/bin/libpixman-1-0.dll" File "%deps%/bin/libfontconfig-1.dll" File "%deps%/bin/libexpat-1.dll" File "%deps%/bin/libbz2.dll" +File "%deps%/bin/cxml.dll" -File "%binaries%/src/wx/dvdomatic-wx.dll" -File "%binaries%/src/lib/dvdomatic.dll" -File "%binaries%/src/tools/dvdomatic.exe" -File "%binaries%/src/tools/dvdomatic_batch.exe" -File "%binaries%/src/tools/servomatic_cli.exe" -File "%binaries%/src/tools/servomatic_gui.exe" +File "%binaries%/src/wx/dcpomatic-wx.dll" +File "%binaries%/src/lib/dcpomatic.dll" +File "%binaries%/src/tools/dcpomatic.exe" +File "%binaries%/src/tools/dcpomatic_batch.exe" +File "%binaries%/src/tools/dcpomatic_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 @@ -95,34 +96,34 @@ SetOutPath "$PROFILE\.magick" File "%deps%/etc/ImageMagick/delegates.xml" SetOutPath "$INSTDIR\locale\fr\LC_MESSAGES" -File "%binaries%/src/lib/mo/fr_FR/libdvdomatic.mo" -File "%binaries%/src/wx/mo/fr_FR/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/fr_FR/dvdomatic.mo" +File "%binaries%/src/lib/mo/fr_FR/libdcpomatic.mo" +File "%binaries%/src/wx/mo/fr_FR/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/fr_FR/dcpomatic.mo" SetOutPath "$INSTDIR\locale\it\LC_MESSAGES" -File "%binaries%/src/lib/mo/it_IT/libdvdomatic.mo" -File "%binaries%/src/wx/mo/it_IT/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/it_IT/dvdomatic.mo" +File "%binaries%/src/lib/mo/it_IT/libdcpomatic.mo" +File "%binaries%/src/wx/mo/it_IT/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/it_IT/dcpomatic.mo" SetOutPath "$INSTDIR\locale\es\LC_MESSAGES" -File "%binaries%/src/lib/mo/es_ES/libdvdomatic.mo" -File "%binaries%/src/wx/mo/es_ES/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/es_ES/dvdomatic.mo" +File "%binaries%/src/lib/mo/es_ES/libdcpomatic.mo" +File "%binaries%/src/wx/mo/es_ES/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/es_ES/dcpomatic.mo" SetOutPath "$INSTDIR\locale\sv\LC_MESSAGES" -File "%binaries%/src/lib/mo/sv_SE/libdvdomatic.mo" -File "%binaries%/src/wx/mo/sv_SE/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/sv_SE/dvdomatic.mo" +File "%binaries%/src/lib/mo/sv_SE/libdcpomatic.mo" +File "%binaries%/src/wx/mo/sv_SE/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/sv_SE/dcpomatic.mo" -CreateShortCut "$DESKTOP\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic_batch.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic batch converter.lnk" "$INSTDIR\bin\dcpomatic_batch.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" -CreateDirectory "$SMPROGRAMS\DVD-o-matic" -CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall DVD-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic_batch.exe" "" "$INSTDIR\bin\dvdomatic_batch.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" "$INSTDIR\bin\servomatic_gui.exe" 0 +CreateDirectory "$SMPROGRAMS\DCP-o-matic" +CreateShortCut "$SMPROGRAMS\DCP-o-matic\Uninstall DCP-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" "$INSTDIR\bin\dcpomatic.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic batch converter.lnk" "$INSTDIR\bin\dcpomatic.exe" "" "$INSTDIR\bin\dcpomatic_batch.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" "$INSTDIR\bin\dcpomatic_server.exe" 0 -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "DisplayName" "DVD-o-matic (remove only)" -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "UninstallString" "$INSTDIR\Uninstall.exe" +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DCP-o-matic" "DisplayName" "DCP-o-matic (remove only)" +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DCP-o-matic" "UninstallString" "$INSTDIR\Uninstall.exe" WriteUninstaller "$INSTDIR\Uninstall.exe" @@ -133,12 +134,12 @@ Section "Uninstall" RMDir /r "$INSTDIR\*.*" RMDir "$INSTDIR" -Delete "$DESKTOP\DVD-o-matic.lnk" -Delete "$DESKTOP\DVD-o-matic bach converter.lnk" -Delete "$DESKTOP\DVD-o-matic encode server.lnk" -Delete "$SMPROGRAMS\DVD-o-matic\*.*" -RmDir "$SMPROGRAMS\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" +Delete "$DESKTOP\DCP-o-matic.lnk" +Delete "$DESKTOP\DCP-o-matic batch converter.lnk" +Delete "$DESKTOP\DCP-o-matic encode server.lnk" +Delete "$SMPROGRAMS\DCP-o-matic\*.*" +RmDir "$SMPROGRAMS\DCP-o-matic" +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DCP-o-matic" +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DCP-o-matic" SectionEnd diff --git a/windows/installer.nsi.64.in b/windows/installer.nsi.64.in index f499c46c2..34b7fe8f6 100644 --- a/windows/installer.nsi.64.in +++ b/windows/installer.nsi.64.in @@ -1,16 +1,16 @@ !include "MUI2.nsh" !include "x64.nsh" -Name "DVD-o-matic" +Name "DCP-o-matic" RequestExecutionLevel admin -outFile "DVD-o-matic @version@ 64-bit Installer.exe" -!define MUI_ICON "%resources%/dvdomatic.ico" -!define MUI_UNICON "%resources%/dvdomatic.ico" -!define MUI_SPECIALBITMAP "%resources%/dvdomatic.bmp" +outFile "DCP-o-matic @version@ 64-bit Installer.exe" +!define MUI_ICON "%resources%/dcpomatic.ico" +!define MUI_UNICON "%resources%/dcpomatic.ico" +!define MUI_SPECIALBITMAP "%resources%/dcpomatic.bmp" -InstallDir "$PROGRAMFILES\DVD-o-matic" +InstallDir "$PROGRAMFILES\DCP-o-matic" !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE "../../COPYING" @@ -32,7 +32,7 @@ ${If} ${RunningX64} ; disable registry redirection (enable access to 64-bit portion of registry) SetRegView 64 ; change install dir - StrCpy $INSTDIR "$PROGRAMFILES64\DVD-o-matic" + StrCpy $INSTDIR "$PROGRAMFILES64\DCP-o-matic" ${EndIf} SetOutPath "$INSTDIR\bin" @@ -89,13 +89,14 @@ File "%deps%/bin/libpixman-1-0.dll" File "%deps%/bin/libfontconfig-1.dll" File "%deps%/bin/libexpat-1.dll" File "%deps%/bin/libbz2.dll" +File "%deps%/bin/cxml.dll" -File "%binaries%/src/wx/dvdomatic-wx.dll" -File "%binaries%/src/lib/dvdomatic.dll" -File "%binaries%/src/tools/dvdomatic.exe" -File "%binaries%/src/tools/dvdomatic_batch.exe" -File "%binaries%/src/tools/servomatic_cli.exe" -File "%binaries%/src/tools/servomatic_gui.exe" +File "%binaries%/src/wx/dcpomatic-wx.dll" +File "%binaries%/src/lib/dcpomatic.dll" +File "%binaries%/src/tools/dcpomatic.exe" +File "%binaries%/src/tools/dcpomatic_batch.exe" +File "%binaries%/src/tools/dcpomatic_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 @@ -105,34 +106,34 @@ SetOutPath "$PROFILE\.magick" File "%deps%/etc/ImageMagick/delegates.xml" SetOutPath "$INSTDIR\locale\fr\LC_MESSAGES" -File "%binaries%/src/lib/mo/fr_FR/libdvdomatic.mo" -File "%binaries%/src/wx/mo/fr_FR/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/fr_FR/dvdomatic.mo" +File "%binaries%/src/lib/mo/fr_FR/libdcpomatic.mo" +File "%binaries%/src/wx/mo/fr_FR/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/fr_FR/dcpomatic.mo" SetOutPath "$INSTDIR\locale\it\LC_MESSAGES" -File "%binaries%/src/lib/mo/it_IT/libdvdomatic.mo" -File "%binaries%/src/wx/mo/it_IT/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/it_IT/dvdomatic.mo" +File "%binaries%/src/lib/mo/it_IT/libdcpomatic.mo" +File "%binaries%/src/wx/mo/it_IT/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/it_IT/dcpomatic.mo" SetOutPath "$INSTDIR\locale\es\LC_MESSAGES" -File "%binaries%/src/lib/mo/es_ES/libdvdomatic.mo" -File "%binaries%/src/wx/mo/es_ES/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/es_ES/dvdomatic.mo" +File "%binaries%/src/lib/mo/es_ES/libdcpomatic.mo" +File "%binaries%/src/wx/mo/es_ES/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/es_ES/dcpomatic.mo" SetOutPath "$INSTDIR\locale\sv\LC_MESSAGES" -File "%binaries%/src/lib/mo/sv_SE/libdvdomatic.mo" -File "%binaries%/src/wx/mo/sv_SE/libdvdomatic-wx.mo" -File "%binaries%/src/tools/mo/sv_SE/dvdomatic.mo" +File "%binaries%/src/lib/mo/sv_SE/libdcpomatic.mo" +File "%binaries%/src/wx/mo/sv_SE/libdcpomatic-wx.mo" +File "%binaries%/src/tools/mo/sv_SE/dcpomatic.mo" -CreateShortCut "$DESKTOP\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic_batch.exe" "" -CreateShortCut "$DESKTOP\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic batch converter.lnk" "$INSTDIR\bin\dcpomatic_batch.exe" "" +CreateShortCut "$DESKTOP\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" -CreateDirectory "$SMPROGRAMS\DVD-o-matic" -CreateShortCut "$SMPROGRAMS\DVD-o-matic\Uninstall DVD-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic batch converter.lnk" "$INSTDIR\bin\dvdomatic.exe" "" "$INSTDIR\bin\dvdomatic_batch.exe" 0 -CreateShortCut "$SMPROGRAMS\DVD-o-matic\DVD-o-matic encode server.lnk" "$INSTDIR\bin\servomatic_gui.exe" "" "$INSTDIR\bin\servomatic_gui.exe" 0 +CreateDirectory "$SMPROGRAMS\DCP-o-matic" +CreateShortCut "$SMPROGRAMS\DCP-o-matic\Uninstall DCP-o-matic.lnk" "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic.lnk" "$INSTDIR\bin\dcpomatic.exe" "" "$INSTDIR\bin\dcpomatic.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic batch converter.lnk" "$INSTDIR\bin\dcpomatic.exe" "" "$INSTDIR\bin\dcpomatic_batch.exe" 0 +CreateShortCut "$SMPROGRAMS\DCP-o-matic\DCP-o-matic encode server.lnk" "$INSTDIR\bin\dcpomatic_server.exe" "" "$INSTDIR\bin\dcpomatic_server.exe" 0 -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "DisplayName" "DVD-o-matic (remove only)" -WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" "UninstallString" "$INSTDIR\Uninstall.exe" +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DCP-o-matic" "DisplayName" "DCP-o-matic (remove only)" +WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\DCP-o-matic" "UninstallString" "$INSTDIR\Uninstall.exe" WriteUninstaller "$INSTDIR\Uninstall.exe" @@ -143,12 +144,12 @@ Section "Uninstall" RMDir /r "$INSTDIR\*.*" RMDir "$INSTDIR" -Delete "$DESKTOP\DVD-o-matic.lnk" -Delete "$DESKTOP\DVD-o-matic batch converter.lnk" -Delete "$DESKTOP\DVD-o-matic encode server.lnk" -Delete "$SMPROGRAMS\DVD-o-matic\*.*" -RmDir "$SMPROGRAMS\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DVD-o-matic" -DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DVD-o-matic" +Delete "$DESKTOP\DCP-o-matic.lnk" +Delete "$DESKTOP\DCP-o-matic batch converter.lnk" +Delete "$DESKTOP\DCP-o-matic encode server.lnk" +Delete "$SMPROGRAMS\DCP-o-matic\*.*" +RmDir "$SMPROGRAMS\DCP-o-matic" +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\DCP-o-matic" +DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DCP-o-matic" SectionEnd @@ -2,8 +2,8 @@ import subprocess import os import sys -APPNAME = 'dvdomatic' -VERSION = '0.90pre' +APPNAME = 'dcpomatic' +VERSION = '1.00pre' def options(opt): opt.load('compiler_cxx') @@ -25,7 +25,7 @@ def configure(conf): '-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: @@ -35,9 +35,9 @@ def configure(conf): 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') @@ -50,12 +50,13 @@ def configure(conf): 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.49', 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) @@ -72,6 +73,9 @@ def configure(conf): 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 @@ -224,21 +228,22 @@ def build(bld): 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 obj = bld(features = 'subst') - obj.source = 'dvdomatic_batch.desktop.in' - obj.target = 'dvdomatic_batch.desktop' + obj.source = 'dcpomatic_batch.desktop.in' + obj.target = 'dcpomatic_batch.desktop' obj.dict = d - bld.install_files('${PREFIX}/share/applications', ['dvdomatic.desktop', 'dvdomatic_batch.desktop']) + bld.install_files('${PREFIX}/share/applications', ['dcpomatic.desktop', 'dcpomatic_batch.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) @@ -260,8 +265,8 @@ def create_version_cc(version): 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) |
