Basics of noting subtitle times in FFmpegSubtitleStreams.
authorCarl Hetherington <cth@carlh.net>
Thu, 22 May 2014 15:53:38 +0000 (16:53 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 22 May 2014 15:53:38 +0000 (16:53 +0100)
15 files changed:
src/lib/dcpomatic_time.h
src/lib/ffmpeg.cc
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_examiner.cc
src/lib/ffmpeg_subtitle_stream.h
src/lib/subrip_content.cc
src/lib/subrip_content.h
src/lib/subrip_decoder.cc
src/lib/subtitle_content.h
src/lib/subtitle_decoder.cc
src/lib/subtitle_decoder.h
src/lib/util.cc
src/lib/util.h

index e56f58c4b10252295b76caf6d1ae022036d19a18..2a871889a529be2ec7e7f6bcc7c0c15c2f2cf5c8 100644 (file)
@@ -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
index ec7ec452e37683bfe374a16589d56c49377df562..8505626df1013a980cf6d100fb4246cb147cc302 100644 (file)
@@ -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 */
        }
 }
 
index a191959fcf6bceb42f94630823fcf1afb1dd45a7..2b507ab371adf3187c08d5b1241ff80c7f359edc 100644 (file)
@@ -392,3 +392,18 @@ FFmpegContent::audio_analysis_path () const
        p /= name;
        return p;
 }
+
+bool
+FFmpegContent::has_subtitle_during (ContentTimePeriod period) const
+{
+       shared_ptr<FFmpegSubtitleStream> stream = subtitle_stream ();
+
+       /* XXX: inefficient */
+       for (vector<ContentTimePeriod>::const_iterator i = stream->periods.begin(); i != stream->periods.end(); ++i) {
+               if (i->from <= period.to && i->to >= period.from) {
+                       return true;
+               }
+       }
+
+       return false;
+}
index 6f4980dc7691e619c29bc229c6c9ec235ad9a99c..1a30fb60693db7683c18efc00b47a642848f1019 100644 (file)
@@ -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<Filter const *> const &);
        
        std::vector<boost::shared_ptr<FFmpegSubtitleStream> > subtitle_streams () const {
index 7d152e4904a32682a8c64f0e84ee4d9470efd280..d668deb6f6bbeee617128acbe05489e30874f9a4 100644 (file)
@@ -62,6 +62,7 @@ using dcp::Size;
 FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log> 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<double> (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];
 
index 013799d036c2fd20a3b60cb4539868b2e025b34f..df12830f819eb182c2c04db9838e71f550e236ff 100644 (file)
@@ -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<const FFmpegContent> 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<FFmpegAudioStr
 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) {
+               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<ContentTime>
index 76c71f0162b3710a14de02307ea83b20559a0db6..b16b825e7d76b3a401bc36c94a28b382771b1cd8 100644 (file)
@@ -30,5 +30,7 @@ public:
        FFmpegSubtitleStream (cxml::ConstNodePtr);
 
        void as_xml (xmlpp::Node *) const;
+
+       std::vector<ContentTimePeriod> periods;
 };
 
index 892578adecc11595bf8059ee2530f87bf88dca14..d7825f518f977a4d525227572469d9f63f2482bb 100644 (file)
@@ -109,3 +109,10 @@ SubRipContent::identifier () const
 
        return s.str ();
 }
+
+bool
+SubRipContent::has_subtitle_during (ContentTimePeriod) const
+{
+       /* XXX */
+       return false;
+}
index 5688f81d5fe44639450e3d8d8a57d4acb9f4a9b3..91de08350e0c97f3c9c455c41d793efdf7c64780 100644 (file)
@@ -37,6 +37,8 @@ public:
        DCPTime full_length () const;
        std::string identifier () const;
 
+       bool has_subtitle_during (ContentTimePeriod) const;
+
 private:
        DCPTime _length;
 };
index cdc8ccbfe4613f9a745966121101be82fb640a1c..e832c2d849d7d09a47de30a8027301d8e9cf380c 100644 (file)
 
 #include <dcp/subtitle_string.h>
 #include "subrip_decoder.h"
+#include "subrip_content.h"
 
 using std::list;
 using boost::shared_ptr;
 
 SubRipDecoder::SubRipDecoder (shared_ptr<const SubRipContent> content)
-       : SubRip (content)
+       : SubtitleDecoder (content)
+       , SubRip (content)
        , _next (0)
 {
 
index 92d072ca6540aa0bdfcde06766f96c193655c532..cc91a2df82eec046faeb58030bcfa7a2ca7a76e3 100644 (file)
@@ -36,9 +36,11 @@ public:
        SubtitleContent (boost::shared_ptr<const Film>, boost::filesystem::path);
        SubtitleContent (boost::shared_ptr<const Film>, cxml::ConstNodePtr, int version);
        SubtitleContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
-       
+
        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;
 
index d114df97977c477748e7e844a0f65db27497d486..13cf481c8027f70e0b468235ef0736cf47278570 100644 (file)
 
 #include <boost/shared_ptr.hpp>
 #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<const SubtitleContent> c)
+       : _subtitle_content (c)
 {
 
 }
@@ -49,6 +51,10 @@ template <class T>
 list<shared_ptr<T> >
 SubtitleDecoder::get (list<shared_ptr<T> > const & subs, ContentTimePeriod period)
 {
+       if (!_subtitle_content->has_subtitle_during (period)) {
+               return list<shared_ptr<T> > ();
+       }
+
        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<shared_ptr<T> > 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))) {}
 
index fea4ab973fc615f8fb4824d647bb166285339e98..164c151e671089a78140339ed836604f05bb018b 100644 (file)
@@ -33,7 +33,7 @@ class Image;
 class SubtitleDecoder : public virtual Decoder
 {
 public:
-       SubtitleDecoder ();
+       SubtitleDecoder (boost::shared_ptr<const SubtitleContent>);
 
        std::list<boost::shared_ptr<ContentImageSubtitle> > get_image_subtitles (ContentTimePeriod period);
        std::list<boost::shared_ptr<ContentTextSubtitle> > get_text_subtitles (ContentTimePeriod period);
@@ -50,6 +50,8 @@ protected:
 private:
        template <class T>
        std::list<boost::shared_ptr<T> > get (std::list<boost::shared_ptr<T> > const & subs, ContentTimePeriod period);
+
+       boost::shared_ptr<const SubtitleContent> _subtitle_content;
 };
 
 #endif
index 14dfd1fa558b6afa86c7dc03a2b05b139d9bce4d..6e370f577d9c8799126499564a24128709c24af3 100644 (file)
@@ -982,3 +982,16 @@ ScopedTemporary::close ()
                _open = 0;
        }
 }
+
+ContentTimePeriod
+subtitle_period (AVSubtitle const & sub)
+{
+       ContentTime const packet_time = ContentTime::from_seconds (static_cast<double> (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;
+}
index 58c2771b7cebd8e46b9d700c3218041abd3dadd4..196a6e8f93b13e3314fca48423f1e70e5702f108 100644 (file)
@@ -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<std::string, std::string> const & kv,
 extern std::string get_optional_string (std::multimap<std::string, std::string> 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