Skip to content

Commit

Permalink
Reverted TBB multithreading for FFmpeg
Browse files Browse the repository at this point in the history
  • Loading branch information
jaskie committed Jan 21, 2019
1 parent 8a3f9c3 commit 58c526f
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 49 deletions.
14 changes: 8 additions & 6 deletions modules/ffmpeg/consumer/ffmpeg_consumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "../StdAfx.h"

#include "../ffmpeg_error.h"
#include "../tbb_avcodec.h"
#include "../ffmpeg.h"
#include "../producer/filter/filter.h"
#include "../producer/util/util.h"
Expand Down Expand Up @@ -519,13 +520,15 @@ namespace caspar {
if (format->flags & AVFMT_GLOBALHEADER)
video_codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
video_codec_ctx_->sample_aspect_ratio = sample_aspect_ratio;

if (video_codec_ctx_->pix_fmt == AV_PIX_FMT_NONE)
video_codec_ctx_->pix_fmt = pix_fmt == AV_PIX_FMT_NONE ? AV_PIX_FMT_YUV420P : pix_fmt;
if (video_codec_ctx_->thread_type & FF_THREAD_FRAME)
video_codec_ctx_->thread_count = 16;

THROW_ON_ERROR2(avcodec_open2(video_codec_ctx_.get(), encoder, &options_), "[ffmpeg_consumer]");
if (tbb_avcodec_open(video_codec_ctx_.get(), encoder, &options_) < 0)
{
CASPAR_LOG(debug) << print() << L" Multithreaded avcodec_open2 failed";
video_codec_ctx_->thread_count = 1;
THROW_ON_ERROR2(avcodec_open2(video_codec_ctx_.get(), encoder, &options_), "[ffmpeg_consumer]");
}

video_stream_ = avformat_new_stream(format_context_.get(), NULL);
if (!video_stream_)
Expand Down Expand Up @@ -585,9 +588,8 @@ namespace caspar {

audio_is_planar_ = av_sample_fmt_is_planar(audio_codec_ctx_->sample_fmt) != 0;


THROW_ON_ERROR2(avcodec_open2(audio_codec_ctx_.get(), encoder, &options_), "[ffmpeg_consumer]");

audio_stream_ = avformat_new_stream(format_context_.get(), NULL);
if (!audio_stream_)
BOOST_THROW_EXCEPTION(caspar_exception() << msg_info("Could not allocate audio-stream") << boost::errinfo_api_function("avformat_new_stream"));
Expand Down
11 changes: 11 additions & 0 deletions modules/ffmpeg/ffmpeg.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,16 @@
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">../../StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../../StdAfx.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="tbb_avcodec.cpp">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">../StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|Win32'">StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Develop|x64'">../StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">../StdAfx.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="producer\util\flv.cpp">
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|Win32'">../../StdAfx.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Profile|x64'">../../StdAfx.h</PrecompiledHeaderFile>
Expand Down Expand Up @@ -633,6 +643,7 @@
<ClInclude Include="producer\input\input.h" />
<ClInclude Include="producer\muxer\display_mode.h" />
<ClInclude Include="producer\muxer\frame_muxer.h" />
<ClInclude Include="tbb_avcodec.h" />
<ClInclude Include="producer\util\flv.h" />
<ClInclude Include="producer\util\util.h" />
<ClInclude Include="producer\video\video_decoder.h" />
Expand Down
6 changes: 6 additions & 0 deletions modules/ffmpeg/ffmpeg.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@
<ClCompile Include="producer\ffmpeg_producer.cpp">
<Filter>source\producer</Filter>
</ClCompile>
<ClCompile Include="tbb_avcodec.cpp">
<Filter>source</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="producer\ffmpeg_producer.h">
Expand Down Expand Up @@ -100,5 +103,8 @@
<ClInclude Include="producer\muxer\display_mode.h">
<Filter>source\producer\muxer</Filter>
</ClInclude>
<ClInclude Include="tbb_avcodec.h">
<Filter>source</Filter>
</ClInclude>
</ItemGroup>
</Project>
6 changes: 2 additions & 4 deletions modules/ffmpeg/producer/audio/audio_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ struct audio_decoder::implementation : boost::noncopyable
static const int BUFFER_SIZE = 480000 * 2;
input input_;
const safe_ptr<AVCodecContext> codec_context_;
int stream_index_;
const AVStream* stream_;
AVStream* stream_;
caspar::core::video_format_desc format_;
const std::shared_ptr<SwrContext> swr_;
core::channel_layout channel_layout_;
Expand All @@ -68,11 +67,10 @@ struct audio_decoder::implementation : boost::noncopyable
public:
explicit implementation(input input, caspar::core::video_format_desc format, const std::wstring& custom_channel_order)
: input_(input)
, codec_context_(input.open_audio_codec(stream_index_))
, codec_context_(input.open_audio_codec(&stream_))
, format_(format)
, channel_layout_(get_audio_channel_layout(*codec_context_, custom_channel_order))
, swr_(alloc_resampler())
, stream_(input_.format_context()->streams[stream_index_])
, stream_start_pts_(stream_->start_time)
, buffer_(BUFFER_SIZE)
{
Expand Down
30 changes: 21 additions & 9 deletions modules/ffmpeg/producer/input/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "../util/flv.h"
#include "../../ffmpeg_error.h"
#include "../../ffmpeg.h"
#include "../../tbb_avcodec.h"

#include <core/video_format.h>

Expand Down Expand Up @@ -109,18 +110,29 @@ struct input::implementation : boost::noncopyable
graph_->set_color("video-buffer-count", diagnostics::color(1.0f, 1.0f, 0.0f));
}

safe_ptr<AVCodecContext> open_audio_codec(int& index)
safe_ptr<AVCodecContext> open_audio_codec(AVStream** stream)
{
auto ret = open_codec(format_context_, AVMEDIA_TYPE_AUDIO, index);
AVCodec* decoder;
int index = THROW_ON_ERROR2(av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_AUDIO, -1, -1, &decoder, 0), print());
THROW_ON_ERROR2(avcodec_open2(format_context_->streams[index]->codec, decoder, NULL), print());
audio_stream_index_ = index;
return ret;
*stream = format_context_->streams[index];
return safe_ptr<AVCodecContext>(format_context_->streams[index]->codec, avcodec_close);
}

safe_ptr<AVCodecContext> open_video_codec(int& index)
safe_ptr<AVCodecContext> open_video_codec(AVStream** stream)
{
auto ret = open_codec(format_context_, AVMEDIA_TYPE_VIDEO, index);
AVCodec* decoder;
int index = THROW_ON_ERROR2(av_find_best_stream(format_context_.get(), AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0), print());
if (tbb_avcodec_open(format_context_->streams[index]->codec, decoder, NULL) < 0)
{
CASPAR_LOG(debug) << print() << L" Multithreaded avcodec_open2 failed";
format_context_->streams[index]->codec->thread_count = 1;
THROW_ON_ERROR2(avcodec_open2(format_context_->streams[index]->codec, decoder, NULL), print());
}
video_stream_index_ = index;
return ret;
*stream = format_context_->streams[index];
return safe_ptr<AVCodecContext>(format_context_->streams[index]->codec, avcodec_close);
}

bool get_flush_av_packet(std::shared_ptr<AVPacket>& packet)
Expand Down Expand Up @@ -272,7 +284,7 @@ struct input::implementation : boost::noncopyable
CASPAR_LOG(trace) << print() << " Seeking: " << target_time / 1000 << " ms";
flush_av_packet_count_ = FLUSH_AV_PACKET_COUNT;
is_eof_ = false;
if (av_seek_frame(format_context_.get(), -1, target_time, AVSEEK_FLAG_BACKWARD) < 0)
if (av_seek_frame(format_context_.get(), -1, target_time - AV_TIME_BASE, AVSEEK_FLAG_BACKWARD) < 0)
CASPAR_LOG(error) << print() << " Seek failed";
tick();
}, high_priority);
Expand All @@ -286,7 +298,7 @@ bool input::try_pop_audio(std::shared_ptr<AVPacket>& packet){return impl_->try_p
bool input::try_pop_video(std::shared_ptr<AVPacket>& packet) { return impl_->try_pop_video(packet); }
safe_ptr<AVFormatContext> input::format_context(){return impl_->format_context_;}
void input::seek(int64_t target_time){impl_->seek(target_time);}
safe_ptr<AVCodecContext> input::open_audio_codec(int& index) { return impl_->open_audio_codec(index);}
safe_ptr<AVCodecContext> input::open_video_codec(int& index) { return impl_->open_video_codec(index); }
safe_ptr<AVCodecContext> input::open_audio_codec(AVStream** stream) { return impl_->open_audio_codec(stream);}
safe_ptr<AVCodecContext> input::open_video_codec(AVStream** stream) { return impl_->open_video_codec(stream); }

}}
4 changes: 2 additions & 2 deletions modules/ffmpeg/producer/input/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class input
{
public:
explicit input(const safe_ptr<diagnostics::graph> graph, const std::wstring& filename, bool thumbnail_mode);
safe_ptr<AVCodecContext> open_audio_codec(int& index);
safe_ptr<AVCodecContext> open_video_codec(int& index);
safe_ptr<AVCodecContext> open_audio_codec(AVStream** stream);
safe_ptr<AVCodecContext> open_video_codec(AVStream** stream);

bool try_pop_audio(std::shared_ptr<AVPacket>& packet);
bool try_pop_video(std::shared_ptr<AVPacket>& packet);
Expand Down
22 changes: 0 additions & 22 deletions modules/ffmpeg/producer/util/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,20 +47,6 @@
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>

#if defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4244)
#endif
extern "C"
{
#include <libswscale/swscale.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#if defined(_MSC_VER)
#pragma warning (pop)
#endif

namespace caspar { namespace ffmpeg {

std::shared_ptr<core::audio_buffer> flush_audio()
Expand Down Expand Up @@ -427,14 +413,6 @@ double read_fps(AVFormatContext& context, double fail_value)
}


safe_ptr<AVCodecContext> open_codec(safe_ptr<AVFormatContext> context, enum AVMediaType type, int& index)
{
AVCodec* decoder;
index = THROW_ON_ERROR2(av_find_best_stream(context.get(), type, -1, -1, &decoder, 0), "[open_codec}");
THROW_ON_ERROR2(avcodec_open2(context->streams[index]->codec, decoder, NULL), "[open_codec]");
return safe_ptr<AVCodecContext>(context->streams[index]->codec, avcodec_close);
}

std::wstring print_mode(size_t width, size_t height, double fps, bool interlaced)
{
std::wostringstream fps_ss;
Expand Down
2 changes: 0 additions & 2 deletions modules/ffmpeg/producer/util/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@ safe_ptr<core::write_frame> make_write_frame(const void* tag, const safe_ptr<AVF
safe_ptr<AVPacket> create_packet();
safe_ptr<AVFrame> create_frame();

safe_ptr<AVCodecContext> open_codec(safe_ptr<AVFormatContext> context, enum AVMediaType type, int& index);

bool is_sane_fps(AVRational time_base);
AVRational fix_time_base(AVRational time_base);

Expand Down
6 changes: 2 additions & 4 deletions modules/ffmpeg/producer/video/video_decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ struct video_decoder::implementation : boost::noncopyable
input input_;
const safe_ptr<AVCodecContext> codec_context_;
const AVCodec* codec_;
int stream_index_;
const AVStream* stream_;
AVStream* stream_;
const uint32_t nb_frames_;
const size_t width_;
const size_t height_;
Expand All @@ -70,11 +69,10 @@ struct video_decoder::implementation : boost::noncopyable
public:
explicit implementation(input input, bool invert_field_order)
: input_(input)
, codec_context_(input.open_video_codec(stream_index_))
, codec_context_(input.open_video_codec(&stream_))
, codec_(codec_context_->codec)
, width_(codec_context_->width)
, height_(codec_context_->height)
, stream_(input_.format_context()->streams[stream_index_])
, stream_start_pts_(stream_->start_time)
, nb_frames_(static_cast<uint32_t>(calc_nb_frames(stream_)))
{
Expand Down
112 changes: 112 additions & 0 deletions modules/ffmpeg/tbb_avcodec.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2013 Sveriges Television AB http://casparcg.com/
*
* This file is part of CasparCG (www.casparcg.com).
*
* CasparCG is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CasparCG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Robert Nagy, [email protected]
*/

#include "StdAfx.h"

#include "tbb_avcodec.h"

#include <common/log/log.h>
#include <common/env.h>
#include <common/utility/assert.h>

#include <tbb/task.h>
#include <tbb/atomic.h>
#include <tbb/parallel_for.h>
#include <tbb/tbb_thread.h>
#include <boost/thread.hpp>

#if defined(_MSC_VER)
#pragma warning (push)
#pragma warning (disable : 4244)
#endif
extern "C"
{
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#include <libavformat/avformat.h>
}
#if defined(_MSC_VER)
#pragma warning (pop)
#endif

namespace caspar {

static const size_t MAX_THREADS = 16; // See mpegvideo.h

int thread_execute(AVCodecContext* s, int (*func)(AVCodecContext *c2, void *arg2), void* arg, int* ret, int count, int size)
{
tbb::parallel_for(0, count, 1, [&](int i)
{
int r = func(s, (char*)arg + i*size);
if(ret)
ret[i] = r;
});

return 0;
}

int thread_execute2(AVCodecContext* s, int (*func)(AVCodecContext* c2, void* arg2, int, int), void* arg, int* ret, int count)
{
tbb::atomic<int> counter;
counter = 0;

//CASPAR_VERIFY(tbb::tbb_thread::hardware_concurrency() < MAX_THREADS);
// Note: this will probably only work when tbb::task_scheduler_init::num_threads() < 16.
tbb::parallel_for(tbb::blocked_range<int>(0, count, 2), [&](const tbb::blocked_range<int> &r)
{
int threadnr = counter++;
for(int jobnr = r.begin(); jobnr != r.end(); ++jobnr)
{
int r = func(s, arg, jobnr, threadnr);
if (ret)
ret[jobnr] = r;
}
--counter;
});

return 0;
}

void thread_init(AVCodecContext* s, bool execute2enable)
{
s->execute = thread_execute;
if (execute2enable)
s->execute2 = thread_execute2;
if (s->thread_type & FF_THREAD_SLICE)
s->slice_count = std::min(tbb::tbb_thread::hardware_concurrency(), MAX_THREADS);
if (s->thread_type & FF_THREAD_FRAME)
s->thread_count = std::min(tbb::tbb_thread::hardware_concurrency(), MAX_THREADS); // We are using a task-scheduler, so use as many "threads/tasks" as possible.

CASPAR_LOG(info) << "Initialized ffmpeg tbb context.";
}

int tbb_avcodec_open(AVCodecContext* avctx, const AVCodec* codec, AVDictionary** options)
{
if(((codec->capabilities & AV_CODEC_CAP_SLICE_THREADS) && (avctx->thread_type & FF_THREAD_SLICE)
|| (codec->capabilities & AV_CODEC_CAP_FRAME_THREADS)) && (avctx->thread_type & FF_THREAD_FRAME))
{
thread_init(avctx, codec->id != AV_CODEC_ID_PRORES); // do not enable execute2 for prores codec as it cause crash
}
avctx->refcounted_frames = 0;
return avcodec_open2(avctx, codec, options);
}

}
32 changes: 32 additions & 0 deletions modules/ffmpeg/tbb_avcodec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2013 Sveriges Television AB http://casparcg.com/
*
* This file is part of CasparCG (www.casparcg.com).
*
* CasparCG is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CasparCG is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Robert Nagy, [email protected]
*/

#pragma once

struct AVCodecContext;
struct AVCodec;
struct AVDictionary;

namespace caspar {

int tbb_avcodec_open(AVCodecContext *avctx, const AVCodec *codec, AVDictionary ** options);

}

0 comments on commit 58c526f

Please sign in to comment.