class ContentSubtitle
{
public:
- ContentSubtitle (ContentTimePeriod p)
- : _period (p)
+ ContentSubtitle (ContentTime f)
+ : _from (f)
{}
- ContentTimePeriod period () const {
- return _period;
+ ContentTime from () const {
+ return _from;
}
private:
- ContentTimePeriod _period;
+ ContentTime _from;
};
class ContentImageSubtitle : public ContentSubtitle
{
public:
- ContentImageSubtitle (ContentTimePeriod p, boost::shared_ptr<Image> im, dcpomatic::Rect<double> r)
- : ContentSubtitle (p)
+ ContentImageSubtitle (ContentTime f, boost::shared_ptr<Image> im, dcpomatic::Rect<double> r)
+ : ContentSubtitle (f)
, sub (im, r)
{}
class ContentTextSubtitle : public ContentSubtitle
{
public:
- ContentTextSubtitle (ContentTimePeriod p, std::list<dcp::SubtitleString> s)
- : ContentSubtitle (p)
+ ContentTextSubtitle (ContentTime f, std::list<dcp::SubtitleString> s)
+ : ContentSubtitle (f)
, subs (s)
{}
FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log> log)
: FFmpeg (c)
, _log (log)
+ , _have_current_subtitle (false)
{
if (c->video) {
video.reset (new VideoDecoder (this, c, log));
if (subtitle_codec_context ()) {
avcodec_flush_buffers (subtitle_codec_context ());
}
+
+ _have_current_subtitle = false;
}
void
return;
}
+ /* Stop any current subtitle, either at the time it was supposed to stop, or now if now is sooner */
+ if (_have_current_subtitle) {
+ if (_current_subtitle_to) {
+ subtitle->emit_stop (min(*_current_subtitle_to, subtitle_period(sub).from + _pts_offset));
+ } else {
+ subtitle->emit_stop (subtitle_period(sub).from + _pts_offset);
+ }
+ _have_current_subtitle = false;
+ }
+
if (sub.num_rects <= 0) {
- /* Sometimes we get an empty AVSubtitle, which is used by some codecs to
- indicate that the previous subtitle should stop. We can ignore it here.
- */
+ /* Nothing new in this subtitle */
return;
}
source that we may have chopped off for the DCP).
*/
FFmpegSubtitlePeriod sub_period = subtitle_period (sub);
- ContentTimePeriod period;
- period.from = sub_period.from + _pts_offset;
- /* We can't trust the `to' time from sub_period as there are some decoders which
- give a sub_period time for `to' which is subsequently overridden by a `stop' subtitle;
- see also FFmpegExaminer.
- */
- period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (subtitle_id (sub));
+ ContentTime from;
+ from = sub_period.from + _pts_offset;
+ _have_current_subtitle = true;
+ if (sub_period.to) {
+ _current_subtitle_to = *sub_period.to + _pts_offset;
+ }
for (unsigned int i = 0; i < sub.num_rects; ++i) {
AVSubtitleRect const * rect = sub.rects[i];
case SUBTITLE_NONE:
break;
case SUBTITLE_BITMAP:
- decode_bitmap_subtitle (rect, period);
+ decode_bitmap_subtitle (rect, from);
break;
case SUBTITLE_TEXT:
cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n";
break;
case SUBTITLE_ASS:
- decode_ass_subtitle (rect->ass, period);
+ decode_ass_subtitle (rect->ass, from);
break;
}
}
}
void
-FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period)
+FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from)
{
/* Note RGBA is expressed little-endian, so the first byte in the word is R, second
G, third B, fourth A.
static_cast<double> (rect->h) / target_height
);
- subtitle->emit_image (period, image, scaled_rect);
+ subtitle->emit_image_start (from, image, scaled_rect);
}
void
-FFmpegDecoder::decode_ass_subtitle (string ass, ContentTimePeriod period)
+FFmpegDecoder::decode_ass_subtitle (string ass, ContentTime from)
{
/* We have no styles and no Format: line, so I'm assuming that FFmpeg
produces a single format of Dialogue: lines...
list<sub::RawSubtitle> raw = sub::SSAReader::parse_line (base, bits[9]);
BOOST_FOREACH (sub::Subtitle const & i, sub::collect<list<sub::Subtitle> > (raw)) {
- subtitle->emit_text (period, i);
+ subtitle->emit_text_start (from, i);
}
}
void decode_audio_packet ();
void decode_subtitle_packet ();
- void decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimePeriod period);
- void decode_ass_subtitle (std::string ass, ContentTimePeriod period);
+ void decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime from);
+ void decode_ass_subtitle (std::string ass, ContentTime from);
void maybe_add_subtitle ();
boost::shared_ptr<AudioBuffers> deinterleave_audio (boost::shared_ptr<FFmpegAudioStream> stream) const;
boost::mutex _filter_graphs_mutex;
ContentTime _pts_offset;
+ boost::optional<ContentTime> _current_subtitle_to;
+ bool _have_current_subtitle;
};
}
}
- if (job) {
- if (_need_video_length) {
- job->sub (_("Finding length and subtitles"));
- } else if (!_subtitle_streams.empty()) {
- job->sub (_("Finding subtitles"));
- } else {
- job->sub (_("Finding length"));
- }
+ if (job && _need_video_length) {
+ job->sub (_("Finding length"));
}
/* Run through until we find:
* - the first video.
* - the first audio for each stream.
- * - the subtitle periods for each stream.
- *
- * We have to note subtitle periods as otherwise we have no way of knowing
- * where we should look for subtitles (video and audio are always present,
- * so they are ok).
*/
int64_t const len = _file_group.length ();
}
}
- for (size_t i = 0; i < _subtitle_streams.size(); ++i) {
- if (_subtitle_streams[i]->uses_index (_format_context, _packet.stream_index)) {
- subtitle_packet (context, _subtitle_streams[i]);
- }
- }
-
av_packet_unref (&_packet);
- if (_first_video && got_all_audio && _subtitle_streams.empty ()) {
+ if (_first_video && got_all_audio) {
/* All done */
break;
}
}
-
- /* Finish off any hanging subtitles at the end */
- for (LastSubtitleMap::const_iterator i = _last_subtitle_start.begin(); i != _last_subtitle_start.end(); ++i) {
- if (i->second) {
- if (i->first->unknown_to (i->second->id)) {
- i->first->set_subtitle_to (
- i->second->id,
- 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 (has_video() && 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
}
}
-void
-FFmpegExaminer::subtitle_packet (AVCodecContext* context, shared_ptr<FFmpegSubtitleStream> stream)
-{
- int frame_finished;
- AVSubtitle sub;
- if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) {
- string id = subtitle_id (sub);
- FFmpegSubtitlePeriod const period = subtitle_period (sub);
- bool const starts_image = subtitle_starts_image (sub);
-
- /* Some streams (notably DVB streams) have subtitles which have a specified end time
- but which are then stopped earlier than this by a zero-num_rect subtitle.
- */
-
- LastSubtitleMap::iterator last = _last_subtitle_start.find (stream);
- if (sub.num_rects == 0 && last != _last_subtitle_start.end() && last->second) {
- /* Set (or fix) the `to' time for the last subtitle */
- stream->set_subtitle_to (last->second->id, period.from);
- _last_subtitle_start[stream] = optional<SubtitleStart> ();
- } else if (sub.num_rects > 0) {
- /* Add a subtitle; if we don't know the `to' time we set it to the from time and fix it later */
- if (starts_image) {
- stream->add_image_subtitle (id, ContentTimePeriod (period.from, period.to.get_value_or (period.from)));
- } else {
- stream->add_text_subtitle (id, ContentTimePeriod (period.from, period.to.get_value_or (period.from)));
- }
-
- _last_subtitle_start[stream] = SubtitleStart (id, starts_image, period.from);
- }
-
- for (unsigned int i = 0; i < sub.num_rects; ++i) {
- if (sub.rects[i]->type == SUBTITLE_BITMAP) {
-#ifdef DCPOMATIC_HAVE_AVSUBTITLERECT_PICT
- uint32_t* palette = (uint32_t *) sub.rects[i]->pict.data[1];
- for (int j = 0; j < sub.rects[i]->nb_colors; ++j) {
- RGBA rgba (
- (palette[j] & 0x00ff0000) >> 16,
- (palette[j] & 0x0000ff00) >> 8,
- (palette[j] & 0x000000ff) >> 0,
- (palette[j] & 0xff000000) >> 24
- );
-
- stream->set_colour (rgba, rgba);
- }
-#else
- uint32_t* palette = (uint32_t *) sub.rects[i]->data[1];
- for (int j = 0; j < sub.rects[i]->nb_colors; ++j) {
- RGBA rgba (
- (palette[j] & 0x00ff0000) >> 16,
- (palette[j] & 0x0000ff00) >> 8,
- (palette[j] & 0x000000ff) >> 0,
- (palette[j] & 0xff000000) >> 24
- );
-
- stream->set_colour (rgba, rgba);
- }
-#endif
- }
- }
-
- avsubtitle_free (&sub);
- }
-}
-
optional<ContentTime>
FFmpegExaminer::frame_time (AVStream* s) const
{
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
private:
void video_packet (AVCodecContext *);
void audio_packet (AVCodecContext *, boost::shared_ptr<FFmpegAudioStream>);
- void subtitle_packet (AVCodecContext *, boost::shared_ptr<FFmpegSubtitleStream>);
std::string stream_name (AVStream* s) const;
std::string subtitle_stream_name (AVStream* s) const;
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
FFmpegSubtitleStream::FFmpegSubtitleStream (cxml::ConstNodePtr node, int version)
: FFmpegStream (node)
{
- if (version == 32) {
- BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Period")) {
- /* In version 32 we assumed that from times were unique, so they were
- used as identifiers. All subtitles were image subtitles.
- */
- add_image_subtitle (
- raw_convert<string> (i->string_child ("From")),
- ContentTimePeriod (
- ContentTime (i->number_child<ContentTime::Type> ("From")),
- ContentTime (i->number_child<ContentTime::Type> ("To"))
- )
- );
- }
- } else {
- /* In version 33 we use a hash of various parts of the subtitle as the id.
- <Subtitle> was initially used for image subtitles; later we have
- <ImageSubtitle> and <TextSubtitle>
- */
- BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Subtitle")) {
- add_image_subtitle (
- raw_convert<string> (i->string_child ("Id")),
- ContentTimePeriod (
- ContentTime (i->number_child<ContentTime::Type> ("From")),
- ContentTime (i->number_child<ContentTime::Type> ("To"))
- )
- );
- }
-
- BOOST_FOREACH (cxml::NodePtr i, node->node_children ("ImageSubtitle")) {
- add_image_subtitle (
- raw_convert<string> (i->string_child ("Id")),
- ContentTimePeriod (
- ContentTime (i->number_child<ContentTime::Type> ("From")),
- ContentTime (i->number_child<ContentTime::Type> ("To"))
- )
- );
- }
-
- BOOST_FOREACH (cxml::NodePtr i, node->node_children ("TextSubtitle")) {
- add_text_subtitle (
- raw_convert<string> (i->string_child ("Id")),
- ContentTimePeriod (
- ContentTime (i->number_child<ContentTime::Type> ("From")),
- ContentTime (i->number_child<ContentTime::Type> ("To"))
- )
- );
- }
-
+ if (version >= 33) {
BOOST_FOREACH (cxml::NodePtr i, node->node_children ("Colour")) {
_colours[RGBA(i->node_child("From"))] = RGBA (i->node_child("To"));
}
{
FFmpegStream::as_xml (root);
- as_xml (root, _image_subtitles, "ImageSubtitle");
- as_xml (root, _text_subtitles, "TextSubtitle");
-
for (map<RGBA, RGBA>::const_iterator i = _colours.begin(); i != _colours.end(); ++i) {
xmlpp::Node* node = root->add_child("Colour");
i->first.as_xml (node->add_child("From"));
}
}
-void
-FFmpegSubtitleStream::as_xml (xmlpp::Node* root, PeriodMap const & subs, string node_name) const
-{
- for (PeriodMap::const_iterator i = subs.begin(); i != subs.end(); ++i) {
- xmlpp::Node* node = root->add_child (node_name);
- node->add_child("Id")->add_child_text (i->first);
- node->add_child("From")->add_child_text (raw_convert<string> (i->second.from.get ()));
- node->add_child("To")->add_child_text (raw_convert<string> (i->second.to.get ()));
- }
-}
-
-void
-FFmpegSubtitleStream::add_image_subtitle (string id, ContentTimePeriod period)
-{
- DCPOMATIC_ASSERT (_image_subtitles.find (id) == _image_subtitles.end ());
- _image_subtitles[id] = period;
-}
-
-void
-FFmpegSubtitleStream::add_text_subtitle (string id, ContentTimePeriod period)
-{
- DCPOMATIC_ASSERT (_text_subtitles.find (id) == _text_subtitles.end ());
- _text_subtitles[id] = period;
-}
-
-ContentTime
-FFmpegSubtitleStream::find_subtitle_to (string id) const
-{
- PeriodMap::const_iterator i = _image_subtitles.find (id);
- if (i != _image_subtitles.end ()) {
- return i->second.to;
- }
-
- i = _text_subtitles.find (id);
- DCPOMATIC_ASSERT (i != _text_subtitles.end ());
- return i->second.to;
-}
-
-/** @param id Subtitle id.
- * @return true if the `from' and `to' times for this id are equal, which indicates
- * that the `to' time is unknown.
- */
-bool
-FFmpegSubtitleStream::unknown_to (string id) const
-{
- PeriodMap::const_iterator i = _image_subtitles.find (id);
- if (i != _image_subtitles.end ()) {
- return i->second.from == i->second.to;
- }
-
- i = _text_subtitles.find (id);
- DCPOMATIC_ASSERT (i != _text_subtitles.end ());
- return i->second.from == i->second.to;
-}
-
-/** Add some offset to all the times in the stream */
-void
-FFmpegSubtitleStream::add_offset (ContentTime offset)
-{
- for (PeriodMap::iterator i = _image_subtitles.begin(); i != _image_subtitles.end(); ++i) {
- i->second.from += offset;
- i->second.to += offset;
- }
-
- for (PeriodMap::iterator i = _text_subtitles.begin(); i != _text_subtitles.end(); ++i) {
- i->second.from += offset;
- i->second.to += offset;
- }
-}
-
map<RGBA, RGBA>
FFmpegSubtitleStream::colours () const
{
{
_colours[from] = to;
}
-
-bool
-FFmpegSubtitleStream::has_text () const
-{
- return !_text_subtitles.empty ();
-}
-
-bool
-FFmpegSubtitleStream::has_image () const
-{
- return !_image_subtitles.empty ();
-}
-
-void
-FFmpegSubtitleStream::set_subtitle_to (string id, ContentTime to)
-{
- PeriodMap::iterator i = _image_subtitles.find (id);
- if (i != _image_subtitles.end ()) {
- i->second.to = to;
- } else {
- i = _text_subtitles.find (id);
- if (i != _text_subtitles.end ()) {
- i->second.to = to;
- } else {
- DCPOMATIC_ASSERT (false);
- }
- }
-}
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
void as_xml (xmlpp::Node *) const;
- void add_image_subtitle (std::string id, ContentTimePeriod period);
- void add_text_subtitle (std::string id, ContentTimePeriod period);
- void set_subtitle_to (std::string id, ContentTime to);
- ContentTime find_subtitle_to (std::string id) const;
- bool unknown_to (std::string id) const;
- void add_offset (ContentTime offset);
void set_colour (RGBA from, RGBA to);
std::map<RGBA, RGBA> colours () const;
- bool has_text () const;
- bool has_image () const;
-
private:
-
- typedef std::map<std::string, ContentTimePeriod> PeriodMap;
-
- void as_xml (xmlpp::Node *, PeriodMap const & subs, std::string node) const;
-
- PeriodMap _image_subtitles;
- PeriodMap _text_subtitles;
std::map<RGBA, RGBA> _colours;
};
}
if (decoder->subtitle) {
- decoder->subtitle->ImageData.connect (bind (&Player::image_subtitle, this, weak_ptr<Piece> (piece), _1));
- decoder->subtitle->TextData.connect (bind (&Player::text_subtitle, this, weak_ptr<Piece> (piece), _1));
+ decoder->subtitle->ImageStart.connect (bind (&Player::image_subtitle_start, this, weak_ptr<Piece> (piece), _1));
+ decoder->subtitle->TextStart.connect (bind (&Player::text_subtitle_start, this, weak_ptr<Piece> (piece), _1));
+ decoder->subtitle->Stop.connect (bind (&Player::subtitle_stop, this, weak_ptr<Piece> (piece), _1));
}
}
optional<PositionImage> subtitles;
- for (list<pair<PlayerSubtitles, DCPTimePeriod> >::const_iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+ for (ActiveSubtitles::const_iterator i = _active_subtitles.begin(); i != _active_subtitles.end(); ++i) {
- if (!i->second.overlap (period)) {
+ shared_ptr<Piece> sub_piece = i->first.lock ();
+ if (!sub_piece) {
continue;
}
+ if (!sub_piece->content->subtitle->use() || (!_always_burn_subtitles && !piece->content->subtitle->burn())) {
+ continue;
+ }
+
+ pair<PlayerSubtitles, DCPTime> sub = i->second;
+
list<PositionImage> sub_images;
/* Image subtitles */
- list<PositionImage> c = transform_image_subtitles (i->first.image);
+ list<PositionImage> c = transform_image_subtitles (sub.first.image);
copy (c.begin(), c.end(), back_inserter (sub_images));
/* Text subtitles (rendered to an image) */
- if (!i->first.text.empty ()) {
- list<PositionImage> s = render_subtitles (i->first.text, i->first.fonts, _video_container_size, time);
+ if (!sub.first.text.empty ()) {
+ list<PositionImage> s = render_subtitles (sub.first.text, sub.first.fonts, _video_container_size, time);
copy (s.begin (), s.end (), back_inserter (sub_images));
}
Video (_last_video, time);
_last_video_time = time + one_video_frame ();
-
- /* Discard any subtitles we no longer need */
-
- for (list<pair<PlayerSubtitles, DCPTimePeriod> >::iterator i = _subtitles.begin (); i != _subtitles.end(); ) {
- list<pair<PlayerSubtitles, DCPTimePeriod> >::iterator tmp = i;
- ++tmp;
-
- if (i->second.to < time) {
- _subtitles.erase (i);
- }
-
- i = tmp;
- }
}
void
}
void
-Player::image_subtitle (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
+Player::image_subtitle_start (weak_ptr<Piece> wp, ContentImageSubtitle subtitle)
{
shared_ptr<Piece> piece = wp.lock ();
if (!piece) {
PlayerSubtitles ps;
ps.image.push_back (subtitle.sub);
- DCPTimePeriod period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
+ DCPTime from (content_time_to_dcp (piece, subtitle.from()));
- if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
- _subtitles.push_back (make_pair (ps, period));
- } else {
- Subtitle (ps, period);
- }
+ _active_subtitles[wp] = make_pair (ps, from);
}
void
-Player::text_subtitle (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
+Player::text_subtitle_start (weak_ptr<Piece> wp, ContentTextSubtitle subtitle)
{
shared_ptr<Piece> piece = wp.lock ();
if (!piece) {
}
PlayerSubtitles ps;
- DCPTimePeriod const period (content_time_to_dcp (piece, subtitle.period().from), content_time_to_dcp (piece, subtitle.period().to));
+ DCPTime const from (content_time_to_dcp (piece, subtitle.from()));
BOOST_FOREACH (dcp::SubtitleString s, subtitle.subs) {
s.set_h_position (s.h_position() + piece->content->subtitle->x_offset ());
s.set_aspect_adjust (xs / ys);
}
- s.set_in (dcp::Time(period.from.seconds(), 1000));
- s.set_out (dcp::Time(period.to.seconds(), 1000));
+ s.set_in (dcp::Time(from.seconds(), 1000));
ps.text.push_back (SubtitleString (s, piece->content->subtitle->outline_width()));
ps.add_fonts (piece->content->subtitle->fonts ());
}
- if (piece->content->subtitle->use() && (piece->content->subtitle->burn() || _always_burn_subtitles)) {
- _subtitles.push_back (make_pair (ps, period));
- } else {
- Subtitle (ps, period);
+ _active_subtitles[wp] = make_pair (ps, from);
+}
+
+void
+Player::subtitle_stop (weak_ptr<Piece> wp, ContentTime to)
+{
+ if (_active_subtitles.find (wp) == _active_subtitles.end ()) {
+ return;
}
+
+ shared_ptr<Piece> piece = wp.lock ();
+ if (!piece) {
+ return;
+ }
+
+ if (piece->content->subtitle->use() && !_always_burn_subtitles && !piece->content->subtitle->burn()) {
+ Subtitle (_active_subtitles[wp].first, DCPTimePeriod (_active_subtitles[wp].second, content_time_to_dcp (piece, to)));
+ }
+
+ _active_subtitles.erase (wp);
}
void
}
_audio_merger.clear ();
+ _active_subtitles.clear ();
BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
i->done = false;
boost::signals2::signal<void (boost::shared_ptr<PlayerVideo>, DCPTime)> Video;
boost::signals2::signal<void (boost::shared_ptr<AudioBuffers>, DCPTime)> Audio;
+ /** Emitted when a subtitle is ready. This signal may be emitted considerably
+ * after the corresponding Video.
+ */
boost::signals2::signal<void (PlayerSubtitles, DCPTimePeriod)> Subtitle;
private:
std::list<boost::shared_ptr<Piece> > overlaps (DCPTime from, DCPTime to, boost::function<bool (Content *)> valid);
void video (boost::weak_ptr<Piece>, ContentVideo);
void audio (boost::weak_ptr<Piece>, AudioStreamPtr, ContentAudio);
- void image_subtitle (boost::weak_ptr<Piece>, ContentImageSubtitle);
- void text_subtitle (boost::weak_ptr<Piece>, ContentTextSubtitle);
+ void image_subtitle_start (boost::weak_ptr<Piece>, ContentImageSubtitle);
+ void text_subtitle_start (boost::weak_ptr<Piece>, ContentTextSubtitle);
+ void subtitle_stop (boost::weak_ptr<Piece>, ContentTime);
boost::shared_ptr<Resampler> resampler (boost::shared_ptr<const AudioContent> content, AudioStreamPtr stream, bool create);
DCPTime one_video_frame () const;
void fill_video (DCPTimePeriod period);
void fill_audio (DCPTimePeriod period);
void audio_flush (boost::shared_ptr<Piece>, AudioStreamPtr stream);
void audio_transform (boost::shared_ptr<AudioContent> content, AudioStreamPtr stream, ContentAudio content_audio, DCPTime time);
- std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (boost::shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to) const;
+ std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (
+ boost::shared_ptr<const AudioBuffers> audio, DCPTime time, DCPTime discard_to
+ ) const;
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
std::list<DCPTimePeriod> _no_video;
std::list<DCPTimePeriod> _no_audio;
- std::list<std::pair<PlayerSubtitles, DCPTimePeriod> > _subtitles;
+ typedef std::map<boost::weak_ptr<Piece>, std::pair<PlayerSubtitles, DCPTime> > ActiveSubtitles;
+ ActiveSubtitles _active_subtitles;
boost::shared_ptr<AudioProcessor> _audio_processor;
typedef std::map<std::pair<boost::shared_ptr<const AudioContent>, AudioStreamPtr>, boost::shared_ptr<Resampler> > ResamplerMap;
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
}
-/** Called by subclasses when an image subtitle is ready.
- * @param period Period of the subtitle.
+/** Called by subclasses when an image subtitle is starting.
+ * @param from From time of the subtitle.
* @param image Subtitle image.
* @param rect Area expressed as a fraction of the video frame that this subtitle
* is for (e.g. a width of 0.5 means the width of the subtitle is half the width
* of the video frame)
*/
void
-SubtitleDecoder::emit_image (ContentTimePeriod period, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
+SubtitleDecoder::emit_image_start (ContentTime from, shared_ptr<Image> image, dcpomatic::Rect<double> rect)
{
- ImageData (ContentImageSubtitle (period, image, rect));
+ ImageStart (ContentImageSubtitle (from, image, rect));
}
void
-SubtitleDecoder::emit_text (ContentTimePeriod period, list<dcp::SubtitleString> s)
+SubtitleDecoder::emit_text_start (ContentTime from, list<dcp::SubtitleString> s)
{
/* We must escape < and > in strings, otherwise they might confuse our subtitle
renderer (which uses some HTML-esque markup to do bold/italic etc.)
i.set_text (t);
}
- TextData (ContentTextSubtitle (period, s));
- _position = period.from;
+ TextStart (ContentTextSubtitle (from, s));
+ _position = from;
}
void
-SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & subtitle)
+SubtitleDecoder::emit_text_start (ContentTime from, sub::Subtitle const & subtitle)
{
/* See if our next subtitle needs to be placed on screen by us */
bool needs_placement = false;
content()->colour(),
j.font_size.points (72 * 11),
1.0,
- dcp::Time (period.from.seconds(), 1000),
- dcp::Time (period.to.seconds(), 1000),
+ dcp::Time (from.seconds(), 1000),
+ /* XXX: hmm; this is a bit ugly (we don't know the to time yet) */
+ dcp::Time (),
0,
dcp::HALIGN_CENTER,
v_position,
}
}
- emit_text (period, out);
+ emit_text_start (from, out);
+}
+
+void
+SubtitleDecoder::emit_stop (ContentTime to)
+{
+ Stop (to);
+}
+
+void
+SubtitleDecoder::emit_text (ContentTimePeriod period, list<dcp::SubtitleString> s)
+{
+ emit_text_start (period.from, s);
+ emit_stop (period.to);
+}
+
+void
+SubtitleDecoder::emit_text (ContentTimePeriod period, sub::Subtitle const & s)
+{
+ emit_text_start (period.from, s);
+ emit_stop (period.to);
}
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
return _position;
}
- void emit_image (ContentTimePeriod period, boost::shared_ptr<Image> image, dcpomatic::Rect<double> rect);
+ void emit_image_start (ContentTime from, boost::shared_ptr<Image> image, dcpomatic::Rect<double> rect);
+ void emit_text_start (ContentTime from, std::list<dcp::SubtitleString> s);
+ void emit_text_start (ContentTime from, sub::Subtitle const & subtitle);
void emit_text (ContentTimePeriod period, std::list<dcp::SubtitleString> s);
void emit_text (ContentTimePeriod period, sub::Subtitle const & subtitle);
+ void emit_stop (ContentTime to);
boost::shared_ptr<const SubtitleContent> content () const {
return _content;
}
- boost::signals2::signal<void (ContentImageSubtitle)> ImageData;
- boost::signals2::signal<void (ContentTextSubtitle)> TextData;
+ boost::signals2::signal<void (ContentImageSubtitle)> ImageStart;
+ boost::signals2::signal<void (ContentTextSubtitle)> TextStart;
+ boost::signals2::signal<void (ContentTime)> Stop;
private:
boost::shared_ptr<const SubtitleContent> _content;
+++ /dev/null
-/*
- Copyright (C) 2016 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "image_subtitle_colour_dialog.h"
-#include "rgba_colour_picker.h"
-#include "wx_util.h"
-#include "lib/ffmpeg_subtitle_stream.h"
-#include "lib/ffmpeg_content.h"
-
-using std::map;
-using std::cout;
-using boost::shared_ptr;
-using boost::bind;
-
-ImageSubtitleColourDialog::ImageSubtitleColourDialog (wxWindow* parent, shared_ptr<FFmpegContent> content, shared_ptr<FFmpegSubtitleStream> stream)
- : wxDialog (parent, wxID_ANY, _("Subtitle colours"))
- , _content (content)
- , _stream (stream)
-{
- wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
- SetSizer (overall_sizer);
-
- wxScrolled<wxPanel>* colours_panel = new wxScrolled<wxPanel> (this);
- colours_panel->EnableScrolling (false, true);
- colours_panel->ShowScrollbars (wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS);
- colours_panel->SetScrollRate (0, 16);
-
- wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
- table->AddGrowableCol (1, 1);
-
- map<RGBA, RGBA> colours = _stream->colours ();
-
- wxStaticText* t = new wxStaticText (colours_panel, wxID_ANY, "");
- t->SetLabelMarkup (_("<b>Original colour</b>"));
- table->Add (t, 1, wxEXPAND);
- t = new wxStaticText (colours_panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE_HORIZONTAL);
- t->SetLabelMarkup (_("<b>New colour</b>"));
- table->Add (t, 1, wxALIGN_CENTER);
-
- for (map<RGBA, RGBA>::const_iterator i = colours.begin(); i != colours.end(); ++i) {
- wxPanel* from = new wxPanel (colours_panel, wxID_ANY);
- from->SetBackgroundColour (wxColour (i->first.r, i->first.g, i->first.b, i->first.a));
- table->Add (from, 1, wxEXPAND);
- RGBAColourPicker* to = new RGBAColourPicker (colours_panel, i->second);
- table->Add (to, 1, wxEXPAND);
- _pickers[i->first] = to;
- }
-
- colours_panel->SetSizer (table);
-
- overall_sizer->Add (colours_panel, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
-
- wxButton* restore = new wxButton (this, wxID_ANY, _("Restore to original colours"));
- restore->Bind (wxEVT_BUTTON, bind (&ImageSubtitleColourDialog::restore, this));
- overall_sizer->Add (restore, 0, wxALL, DCPOMATIC_SIZER_X_GAP);
-
- wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
- if (buttons) {
- overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
- }
-}
-
-void
-ImageSubtitleColourDialog::apply ()
-{
- for (map<RGBA, RGBAColourPicker*>::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) {
- _stream->set_colour (i->first, i->second->colour ());
- }
-
- _content->signal_subtitle_stream_changed ();
-}
-
-void
-ImageSubtitleColourDialog::restore ()
-{
- for (map<RGBA, RGBAColourPicker*>::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) {
- i->second->set (i->first);
- }
-}
+++ /dev/null
-/*
- Copyright (C) 2016 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "table_dialog.h"
-#include "lib/rgba.h"
-#include <map>
-
-class RGBAColourPicker;
-class FFmpegContent;
-class FFmpegSubtitleStream;
-
-class ImageSubtitleColourDialog : public wxDialog
-{
-public:
- ImageSubtitleColourDialog (wxWindow* parent, boost::shared_ptr<FFmpegContent> content, boost::shared_ptr<FFmpegSubtitleStream> stream);
-
- void apply ();
-
-private:
- void restore ();
-
- boost::shared_ptr<FFmpegContent> _content;
- boost::shared_ptr<FFmpegSubtitleStream> _stream;
- std::map<RGBA, RGBAColourPicker*> _pickers;
-};
#include "subtitle_view.h"
#include "content_panel.h"
#include "fonts_dialog.h"
-#include "text_subtitle_appearance_dialog.h"
-#include "image_subtitle_colour_dialog.h"
+#include "subtitle_appearance_dialog.h"
#include "lib/ffmpeg_content.h"
#include "lib/text_subtitle_content.h"
#include "lib/ffmpeg_subtitle_stream.h"
{
int any_subs = 0;
int ffmpeg_subs = 0;
- int text_subs = 0;
- int image_subs = 0;
ContentList sel = _parent->selected_subtitle ();
BOOST_FOREACH (shared_ptr<Content> i, sel) {
/* These are the content types that could include subtitles */
shared_ptr<const DCPSubtitleContent> dsc = boost::dynamic_pointer_cast<const DCPSubtitleContent> (i);
if (fc) {
if (fc->subtitle) {
- DCPOMATIC_ASSERT (fc->subtitle_stream());
- /* This content has some subtitles; check the selected stream to decide what type */
- if (fc->subtitle_stream()->has_text()) {
- ++text_subs;
- } else if (fc->subtitle_stream()->has_image()) {
- ++image_subs;
- }
++ffmpeg_subs;
++any_subs;
}
} else if (sc || dc || dsc) {
/* XXX: in the future there could be bitmap subs from DCPs */
- ++text_subs;
++any_subs;
}
}
- if (image_subs) {
- BOOST_FOREACH (shared_ptr<Content> i, sel) {
- /* We must burn image subtitles at the moment */
- i->subtitle->set_burn (true);
- }
- }
-
/* Decide whether we can reference these subs */
shared_ptr<DCPContent> dcp;
/* Set up sensitivity */
_use->Enable (!reference && any_subs > 0);
bool const use = _use->GetValue ();
- _burn->Enable (!reference && any_subs > 0 && use && image_subs == 0);
+ _burn->Enable (!reference && any_subs > 0 && use);
_x_offset->Enable (!reference && any_subs > 0 && use);
_y_offset->Enable (!reference && any_subs > 0 && use);
_x_scale->Enable (!reference && any_subs > 0 && use);
_y_scale->Enable (!reference && any_subs > 0 && use);
- _line_spacing->Enable (!reference && text_subs > 0 && use);
+ _line_spacing->Enable (!reference && use);
_language->Enable (!reference && any_subs > 0 && use);
_stream->Enable (!reference && ffmpeg_subs == 1);
- _subtitle_view_button->Enable (!reference && text_subs == 1);
- _fonts_dialog_button->Enable (!reference && text_subs == 1);
+ _subtitle_view_button->Enable (!reference);
+ _fonts_dialog_button->Enable (!reference);
_appearance_dialog_button->Enable (!reference && any_subs > 0 && use);
}
ContentList c = _parent->selected_subtitle ();
DCPOMATIC_ASSERT (c.size() == 1);
- bool text = false;
- bool image = false;
-
- if (
- dynamic_pointer_cast<TextSubtitleContent> (c.front()) ||
- dynamic_pointer_cast<DCPContent> (c.front()) ||
- dynamic_pointer_cast<DCPSubtitleContent> (c.front())) {
-
- text = true;
- }
-
- shared_ptr<FFmpegContent> fc = dynamic_pointer_cast<FFmpegContent> (c.front());
- if (fc) {
- if (fc->subtitle_stream()->has_text()) {
- text = true;
- } else if (fc->subtitle_stream()->has_image()) {
- image = true;
- }
- }
-
- if (text) {
- TextSubtitleAppearanceDialog* d = new TextSubtitleAppearanceDialog (this, c.front());
- if (d->ShowModal () == wxID_OK) {
- d->apply ();
- }
- d->Destroy ();
- } else if (image) {
- DCPOMATIC_ASSERT (fc);
- ImageSubtitleColourDialog* d = new ImageSubtitleColourDialog (this, fc, fc->subtitle_stream ());
- if (d->ShowModal() == wxID_OK) {
- d->apply ();
- }
- d->Destroy ();
+ SubtitleAppearanceDialog* d = new SubtitleAppearanceDialog (this, c.front());
+ if (d->ShowModal () == wxID_OK) {
+ d->apply ();
}
+ d->Destroy ();
}
_subs = 0;
_frc = film->active_frame_rate_change (position);
- decoder->subtitle->TextData.connect (bind (&SubtitleView::data, this, _1));
+ decoder->subtitle->TextStart.connect (bind (&SubtitleView::data_start, this, _1));
+ decoder->subtitle->Stop.connect (bind (&SubtitleView::data_stop, this, _1));
while (!decoder->pass ()) {}
SetSizerAndFit (sizer);
}
void
-SubtitleView::data (ContentTextSubtitle cts)
+SubtitleView::data_start (ContentTextSubtitle cts)
{
- for (list<dcp::SubtitleString>::const_iterator i = cts.subs.begin(); i != cts.subs.end(); ++i) {
+ BOOST_FOREACH (dcp::SubtitleString const & i, cts.subs) {
wxListItem list_item;
list_item.SetId (_subs);
_list->InsertItem (list_item);
- ContentTimePeriod const p = cts.period ();
- _list->SetItem (_subs, 0, std_to_wx (p.from.timecode (_frc->source)));
- _list->SetItem (_subs, 1, std_to_wx (p.to.timecode (_frc->source)));
- _list->SetItem (_subs, 2, std_to_wx (i->text ()));
+ _list->SetItem (_subs, 0, std_to_wx (cts.from().timecode (_frc->source)));
+ _list->SetItem (_subs, 2, std_to_wx (i.text ()));
++_subs;
}
+
+ _last_count = cts.subs.size ();
+}
+
+void
+SubtitleView::data_stop (ContentTime time)
+{
+ if (!_last_count) {
+ return;
+ }
+
+ for (int i = _subs - *_last_count; i < _subs; ++i) {
+ _list->SetItem (i, 1, std_to_wx (time.timecode (_frc->source)));
+ }
}
SubtitleView (wxWindow *, boost::shared_ptr<Film>, boost::shared_ptr<Decoder>, DCPTime position);
private:
- void data (ContentTextSubtitle cts);
+ void data_start (ContentTextSubtitle cts);
+ void data_stop (ContentTime time);
wxListCtrl* _list;
int _subs;
boost::optional<FrameRateChange> _frc;
+ boost::optional<int> _last_count;
};
+++ /dev/null
-/*
- Copyright (C) 2015-2016 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "text_subtitle_appearance_dialog.h"
-#include "lib/text_subtitle_content.h"
-#include "lib/subtitle_content.h"
-#include <wx/wx.h>
-#include <wx/clrpicker.h>
-#include <wx/spinctrl.h>
-#include <wx/gbsizer.h>
-
-using boost::shared_ptr;
-using boost::bind;
-
-int const TextSubtitleAppearanceDialog::NONE = 0;
-int const TextSubtitleAppearanceDialog::OUTLINE = 1;
-int const TextSubtitleAppearanceDialog::SHADOW = 2;
-
-TextSubtitleAppearanceDialog::TextSubtitleAppearanceDialog (wxWindow* parent, shared_ptr<Content> content)
- : wxDialog (parent, wxID_ANY, _("Subtitle appearance"))
- , _content (content)
-{
- wxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
- SetSizer (overall_sizer);
-
- _table = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
-
- overall_sizer->Add (_table, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER);
-
- int r = 0;
-
- add_label_to_sizer (_table, this, _("Colour"), true, wxGBPosition (r, 0));
- _colour = new wxColourPickerCtrl (this, wxID_ANY);
- _table->Add (_colour, wxGBPosition (r, 1));
- ++r;
-
- add_label_to_sizer (_table, this, _("Effect"), true, wxGBPosition (r, 0));
- _effect = new wxChoice (this, wxID_ANY);
- _table->Add (_effect, wxGBPosition (r, 1));
- ++r;
-
- add_label_to_sizer (_table, this, _("Effect colour"), true, wxGBPosition (r, 0));
- _effect_colour = new wxColourPickerCtrl (this, wxID_ANY);
- _table->Add (_effect_colour, wxGBPosition (r, 1));
- ++r;
-
- add_label_to_sizer (_table, this, _("Outline width"), true, wxGBPosition (r, 0));
- _outline_width = new wxSpinCtrl (this, wxID_ANY);
- _table->Add (_outline_width, wxGBPosition (r, 1));
- ++r;
-
- add_label_to_sizer (_table, this, _("Fade in time"), true, wxGBPosition (r, 0));
- _fade_in = new Timecode<ContentTime> (this);
- _table->Add (_fade_in, wxGBPosition (r, 1));
- ++r;
-
- add_label_to_sizer (_table, this, _("Fade out time"), true, wxGBPosition (r, 0));
- _fade_out = new Timecode<ContentTime> (this);
- _table->Add (_fade_out, wxGBPosition (r, 1));
- ++r;
-
- wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
- if (buttons) {
- overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
- }
-
- overall_sizer->Layout ();
- overall_sizer->SetSizeHints (this);
-
- /* Keep these Appends() up to date with NONE/OUTLINE/SHADOW variables */
- _effect->Append (_("None"));
- _effect->Append (_("Outline"));
- _effect->Append (_("Shadow"));;
-
- _colour->SetColour (wxColour (_content->subtitle->colour().r, _content->subtitle->colour().g, _content->subtitle->colour().b));
- if (_content->subtitle->outline()) {
- _effect->SetSelection (OUTLINE);
- } else if (_content->subtitle->shadow()) {
- _effect->SetSelection (SHADOW);
- } else {
- _effect->SetSelection (NONE);
- }
- _effect_colour->SetColour (
- wxColour (_content->subtitle->effect_colour().r, _content->subtitle->effect_colour().g, _content->subtitle->effect_colour().b)
- );
- _fade_in->set (_content->subtitle->fade_in(), _content->active_video_frame_rate ());
- _fade_out->set (_content->subtitle->fade_out(), _content->active_video_frame_rate ());
- _outline_width->SetValue (_content->subtitle->outline_width ());
-
- _effect->Bind (wxEVT_CHOICE, bind (&TextSubtitleAppearanceDialog::setup_sensitivity, this));
- _content_connection = _content->Changed.connect (bind (&TextSubtitleAppearanceDialog::setup_sensitivity, this));
-
- setup_sensitivity ();
-}
-
-void
-TextSubtitleAppearanceDialog::apply ()
-{
- wxColour const c = _colour->GetColour ();
- _content->subtitle->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue()));
- _content->subtitle->set_outline (_effect->GetSelection() == OUTLINE);
- _content->subtitle->set_shadow (_effect->GetSelection() == SHADOW);
- wxColour const ec = _effect_colour->GetColour ();
- _content->subtitle->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue()));
- _content->subtitle->set_fade_in (_fade_in->get (_content->active_video_frame_rate ()));
- _content->subtitle->set_fade_out (_fade_out->get (_content->active_video_frame_rate ()));
- _content->subtitle->set_outline_width (_outline_width->GetValue ());
-}
-
-void
-TextSubtitleAppearanceDialog::setup_sensitivity ()
-{
- _effect_colour->Enable (_effect->GetSelection() != NONE);
-
- bool const can_outline_width = _effect->GetSelection() == OUTLINE && _content->subtitle->burn ();
- _outline_width->Enable (can_outline_width);
- if (can_outline_width) {
- _outline_width->UnsetToolTip ();
- } else {
- _outline_width->SetToolTip (_("Outline width cannot be set unless you are burning in subtitles"));
- }
-}
+++ /dev/null
-/*
- Copyright (C) 2015-2016 Carl Hetherington <cth@carlh.net>
-
- This file is part of DCP-o-matic.
-
- DCP-o-matic 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.
-
- DCP-o-matic 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 DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include "timecode.h"
-#include <wx/wx.h>
-#include <boost/shared_ptr.hpp>
-#include <boost/signals2.hpp>
-
-class wxRadioButton;
-class wxColourPickerCtrl;
-class wxGridBagSizer;
-class Content;
-
-class TextSubtitleAppearanceDialog : public wxDialog
-{
-public:
- TextSubtitleAppearanceDialog (wxWindow* parent, boost::shared_ptr<Content> content);
-
- void apply ();
-
-private:
- void setup_sensitivity ();
-
- wxColourPickerCtrl* _colour;
- wxChoice* _effect;
- wxColourPickerCtrl* _effect_colour;
- Timecode<ContentTime>* _fade_in;
- Timecode<ContentTime>* _fade_out;
- wxSpinCtrl* _outline_width;
- wxGridBagSizer* _table;
-
- boost::shared_ptr<Content> _content;
-
- boost::signals2::scoped_connection _content_connection;
-
- static int const NONE;
- static int const OUTLINE;
- static int const SHADOW;
-};
dcp_panel.cc
email_dialog.cc
image_sequence_dialog.cc
- image_subtitle_colour_dialog.cc
isdcf_metadata_dialog.cc
dir_picker_ctrl.cc
dolby_doremi_certificate_panel.cc
self_dkdm_dialog.cc
server_dialog.cc
servers_list_dialog.cc
+ subtitle_appearance_dialog.cc
subtitle_panel.cc
subtitle_view.cc
system_font_dialog.cc
table_dialog.cc
templates_dialog.cc
- text_subtitle_appearance_dialog.cc
time_picker.cc
timecode.cc
timeline.cc
wait_for_jobs ();
shared_ptr<DCPDecoder> decoder (new DCPDecoder (content, film->log()));
- decoder->subtitle->TextData.connect (bind (store, _1));
+ decoder->subtitle->TextStart.connect (bind (store, _1));
stored = optional<ContentTextSubtitle> ();
while (!decoder->pass() && !stored) {}
wait_for_jobs ();
shared_ptr<DCPSubtitleDecoder> decoder (new DCPSubtitleDecoder (content, film->log()));
- decoder->subtitle->TextData.connect (bind (store, _1));
+ decoder->subtitle->TextStart.connect (bind (store, _1));
stored = optional<ContentTextSubtitle> ();
while (!decoder->pass ()) {
- if (stored && stored->period().from == ContentTime(0)) {
+ if (stored && stored->from() == ContentTime(0)) {
BOOST_CHECK_EQUAL (stored->subs.front().text(), "<b>Hello world!</b>");
}
}
shared_ptr<DCPSubtitleDecoder> decoder (new DCPSubtitleDecoder (content, film->log()));
stored = optional<ContentTextSubtitle> ();
while (!decoder->pass ()) {
- decoder->subtitle->TextData.connect (bind (store, _1));
- if (stored && stored->period().from == ContentTime::from_seconds(0.08)) {
+ decoder->subtitle->TextStart.connect (bind (store, _1));
+ if (stored && stored->from() == ContentTime::from_seconds(0.08)) {
list<dcp::SubtitleString> s = stored->subs;
list<dcp::SubtitleString>::const_iterator i = s.begin ();
BOOST_CHECK_EQUAL (i->text(), "This");