summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2015-09-27 01:36:47 +0100
committerCarl Hetherington <cth@carlh.net>2015-09-27 01:36:47 +0100
commit267e21bfb78593bcb87eb24ce01b88d0859566f7 (patch)
treeeb716ee81a8eea4c1519e46fed93370fe24285c6 /src
parentbf38748add4653a214724f49d55890dae1ce2361 (diff)
parent0fa64650da252ce29389b62e5c40f32096dc9c4b (diff)
Merge branch 'master' of ssh://git.carlh.net/home/carl/git/dcpomatic
Diffstat (limited to 'src')
-rw-r--r--src/lib/dcpomatic_time.h2
-rw-r--r--src/lib/exceptions.h8
-rw-r--r--src/lib/ffmpeg.cc57
-rw-r--r--src/lib/ffmpeg.h5
-rw-r--r--src/lib/ffmpeg_decoder.cc46
-rw-r--r--src/lib/ffmpeg_examiner.cc39
-rw-r--r--src/lib/ffmpeg_examiner.h3
-rw-r--r--src/lib/ffmpeg_subtitle_stream.cc11
-rw-r--r--src/lib/ffmpeg_subtitle_stream.h2
-rw-r--r--src/lib/job.cc5
-rw-r--r--src/lib/job_manager.cc1
-rw-r--r--src/lib/server.cc13
-rw-r--r--src/lib/server_finder.cc2
-rw-r--r--src/lib/subrip.cc39
-rw-r--r--src/lib/update_checker.cc1
-rw-r--r--src/lib/writer.cc1
-rw-r--r--src/lib/wscript2
-rw-r--r--src/tools/wscript1
-rw-r--r--src/wx/hints_dialog.cc2
19 files changed, 177 insertions, 63 deletions
diff --git a/src/lib/dcpomatic_time.h b/src/lib/dcpomatic_time.h
index 792eb2c97..7d755a46c 100644
--- a/src/lib/dcpomatic_time.h
+++ b/src/lib/dcpomatic_time.h
@@ -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));
diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h
index 7240611ee..6939f81a3 100644
--- a/src/lib/exceptions.h
+++ b/src/lib/exceptions.h
@@ -263,4 +263,12 @@ public:
ProgrammingError (std::string file, int line);
};
+class TextEncodingError : public StringError
+{
+public:
+ TextEncodingError (std::string s)
+ : StringError (s)
+ {}
+};
+
#endif
diff --git a/src/lib/ffmpeg.cc b/src/lib/ffmpeg.cc
index 8fa03fdff..29dee2c86 100644
--- a/src/lib/ffmpeg.cc
+++ b/src/lib/ffmpeg.cc
@@ -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;
+}
diff --git a/src/lib/ffmpeg.h b/src/lib/ffmpeg.h
index 961f5cbb1..b3bc13e5c 100644
--- a/src/lib/ffmpeg.h
+++ b/src/lib/ffmpeg.h
@@ -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;
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index d343ec317..475418d3d 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -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];
diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc
index 576782d0b..40fe0d28e 100644
--- a/src/lib/ffmpeg_examiner.cc
+++ b/src/lib/ffmpeg_examiner.cc
@@ -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);
diff --git a/src/lib/ffmpeg_examiner.h b/src/lib/ffmpeg_examiner.h
index 6fd3220d4..27bff08b4 100644
--- a/src/lib/ffmpeg_examiner.h
+++ b/src/lib/ffmpeg_examiner.h
@@ -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;
};
diff --git a/src/lib/ffmpeg_subtitle_stream.cc b/src/lib/ffmpeg_subtitle_stream.cc
index 27099b0f3..e12075581 100644
--- a/src/lib/ffmpeg_subtitle_stream.cc
+++ b/src/lib/ffmpeg_subtitle_stream.cc
@@ -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;
+}
diff --git a/src/lib/ffmpeg_subtitle_stream.h b/src/lib/ffmpeg_subtitle_stream.h
index a39b10ffd..0809b359a 100644
--- a/src/lib/ffmpeg_subtitle_stream.h
+++ b/src/lib/ffmpeg_subtitle_stream.h
@@ -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;
};
-
diff --git a/src/lib/job.cc b/src/lib/job.cc
index c228defc5..7aaac748c 100644
--- a/src/lib/job.cc
+++ b/src/lib/job.cc
@@ -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);
}
}
-
diff --git a/src/lib/job_manager.cc b/src/lib/job_manager.cc
index 545fd956c..86e010c10 100644
--- a/src/lib/job_manager.cc
+++ b/src/lib/job_manager.cc
@@ -62,6 +62,7 @@ JobManager::~JobManager ()
}
if (_scheduler) {
+ DCPOMATIC_ASSERT (_scheduler->joinable ());
_scheduler->join ();
}
diff --git a/src/lib/server.cc b/src/lib/server.cc
index c1db1e6ac..f0b2d9816 100644
--- a/src/lib/server.cc
+++ b/src/lib/server.cc
@@ -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.
diff --git a/src/lib/server_finder.cc b/src/lib/server_finder.cc
index 4b532f981..642767e8b 100644
--- a/src/lib/server_finder.cc
+++ b/src/lib/server_finder.cc
@@ -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 ();
}
}
diff --git a/src/lib/subrip.cc b/src/lib/subrip.cc
index f19867952..a707d1f9f 100644
--- a/src/lib/subrip.cc
+++ b/src/lib/subrip.cc
@@ -21,22 +21,51 @@
#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 ());
}
diff --git a/src/lib/update_checker.cc b/src/lib/update_checker.cc
index 4ee728fde..4c5075e20 100644
--- a/src/lib/update_checker.cc
+++ b/src/lib/update_checker.cc
@@ -86,6 +86,7 @@ UpdateChecker::~UpdateChecker ()
_condition.notify_all ();
if (_thread) {
+ DCPOMATIC_ASSERT (_thread->joinable ());
_thread->join ();
}
delete _thread;
diff --git a/src/lib/writer.cc b/src/lib/writer.cc
index 1318cc20f..bace6602d 100644
--- a/src/lib/writer.cc
+++ b/src/lib/writer.cc
@@ -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 ();
diff --git a/src/lib/wscript b/src/lib/wscript
index a95905578..3daf3ecf4 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -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:
diff --git a/src/tools/wscript b/src/tools/wscript
index 33a631e6e..72d8ac9a8 100644
--- a/src/tools/wscript
+++ b/src/tools/wscript
@@ -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'
diff --git a/src/wx/hints_dialog.cc b/src/wx/hints_dialog.cc
index 66a53c559..e15263670 100644
--- a/src/wx/hints_dialog.cc
+++ b/src/wx/hints_dialog.cc
@@ -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 ();