Merge branch 'master' of ssh://git.carlh.net/home/carl/git/dcpomatic
authorCarl Hetherington <cth@carlh.net>
Sun, 27 Sep 2015 00:36:47 +0000 (01:36 +0100)
committerCarl Hetherington <cth@carlh.net>
Sun, 27 Sep 2015 00:36:47 +0000 (01:36 +0100)
27 files changed:
ChangeLog
cscript
debian/changelog
platform/osx/make_dmg.sh
platform/windows/wscript
src/lib/dcpomatic_time.h
src/lib/exceptions.h
src/lib/ffmpeg.cc
src/lib/ffmpeg.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_examiner.cc
src/lib/ffmpeg_examiner.h
src/lib/ffmpeg_subtitle_stream.cc
src/lib/ffmpeg_subtitle_stream.h
src/lib/job.cc
src/lib/job_manager.cc
src/lib/server.cc
src/lib/server_finder.cc
src/lib/subrip.cc
src/lib/update_checker.cc
src/lib/writer.cc
src/lib/wscript
src/tools/wscript
src/wx/hints_dialog.cc
test/data
test/wscript
wscript

index aeb563d52e5aab821689804a42737867f2374d02..60b6254420a9f2664a644db49e35c7c698c6ec29 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2015-09-26  Carl Hetherington  <cth@carlh.net>
+
+       * Fix crash with embedded subtitles with some
+       video files.
+
+       * Version 2.3.6 released.
+
+2015-09-25  Carl Hetherington  <cth@carlh.net>
+
+       * Fix assertion failure when loading .MTS files (#702).
+
+       * Fix incorrect hint about 3D content in a 2D DCP.
+
+       * Detect and convert from non-UTF-8
+       subtitle encodings.
+
 2015-09-21  Carl Hetherington  <cth@carlh.net>
 
        * Version 2.3.5 released.
diff --git a/cscript b/cscript
index 48f585d42b0b09b229a96e85b623dfe5d0c77ba4..b6e2a09f5d16e85ed721721649b26b16be2875b1 100644 (file)
--- a/cscript
+++ b/cscript
@@ -45,7 +45,8 @@ deb_depends['12.04'] = {'libc6': '2.15',
                         'libboost-date-time1.48.0': '1.48.0-3',
                         'libcurl3': '7.22.0-3ubuntu4',
                         'libzip2': '0.10-1ubuntu1',
-                        'libsamplerate0': '0.1.8-4'}
+                        'libsamplerate0': '0.1.8-4',
+                        'libicu48': '4.8.1.1-3'}
 
 deb_depends['14.04'] = {'libc6': '2.19-0ubuntu6',
                         'libssh-4': '0.6.1-0ubuntu3',
@@ -61,7 +62,8 @@ deb_depends['14.04'] = {'libc6': '2.19-0ubuntu6',
                         'libboost-date-time1.54.0': '1.54.0-4ubuntu3',
                         'libcurl3': '7.35.0-1ubuntu2',
                         'libzip2': '0.10.1-1.2',
-                        'libsamplerate0': '0.1.8-7'}
+                        'libsamplerate0': '0.1.8-7',
+                        'libicu52': '52.1-3'}
 
 deb_depends['15.04'] = {'libc6': '2.21-0ubuntu4',
                         'libssh-4': '0.6.3-3ubuntu3',
@@ -78,7 +80,8 @@ deb_depends['15.04'] = {'libc6': '2.21-0ubuntu4',
                         'libcurl3': '7.38.0-3ubuntu2',
                         'libzip2': '0.11.2-1.2',
                         'libwxgtk3.0-0': '3.0.2-1',
-                        'libsamplerate0': '0.1.8-8'}
+                        'libsamplerate0': '0.1.8-8',
+                        'libicu52': '52.1-8'}
 
 deb_depends['7'] = {'libc6': '2.13',
                     'libssh-4': '0.5.4',
@@ -96,7 +99,8 @@ deb_depends['7'] = {'libc6': '2.13',
                     'libzip2': '0.10.1',
                     'libcairomm-1.0-1': '1.10.0-1',
                     'libpangomm-1.4-1': '2.28.4-1',
-                    'libsamplerate0': '0.1.8-5'}
+                    'libsamplerate0': '0.1.8-5',
+                    'libicu48': '4.8.1.1-12+deb7u3'}
 
 deb_depends['8'] = {'libc6': '2.19-18',
                     'libssh-4': '0.6.3-4',
@@ -115,7 +119,8 @@ deb_depends['8'] = {'libc6': '2.19-18',
                     'libwxgtk3.0-0': '3.0.2',
                     'libxcb-xfixes0': '1.10',
                     'libxcb-shape0': '1.10',
-                    'libsamplerate0': '0.1.8-8'}
+                    'libsamplerate0': '0.1.8-8',
+                    'libicu52': '52.1-8+deb8u2'}
 
 deb_depends['unstable'] = {'libc6': '2.13',
                     'libssh-4': '0.5.4',
@@ -131,7 +136,8 @@ deb_depends['unstable'] = {'libc6': '2.13',
                     'libxmlsec1-openssl': '1.2.18',
                     'libcurl3': '7.26.0',
                     'libzip2': '0.10.1',
-                    'libsamplerate0': '0.1.8-8'}
+                    'libsamplerate0': '0.1.8-8',
+                    'libicu52': '52.1-9'}
 
 def packages(name, packages, f):
     s = '%s: ' % name
@@ -246,8 +252,8 @@ def dependencies(target):
         ffmpeg_options = {}
 
     return (('ffmpeg-cdist', '0e27b2b', ffmpeg_options),
-            ('libdcp', '1484800'),
-            ('libsub', '992b91d'))
+            ('libdcp', 'b3adb83'),
+            ('libsub', 'e959731'))
 
 def configure_options(target):
     opt = ''
index 4cb7d9714fa1b5dc2ac4cd4d86b426a1c6e3b5b7..5d06eb766e9da1e450b96ac5c7b39397a5af7c27 100644 (file)
@@ -1,4 +1,4 @@
-dcpomatic (2.3.5-1) UNRELEASED; urgency=low
+dcpomatic (2.3.6-1) UNRELEASED; urgency=low
 
   [ Carl Hetherington ]
   * New upstream release.
@@ -312,8 +312,9 @@ dcpomatic (2.3.5-1) UNRELEASED; urgency=low
   * New upstream release.
   * New upstream release.
   * New upstream release.
+  * New upstream release.
 
- -- Carl Hetherington <carl@d1stkfactory>  Mon, 21 Sep 2015 12:26:01 +0100
+ -- Carl Hetherington <carl@d1stkfactory>  Sat, 26 Sep 2015 14:43:58 +0100
 
 dcpomatic (0.87-1) UNRELEASED; urgency=low
 
index 30a0f8e992abceac90f0415541a28cd7ad807457..0e3d13de8383349327921634a084eb723625f80f 100644 (file)
@@ -113,6 +113,16 @@ universal_copy_lib $ENV libcairo "$WORK/$libs"
 universal_copy_lib $ENV libpixman "$WORK/$libs"
 universal_copy_lib $ENV libharfbuzz "$WORK/$libs"
 universal_copy_lib $ENV libsamplerate "$WORK/$libs"
+universal_copy_lib $ENV libicui18n "$WORK/$libs"
+universal_copy_lib $ENV libicudata "$WORK/$libs"
+universal_copy_lib $ENV libicuio "$WORK/$libs"
+universal_copy_lib $ENV libicule "$WORK/$libs"
+universal_copy_lib $ENV libiculx "$WORK/$libs"
+universal_copy_lib $ENV libicutest "$WORK/$libs"
+universal_copy_lib $ENV libicutu "$WORK/$libs"
+universal_copy_lib $ENV libicuuc "$WORK/$libs"
+universal_copy_lib $ENV libicudt "$WORK/$libs"
+universal_copy_lib $ENV libicuin "$WORK/$libs"
 
 relink=`echo $relink | sed -e "s/\+//g"`
 
index 5277a5ee18bda622a46dc2da501ec4fbb79d5c74..3b9882b2fbb11471acef74c1714b6f8c8b95fe29 100644 (file)
@@ -120,6 +120,9 @@ File "%static_deps%/bin/libcairomm-1.0-1.dll"
 File "%static_deps%/bin/libpangomm-1.4-1.dll"
 File "%static_deps%/bin/pango-querymodules.exe"
 File "%static_deps%/bin/libsamplerate-0.dll"
+File "%static_deps%/lib/icuuc52.dll"
+File "%static_deps%/lib/icudt52.dll"
+File "%static_deps%/lib/icuin52.dll"
 
 File "%cdist_deps%/bin/asdcp-libdcp-1.0.dll"
 File "%cdist_deps%/bin/kumu-libdcp-1.0.dll"
index 792eb2c97cb9c793762a701d4a1cd5c32606ab51..7d755a46cf1a7e53a826f3f7ddd1f596b35ccc81 100644 (file)
@@ -116,7 +116,7 @@ public:
         *  at some sampling rate.
         *  @param r Sampling rate.
         */
-       Time<S, O> round_up (float r) {
+       Time<S, O> round_up (float r) const {
                Type const n = llrintf (HZ / r);
                Type const a = _t + n - 1;
                return Time<S, O> (a - (a % n));
index 7240611ee7757b3acc760ed0acb9f4e2d571e794..6939f81a34765b68366d5b958019299e4a3f3ab8 100644 (file)
@@ -263,4 +263,12 @@ public:
        ProgrammingError (std::string file, int line);
 };
 
+class TextEncodingError : public StringError
+{
+public:
+       TextEncodingError (std::string s)
+               : StringError (s)
+       {}
+};
+
 #endif
index 8fa03fdffb552f3ddbd49042df0d41d084d5d6ed..29dee2c86d1e7679c15d7b6442529c8298d9d7ae 100644 (file)
@@ -25,6 +25,7 @@
 #include "raw_convert.h"
 #include "log.h"
 #include "ffmpeg_subtitle_stream.h"
+#include "ffmpeg_audio_stream.h"
 #include "compose.hpp"
 extern "C" {
 #include <libavcodec/avcodec.h>
@@ -32,6 +33,7 @@ extern "C" {
 #include <libswscale/swscale.h>
 }
 #include <boost/algorithm/string.hpp>
+#include <boost/foreach.hpp>
 #include <iostream>
 
 #include "i18n.h"
@@ -39,7 +41,9 @@ extern "C" {
 using std::string;
 using std::cout;
 using std::cerr;
+using std::vector;
 using boost::shared_ptr;
+using boost::optional;
 
 boost::mutex FFmpeg::_mutex;
 boost::weak_ptr<Log> FFmpeg::_ffmpeg_log;
@@ -263,3 +267,56 @@ FFmpeg::subtitle_period (AVSubtitle const & sub)
                packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3)
                );
 }
+
+/** Compute the pts offset to use given a set of audio streams and some video details.
+ *  Sometimes these parameters will have just been determined by an Examiner, sometimes
+ *  they will have been retrieved from a piece of Content, hence the need for this method
+ *  in FFmpeg.
+ */
+ContentTime
+FFmpeg::pts_offset (vector<shared_ptr<FFmpegAudioStream> > audio_streams, optional<ContentTime> first_video, double video_frame_rate) const
+{
+       /* Audio and video frame PTS values may not start with 0.  We want
+          to fiddle them so that:
+
+          1.  One of them starts at time 0.
+          2.  The first video PTS value ends up on a frame boundary.
+
+          Then we remove big initial gaps in PTS and we allow our
+          insertion of black frames to work.
+
+          We will do:
+            audio_pts_to_use = audio_pts_from_ffmpeg + pts_offset;
+            video_pts_to_use = video_pts_from_ffmpeg + pts_offset;
+       */
+
+       /* First, make one of them start at 0 */
+
+       ContentTime po = ContentTime::min ();
+
+       if (first_video) {
+               po = - first_video.get ();
+       }
+
+       BOOST_FOREACH (shared_ptr<FFmpegAudioStream> i, audio_streams) {
+               if (i->first_audio) {
+                       po = max (po, - i->first_audio.get ());
+               }
+       }
+
+       /* If the offset is positive we would be pushing things from a -ve PTS to be played.
+          I don't think we ever want to do that, as it seems things at -ve PTS are not meant
+          to be seen (use for alignment bars etc.); see mantis #418.
+       */
+       if (po > ContentTime ()) {
+               po = ContentTime ();
+       }
+
+       /* Now adjust so that the video pts starts on a frame */
+       if (first_video) {
+               ContentTime const fvc = first_video.get() + po;
+               po += fvc.round_up (video_frame_rate) - fvc;
+       }
+
+       return po;
+}
index 961f5cbb1e2b259b8d205ceb93e808bc0867e767..b3bc13e5cc222b9033472bfb43df8f3b70693fd7 100644 (file)
@@ -33,6 +33,7 @@ struct AVFrame;
 struct AVIOContext;
 
 class FFmpegContent;
+class FFmpegAudioStream;
 class Log;
 
 class FFmpeg
@@ -51,6 +52,10 @@ public:
 protected:
        AVCodecContext* video_codec_context () const;
        AVCodecContext* subtitle_codec_context () const;
+       ContentTime pts_offset (
+               std::vector<boost::shared_ptr<FFmpegAudioStream> > audio_streams, boost::optional<ContentTime> first_video, double video_frame_rate
+               ) const;
+
        static FFmpegSubtitlePeriod subtitle_period (AVSubtitle const &);
 
        boost::shared_ptr<const FFmpegContent> _ffmpeg_content;
index d343ec317e14c6a32cc8f23574ff9613f698627b..475418d3d2be34f9d30a65b7cfcfcce5e3089f69 100644 (file)
@@ -67,51 +67,9 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log>
        , SubtitleDecoder (c)
        , FFmpeg (c)
        , _log (log)
+       , _pts_offset (pts_offset (c->ffmpeg_audio_streams(), c->first_video(), c->video_frame_rate()))
 {
-       /* Audio and video frame PTS values may not start with 0.  We want
-          to fiddle them so that:
 
-          1.  One of them starts at time 0.
-          2.  The first video PTS value ends up on a frame boundary.
-
-          Then we remove big initial gaps in PTS and we allow our
-          insertion of black frames to work.
-
-          We will do:
-            audio_pts_to_use = audio_pts_from_ffmpeg + pts_offset;
-            video_pts_to_use = video_pts_from_ffmpeg + pts_offset;
-       */
-
-       /* First, make one of them start at 0 */
-
-       vector<shared_ptr<FFmpegAudioStream> > streams = c->ffmpeg_audio_streams ();
-
-       _pts_offset = ContentTime::min ();
-
-       if (c->first_video ()) {
-               _pts_offset = - c->first_video().get ();
-       }
-
-       BOOST_FOREACH (shared_ptr<FFmpegAudioStream> i, streams) {
-               if (i->first_audio) {
-                       _pts_offset = max (_pts_offset, - i->first_audio.get ());
-               }
-       }
-
-       /* If _pts_offset is positive we would be pushing things from a -ve PTS to be played.
-          I don't think we ever want to do that, as it seems things at -ve PTS are not meant
-          to be seen (use for alignment bars etc.); see mantis #418.
-       */
-       if (_pts_offset > ContentTime ()) {
-               _pts_offset = ContentTime ();
-       }
-
-       /* Now adjust so that the video pts starts on a frame */
-       if (c->first_video ()) {
-               ContentTime first_video = c->first_video().get() + _pts_offset;
-               ContentTime const old_first_video = first_video;
-               _pts_offset += first_video.round_up (c->video_frame_rate ()) - old_first_video;
-       }
 }
 
 void
@@ -457,7 +415,7 @@ FFmpegDecoder::decode_subtitle_packet ()
                period.to = sub_period.to.get() + _pts_offset;
        } else {
                /* We have to look up the `to' time in the stream's records */
-               period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (sub_period.from);
+               period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (period.from);
        }
 
        AVSubtitleRect const * rect = sub.rects[0];
index 576782d0bd0d290b447a2e7b000d95d0559ea77d..40fe0d28ec5cae2e6bc89d800dd9ef57bb195402 100644 (file)
@@ -30,6 +30,7 @@ extern "C" {
 #include "ffmpeg_subtitle_stream.h"
 #include "util.h"
 #include "safe_stringstream.h"
+#include <boost/foreach.hpp>
 #include <iostream>
 
 #include "i18n.h"
@@ -134,6 +135,27 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr<const FFmpegContent> c, shared_ptr<Jo
                        break;
                }
        }
+
+       for (LastSubtitleMap::const_iterator i = _last_subtitle_start.begin(); i != _last_subtitle_start.end(); ++i) {
+               if (i->second) {
+                       i->first->add_subtitle (
+                               ContentTimePeriod (
+                                       i->second.get (),
+                                       ContentTime::from_frames (video_length(), video_frame_rate().get_value_or (24))
+                                       )
+                               );
+               }
+       }
+
+       /* We just added subtitles to our streams without taking the PTS offset into account;
+          this is because we might not know the PTS offset when the first subtitle is seen.
+          Now we know the PTS offset so we can apply it to those subtitles.
+       */
+       if (video_frame_rate()) {
+               BOOST_FOREACH (shared_ptr<FFmpegSubtitleStream> i, _subtitle_streams) {
+                       i->add_offset (pts_offset (_audio_streams, _first_video, video_frame_rate().get()));
+               }
+       }
 }
 
 void
@@ -176,14 +198,23 @@ FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr<FFmpegSubti
        AVSubtitle sub;
        if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) {
                FFmpegSubtitlePeriod const period = subtitle_period (sub);
-               if (sub.num_rects <= 0 && _last_subtitle_start) {
-                       stream->add_subtitle (ContentTimePeriod (_last_subtitle_start.get (), period.from));
-                       _last_subtitle_start = optional<ContentTime> ();
+               LastSubtitleMap::iterator last = _last_subtitle_start.find (stream);
+               if (last != _last_subtitle_start.end() && last->second) {
+                       /* We have seen the start of a subtitle but not yet the end.  Whatever this is
+                          finishes the previous subtitle, so add it */
+                       stream->add_subtitle (ContentTimePeriod (last->second.get (), period.from));
+                       if (sub.num_rects == 0) {
+                               /* This is a `proper' end-of-subtitle */
+                               _last_subtitle_start[stream] = optional<ContentTime> ();
+                       } else {
+                               /* This is just another subtitle, so we start again */
+                               _last_subtitle_start[stream] = period.from;
+                       }
                } else if (sub.num_rects == 1) {
                        if (period.to) {
                                stream->add_subtitle (ContentTimePeriod (period.from, period.to.get ()));
                        } else {
-                               _last_subtitle_start = period.from;
+                               _last_subtitle_start[stream] = period.from;
                        }
                }
                avsubtitle_free (&sub);
index 6fd3220d426377a03b9b609ec98a8f899a7483f5..27bff08b47426749eeee7201234a1aed4e955238 100644 (file)
@@ -85,5 +85,6 @@ private:
        Frame _video_length;
        bool _need_video_length;
 
-       boost::optional<ContentTime> _last_subtitle_start;
+       typedef std::map<boost::shared_ptr<FFmpegSubtitleStream>, boost::optional<ContentTime> > LastSubtitleMap;
+       LastSubtitleMap _last_subtitle_start;
 };
index 27099b0f34ccf000b3d93179b4cad40c8f2c63ed..e12075581b12705a71d53153904218a6950b1cce 100644 (file)
@@ -86,3 +86,14 @@ FFmpegSubtitleStream::find_subtitle_to (ContentTime from) const
        DCPOMATIC_ASSERT (i != _subtitles.end ());
        return i->second;
 }
+
+/** Add some offset to all the times in the stream */
+void
+FFmpegSubtitleStream::add_offset (ContentTime offset)
+{
+       map<ContentTime, ContentTime> fixed;
+       for (map<ContentTime, ContentTime>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+               fixed[i->first + offset] = i->second + offset;
+       }
+       _subtitles = fixed;
+}
index a39b10ffd6624804baa80916a1cfe1f894d32a9e..0809b359a1a6daa7b5349455fadab624ec08e418 100644 (file)
@@ -34,8 +34,8 @@ public:
        void add_subtitle (ContentTimePeriod period);
        std::list<ContentTimePeriod> subtitles_during (ContentTimePeriod period, bool starting) const;
        ContentTime find_subtitle_to (ContentTime from) const;
+       void add_offset (ContentTime offset);
 
 private:
        std::map<ContentTime, ContentTime> _subtitles;
 };
-
index c228defc541737542c67b5f08045a428b16d849c..7aaac748c115a1d9e65144027e7eacf96f1d6640 100644 (file)
@@ -59,6 +59,7 @@ Job::~Job ()
 {
        if (_thread) {
                _thread->interrupt ();
+               DCPOMATIC_ASSERT (_thread->joinable ());
                _thread->join ();
        }
 
@@ -416,7 +417,10 @@ Job::cancel ()
        }
 
        _thread->interrupt ();
+       DCPOMATIC_ASSERT (_thread->joinable ());
        _thread->join ();
+       delete _thread;
+       _thread = 0;
 }
 
 void
@@ -447,4 +451,3 @@ Job::when_finished (boost::signals2::connection& connection, function<void()> fi
                connection = Finished.connect (finished);
        }
 }
-
index 545fd956c11548bdc409525c93d8bce5454b0fe3..86e010c106b929d0cb6869026eda4a70baab86e5 100644 (file)
@@ -62,6 +62,7 @@ JobManager::~JobManager ()
        }
 
        if (_scheduler) {
+               DCPOMATIC_ASSERT (_scheduler->joinable ());
                _scheduler->join ();
        }
 
index c1db1e6ac866d632c62aead5cd71986875a13151..f0b2d9816eedf581178853c72b873c8622649476 100644 (file)
@@ -39,6 +39,7 @@
 #include <libxml++/libxml++.h>
 #include <boost/algorithm/string.hpp>
 #include <boost/scoped_array.hpp>
+#include <boost/foreach.hpp>
 #include <string>
 #include <vector>
 #include <iostream>
@@ -81,15 +82,19 @@ Server::~Server ()
                _full_condition.notify_all ();
        }
 
-       for (vector<boost::thread*>::iterator i = _worker_threads.begin(); i != _worker_threads.end(); ++i) {
-               (*i)->join ();
-               delete *i;
+       BOOST_FOREACH (boost::thread* i, _worker_threads) {
+               DCPOMATIC_ASSERT (i->joinable ());
+               i->join ();
+               delete i;
        }
 
        _io_service.stop ();
 
        _broadcast.io_service.stop ();
-       _broadcast.thread->join ();
+       if (_broadcast.thread) {
+               DCPOMATIC_ASSERT (_broadcast.thread->joinable ());
+               _broadcast.thread->join ();
+       }
 }
 
 /** @param after_read Filled in with gettimeofday() after reading the input from the network.
index 4b532f98129d4f70386040d9dfc182761973312f..642767e8bdda60f1c5e2f751619015da886c4382 100644 (file)
@@ -63,11 +63,13 @@ ServerFinder::~ServerFinder ()
 
        _search_condition.notify_all ();
        if (_search_thread) {
+               DCPOMATIC_ASSERT (_search_thread->joinable ());
                _search_thread->join ();
        }
 
        _listen_io_service.stop ();
        if (_listen_thread) {
+               DCPOMATIC_ASSERT (_listen_thread->joinable ());
                _listen_thread->join ();
        }
 }
index f1986795270c4cf2b8680cd4145035abaadf5aa8..a707d1f9fd13641301a3782f0ee84b272ea5a8c9 100644 (file)
 #include "cross.h"
 #include "exceptions.h"
 #include "subrip_content.h"
+#include "data.h"
 #include <sub/subrip_reader.h>
 #include <sub/collect.h>
+#include <unicode/ucsdet.h>
+#include <unicode/ucnv.h>
+#include <iostream>
 
 #include "i18n.h"
 
 using std::vector;
+using std::cout;
+using std::string;
 using boost::shared_ptr;
+using boost::scoped_array;
 
 SubRip::SubRip (shared_ptr<const SubRipContent> content)
 {
-       FILE* f = fopen_boost (content->path (0), "r");
-       if (!f) {
-               throw OpenFileError (content->path (0));
-       }
+       Data in (content->path (0));
+
+       UErrorCode status = U_ZERO_ERROR;
+       UCharsetDetector* detector = ucsdet_open (&status);
+       ucsdet_setText (detector, reinterpret_cast<const char *> (in.data().get()), in.size(), &status);
+
+       UCharsetMatch const * match = ucsdet_detect (detector, &status);
+       char const * in_charset = ucsdet_getName (match, &status);
+
+       UConverter* to_utf16 = ucnv_open (in_charset, &status);
+       /* This is a guess; I think we should be able to encode any input in 4 times its input size */
+       scoped_array<uint16_t> utf16 (new uint16_t[in.size() * 2]);
+       int const utf16_len = ucnv_toUChars (
+               to_utf16, reinterpret_cast<UChar*>(utf16.get()), in.size() * 2,
+               reinterpret_cast<const char *> (in.data().get()), in.size(),
+               &status
+               );
+
+       UConverter* to_utf8 = ucnv_open ("UTF-8", &status);
+       /* Another guess */
+       scoped_array<char> utf8 (new char[utf16_len * 2]);
+       ucnv_fromUChars (to_utf8, utf8.get(), utf16_len * 2, reinterpret_cast<UChar*>(utf16.get()), utf16_len, &status);
+
+       ucsdet_close (detector);
+       ucnv_close (to_utf16);
+       ucnv_close (to_utf8);
 
-       sub::SubripReader reader (f);
+       sub::SubripReader reader (utf8.get());
        _subtitles = sub::collect<vector<sub::Subtitle> > (reader.subtitles ());
 }
 
index 4ee728fdeb6252ecad7f9f6bfecc52bc744e94bf..4c5075e20f3d81a1e298cf0f914e3434fd616497 100644 (file)
@@ -86,6 +86,7 @@ UpdateChecker::~UpdateChecker ()
 
        _condition.notify_all ();
        if (_thread) {
+               DCPOMATIC_ASSERT (_thread->joinable ());
                _thread->join ();
        }
        delete _thread;
index 1318cc20f2a6b93514dc897ba26a5218493ce6b7..bace6602dfb6331fa74a643244757fc4f9caeb81 100644 (file)
@@ -477,6 +477,7 @@ Writer::terminate_thread (bool can_throw)
        _full_condition.notify_all ();
        lock.unlock ();
 
+       DCPOMATIC_ASSERT (_thread->joinable ());
        _thread->join ();
        if (can_throw) {
                rethrow ();
index a95905578ecc494458b0c9906ac7fd4e9e60c48d..3daf3ecf4b667d182b96a79002c51f849f7a0292 100644 (file)
@@ -143,7 +143,7 @@ def build(bld):
                  AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
                  BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 BOOST_REGEX
                  SNDFILE SAMPLERATE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
-                 CURL ZIP FONTCONFIG PANGOMM CAIROMM XMLSEC SUB
+                 CURL ZIP FONTCONFIG PANGOMM CAIROMM XMLSEC SUB ICU
                  """
 
     if bld.env.TARGET_OSX:
index 33a631e6e2679c4d735c2400d298ab48d0b7e719..72d8ac9a80a225494e8f3a868f79ff99fa743633 100644 (file)
@@ -29,6 +29,7 @@ def configure(conf):
 def build(bld):
     uselib =  'BOOST_THREAD BOOST_DATETIME OPENJPEG DCP XMLSEC CXML XMLPP AVFORMAT AVFILTER AVCODEC '
     uselib += 'AVUTIL SWSCALE POSTPROC CURL BOOST_FILESYSTEM SSH ZIP CAIROMM FONTCONFIG PANGOMM SUB MAGICK SNDFILE SAMPLERATE BOOST_REGEX '
+    uselib += 'ICU '
 
     if bld.env.TARGET_WINDOWS:
         uselib += 'WINSOCK2'
index 66a53c5594a9cb14f61c40b567c7313accc35308..e15263670059eb72ab496d8ead51e0d16dd954ff 100644 (file)
@@ -166,7 +166,7 @@ HintsDialog::film_changed ()
                }
        }
 
-       if (three_d > 0) {
+       if (three_d > 0 && !film->three_d()) {
                hint = true;
                _text->WriteText (_("You are using 3D content but your DCP is set to 2D.  Set the DCP to 3D if you want to play it back on a 3D system (e.g. Real-D, MasterImage etc.)"));
                _text->Newline ();
index af7953f8fd52abcf444be8fe741781ce523dae14..7d3498e750ed7c8ed57e3efaf521cf35353a21e6 160000 (submodule)
--- a/test/data
+++ b/test/data
@@ -1 +1 @@
-Subproject commit af7953f8fd52abcf444be8fe741781ce523dae14
+Subproject commit 7d3498e750ed7c8ed57e3efaf521cf35353a21e6
index 1a1038e8dba8f2cacb5ec441cdfa5afd82b0946f..6ee995e36c114de49a6eb77fd2bdf1c6d069769b 100644 (file)
@@ -31,7 +31,7 @@ def build(bld):
     obj = bld(features='cxx cxxprogram')
     obj.name   = 'unit-tests'
     obj.uselib =  'BOOST_TEST BOOST_THREAD BOOST_FILESYSTEM BOOST_DATETIME SNDFILE SAMPLERATE DCP OPENJPEG FONTCONFIG CAIROMM PANGOMM XMLPP '
-    obj.uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML MAGICK SUB GLIB CURL SSH XMLSEC BOOST_REGEX '
+    obj.uselib += 'AVFORMAT AVFILTER AVCODEC AVUTIL SWSCALE POSTPROC CXML MAGICK SUB GLIB CURL SSH XMLSEC BOOST_REGEX ICU '
     if bld.env.TARGET_WINDOWS:
         obj.uselib += 'WINSOCK2'
     obj.use    = 'libdcpomatic2'
diff --git a/wscript b/wscript
index 3afb75caeaee8308985355888c4b10b4bea98f4e..0c08e499eabb6ff082e1c8bfb1cb7f83c15de68f 100644 (file)
--- a/wscript
+++ b/wscript
@@ -26,7 +26,7 @@ import distutils.spawn
 from waflib import Logs
 
 APPNAME = 'dcpomatic'
-VERSION = '2.3.5devel'
+VERSION = '2.3.6devel'
 
 def options(opt):
     opt.load('compiler_cxx')
@@ -159,6 +159,22 @@ def configure(conf):
     else:
         conf.check_cfg(package='libcurl', args='--cflags --libs', uselib_store='CURL', mandatory=True)
 
+    # libicu
+    if conf.check_cfg(package='icu-i18n', args='--cflags --libs', uselib_store='ICU', mandatory=False) is None:
+        if conf.check_cfg(package='icu', args='--cflags --libs', uselib_store='ICU', mandatory=False) is None:
+            conf.check_cxx(fragment="""
+                            #include <unicode/ucsdet.h>
+                            int main(void) {
+                                UErrorCode status = U_ZERO_ERROR;
+                                UCharsetDetector* detector = ucsdet_open (&status);
+                                return 0; }\n
+                            """,
+                       mandatory=True,
+                       msg='Checking for libicu',
+                       okmsg='yes',
+                       libpath=['/usr/local/lib', '/usr/lib', '/usr/lib/x86_64-linux-gnu'],
+                       lib=['icuio', 'icui18n', 'icudata', 'icuuc'],
+                       uselib_store='ICU')
 
     # libsndfile
     conf.check_cfg(package='sndfile', args='--cflags --libs', uselib_store='SNDFILE', mandatory=True)