ffmpeg: store divided frames in memory cache

Since users will most likely be working with a divider most of the time,
there's no reason to waste memory space with undivided frames considering
the dividing process is quite fast.
This commit is contained in:
itsmattkc
2020-08-18 04:55:23 +10:00
parent d9851dc329
commit f3f81eb2be
7 changed files with 147 additions and 52 deletions
+94 -21
View File
@@ -106,10 +106,7 @@ bool FFmpegDecoder::Open()
if (!frame_pool) {
// FIXME: Hardcoded value. It seems to work fine, but is there a possibility we should make
// this a dynamic value somehow or a configurable value?
frame_pool = new FFmpegFramePool(64,
our_instance->stream()->codecpar->width,
our_instance->stream()->codecpar->height,
static_cast<AVPixelFormat>(our_instance->stream()->codecpar->format));
frame_pool = new FFmpegFramePool(64);
frame_pool_map_.insert(stream().get(), frame_pool);
}
@@ -194,13 +191,28 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid
FFmpegDecoderInstance* working_instance = nullptr;
int divided_width = VideoParams::GetScaledDimension(vs->width(), divider);
int divided_height = VideoParams::GetScaledDimension(vs->height(), divider);
// Find instance
do {
QMutexLocker list_locker(&instance_map_lock_);
QList<FFmpegDecoderInstance*> non_ideal_contenders;
const QList<FFmpegDecoderInstance*>& instances = instance_map_.value(stream().get());
QList<FFmpegDecoderInstance*> instances = instance_map_.value(stream().get());
FFmpegFramePool* pool = frame_pool_map_.value(stream().get());
if (pool->width() != divided_width || pool->height() != divided_height) {
// Clear all instance queues
foreach (FFmpegDecoderInstance* i, instances) {
i->ClearFrameCache();
}
// Set new frame pool parameters
pool->SetParameters(divided_width, divided_height, src_pix_fmt_);
}
QList<FFmpegDecoderInstance*> non_ideal_contenders;
foreach (FFmpegDecoderInstance* i, instances) {
@@ -291,7 +303,7 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid
working_instance->SetWorking(true);
// Retrieve frame
return_frame = working_instance->RetrieveFrame(target_ts, true);
return_frame = working_instance->RetrieveFrame(target_ts, divider, true);
// Set working to false and wake any threads waiting
working_instance->cache_lock()->lock();
@@ -310,8 +322,8 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid
input_linesize,
reinterpret_cast<const uint8_t*>(return_frame->data()),
src_pix_fmt_,
vs->width(),
vs->height(),
divided_width,
divided_height,
1);
return BuffersToNativeFrame(divider,
@@ -750,11 +762,6 @@ bool FFmpegDecoder::ConformAudio(const QAtomicInt *cancelled, const AudioParams
return success;
}
int FFmpegDecoder::GetScaledDimension(int dim, int divider)
{
return dim / divider;
}
PixelFormat::Format FFmpegDecoder::GetNativePixelFormat(AVPixelFormat pix_fmt)
{
switch (pix_fmt) {
@@ -812,7 +819,7 @@ FramePtr FFmpegDecoder::BuffersToNativeFrame(int divider, int width, int height,
input_data,
input_linesize,
0,
height,
VideoParams::GetScaledDimension(height, divider),
&output_data,
&output_linesize);
@@ -954,12 +961,17 @@ void FFmpegDecoderInstance::ClearFrameCache()
cache_at_zero_ = false;
}
FFmpegFramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, bool cache_is_locked)
FFmpegFramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t& target_ts, int divider, bool cache_is_locked)
{
if (!cache_is_locked) {
cache_lock_.lock();
}
if (scale_divider_ != divider) {
FreeScaler();
InitScaler(divider);
}
int64_t seek_ts = target_ts;
bool still_seeking = false;
@@ -1052,7 +1064,7 @@ FFmpegFramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t&
break;
}
FFmpegFramePool::ElementPtr cached = frame_pool_->Get(working_frame.frame());
FFmpegFramePool::ElementPtr cached = frame_pool_->Get();
if (!cached) {
qCritical() << "Frame pool failed to return a valid frame - out of memory?";
@@ -1060,6 +1072,27 @@ FFmpegFramePool::ElementPtr FFmpegDecoderInstance::RetrieveFrame(const int64_t&
break;
}
{
uint8_t* scale_data[4];
int scale_linesize[4];
av_image_fill_arrays(scale_data,
scale_linesize,
cached->data(),
static_cast<AVPixelFormat>(working_frame.frame()->format),
VideoParams::GetScaledDimension(working_frame.frame()->width, divider),
VideoParams::GetScaledDimension(working_frame.frame()->height, divider),
1);
sws_scale(scale_ctx_,
working_frame.frame()->data,
working_frame.frame()->linesize,
0,
working_frame.frame()->height,
scale_data,
scale_linesize);
}
// Set timestamp so this frame can be identified later
cached->set_timestamp(working_frame.frame()->pts);
@@ -1113,11 +1146,14 @@ void FFmpegDecoder::InitScaler(int divider)
{
VideoStream* vs = static_cast<VideoStream*>(stream().get());
scale_ctx_ = sws_getContext(vs->width(),
vs->height(),
int scaled_width = VideoParams::GetScaledDimension(vs->width(), divider);
int scaled_height = VideoParams::GetScaledDimension(vs->height(), divider);
scale_ctx_ = sws_getContext(scaled_width,
scaled_height,
src_pix_fmt_,
GetScaledDimension(vs->width(), divider),
GetScaledDimension(vs->height(), divider),
scaled_width,
scaled_height,
ideal_pix_fmt_,
SWS_FAST_BILINEAR,
nullptr,
@@ -1141,6 +1177,39 @@ void FFmpegDecoder::FreeScaler()
}
}
void FFmpegDecoderInstance::InitScaler(int divider)
{
int scaled_width = VideoParams::GetScaledDimension(avstream_->codecpar->width, divider);
int scaled_height = VideoParams::GetScaledDimension(avstream_->codecpar->height, divider);
scale_ctx_ = sws_getContext(avstream_->codecpar->width,
avstream_->codecpar->height,
static_cast<AVPixelFormat>(avstream_->codecpar->format),
scaled_width,
scaled_height,
static_cast<AVPixelFormat>(avstream_->codecpar->format),
SWS_FAST_BILINEAR,
nullptr,
nullptr,
nullptr);
if (scale_ctx_) {
scale_divider_ = divider;
} else {
scale_divider_ = 0;
}
}
void FFmpegDecoderInstance::FreeScaler()
{
if (scale_ctx_) {
sws_freeContext(scale_ctx_);
scale_ctx_ = nullptr;
scale_divider_ = 0;
}
}
int64_t FFmpegDecoderInstance::RangeStart() const
{
if (cached_frames_.isEmpty()) {
@@ -1236,6 +1305,8 @@ void FFmpegDecoderInstance::TruncateCacheRangeTo(const qint64 &t)
FFmpegDecoderInstance::FFmpegDecoderInstance(const char *filename, int stream_index) :
fmt_ctx_(nullptr),
opts_(nullptr),
scale_ctx_(nullptr),
scale_divider_(0),
frame_pool_(nullptr),
is_working_(false),
cache_at_zero_(false),
@@ -1365,6 +1436,8 @@ void FFmpegDecoderInstance::ClearResources()
avformat_close_input(&fmt_ctx_);
fmt_ctx_ = nullptr;
}
FreeScaler();
}
void FFmpegDecoderInstance::ClearTimerEvent()