Make terminate_threads() less likely to leave _threads containing invalid pointers.
[dcpomatic.git] / src / lib / ffmpeg.cc
index 8c0f0798eb0742d7e1ddc388c39a451fafd437c8..f26d1d2cf224530582f709482840a2fb6840be39 100644 (file)
 #include "film.h"
 #include "exceptions.h"
 #include "util.h"
-#include "raw_convert.h"
 #include "log.h"
+#include "dcpomatic_log.h"
 #include "ffmpeg_subtitle_stream.h"
 #include "ffmpeg_audio_stream.h"
 #include "digester.h"
 #include "compose.hpp"
+#include <dcp/raw_convert.h>
 extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libavformat/avformat.h>
@@ -46,9 +47,9 @@ using std::cerr;
 using std::vector;
 using boost::shared_ptr;
 using boost::optional;
+using dcp::raw_convert;
 
 boost::mutex FFmpeg::_mutex;
-boost::weak_ptr<Log> FFmpeg::_ffmpeg_log;
 
 FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
        : _ffmpeg_content (c)
@@ -96,25 +97,17 @@ FFmpeg::ffmpeg_log_callback (void* ptr, int level, const char* fmt, va_list vl)
        char line[1024];
        static int prefix = 0;
        av_log_format_line (ptr, level, fmt, vl, line, sizeof (line), &prefix);
-       shared_ptr<Log> log = _ffmpeg_log.lock ();
-       if (log) {
-               string str (line);
-               boost::algorithm::trim (str);
-               log->log (String::compose ("FFmpeg: %1", str), LogEntry::TYPE_GENERAL);
-       } else {
-               cerr << line;
-       }
+       string str (line);
+       boost::algorithm::trim (str);
+       dcpomatic_log->log (String::compose ("FFmpeg: %1", str), LogEntry::TYPE_GENERAL);
 }
 
 void
 FFmpeg::setup_general ()
 {
-       av_register_all ();
-
        /* This might not work too well in some cases of multiple FFmpeg decoders,
           but it's probably good enough.
        */
-       _ffmpeg_log = _ffmpeg_content->film()->log ();
        av_log_set_callback (FFmpeg::ffmpeg_log_callback);
 
        _file_group.set_paths (_ffmpeg_content->paths ());
@@ -129,9 +122,13 @@ FFmpeg::setup_general ()
        */
        av_dict_set (&options, "analyzeduration", raw_convert<string> (5 * 60 * 1000000).c_str(), 0);
        av_dict_set (&options, "probesize", raw_convert<string> (5 * 60 * 1000000).c_str(), 0);
+       if (_ffmpeg_content->decryption_key()) {
+               av_dict_set (&options, "decryption_key", _ffmpeg_content->decryption_key()->c_str(), 0);
+       }
 
-       if (avformat_open_input (&_format_context, 0, 0, &options) < 0) {
-               throw OpenFileError (_ffmpeg_content->path(0).string ());
+       int e = avformat_open_input (&_format_context, 0, 0, &options);
+       if (e < 0) {
+               throw OpenFileError (_ffmpeg_content->path(0).string(), e, OpenFileError::READ);
        }
 
        if (avformat_find_stream_info (_format_context, 0) < 0) {
@@ -144,7 +141,7 @@ FFmpeg::setup_general ()
 
        for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
                AVStream* s = _format_context->streams[i];
-               if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
+               if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO && avcodec_find_decoder(s->codec->codec_id)) {
                        if (s->avg_frame_rate.num > 0 && s->avg_frame_rate.den > 0) {
                                /* This is definitely our video stream */
                                _video_stream = i;
@@ -162,6 +159,11 @@ FFmpeg::setup_general ()
                _video_stream = video_stream_undefined_frame_rate.get();
        }
 
+       /* Ignore video streams with crazy frame rates.  These are usually things like album art on MP3s. */
+       if (_video_stream && av_q2d(av_guess_frame_rate(_format_context, _format_context->streams[_video_stream.get()], 0)) > 1000) {
+               _video_stream = optional<int>();
+       }
+
        /* Hack: if the AVStreams have duplicate IDs, replace them with our
           own.  We use the IDs so that we can cope with VOBs, in which streams
           move about in index but remain with the same ID in different
@@ -211,20 +213,25 @@ FFmpeg::setup_decoders ()
                           https://trac.ffmpeg.org/ticket/5681
                        */
                        av_dict_set_int (&options, "strict", FF_COMPLIANCE_EXPERIMENTAL, 0);
+                       /* Enable following of links in files */
+                       av_dict_set_int (&options, "enable_drefs", 1, 0);
 
                        if (avcodec_open2 (context, codec, &options) < 0) {
                                throw DecodeError (N_("could not open decoder"));
                        }
+               } else {
+                       dcpomatic_log->log (String::compose ("No codec found for stream %1", i), LogEntry::TYPE_WARNING);
                }
-
-               /* We are silently ignoring any failures to find suitable decoders here */
        }
 }
 
 AVCodecContext *
 FFmpeg::video_codec_context () const
 {
-       DCPOMATIC_ASSERT (_video_stream);
+       if (!_video_stream) {
+               return 0;
+       }
+
        return _format_context->streams[_video_stream.get()]->codec;
 }
 
@@ -270,59 +277,6 @@ FFmpeg::subtitle_period (AVSubtitle const & sub)
                );
 }
 
-string
-FFmpeg::subtitle_id (AVSubtitle const & sub)
-{
-       Digester digester;
-       digester.add (sub.pts);
-       for (unsigned int i = 0; i < sub.num_rects; ++i) {
-               AVSubtitleRect* rect = sub.rects[i];
-               digester.add (rect->x);
-               digester.add (rect->y);
-               digester.add (rect->w);
-               digester.add (rect->h);
-#ifdef DCPOMATIC_HAVE_AVSUBTITLERECT_PICT
-               int const line = rect->pict.linesize[0];
-               for (int j = 0; j < rect->h; ++j) {
-                       digester.add (rect->pict.data[0] + j * line, line);
-               }
-#else
-               int const line = rect->linesize[0];
-               for (int j = 0; j < rect->h; ++j) {
-                       digester.add (rect->data[0] + j * line, line);
-               }
-#endif
-       }
-       return digester.get ();
-}
-
-/** @return true if sub starts a new image subtitle */
-bool
-FFmpeg::subtitle_starts_image (AVSubtitle const & sub)
-{
-       bool image = false;
-       bool text = false;
-
-       for (unsigned int i = 0; i < sub.num_rects; ++i) {
-               switch (sub.rects[i]->type) {
-               case SUBTITLE_BITMAP:
-                       image = true;
-                       break;
-               case SUBTITLE_TEXT:
-               case SUBTITLE_ASS:
-                       text = true;
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       /* We can't cope with mixed image/text in one AVSubtitle */
-       DCPOMATIC_ASSERT (!image || !text);
-
-       return image;
-}
-
 /** Compute the pts offset to use given a set of audio streams and some video details.
  *  Sometimes these parameters will have just been determined by an Examiner, sometimes
  *  they will have been retrieved from a piece of Content, hence the need for this method
@@ -370,7 +324,7 @@ FFmpeg::pts_offset (vector<shared_ptr<FFmpegAudioStream> > audio_streams, option
        /* Now adjust so that the video pts starts on a frame */
        if (first_video) {
                ContentTime const fvc = first_video.get() + po;
-               po += fvc.round_up (video_frame_rate) - fvc;
+               po += fvc.ceil (video_frame_rate) - fvc;
        }
 
        return po;