/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "exceptions.h"
#include "util.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 "config.h"
#include <dcp/raw_convert.h>
extern "C" {
#include <libavcodec/avcodec.h>
using std::cout;
using std::cerr;
using std::vector;
-using boost::shared_ptr;
+using std::shared_ptr;
using boost::optional;
using dcp::raw_convert;
+using namespace dcpomatic;
boost::mutex FFmpeg::_mutex;
-boost::weak_ptr<Log> FFmpeg::_ffmpeg_log;
-FFmpeg::FFmpeg (boost::shared_ptr<const FFmpegContent> c)
+FFmpeg::FFmpeg (std::shared_ptr<const FFmpegContent> c)
: _ffmpeg_content (c)
, _avio_buffer (0)
, _avio_buffer_size (4096)
{
boost::mutex::scoped_lock lm (_mutex);
+DCPOMATIC_DISABLE_WARNINGS
for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
avcodec_close (_format_context->streams[i]->codec);
}
+DCPOMATIC_ENABLE_WARNINGS
av_frame_free (&_frame);
avformat_close_input (&_format_context);
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 ());
_avio_buffer = static_cast<uint8_t*> (wrapped_av_malloc (_avio_buffer_size));
_avio_context = avio_alloc_context (_avio_buffer, _avio_buffer_size, 0, this, avio_read_wrapper, 0, avio_seek_wrapper);
+ if (!_avio_context) {
+ throw std::bad_alloc ();
+ }
_format_context = avformat_alloc_context ();
+ if (!_format_context) {
+ throw std::bad_alloc ();
+ }
_format_context->pb = _avio_context;
AVDictionary* options = 0;
- /* These durations are in microseconds, and represent how far into the content file
- we will look for streams.
- */
- 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);
-
int e = avformat_open_input (&_format_context, 0, 0, &options);
if (e < 0) {
- throw OpenFileError (_ffmpeg_content->path(0).string(), e, true);
+ throw OpenFileError (_ffmpeg_content->path(0).string(), e, OpenFileError::READ);
}
if (avformat_find_stream_info (_format_context, 0) < 0) {
optional<int> video_stream_undefined_frame_rate;
+DCPOMATIC_DISABLE_WARNINGS
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;
}
}
}
+DCPOMATIC_ENABLE_WARNINGS
/* Files from iTunes sometimes have two video streams, one with the avg_frame_rate.num and .den set
to zero. Only use such a stream if there is no alternative.
_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
{
boost::mutex::scoped_lock lm (_mutex);
+DCPOMATIC_DISABLE_WARNINGS
for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
AVCodecContext* context = _format_context->streams[i]->codec;
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 */
}
+DCPOMATIC_ENABLE_WARNINGS
}
+DCPOMATIC_DISABLE_WARNINGS
AVCodecContext *
FFmpeg::video_codec_context () const
{
return _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
}
+DCPOMATIC_ENABLE_WARNINGS
int
FFmpeg::avio_read (uint8_t* buffer, int const amount)
);
}
-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];
- if (rect->type == SUBTITLE_BITMAP) {
- 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
- } else if (rect->type == SUBTITLE_TEXT) {
- digester.add (string (rect->text));
- } else if (rect->type == SUBTITLE_ASS) {
- digester.add (string (rect->ass));
- }
- }
- 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