ContentTime from;
ContentTime to;
+
+ ContentTimePeriod operator+ (ContentTime const & o) const {
+ return ContentTimePeriod (from + o, to + o);
+ }
};
class DCPTime : public Time
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 */
}
}
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;
+}
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 {
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)
/* 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];
#include "ffmpeg_content.h"
#include "ffmpeg_audio_stream.h"
#include "ffmpeg_subtitle_stream.h"
+#include "util.h"
#include "i18n.h"
}
}
- /* 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) {
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>
FFmpegSubtitleStream (cxml::ConstNodePtr);
void as_xml (xmlpp::Node *) const;
+
+ std::vector<ContentTimePeriod> periods;
};
return s.str ();
}
+
+bool
+SubRipContent::has_subtitle_during (ContentTimePeriod) const
+{
+ /* XXX */
+ return false;
+}
DCPTime full_length () const;
std::string identifier () const;
+ bool has_subtitle_during (ContentTimePeriod) const;
+
private:
DCPTime _length;
};
#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)
{
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);
boost::mutex::scoped_lock lm (_mutex);
return _subtitle_scale;
}
-
+
private:
friend class ffmpeg_pts_offset_test;
#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)
{
}
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);
/* 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))) {}
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);
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
_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;
+}
}
class Job;
+struct AVSubtitle;
extern std::string seconds_to_hms (int);
extern std::string seconds_to_approximate_hms (int);
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