Subtitle "to" times used to be stored against their "from" times.
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
index 7923be59bb6c3e696ee372135cc39e512c3c4dbd..c25efa463c8f61121d70a5e6fcac98a88bb63211 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include "ffmpeg_decoder.h"
 #include "ffmpeg_audio_stream.h"
 #include "ffmpeg_subtitle_stream.h"
-#include "filter_graph.h"
+#include "video_filter_graph.h"
 #include "audio_buffers.h"
 #include "ffmpeg_content.h"
 #include "raw_image_proxy.h"
 #include "film.h"
+#include "md5_digester.h"
 #include "compose.hpp"
 extern "C" {
 #include <libavcodec/avcodec.h>
@@ -47,10 +48,10 @@ extern "C" {
 
 #include "i18n.h"
 
-#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
-#define LOG_ERROR(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
-#define LOG_WARNING_NC(...) _log->log (__VA_ARGS__, Log::TYPE_WARNING);
-#define LOG_WARNING(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_WARNING);
+#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
+#define LOG_ERROR(...) _log->log (String::compose (__VA_ARGS__), LogEntry::TYPE_ERROR);
+#define LOG_WARNING_NC(...) _log->log (__VA_ARGS__, LogEntry::TYPE_WARNING);
+#define LOG_WARNING(...) _log->log (String::compose (__VA_ARGS__), LogEntry::TYPE_WARNING);
 
 using std::cout;
 using std::vector;
@@ -89,7 +90,7 @@ FFmpegDecoder::flush ()
 }
 
 bool
-FFmpegDecoder::pass ()
+FFmpegDecoder::pass (PassReason reason, bool accurate)
 {
        int r = av_read_frame (_format_context, &_packet);
 
@@ -112,11 +113,11 @@ FFmpegDecoder::pass ()
        int const si = _packet.stream_index;
        shared_ptr<const FFmpegContent> fc = _ffmpeg_content;
 
-       if (si == _video_stream && !_ignore_video) {
+       if (si == _video_stream && !_ignore_video && (accurate || reason != PASS_REASON_SUBTITLE)) {
                decode_video_packet ();
        } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index (_format_context, si)) {
                decode_subtitle_packet ();
-       } else {
+       } else if (accurate || reason != PASS_REASON_SUBTITLE) {
                decode_audio_packet ();
        }
 
@@ -337,7 +338,7 @@ FFmpegDecoder::decode_audio_packet ()
 
                        if (ct < ContentTime ()) {
                                /* Discard audio data that comes before time 0 */
-                               Frame const remove = min (int64_t (data->frames()), -ct.frames_round ((*stream)->frame_rate ()));
+                               Frame const remove = min (int64_t (data->frames()), (-ct).frames_ceil(double((*stream)->frame_rate ())));
                                data->move (remove, 0, data->frames() - remove);
                                data->set_frames (data->frames() - remove);
                                ct += ContentTime::from_frames (remove, (*stream)->frame_rate ());
@@ -363,15 +364,16 @@ FFmpegDecoder::decode_video_packet ()
 
        boost::mutex::scoped_lock lm (_filter_graphs_mutex);
 
-       shared_ptr<FilterGraph> graph;
+       shared_ptr<VideoFilterGraph> graph;
 
-       list<shared_ptr<FilterGraph> >::iterator i = _filter_graphs.begin();
+       list<shared_ptr<VideoFilterGraph> >::iterator i = _filter_graphs.begin();
        while (i != _filter_graphs.end() && !(*i)->can_process (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) {
                ++i;
        }
 
        if (i == _filter_graphs.end ()) {
-               graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
+               graph.reset (new VideoFilterGraph (dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
+               graph->setup (_ffmpeg_content->filters ());
                _filter_graphs.push_back (graph);
                LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
        } else {
@@ -412,8 +414,6 @@ FFmpegDecoder::decode_subtitle_packet ()
                   indicate that the previous subtitle should stop.  We can ignore it here.
                */
                return;
-       } else if (sub.num_rects > 1) {
-               throw DecodeError (_("multi-part subtitles not yet supported"));
        }
 
        /* Subtitle PTS (within the source, not taking into account any of the
@@ -427,23 +427,25 @@ 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 (period.from);
+               period.to = ffmpeg_content()->subtitle_stream()->find_subtitle_to (subtitle_id (sub));
        }
 
-       AVSubtitleRect const * rect = sub.rects[0];
-
-       switch (rect->type) {
-       case SUBTITLE_NONE:
-               break;
-       case SUBTITLE_BITMAP:
-               decode_bitmap_subtitle (rect, period);
-               break;
-       case SUBTITLE_TEXT:
-               cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n";
-               break;
-       case SUBTITLE_ASS:
-               cout << "XXX: SUBTITLE_ASS " << rect->ass << "\n";
-               break;
+       for (unsigned int i = 0; i < sub.num_rects; ++i) {
+               AVSubtitleRect const * rect = sub.rects[i];
+
+               switch (rect->type) {
+               case SUBTITLE_NONE:
+                       break;
+               case SUBTITLE_BITMAP:
+                       decode_bitmap_subtitle (rect, period);
+                       break;
+               case SUBTITLE_TEXT:
+                       cout << "XXX: SUBTITLE_TEXT " << rect->text << "\n";
+                       break;
+               case SUBTITLE_ASS:
+                       cout << "XXX: SUBTITLE_ASS " << rect->ass << "\n";
+                       break;
+               }
        }
 
        avsubtitle_free (&sub);
@@ -467,7 +469,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTimeP
        /* Note RGBA is expressed little-endian, so the first byte in the word is R, second
           G, third B, fourth A.
        */
-       shared_ptr<Image> image (new Image (PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true));
+       shared_ptr<Image> image (new Image (AV_PIX_FMT_RGBA, dcp::Size (rect->w, rect->h), true));
 
        /* Start of the first line in the subtitle */
        uint8_t* sub_p = rect->pict.data[0];