Merge master.
[dcpomatic.git] / src / lib / ffmpeg_decoder.cc
index c9efd80fc47ba87b052362fd34b7940b570ff127..0dae2a4edc41be11d70855d94e93b9594dc27c39 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2014 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
@@ -27,7 +27,6 @@
 #include <iomanip>
 #include <iostream>
 #include <stdint.h>
-#include <boost/lexical_cast.hpp>
 #include <sndfile.h>
 extern "C" {
 #include <libavcodec/avcodec.h>
@@ -39,12 +38,20 @@ extern "C" {
 #include "util.h"
 #include "log.h"
 #include "ffmpeg_decoder.h"
+#include "ffmpeg_audio_stream.h"
+#include "ffmpeg_subtitle_stream.h"
 #include "filter_graph.h"
 #include "audio_buffers.h"
 #include "ffmpeg_content.h"
+#include "image_proxy.h"
+#include "film.h"
 
 #include "i18n.h"
 
+#define LOG_GENERAL(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_WARNING(...) _video_content->film()->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+
 using std::cout;
 using std::string;
 using std::vector;
@@ -60,13 +67,12 @@ 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)
        , _subtitle_codec (0)
 {
-       setup_subtitle ();
-
        /* Audio and video frame PTS values may not start with 0.  We want
           to fiddle them so that:
 
@@ -137,7 +143,7 @@ FFmpegDecoder::pass ()
                        /* Maybe we should fail here, but for now we'll just finish off instead */
                        char buf[256];
                        av_strerror (r, buf, sizeof(buf));
-                       _log->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
+                       LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r);
                }
 
                flush ();
@@ -313,9 +319,9 @@ FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optiona
 
                        avcodec_get_frame_defaults (_frame);
                        
-                       int finished = 0;
-                       r = avcodec_decode_video2 (video_codec_context(), _frame, &finished, &_packet);
-                       if (r >= 0 && finished) {
+                       int got_picture = 0;
+                       r = avcodec_decode_video2 (video_codec_context(), _frame, &got_picture, &_packet);
+                       if (r >= 0 && got_picture) {
                                last_video = ContentTime::from_seconds (av_frame_get_best_effort_timestamp (_frame) * time_base) + _pts_offset;
                        }
 
@@ -323,9 +329,9 @@ FFmpegDecoder::minimal_run (boost::function<bool (optional<ContentTime>, optiona
                        AVPacket copy_packet = _packet;
                        while (copy_packet.size > 0) {
 
-                               int finished;
-                               r = avcodec_decode_audio4 (audio_codec_context(), _frame, &finished, &_packet);
-                               if (r >= 0 && finished) {
+                               int got_frame;
+                               r = avcodec_decode_audio4 (audio_codec_context(), _frame, &got_frame, &_packet);
+                               if (r >= 0 && got_frame) {
                                        last_audio = ContentTime::from_seconds (av_frame_get_best_effort_timestamp (_frame) * time_base) + _pts_offset;
                                }
                                        
@@ -387,12 +393,12 @@ FFmpegDecoder::seek (ContentTime time, bool accurate)
        VideoDecoder::seek (time, accurate);
        AudioDecoder::seek (time, accurate);
        
-       /* If we are doing an accurate seek, our initial shot will be 200ms (200 being
+       /* If we are doing an accurate seek, our initial shot will be 2s (2 being
           a number plucked from the air) earlier than we want to end up.  The loop below
           will hopefully then step through to where we want to be.
        */
 
-       ContentTime pre_roll = accurate ? ContentTime::from_seconds (0.2) : ContentTime (0);
+       ContentTime pre_roll = accurate ? ContentTime::from_seconds (2) : ContentTime (0);
        ContentTime initial_seek = time - pre_roll;
        if (initial_seek < ContentTime (0)) {
                initial_seek = ContentTime (0);
@@ -430,7 +436,7 @@ FFmpegDecoder::decode_audio_packet ()
                int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, &copy_packet);
 
                if (decode_result < 0) {
-                       _log->log (String::compose ("avcodec_decode_audio4 failed (%1)", decode_result));
+                       LOG_ERROR ("avcodec_decode_audio4 failed (%1)", decode_result);
                        return;
                }
 
@@ -472,7 +478,7 @@ FFmpegDecoder::decode_video_packet ()
        if (i == _filter_graphs.end ()) {
                graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
                _filter_graphs.push_back (graph);
-               _log->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
+               LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
        } else {
                graph = *i;
        }
@@ -485,41 +491,18 @@ FFmpegDecoder::decode_video_packet ()
                
                if (i->second != AV_NOPTS_VALUE) {
                        double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds ();
-                       video (image, rint (pts * _ffmpeg_content->video_frame_rate ()));
+                       video (
+                               shared_ptr<ImageProxy> (new RawImageProxy (image, _video_content->film()->log())),
+                               rint (pts * _ffmpeg_content->video_frame_rate ())
+                               );
                } else {
-                       _log->log ("Dropping frame without PTS");
+                       LOG_WARNING ("Dropping frame without PTS");
                }
        }
 
        return true;
 }
-
        
-void
-FFmpegDecoder::setup_subtitle ()
-{
-       boost::mutex::scoped_lock lm (_mutex);
-       
-       if (!_ffmpeg_content->subtitle_stream()) {
-               return;
-       }
-
-       _subtitle_codec_context = _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
-       if (_subtitle_codec_context == 0) {
-               throw DecodeError (N_("could not find subtitle stream"));
-       }
-
-       _subtitle_codec = avcodec_find_decoder (_subtitle_codec_context->codec_id);
-
-       if (_subtitle_codec == 0) {
-               throw DecodeError (N_("could not find subtitle decoder"));
-       }
-       
-       if (avcodec_open2 (_subtitle_codec_context, _subtitle_codec, 0) < 0) {
-               throw DecodeError (N_("could not open subtitle decoder"));
-       }
-}
-
 void
 FFmpegDecoder::decode_subtitle_packet ()
 {
@@ -533,7 +516,7 @@ FFmpegDecoder::decode_subtitle_packet ()
           indicate that the previous subtitle should stop.
        */
        if (sub.num_rects <= 0) {
-               image_subtitle (ContentTime (), ContentTime (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
+               image_subtitle (ContentTimePeriod (), shared_ptr<Image> (), dcpomatic::Rect<double> ());
                return;
        } else if (sub.num_rects > 1) {
                throw DecodeError (_("multi-part subtitles not yet supported"));
@@ -542,16 +525,14 @@ 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;
-       
-       /* hence start time for this sub */
-       ContentTime const from = packet_time + ContentTime::from_seconds (sub.start_display_time / 1e3);
-       ContentTime const to = packet_time + ContentTime::from_seconds (sub.end_display_time / 1e3);
+       ContentTimePeriod period = subtitle_period (sub) + _pts_offset;
 
        AVSubtitleRect const * rect = sub.rects[0];
 
        if (rect->type != SUBTITLE_BITMAP) {
-               throw DecodeError (_("non-bitmap subtitles not yet supported"));
+               /* XXX */
+               // throw DecodeError (_("non-bitmap subtitles not yet supported"));
+               return;
        }
 
        /* Note RGBA is expressed little-endian, so the first byte in the word is R, second
@@ -582,8 +563,7 @@ FFmpegDecoder::decode_subtitle_packet ()
        dcp::Size const vs = _ffmpeg_content->video_size ();
 
        image_subtitle (
-               from,
-               to,
+               period,
                image,
                dcpomatic::Rect<double> (
                        static_cast<double> (rect->x) / vs.width,