2#include <Media/Libav.hpp>
6#include <libavcodec/avcodec.h>
7#include <libavutil/cpu.h>
8#include <libavutil/imgutils.h>
9#include <libavutil/opt.h>
53 const char* reason{
""};
73 static constexpr std::string_view names[]{
82 const std::string_view name = codec.name ? codec.name :
"";
83 return std::find(std::begin(names), std::end(names), name) != std::end(names);
90 static constexpr std::string_view names[]{
91 "libvpx",
"libvpx-vp9",
"libaom-av1",
"libjxl",
"libjxl_anim"};
92 const std::string_view name = codec.name ? codec.name :
"";
93 return std::find(std::begin(names), std::end(names), name) != std::end(names);
99 return codec.priv_class
101 (
void*)&codec.priv_class,
"max_frame_delay",
nullptr, 0,
102 AV_OPT_SEARCH_FAKE_OBJ);
105inline int cpuCount() noexcept
107 return std::max(1, av_cpu_count());
110inline int64_t frameBytes(
const AVCodecParameters* par)
noexcept
112 if(!par || par->width <= 0 || par->height <= 0)
116 const int sz = av_image_get_buffer_size(
117 (AVPixelFormat)par->format, par->width, par->height, 1);
121 return int64_t(par->width) * par->height * 4;
124inline int frameThreads(
const AVCodecParameters* par)
noexcept
126 int n = std::min(cpuCount(), max_frame_threads);
127 if(
const auto bytes = frameBytes(par); bytes > 0)
128 n = std::min<int64_t>(n, std::max<int64_t>(2, frame_memory_budget / bytes));
132inline int sliceThreads(
const AVCodecParameters* par)
noexcept
134 int n = std::min(cpuCount(), max_slice_threads);
136 if(par && par->height > 0)
137 n = std::min(n, std::max(1, (par->height + 15) / 16));
146 const char* env = std::getenv(
"SCORE_VIDEO_THREADING");
149 const std::string_view v = env;
152 return FF_THREAD_FRAME;
154 return FF_THREAD_SLICE;
161inline int envThreadCount() noexcept
163 const char* env = std::getenv(
"SCORE_VIDEO_THREADS");
164 return env ? std::max(0, std::atoi(env)) : 0;
175 const AVCodec& codec,
const AVCodecParameters* par,
DecodeUseCase use,
176 int userThreads = 0,
bool hardware =
false) noexcept
178 using namespace threading;
179#if defined(__EMSCRIPTEN__)
182 return {1, 0,
false,
"single-threaded on wasm"};
184 if(hardware || (codec.capabilities & AV_CODEC_CAP_HARDWARE))
185 return {1, 0,
false,
"hardware decoder"};
187 if(
const int n = envThreadCount(); n > 0)
190 const int forcedType = envThreadType(forced);
192 const int caps = codec.capabilities;
193 const bool canFrame = caps & AV_CODEC_CAP_FRAME_THREADS;
194 const bool canSlice = caps & AV_CODEC_CAP_SLICE_THREADS;
195 const bool delayOk = use == DecodeUseCase::Playback;
197 if(caps & AV_CODEC_CAP_OTHER_THREADS)
201 const int count = userThreads > 0 ? userThreads : 0;
204 count, FF_THREAD_FRAME | FF_THREAD_SLICE,
false,
"codec's own thread pool"};
205 if(ownPoolHasNoFrameDelay(codec))
206 return {count, FF_THREAD_SLICE,
false,
"codec's own thread pool, no frame delay"};
207 if(ownPoolHasDelayOption(codec))
209 count, FF_THREAD_SLICE,
true,
"codec's own thread pool, frame delay disabled"};
210 return {1, 0,
false,
"codec's own thread pool may delay frames"};
215 if(forcedType == FF_THREAD_FRAME && canFrame)
217 userThreads > 0 ? userThreads : frameThreads(par), FF_THREAD_FRAME,
218 false,
"forced by SCORE_VIDEO_THREADING"};
219 if(forcedType == FF_THREAD_SLICE && canSlice)
221 userThreads > 0 ? userThreads : sliceThreads(par), FF_THREAD_SLICE,
222 false,
"forced by SCORE_VIDEO_THREADING"};
224 return {1, 0,
false,
"forced by SCORE_VIDEO_THREADING"};
227 if(delayOk && canFrame && !(canSlice && sliceRich(codec)))
229 userThreads > 0 ? userThreads : frameThreads(par), FF_THREAD_FRAME,
false,
230 canSlice ?
"frame threads: the stream may hold a single slice"
231 :
"frame threads: the only model the codec has"};
235 userThreads > 0 ? userThreads : sliceThreads(par), FF_THREAD_SLICE,
false,
236 !delayOk ?
"slice threads: no frame delay allowed"
237 : canFrame ?
"slice threads: the format mandates many slices"
238 :
"slice threads: the only model the codec has"};
241 return {1, 0,
false,
"frame threading would delay output"};
242 return {1, 0,
false,
"codec has no threading"};
248 ctx.thread_count = t.count;
250 ctx.thread_type = t.type;
251 if(t.zeroDelay && ctx.priv_data)
252 av_opt_set_int(ctx.priv_data,
"max_frame_delay", 1, 0);
260 std::string s = ctx.codec && ctx.codec->name ? ctx.codec->name :
"?";
261 switch(ctx.active_thread_type)
263 case FF_THREAD_FRAME:
264 s +=
": frame threads x";
266 case FF_THREAD_SLICE:
267 s +=
": slice threads x";
270 if(ctx.codec && (ctx.codec->capabilities & AV_CODEC_CAP_OTHER_THREADS))
271 s += ctx.thread_count > 0 ?
": own thread pool x" :
": own thread pool, auto";
273 s +=
": single thread";
276 if(ctx.active_thread_type
277 || (ctx.thread_count > 0 && ctx.codec
278 && (ctx.codec->capabilities & AV_CODEC_CAP_OTHER_THREADS)))
279 s += std::to_string(ctx.thread_count);
int envThreadType(bool &set) noexcept
Definition DecoderThreading.hpp:143
constexpr int max_frame_threads
Definition DecoderThreading.hpp:60
void applyDecoderThreading(AVCodecContext &ctx, const DecoderThreading &t) noexcept
Applies to a context allocated for its codec, before avcodec_open2.
Definition DecoderThreading.hpp:246
bool ownPoolHasNoFrameDelay(const AVCodec &codec) noexcept
Definition DecoderThreading.hpp:88
constexpr int max_slice_threads
Definition DecoderThreading.hpp:63
DecodeUseCase
What consumes the decoded frames, which sets how much delay is acceptable.
Definition DecoderThreading.hpp:36
@ FrameExact
A packet in must give its frame out (per-frame seeking, scrubbing).
@ Live
Cameras and network streams: any delay is visible latency.
@ Thumbnail
One frame is decoded, then the context is dropped.
DecoderThreading chooseDecoderThreading(const AVCodec &codec, const AVCodecParameters *par, DecodeUseCase use, int userThreads=0, bool hardware=false) noexcept
Definition DecoderThreading.hpp:174
std::string describeDecoderThreading(const AVCodecContext &ctx, const DecoderThreading &t)
Definition DecoderThreading.hpp:257
constexpr int64_t frame_memory_budget
Upper bound for the decoded frames frame threading keeps in flight.
Definition DecoderThreading.hpp:65
bool ownPoolHasDelayOption(const AVCodec &codec) noexcept
Wrappers whose pipelining can be switched off (libdav1d).
Definition DecoderThreading.hpp:97
bool sliceRich(const AVCodec &codec) noexcept
Definition DecoderThreading.hpp:71
Definition DecoderThreading.hpp:49
int type
FF_THREAD_FRAME / FF_THREAD_SLICE, 0 leaves the default.
Definition DecoderThreading.hpp:51
int count
AVCodecContext::thread_count; 0 lets the codec pick.
Definition DecoderThreading.hpp:50
bool zeroDelay
ask a codec with its own thread pool for no frame delay
Definition DecoderThreading.hpp:52