Merge branch 'master' into hashremoval
This commit is contained in:
@@ -335,7 +335,7 @@ void Decoder::UpdateLastAccessed()
|
||||
|
||||
uint qHash(Decoder::CodecStream stream, uint seed)
|
||||
{
|
||||
return qHash(stream.filename(), seed) ^ qHash(stream.stream(), seed);
|
||||
return qHash(stream.filename(), seed) ^ qHash(stream.stream(), seed) ^ qHash(stream.block(), seed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-3
@@ -34,6 +34,7 @@ extern "C" {
|
||||
#include "codec/frame.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "common/rational.h"
|
||||
#include "node/block/block.h"
|
||||
#include "node/project/footage/footage.h"
|
||||
#include "node/project/footage/footagedescription.h"
|
||||
#include "task/task.h"
|
||||
@@ -85,13 +86,15 @@ public:
|
||||
{
|
||||
public:
|
||||
CodecStream() :
|
||||
stream_(-1)
|
||||
stream_(-1),
|
||||
block_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
CodecStream(const QString& filename, int stream) :
|
||||
CodecStream(const QString& filename, int stream, Block *block) :
|
||||
filename_(filename),
|
||||
stream_(stream)
|
||||
stream_(stream),
|
||||
block_(block)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -125,11 +128,18 @@ public:
|
||||
return stream_;
|
||||
}
|
||||
|
||||
Block *block() const
|
||||
{
|
||||
return block_;
|
||||
}
|
||||
|
||||
private:
|
||||
QString filename_;
|
||||
|
||||
int stream_;
|
||||
|
||||
Block *block_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,7 +62,6 @@ FFmpegDecoder::FFmpegDecoder() :
|
||||
native_output_pix_fmt_(VideoParams::kFormatInvalid),
|
||||
working_frame_(nullptr),
|
||||
working_packet_(nullptr),
|
||||
is_working_(false),
|
||||
cache_at_zero_(false),
|
||||
cache_at_eof_(false)
|
||||
{
|
||||
@@ -749,7 +748,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
|
||||
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time);
|
||||
|
||||
const int64_t min_seek = -instance_.avstream()->start_time;
|
||||
int64_t seek_ts = target_ts;
|
||||
int64_t seek_ts = std::max(min_seek, target_ts - MaximumQueueSize());
|
||||
bool still_seeking = false;
|
||||
|
||||
if (time != kAnyTimecode) {
|
||||
@@ -838,7 +837,7 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *
|
||||
} else {
|
||||
|
||||
// Cut down to thread count - 1 before we acquire a new frame
|
||||
if (cached_frames_.size() == size_t(QThread::idealThreadCount())) {
|
||||
if (cached_frames_.size() == size_t(MaximumQueueSize())) {
|
||||
RemoveFirstFrame();
|
||||
}
|
||||
|
||||
@@ -1039,6 +1038,11 @@ void FFmpegDecoder::RemoveFirstFrame()
|
||||
cache_at_zero_ = false;
|
||||
}
|
||||
|
||||
int FFmpegDecoder::MaximumQueueSize()
|
||||
{
|
||||
return QThread::idealThreadCount();
|
||||
}
|
||||
|
||||
FFmpegDecoder::Instance::Instance() :
|
||||
fmt_ctx_(nullptr),
|
||||
codec_ctx_(nullptr),
|
||||
|
||||
@@ -155,6 +155,8 @@ private:
|
||||
|
||||
void RemoveFirstFrame();
|
||||
|
||||
static int MaximumQueueSize();
|
||||
|
||||
RetrieveVideoParams filter_params_;
|
||||
AVFilterGraph* filter_graph_;
|
||||
AVFilterContext* buffersrc_ctx_;
|
||||
@@ -171,9 +173,6 @@ private:
|
||||
|
||||
std::list<AVFramePtr> cached_frames_;
|
||||
|
||||
bool is_working_;
|
||||
QMutex is_working_mutex_;
|
||||
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
|
||||
@@ -350,7 +350,9 @@ NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeR
|
||||
NodeValueTable table;
|
||||
|
||||
if (active_block) {
|
||||
block_stack_.push_back(active_block);
|
||||
table = GenerateTable(active_block, Track::TransformRangeForBlock(active_block, range), track);
|
||||
block_stack_.pop_back();
|
||||
}
|
||||
|
||||
return table;
|
||||
|
||||
@@ -146,6 +146,11 @@ protected:
|
||||
|
||||
void ResolveJobs(NodeValue &value, const TimeRange &range);
|
||||
|
||||
Block *GetCurrentBlock() const
|
||||
{
|
||||
return block_stack_.empty() ? nullptr : block_stack_.back();
|
||||
}
|
||||
|
||||
private:
|
||||
void PreProcessRow(const TimeRange &range, NodeValueRow &row);
|
||||
|
||||
@@ -162,6 +167,8 @@ private:
|
||||
const Node *transform_now_;
|
||||
QTransform *transform_;
|
||||
|
||||
std::list<Block*> block_stack_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -39,15 +39,10 @@ private:
|
||||
|
||||
};
|
||||
|
||||
struct DecoderPair {
|
||||
DecoderPair()
|
||||
{
|
||||
decoder = nullptr;
|
||||
last_modified = 0;
|
||||
}
|
||||
|
||||
DecoderPtr decoder;
|
||||
qint64 last_modified;
|
||||
struct DecoderPair
|
||||
{
|
||||
DecoderPtr decoder = nullptr;
|
||||
qint64 last_modified = 0;
|
||||
};
|
||||
|
||||
using DecoderCache = RenderCache<Decoder::CodecStream, DecoderPair>;
|
||||
|
||||
@@ -72,8 +72,21 @@ TexturePtr Renderer::InterlaceTexture(TexturePtr top, TexturePtr bottom, const V
|
||||
return output;
|
||||
}
|
||||
|
||||
QVariant Renderer::GetDefaultShader()
|
||||
{
|
||||
if (default_shader_.isNull()) {
|
||||
default_shader_ = CreateNativeShader(ShaderCode(QString(), QString()));
|
||||
}
|
||||
|
||||
return default_shader_;
|
||||
}
|
||||
|
||||
void Renderer::Destroy()
|
||||
{
|
||||
if (!default_shader_.isNull()) {
|
||||
DestroyNativeShader(default_shader_);
|
||||
}
|
||||
|
||||
color_cache_.clear();
|
||||
|
||||
if (!interlace_texture_.isNull()) {
|
||||
|
||||
@@ -76,6 +76,8 @@ public:
|
||||
|
||||
TexturePtr InterlaceTexture(TexturePtr top, TexturePtr bottom, const VideoParams ¶ms);
|
||||
|
||||
QVariant GetDefaultShader();
|
||||
|
||||
void Destroy();
|
||||
|
||||
virtual void PostDestroy() = 0;
|
||||
@@ -134,6 +136,8 @@ private:
|
||||
|
||||
QMutex color_cache_mutex_;
|
||||
|
||||
QVariant default_shader_;
|
||||
|
||||
QVariant interlace_texture_;
|
||||
|
||||
};
|
||||
|
||||
@@ -54,7 +54,6 @@ RenderManager::RenderManager(QObject *parent) :
|
||||
|
||||
decoder_cache_ = new DecoderCache();
|
||||
shader_cache_ = new ShaderCache();
|
||||
default_shader_ = context_->CreateNativeShader(ShaderCode(QString(), QString()));
|
||||
} else {
|
||||
qCritical() << "Tried to initialize unknown graphics backend";
|
||||
context_ = nullptr;
|
||||
@@ -65,8 +64,6 @@ RenderManager::RenderManager(QObject *parent) :
|
||||
RenderManager::~RenderManager()
|
||||
{
|
||||
if (context_) {
|
||||
context_->DestroyNativeShader(default_shader_);
|
||||
|
||||
delete shader_cache_;
|
||||
delete decoder_cache_;
|
||||
|
||||
@@ -130,7 +127,7 @@ void RenderManager::RunTicket(RenderTicketPtr ticket) const
|
||||
return;
|
||||
}
|
||||
|
||||
RenderProcessor::Process(ticket, context_, decoder_cache_, shader_cache_, default_shader_);
|
||||
RenderProcessor::Process(ticket, context_, decoder_cache_, shader_cache_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -183,8 +183,6 @@ private:
|
||||
|
||||
ShaderCache* shader_cache_;
|
||||
|
||||
QVariant default_shader_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -35,12 +35,11 @@ namespace olive {
|
||||
|
||||
#define super NodeTraverser
|
||||
|
||||
RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache* decoder_cache, ShaderCache *shader_cache, QVariant default_shader) :
|
||||
RenderProcessor::RenderProcessor(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache* decoder_cache, ShaderCache *shader_cache) :
|
||||
ticket_(ticket),
|
||||
render_ctx_(render_ctx),
|
||||
decoder_cache_(decoder_cache),
|
||||
shader_cache_(shader_cache),
|
||||
default_shader_(default_shader)
|
||||
shader_cache_(shader_cache)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -116,7 +115,7 @@ FramePtr RenderProcessor::GenerateFrame(TexturePtr texture, const rational& time
|
||||
job.Insert(QStringLiteral("ove_maintex"), NodeValue(NodeValue::kTexture, QVariant::fromValue(texture)));
|
||||
job.Insert(QStringLiteral("ove_mvpmat"), NodeValue(NodeValue::kMatrix, matrix));
|
||||
|
||||
render_ctx_->BlitToTexture(default_shader_, job, blit_tex.get());
|
||||
render_ctx_->BlitToTexture(render_ctx_->GetDefaultShader(), job, blit_tex.get());
|
||||
|
||||
// Replace texture that we're going to download in the next step
|
||||
texture = blit_tex;
|
||||
@@ -258,10 +257,10 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c
|
||||
// No decoder
|
||||
decoder.decoder = Decoder::CreateFromID(decoder_id);
|
||||
decoder.last_modified = file_last_modified;
|
||||
decoder_cache_->insert(stream, decoder);
|
||||
locker.unlock();
|
||||
|
||||
if (decoder.decoder->Open(stream)) {
|
||||
decoder_cache_->insert(stream, decoder);
|
||||
} else {
|
||||
if (!decoder.decoder->Open(stream)) {
|
||||
qWarning() << "Failed to open decoder for" << stream.filename()
|
||||
<< "::" << stream.stream();
|
||||
return nullptr;
|
||||
@@ -271,9 +270,9 @@ DecoderPtr RenderProcessor::ResolveDecoderFromInput(const QString& decoder_id, c
|
||||
return decoder.decoder;
|
||||
}
|
||||
|
||||
void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache *decoder_cache, ShaderCache *shader_cache, QVariant default_shader)
|
||||
void RenderProcessor::Process(RenderTicketPtr ticket, Renderer *render_ctx, DecoderCache *decoder_cache, ShaderCache *shader_cache)
|
||||
{
|
||||
RenderProcessor p(ticket, render_ctx, decoder_cache, shader_cache, default_shader);
|
||||
RenderProcessor p(ticket, render_ctx, decoder_cache, shader_cache);
|
||||
p.Run();
|
||||
}
|
||||
|
||||
@@ -404,7 +403,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
qWarning() << "HAVEN'T GOTTEN DEFAULT INPUT COLORSPACE";
|
||||
}
|
||||
|
||||
Decoder::CodecStream default_codec_stream(stream.filename(), stream_data.stream_index());
|
||||
Decoder::CodecStream default_codec_stream(stream.filename(), stream_data.stream_index(), GetCurrentBlock());
|
||||
|
||||
QString decoder_id = stream.decoder();
|
||||
|
||||
@@ -426,7 +425,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
|
||||
|
||||
// Decoder will close automatically since it's a stream_ptr
|
||||
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index()));
|
||||
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index(), GetCurrentBlock()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -472,7 +471,7 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
|
||||
void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const FootageJob &stream, const TimeRange &input_time)
|
||||
{
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index()));
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index(), nullptr));
|
||||
|
||||
if (decoder) {
|
||||
const AudioParams& audio_params = GetCacheAudioParams();
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace olive {
|
||||
class RenderProcessor : public NodeTraverser
|
||||
{
|
||||
public:
|
||||
static void Process(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader);
|
||||
static void Process(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache);
|
||||
|
||||
struct RenderedWaveform {
|
||||
const ClipBlock* block;
|
||||
@@ -69,7 +69,7 @@ protected:
|
||||
virtual void ConvertToReferenceSpace(TexturePtr destination, TexturePtr source, const QString &input_cs) override;
|
||||
|
||||
private:
|
||||
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache, QVariant default_shader);
|
||||
RenderProcessor(RenderTicketPtr ticket, Renderer* render_ctx, DecoderCache* decoder_cache, ShaderCache* shader_cache);
|
||||
|
||||
TexturePtr GenerateTexture(const rational& time, const rational& frame_length);
|
||||
|
||||
@@ -87,8 +87,6 @@ private:
|
||||
|
||||
ShaderCache* shader_cache_;
|
||||
|
||||
QVariant default_shader_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -29,52 +29,67 @@ ThreadPool::ThreadPool(unsigned threads, QObject *parent) :
|
||||
threads = std::thread::hardware_concurrency();
|
||||
}
|
||||
|
||||
available_count_ = threads;
|
||||
for (unsigned i = 0; i < threads; i += 1) {
|
||||
worker_threads_.emplace_back(std::bind(&ThreadPool::thread_exec, this));
|
||||
worker_threads_.emplace_back(std::bind(&ThreadPool::thread_exec, this, &tasks_, &task_mutex_, &cond_));
|
||||
}
|
||||
|
||||
// Make single reserved thread for high priority tasks (usually audio) so they don't get stuck
|
||||
// behind a lot of slow tasks
|
||||
high_thread_ = std::thread(std::thread(std::bind(&ThreadPool::thread_exec, this, &high_tasks_, &high_mutex_, &high_cond_)));
|
||||
}
|
||||
|
||||
void ThreadPool::AddTicket(RenderTicketPtr ticket, RenderTicketPriority priority)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
if (priority == RenderTicketPriority::kHigh) {
|
||||
tasks_.emplace_front(std::move(ticket));
|
||||
std::lock_guard<std::mutex> lock(high_mutex_);
|
||||
high_tasks_.emplace_back(std::move(ticket));
|
||||
high_cond_.notify_one();
|
||||
} else {
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
tasks_.emplace_back(std::move(ticket));
|
||||
cond_.notify_one();
|
||||
}
|
||||
|
||||
cond_.notify_one();
|
||||
}
|
||||
|
||||
bool ThreadPool::RemoveTicket(RenderTicketPtr ticket)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
|
||||
const auto it = std::find(tasks_.begin(), tasks_.end(), ticket);
|
||||
if (it == tasks_.end()) {
|
||||
return false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(task_mutex_);
|
||||
const auto it = std::find(tasks_.begin(), tasks_.end(), ticket);
|
||||
if (it != tasks_.end()) {
|
||||
tasks_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
tasks_.erase(it);
|
||||
return true;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(high_mutex_);
|
||||
const auto it = std::find(high_tasks_.begin(), high_tasks_.end(), ticket);
|
||||
if (it != high_tasks_.end()) {
|
||||
high_tasks_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ThreadPool::thread_exec()
|
||||
void ThreadPool::thread_exec(std::deque<TaskType> *queue, std::mutex *mutex, std::condition_variable *cond)
|
||||
{
|
||||
while (true) {
|
||||
TaskType task;
|
||||
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(task_mutex_);
|
||||
cond_.wait(lock, [this]{ return this->end_threadp_ || !this->tasks_.empty(); });
|
||||
std::unique_lock<std::mutex> lock(*mutex);
|
||||
cond->wait(lock, [this, queue]{ return this->end_threadp_ || !queue->empty(); });
|
||||
|
||||
if (this->end_threadp_ && this->tasks_.empty()) {
|
||||
if (this->end_threadp_ && queue->empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
task = std::move(tasks_.front());
|
||||
tasks_.pop_front();
|
||||
task = std::move(queue->front());
|
||||
queue->pop_front();
|
||||
}
|
||||
|
||||
RunTicket(task);
|
||||
@@ -85,10 +100,12 @@ ThreadPool::~ThreadPool()
|
||||
{
|
||||
end_threadp_ = true;
|
||||
cond_.notify_all();
|
||||
high_cond_.notify_all();
|
||||
|
||||
for (auto &e : worker_threads_) {
|
||||
e.join();
|
||||
}
|
||||
high_thread_.join();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,13 +49,20 @@ public:
|
||||
virtual ~ThreadPool() override;
|
||||
|
||||
private:
|
||||
void thread_exec();
|
||||
void thread_exec(std::deque<TaskType> *queue, std::mutex *mutex, std::condition_variable *cond);
|
||||
|
||||
std::vector<std::thread> worker_threads_;
|
||||
std::deque<TaskType> tasks_;
|
||||
std::mutex task_mutex_;
|
||||
std::condition_variable cond_;
|
||||
|
||||
std::thread high_thread_;
|
||||
std::deque<TaskType> high_tasks_;
|
||||
std::mutex high_mutex_;
|
||||
std::condition_variable high_cond_;
|
||||
|
||||
std::atomic_bool end_threadp_{false};
|
||||
std::atomic_int available_count_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -702,7 +702,10 @@ void ViewerWidget::UpdateTextureFromNode()
|
||||
connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::RendererGeneratedFrame);
|
||||
nonqueue_watchers_.append(watcher);
|
||||
|
||||
watcher->SetTicket(GetFrame(time, RenderTicketPriority::kHigh));
|
||||
// Clear queue because we want this frame more than any others
|
||||
auto_cacher_->ClearSingleFrameRenders();
|
||||
|
||||
watcher->SetTicket(GetFrame(time, RenderTicketPriority::kNormal));
|
||||
} else {
|
||||
// There is definitely no frame here, we can immediately flip to showing nothing
|
||||
nonqueue_watchers_.clear();
|
||||
@@ -753,7 +756,7 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
playback_speed_ = speed;
|
||||
play_in_to_out_only_ = in_to_out_only;
|
||||
|
||||
playback_queue_next_frame_ = GetTimestamp();
|
||||
playback_queue_next_frame_ = GetTimestamp() + playback_speed_;
|
||||
|
||||
controls_->ShowPauseButton();
|
||||
|
||||
@@ -767,18 +770,10 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
prequeuing_video_ = true;
|
||||
prequeue_count_ = 0;
|
||||
|
||||
// We "prioritize" the frames, which means they're pushed to the top of the render queue,
|
||||
// we queue in reverse so that they're still queued in order
|
||||
|
||||
playback_queue_next_frame_ += playback_speed_ * prequeue_length_;
|
||||
int64_t temp = playback_queue_next_frame_;
|
||||
|
||||
for (int i=0; i<prequeue_length_; i++) {
|
||||
playback_queue_next_frame_ -= playback_speed_;
|
||||
RequestNextFrameForQueue(RenderTicketPriority::kHigh, false);
|
||||
RequestNextFrameForQueue(RenderTicketPriority::kNormal, false);
|
||||
playback_queue_next_frame_ += playback_speed_;
|
||||
}
|
||||
|
||||
playback_queue_next_frame_ = temp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1484,10 +1479,11 @@ void ViewerWidget::PlaybackTimerUpdate()
|
||||
}
|
||||
|
||||
if (IsPlaying()) {
|
||||
int count = 0;
|
||||
for (int i=display_widget_->queue()->size(); i<DeterminePlaybackQueueSize(); i++) {
|
||||
RequestNextFrameForQueue();
|
||||
count++;
|
||||
while (queue_watchers_.size() < DeterminePlaybackQueueSize()) {
|
||||
if (!RequestNextFrameForQueue()) {
|
||||
// Prevent infinite loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user