From 24d54ea7fe1ba128cf8d3521d6738fc73a7c623e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 22 May 2014 16:53:38 +0100 Subject: [PATCH] Basics of noting subtitle times in FFmpegSubtitleStreams. --- src/lib/dcpomatic_time.h | 4 ++++ src/lib/ffmpeg.cc | 12 ++++++------ src/lib/ffmpeg_content.cc | 15 +++++++++++++++ src/lib/ffmpeg_content.h | 3 +++ src/lib/ffmpeg_decoder.cc | 8 ++------ src/lib/ffmpeg_examiner.cc | 24 +++++++++++++++++++++--- src/lib/ffmpeg_subtitle_stream.h | 2 ++ src/lib/subrip_content.cc | 7 +++++++ src/lib/subrip_content.h | 2 ++ src/lib/subrip_decoder.cc | 4 +++- src/lib/subtitle_content.h | 6 ++++-- src/lib/subtitle_decoder.cc | 10 +++++++--- src/lib/subtitle_decoder.h | 4 +++- src/lib/util.cc | 13 +++++++++++++ src/lib/util.h | 2 ++ 15 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/lib/dcpomatic_time.h b/src/lib/dcpomatic_time.h index e56f58c4b..2a871889a 100644 --- a/src/lib/dcpomatic_time.h +++ b/src/lib/dcpomatic_time.h @@ -157,6 +157,10 @@ public: ContentTime from; ContentTime to; + + ContentTimePeriod operator+ (ContentTime const & o) const { + return ContentTimePeriod (from + o, to + o); + } }; class DCPTime : public Time diff --git a/src/lib/ffmpeg.cc b/src/lib/ffmpeg.cc index ec7ec452e..8505626df 100644 --- a/src/lib/ffmpeg.cc +++ b/src/lib/ffmpeg.cc @@ -148,13 +148,13 @@ FFmpeg::setup_decoders () AVCodecContext* context = _format_context->streams[i]->codec; AVCodec* codec = avcodec_find_decoder (context->codec_id); - if (codec == 0) { - throw DecodeError (N_("could not find decoder")); - } - - if (avcodec_open2 (context, codec, 0) < 0) { - throw DecodeError (N_("could not open decoder")); + if (codec) { + if (avcodec_open2 (context, codec, 0) < 0) { + throw DecodeError (N_("could not open decoder")); + } } + + /* We are silently ignoring any failures to find suitable decoders here */ } } diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index a191959fc..2b507ab37 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -392,3 +392,18 @@ FFmpegContent::audio_analysis_path () const p /= name; return p; } + +bool +FFmpegContent::has_subtitle_during (ContentTimePeriod period) const +{ + shared_ptr stream = subtitle_stream (); + + /* XXX: inefficient */ + for (vector::const_iterator i = stream->periods.begin(); i != stream->periods.end(); ++i) { + if (i->from <= period.to && i->to >= period.from) { + return true; + } + } + + return false; +} diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 6f4980dc7..1a30fb606 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -73,6 +73,9 @@ public: void set_audio_mapping (AudioMapping); boost::filesystem::path audio_analysis_path () const; + /* SubtitleContent */ + bool has_subtitle_during (ContentTimePeriod) const; + void set_filters (std::vector const &); std::vector > subtitle_streams () const { diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 7d152e490..d668deb6f 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -62,6 +62,7 @@ using dcp::Size; FFmpegDecoder::FFmpegDecoder (shared_ptr c, shared_ptr log) : VideoDecoder (c) , AudioDecoder (c) + , SubtitleDecoder (c) , FFmpeg (c) , _log (log) , _subtitle_codec_context (0) @@ -516,12 +517,7 @@ FFmpegDecoder::decode_subtitle_packet () /* Subtitle PTS (within the source, not taking into account any of the source that we may have chopped off for the DCP) */ - ContentTime packet_time = ContentTime::from_seconds (static_cast (sub.pts) / AV_TIME_BASE) + _pts_offset; - - ContentTimePeriod period ( - packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3), - packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3) - ); + ContentTimePeriod period = subtitle_period (sub) + _pts_offset; AVSubtitleRect const * rect = sub.rects[0]; diff --git a/src/lib/ffmpeg_examiner.cc b/src/lib/ffmpeg_examiner.cc index 013799d03..df12830f8 100644 --- a/src/lib/ffmpeg_examiner.cc +++ b/src/lib/ffmpeg_examiner.cc @@ -25,6 +25,7 @@ extern "C" { #include "ffmpeg_content.h" #include "ffmpeg_audio_stream.h" #include "ffmpeg_subtitle_stream.h" +#include "util.h" #include "i18n.h" @@ -63,8 +64,15 @@ FFmpegExaminer::FFmpegExaminer (shared_ptr c) } } - /* Run through until we find the first audio (for each stream) and video */ - + /* 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). + */ while (1) { int r = av_read_frame (_format_context, &_packet); if (r < 0) { @@ -122,7 +130,17 @@ FFmpegExaminer::audio_packet (AVCodecContext* context, shared_ptr stream) { - + int frame_finished; + AVSubtitle sub; + if (avcodec_decode_subtitle2 (context, &sub, &frame_finished, &_packet) >= 0 && frame_finished) { + ContentTimePeriod const period = subtitle_period (sub); + if (sub.num_rects == 0 && !stream->periods.empty () && stream->periods.back().to > period.from) { + /* Finish the last subtitle */ + stream->periods.back().to = period.from; + } else if (sub.num_rects == 1) { + stream->periods.push_back (period); + } + } } optional diff --git a/src/lib/ffmpeg_subtitle_stream.h b/src/lib/ffmpeg_subtitle_stream.h index 76c71f016..b16b825e7 100644 --- a/src/lib/ffmpeg_subtitle_stream.h +++ b/src/lib/ffmpeg_subtitle_stream.h @@ -30,5 +30,7 @@ public: FFmpegSubtitleStream (cxml::ConstNodePtr); void as_xml (xmlpp::Node *) const; + + std::vector periods; }; diff --git a/src/lib/subrip_content.cc b/src/lib/subrip_content.cc index 892578ade..d7825f518 100644 --- a/src/lib/subrip_content.cc +++ b/src/lib/subrip_content.cc @@ -109,3 +109,10 @@ SubRipContent::identifier () const return s.str (); } + +bool +SubRipContent::has_subtitle_during (ContentTimePeriod) const +{ + /* XXX */ + return false; +} diff --git a/src/lib/subrip_content.h b/src/lib/subrip_content.h index 5688f81d5..91de08350 100644 --- a/src/lib/subrip_content.h +++ b/src/lib/subrip_content.h @@ -37,6 +37,8 @@ public: DCPTime full_length () const; std::string identifier () const; + bool has_subtitle_during (ContentTimePeriod) const; + private: DCPTime _length; }; diff --git a/src/lib/subrip_decoder.cc b/src/lib/subrip_decoder.cc index cdc8ccbfe..e832c2d84 100644 --- a/src/lib/subrip_decoder.cc +++ b/src/lib/subrip_decoder.cc @@ -19,12 +19,14 @@ #include #include "subrip_decoder.h" +#include "subrip_content.h" using std::list; using boost::shared_ptr; SubRipDecoder::SubRipDecoder (shared_ptr content) - : SubRip (content) + : SubtitleDecoder (content) + , SubRip (content) , _next (0) { diff --git a/src/lib/subtitle_content.h b/src/lib/subtitle_content.h index 92d072ca6..cc91a2df8 100644 --- a/src/lib/subtitle_content.h +++ b/src/lib/subtitle_content.h @@ -36,9 +36,11 @@ public: SubtitleContent (boost::shared_ptr, boost::filesystem::path); SubtitleContent (boost::shared_ptr, cxml::ConstNodePtr, int version); SubtitleContent (boost::shared_ptr, std::vector >); - + void as_xml (xmlpp::Node *) const; + virtual bool has_subtitle_during (ContentTimePeriod) const = 0; + void set_subtitle_x_offset (double); void set_subtitle_y_offset (double); void set_subtitle_scale (double); @@ -57,7 +59,7 @@ public: boost::mutex::scoped_lock lm (_mutex); return _subtitle_scale; } - + private: friend class ffmpeg_pts_offset_test; diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc index d114df979..13cf481c8 100644 --- a/src/lib/subtitle_decoder.cc +++ b/src/lib/subtitle_decoder.cc @@ -19,13 +19,15 @@ #include #include "subtitle_decoder.h" +#include "subtitle_content.h" using std::list; using std::cout; using boost::shared_ptr; using boost::optional; -SubtitleDecoder::SubtitleDecoder () +SubtitleDecoder::SubtitleDecoder (shared_ptr c) + : _subtitle_content (c) { } @@ -49,6 +51,10 @@ template list > SubtitleDecoder::get (list > const & subs, ContentTimePeriod period) { + if (!_subtitle_content->has_subtitle_during (period)) { + return list > (); + } + if (subs.empty() || period.from < subs.front()->period().from || period.to > (subs.back()->period().to + ContentTime::from_seconds (10))) { /* Either we have no decoded data, or what we do have is a long way from what we want: seek */ seek (period.from, true); @@ -57,8 +63,6 @@ SubtitleDecoder::get (list > const & subs, ContentTimePeriod perio /* Now enough pass() calls will either: * (a) give us what we want, or * (b) hit the end of the decoder. - * - * XXX: with subs being sparse, this may need more care... */ while (!pass() && (subs.empty() || (subs.front()->period().from > period.from || period.to < subs.back()->period().to))) {} diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h index fea4ab973..164c151e6 100644 --- a/src/lib/subtitle_decoder.h +++ b/src/lib/subtitle_decoder.h @@ -33,7 +33,7 @@ class Image; class SubtitleDecoder : public virtual Decoder { public: - SubtitleDecoder (); + SubtitleDecoder (boost::shared_ptr); std::list > get_image_subtitles (ContentTimePeriod period); std::list > get_text_subtitles (ContentTimePeriod period); @@ -50,6 +50,8 @@ protected: private: template std::list > get (std::list > const & subs, ContentTimePeriod period); + + boost::shared_ptr _subtitle_content; }; #endif diff --git a/src/lib/util.cc b/src/lib/util.cc index 14dfd1fa5..6e370f577 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -982,3 +982,16 @@ ScopedTemporary::close () _open = 0; } } + +ContentTimePeriod +subtitle_period (AVSubtitle const & sub) +{ + ContentTime const packet_time = ContentTime::from_seconds (static_cast (sub.pts) / AV_TIME_BASE); + + ContentTimePeriod period ( + packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3), + packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3) + ); + + return period; +} diff --git a/src/lib/util.h b/src/lib/util.h index 58c2771b7..196a6e8f9 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -59,6 +59,7 @@ namespace libdcp { } class Job; +struct AVSubtitle; extern std::string seconds_to_hms (int); extern std::string seconds_to_approximate_hms (int); @@ -91,6 +92,7 @@ extern int get_optional_int (std::multimap const & kv, extern std::string get_optional_string (std::multimap const & kv, std::string k); extern void* wrapped_av_malloc (size_t); extern int64_t divide_with_round (int64_t a, int64_t b); +extern ContentTimePeriod subtitle_period (AVSubtitle const &); /** @class Socket * @brief A class to wrap a boost::asio::ip::tcp::socket with some things -- 2.30.2