/*
- Copyright (C) 2013-2019 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
+
#include "ffmpeg.h"
#include "ffmpeg_content.h"
#include "film.h"
#include "dcpomatic_log.h"
#include "ffmpeg_subtitle_stream.h"
#include "ffmpeg_audio_stream.h"
-#include "decrypted_ecinema_kdm.h"
#include "digester.h"
#include "compose.hpp"
#include "config.h"
#include <libswscale/swscale.h>
}
#include <boost/algorithm/string.hpp>
-#include <boost/foreach.hpp>
#include <iostream>
#include "i18n.h"
+
using std::string;
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;
-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)
- , _avio_context (0)
- , _format_context (0)
- , _frame (0)
{
setup_general ();
setup_decoders ();
}
+
FFmpeg::~FFmpeg ()
{
boost::mutex::scoped_lock lm (_mutex);
- for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
- avcodec_close (_format_context->streams[i]->codec);
+ for (auto& i: _codec_context) {
+ avcodec_free_context (&i);
}
av_frame_free (&_frame);
avformat_close_input (&_format_context);
}
+
static int
avio_read_wrapper (void* data, uint8_t* buffer, int amount)
{
return reinterpret_cast<FFmpeg*>(data)->avio_read (buffer, amount);
}
+
static int64_t
avio_seek_wrapper (void* data, int64_t offset, int whence)
{
return reinterpret_cast<FFmpeg*>(data)->avio_seek (offset, whence);
}
+
void
FFmpeg::ffmpeg_log_callback (void* ptr, int level, const char* fmt, va_list vl)
{
dcpomatic_log->log (String::compose ("FFmpeg: %1", str), LogEntry::TYPE_GENERAL);
}
+
void
FFmpeg::setup_general ()
{
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_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->pb = _avio_context;
- AVDictionary* options = 0;
-#ifdef DCPOMATIC_VARIANT_SWAROOP
- if (_ffmpeg_content->kdm()) {
- DecryptedECinemaKDM kdm (_ffmpeg_content->kdm().get(), Config::instance()->decryption_chain()->key().get());
- av_dict_set (&options, "decryption_key", kdm.key().hex().c_str(), 0);
- }
-#endif
-
+ AVDictionary* options = nullptr;
int e = avformat_open_input (&_format_context, 0, 0, &options);
if (e < 0) {
throw OpenFileError (_ffmpeg_content->path(0).string(), e, OpenFileError::READ);
optional<int> video_stream_undefined_frame_rate;
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 && avcodec_find_decoder(s->codec->codec_id)) {
+ auto s = _format_context->streams[i];
+ if (s->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && avcodec_find_decoder(s->codecpar->codec_id)) {
if (s->avg_frame_rate.num > 0 && s->avg_frame_rate.den > 0) {
/* This is definitely our video stream */
_video_stream = i;
_frame = av_frame_alloc ();
if (_frame == 0) {
- throw DecodeError (N_("could not allocate frame"));
+ throw std::bad_alloc ();
}
}
+
void
FFmpeg::setup_decoders ()
{
boost::mutex::scoped_lock lm (_mutex);
+ _codec_context.resize (_format_context->nb_streams);
for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
- AVCodecContext* context = _format_context->streams[i]->codec;
-
- AVCodec* codec = avcodec_find_decoder (context->codec_id);
+ auto codec = avcodec_find_decoder (_format_context->streams[i]->codecpar->codec_id);
if (codec) {
+ auto context = avcodec_alloc_context3 (codec);
+ if (!context) {
+ throw std::bad_alloc ();
+ }
+ _codec_context[i] = context;
+
+ int r = avcodec_parameters_to_context (context, _format_context->streams[i]->codecpar);
+ if (r < 0) {
+ throw DecodeError ("avcodec_parameters_to_context", "FFmpeg::setup_decoders", r);
+ }
+
+ context->thread_count = 8;
+ context->thread_type = FF_THREAD_FRAME | FF_THREAD_SLICE;
- AVDictionary* options = 0;
+ AVDictionary* options = nullptr;
/* This option disables decoding of DCA frame footers in our patched version
of FFmpeg. I believe these footers are of no use to us, and they can cause
problems when FFmpeg fails to decode them (mantis #352).
/* 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"));
+ r = avcodec_open2 (context, codec, &options);
+ if (r < 0) {
+ throw DecodeError (N_("avcodec_open2"), N_("FFmpeg::setup_decoders"), r);
}
} else {
dcpomatic_log->log (String::compose ("No codec found for stream %1", i), LogEntry::TYPE_WARNING);
}
}
+
AVCodecContext *
FFmpeg::video_codec_context () const
{
if (!_video_stream) {
- return 0;
+ return nullptr;
}
- return _format_context->streams[_video_stream.get()]->codec;
+ return _codec_context[_video_stream.get()];
}
+
AVCodecContext *
FFmpeg::subtitle_codec_context () const
{
- if (!_ffmpeg_content->subtitle_stream ()) {
- return 0;
+ auto str = _ffmpeg_content->subtitle_stream();
+ if (!str) {
+ return nullptr;
}
- return _ffmpeg_content->subtitle_stream()->stream(_format_context)->codec;
+ return _codec_context[str->index(_format_context)];
}
+
int
FFmpeg::avio_read (uint8_t* buffer, int const amount)
{
return _file_group.read (buffer, amount);
}
+
int64_t
FFmpeg::avio_seek (int64_t const pos, int whence)
{
return _file_group.seek (pos, whence);
}
+
FFmpegSubtitlePeriod
FFmpeg::subtitle_period (AVSubtitle const & sub)
{
- ContentTime const packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE);
+ auto const packet_time = ContentTime::from_seconds (static_cast<double> (sub.pts) / AV_TIME_BASE);
if (sub.end_display_time == static_cast<uint32_t> (-1)) {
/* End time is not known */
);
}
+
/** 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
* in FFmpeg.
*/
ContentTime
-FFmpeg::pts_offset (vector<shared_ptr<FFmpegAudioStream> > audio_streams, optional<ContentTime> first_video, double video_frame_rate) const
+FFmpeg::pts_offset (vector<shared_ptr<FFmpegAudioStream>> audio_streams, optional<ContentTime> first_video, double video_frame_rate) const
{
/* Audio and video frame PTS values may not start with 0. We want
to fiddle them so that:
/* First, make one of them start at 0 */
- ContentTime po = ContentTime::min ();
+ auto po = ContentTime::min ();
if (first_video) {
po = - first_video.get ();
}
- BOOST_FOREACH (shared_ptr<FFmpegAudioStream> i, audio_streams) {
+ for (auto i: audio_streams) {
if (i->first_audio) {
po = max (po, - i->first_audio.get ());
}
/* Now adjust so that the video pts starts on a frame */
if (first_video) {
- ContentTime const fvc = first_video.get() + po;
+ auto const fvc = first_video.get() + po;
po += fvc.ceil (video_frame_rate) - fvc;
}