decoder/renderer: no longer index automatically as part of the retrieve
functions Indexing is a lengthy process and had a high chance of getting RenderWorkers stuck doing it rather than being responsive to cache requests. This commit introduces a system where workers never index media, but instead signal that media is not ready to their RenderBackends which ensure that the media gets indexed and re-queues the affected frames when those indexes are ready.
This commit is contained in:
@@ -26,6 +26,8 @@ set(OLIVE_SOURCES
|
||||
render/colorprocessor.cpp
|
||||
render/diskmanager.h
|
||||
render/diskmanager.cpp
|
||||
render/indexmanager.h
|
||||
render/indexmanager.cpp
|
||||
render/pixelformat.h
|
||||
render/pixelformat.cpp
|
||||
render/pixelservice.h
|
||||
|
||||
@@ -83,6 +83,11 @@ int AudioRenderingParams::samples_to_bytes(const int &samples) const
|
||||
return samples * channel_count() * bytes_per_sample_per_channel();
|
||||
}
|
||||
|
||||
rational AudioRenderingParams::samples_to_time(const int &samples) const
|
||||
{
|
||||
return rational(samples, sample_rate());
|
||||
}
|
||||
|
||||
int AudioRenderingParams::bytes_to_samples(const int &bytes) const
|
||||
{
|
||||
Q_ASSERT(is_valid());
|
||||
@@ -90,6 +95,13 @@ int AudioRenderingParams::bytes_to_samples(const int &bytes) const
|
||||
return bytes / (channel_count() * bytes_per_sample_per_channel());
|
||||
}
|
||||
|
||||
rational AudioRenderingParams::bytes_to_time(const int &bytes) const
|
||||
{
|
||||
Q_ASSERT(is_valid());
|
||||
|
||||
return samples_to_time(bytes_to_samples(bytes));
|
||||
}
|
||||
|
||||
int AudioRenderingParams::channel_count() const
|
||||
{
|
||||
return av_get_channel_layout_nb_channels(channel_layout());
|
||||
|
||||
@@ -31,7 +31,9 @@ public:
|
||||
int time_to_bytes(const rational& time) const;
|
||||
int time_to_samples(const rational& time) const;
|
||||
int samples_to_bytes(const int& samples) const;
|
||||
rational samples_to_time(const int& samples) const;
|
||||
int bytes_to_samples(const int &bytes) const;
|
||||
rational bytes_to_time(const int &bytes) const;
|
||||
int channel_count() const;
|
||||
int bytes_per_sample_per_channel() const;
|
||||
int bits_per_sample() const;
|
||||
|
||||
@@ -51,8 +51,7 @@ void AudioWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, c
|
||||
|
||||
// If the input isn't keyframing, we don't need to update it unless it's connected, in which case it may change
|
||||
if (input->IsConnected() || input->is_keyframing()) {
|
||||
input_params.Insert(input, ProcessInput(input,
|
||||
TimeRange(this_sample_time, this_sample_time)));
|
||||
input_params.Insert(input, ProcessInput(input, TimeRange(this_sample_time, this_sample_time)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ void AudioRenderWorker::CloseInternal()
|
||||
// Nothing to init yet
|
||||
}
|
||||
|
||||
FramePtr AudioRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRange &range, const QAtomicInt *cancelled)
|
||||
FramePtr AudioRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRange &range)
|
||||
{
|
||||
return decoder->RetrieveAudio(range.in(), range.out() - range.in(), audio_params_, cancelled);
|
||||
return decoder->RetrieveAudio(range.in(), range.out() - range.in(), audio_params_);
|
||||
}
|
||||
|
||||
NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const TimeRange &range)
|
||||
@@ -42,12 +42,12 @@ NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const Ti
|
||||
TimeRange range_for_block(qMax(b->in(), range.in()),
|
||||
qMin(b->out(), range.out()));
|
||||
|
||||
NodeValueTable table = ProcessNode(NodeDependency(b,
|
||||
range_for_block));
|
||||
NodeValueTable table = ProcessNode(NodeDependency(b, range_for_block));
|
||||
|
||||
QByteArray samples_from_this_block = table.Take(NodeParam::kSamples).toByteArray();
|
||||
int destination_offset = audio_params_.time_to_bytes(range_for_block.in() - range.in());
|
||||
int maximum_copy_size = audio_params_.time_to_bytes(range_for_block.length());
|
||||
|
||||
int copied_size = 0;
|
||||
|
||||
if (!samples_from_this_block.isEmpty()) {
|
||||
@@ -81,13 +81,7 @@ NodeValueTable AudioRenderWorker::RenderBlock(const TrackOutput *track, const Ti
|
||||
|
||||
memcpy(block_range_buffer.data()+destination_offset,
|
||||
samples_from_this_block.data(),
|
||||
static_cast<size_t>(copied_size));
|
||||
}
|
||||
|
||||
if (copied_size < maximum_copy_size) {
|
||||
memset(block_range_buffer.data()+destination_offset+copied_size,
|
||||
0,
|
||||
static_cast<size_t>(maximum_copy_size - copied_size));
|
||||
qMax(maximum_copy_size, copied_size));
|
||||
}
|
||||
|
||||
NodeValueTable::Merge({merged_table, table});
|
||||
|
||||
@@ -16,7 +16,7 @@ protected:
|
||||
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range, const QAtomicInt* cancelled) override;
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) override;
|
||||
|
||||
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
|
||||
@@ -216,14 +216,14 @@ void Exporter::EncoderOpenedSuccessfully()
|
||||
video_backend_->SetOperatingMode(VideoRenderWorker::kHashOnly);
|
||||
connect(video_backend_, &VideoRenderBackend::QueueComplete, this, &Exporter::VideoHashesComplete);
|
||||
|
||||
video_backend_->InvalidateCache(0, viewer_node_->Length());
|
||||
video_backend_->InvalidateCache(TimeRange(0, viewer_node_->Length()));
|
||||
}
|
||||
|
||||
if (!audio_done_) {
|
||||
// We set the audio backend to render the full sequence to the disk
|
||||
connect(audio_backend_, &AudioRenderBackend::QueueComplete, this, &Exporter::AudioRendered);
|
||||
|
||||
audio_backend_->InvalidateCache(0, viewer_node_->Length());
|
||||
audio_backend_->InvalidateCache(TimeRange(0, viewer_node_->Length()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,6 +282,6 @@ void Exporter::VideoHashesComplete()
|
||||
//connect(video_backend_, &VideoRenderBackend::CachedFrameReady, this, &Exporter::FrameRendered);
|
||||
|
||||
foreach (const TimeRange& range, ranges) {
|
||||
video_backend_->InvalidateCache(range.in(), range.out());
|
||||
video_backend_->InvalidateCache(range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QThread>
|
||||
|
||||
#include "core.h"
|
||||
#include "render/indexmanager.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
RenderBackend::RenderBackend(QObject *parent) :
|
||||
@@ -17,6 +18,8 @@ RenderBackend::RenderBackend(QObject *parent) :
|
||||
{
|
||||
// FIXME: Don't create in CLI mode
|
||||
cancel_dialog_ = new RenderCancelDialog(Core::instance()->main_window());
|
||||
|
||||
connect(IndexManager::instance(), &IndexManager::StreamIndexUpdated, this, &RenderBackend::IndexUpdated);
|
||||
}
|
||||
|
||||
bool RenderBackend::Init()
|
||||
@@ -111,27 +114,6 @@ bool RenderBackend::IsInitiated()
|
||||
return started_;
|
||||
}
|
||||
|
||||
void RenderBackend::InvalidateCache(const rational &start_range, const rational &end_range)
|
||||
{
|
||||
if (!CanRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust range to min/max values
|
||||
rational start_range_adj = qMax(rational(0), start_range);
|
||||
rational end_range_adj = qMin(GetSequenceLength(), end_range);
|
||||
|
||||
qDebug() << "Cache invalidated between"
|
||||
<< start_range_adj.toDouble()
|
||||
<< "and"
|
||||
<< end_range_adj.toDouble();
|
||||
|
||||
// Queue value update
|
||||
QueueValueUpdate();
|
||||
|
||||
InvalidateCacheInternal(start_range_adj, end_range_adj);
|
||||
}
|
||||
|
||||
bool RenderBackend::Compile()
|
||||
{
|
||||
if (compiled_) {
|
||||
@@ -347,6 +329,27 @@ void RenderBackend::CancelQueue()
|
||||
cancel_dialog_->RunIfWorkersAreBusy();
|
||||
}
|
||||
|
||||
void RenderBackend::InvalidateCache(const TimeRange &range)
|
||||
{
|
||||
if (!CanRender()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Adjust range to min/max values
|
||||
rational start_range_adj = qMax(rational(0), range.in());
|
||||
rational end_range_adj = qMin(GetSequenceLength(), range.out());
|
||||
|
||||
qDebug() << "Cache invalidated between"
|
||||
<< start_range_adj.toDouble()
|
||||
<< "and"
|
||||
<< end_range_adj.toDouble();
|
||||
|
||||
// Queue value update
|
||||
QueueValueUpdate();
|
||||
|
||||
InvalidateCacheInternal(start_range_adj, end_range_adj);
|
||||
}
|
||||
|
||||
bool RenderBackend::ViewerIsConnected() const
|
||||
{
|
||||
return viewer_node_ != nullptr;
|
||||
@@ -412,6 +415,7 @@ void RenderBackend::InitWorkers()
|
||||
|
||||
// Connect cancel dialog to it
|
||||
connect(processor, &RenderWorker::CompletedCache, cancel_dialog_, &RenderCancelDialog::WorkerDone, Qt::QueuedConnection);
|
||||
connect(processor, &RenderWorker::FootageUnavailable, this, &RenderBackend::FootageUnavailable, Qt::QueuedConnection);
|
||||
|
||||
// Finally, we can move it to its own thread
|
||||
processor->moveToThread(thread);
|
||||
@@ -428,3 +432,84 @@ void RenderBackend::QueueRecompile()
|
||||
{
|
||||
recompile_queued_ = true;
|
||||
}
|
||||
|
||||
void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange &range, const rational &stream_time)
|
||||
{
|
||||
if (state == Decoder::kFailedToOpen){
|
||||
|
||||
qWarning() << "For range" << range.in() << "-" << range.out() << stream->footage()->filename() << "stream" << stream->index() << "failed to open";
|
||||
|
||||
} else if (state == Decoder::kIndexUnavailable) {
|
||||
|
||||
FootageWaitInfo info = {stream, range, stream_time};
|
||||
|
||||
foreach (const FootageWaitInfo& compare, footage_wait_info_) {
|
||||
if (info.stream == compare.stream
|
||||
&& info.stream_time == compare.stream_time
|
||||
&& info.affected_range == compare.affected_range) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "Waiting for" << stream.get() << "time" << stream_time.toDouble() << "for frame" << range.in();
|
||||
|
||||
if (IndexManager::instance()->IsIndexing(stream)) {
|
||||
|
||||
footage_wait_info_.append(info);
|
||||
|
||||
} else if ((stream->type() == Stream::kVideo && std::static_pointer_cast<VideoStream>(stream)->is_frame_index_ready())
|
||||
|| (stream->type() == Stream::kAudio && std::static_pointer_cast<AudioStream>(stream)->index_done())) {
|
||||
|
||||
// Index JUST finished, requeue this time
|
||||
InvalidateCache(range);
|
||||
|
||||
} else {
|
||||
|
||||
// Start indexing process
|
||||
footage_wait_info_.append(info);
|
||||
IndexManager::instance()->StartIndexingStream(stream);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void RenderBackend::IndexUpdated(Stream* stream)
|
||||
{
|
||||
for (int i=0;i<footage_wait_info_.size();i++) {
|
||||
const FootageWaitInfo& info = footage_wait_info_.at(i);
|
||||
|
||||
if (info.stream.get() == stream) {
|
||||
bool footage_ready = false;
|
||||
|
||||
// See if this stream now has an index for the requested time
|
||||
if (stream->type() == Stream::kVideo) {
|
||||
|
||||
VideoStream* video_stream = static_cast<VideoStream*>(stream);
|
||||
|
||||
if (video_stream->get_closest_timestamp_in_frame_index(info.stream_time) >= 0) {
|
||||
// This index now has this frame, we can re-render it
|
||||
qDebug() << "Re-ICing video" << info.affected_range.in().toDouble() << "to" << info.affected_range.out().toDouble();
|
||||
footage_ready = true;
|
||||
}
|
||||
|
||||
} else if (stream->type() == Stream::kAudio) {
|
||||
|
||||
AudioStream* audio_stream = static_cast<AudioStream*>(stream);
|
||||
|
||||
if (audio_stream->index_length() >= info.stream_time) {
|
||||
// The index now has this audio, we can re-render it
|
||||
qDebug() << "Re-ICing audio" << info.affected_range.in().toDouble() << "to" << info.affected_range.out().toDouble();
|
||||
footage_ready = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (footage_ready) {
|
||||
InvalidateCache(info.affected_range);
|
||||
footage_wait_info_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
void CancelQueue();
|
||||
|
||||
public slots:
|
||||
void InvalidateCache(const rational &start_range, const rational &end_range);
|
||||
void InvalidateCache(const TimeRange &range);
|
||||
|
||||
bool Compile();
|
||||
|
||||
@@ -145,6 +145,19 @@ private:
|
||||
|
||||
RenderCancelDialog* cancel_dialog_;
|
||||
|
||||
struct FootageWaitInfo {
|
||||
StreamPtr stream;
|
||||
TimeRange affected_range;
|
||||
rational stream_time;
|
||||
};
|
||||
|
||||
QList<FootageWaitInfo> footage_wait_info_;
|
||||
|
||||
private slots:
|
||||
void FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange& path, const rational& stream_time);
|
||||
|
||||
void IndexUpdated(Stream *stream);
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERBACKEND_H
|
||||
|
||||
@@ -34,6 +34,8 @@ void RenderWorker::Close()
|
||||
|
||||
void RenderWorker::Render(NodeDependency path, qint64 job_time)
|
||||
{
|
||||
path_ = path;
|
||||
|
||||
emit CompletedCache(path, RenderInternal(path, job_time), job_time);
|
||||
}
|
||||
|
||||
@@ -104,8 +106,7 @@ NodeValueTable RenderWorker::ProcessInput(const NodeInput *input, const TimeRang
|
||||
{
|
||||
if (input->IsConnected()) {
|
||||
// Value will equal something from the connected node, follow it
|
||||
return ProcessNode(NodeDependency(input->get_connected_node(),
|
||||
range));
|
||||
return ProcessNode(NodeDependency(input->get_connected_node(), range));
|
||||
} else {
|
||||
// Push onto the table the value at this time from the input
|
||||
QVariant input_value = input->get_value_at_time(range.in());
|
||||
@@ -116,6 +117,16 @@ NodeValueTable RenderWorker::ProcessInput(const NodeInput *input, const TimeRang
|
||||
}
|
||||
}
|
||||
|
||||
void RenderWorker::ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time)
|
||||
{
|
||||
emit FootageUnavailable(stream, state, path_.range(), stream_time);
|
||||
}
|
||||
|
||||
const NodeDependency &RenderWorker::CurrentPath() const
|
||||
{
|
||||
return path_;
|
||||
}
|
||||
|
||||
NodeValueDatabase RenderWorker::GenerateDatabase(const Node* node, const TimeRange &range)
|
||||
{
|
||||
NodeValueDatabase database;
|
||||
@@ -140,10 +151,17 @@ NodeValueDatabase RenderWorker::GenerateDatabase(const Node* node, const TimeRan
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
|
||||
if (decoder) {
|
||||
FramePtr frame = RetrieveFromDecoder(decoder, input_time, &IsCancelled());
|
||||
|
||||
if (frame) {
|
||||
FrameToValue(stream, frame, &table);
|
||||
Decoder::RetrieveState state = decoder->GetRetrieveState(input_time.out());
|
||||
|
||||
if (state == Decoder::kReady) {
|
||||
FramePtr frame = RetrieveFromDecoder(decoder, input_time);
|
||||
|
||||
if (frame) {
|
||||
FrameToValue(stream, frame, &table);
|
||||
}
|
||||
} else {
|
||||
ReportUnavailableFootage(stream, state, input_time.out());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,24 +24,26 @@ public:
|
||||
public slots:
|
||||
void Close();
|
||||
|
||||
void Render(NodeDependency path, qint64 job_time);
|
||||
void Render(NodeDependency CurrentPath, qint64 job_time);
|
||||
|
||||
signals:
|
||||
void CompletedCache(NodeDependency dep, NodeValueTable data, qint64 job_time);
|
||||
|
||||
void FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange& range, const rational& stream_time);
|
||||
|
||||
protected:
|
||||
virtual bool InitInternal() = 0;
|
||||
|
||||
virtual void CloseInternal() = 0;
|
||||
|
||||
virtual NodeValueTable RenderInternal(const NodeDependency& path, const qint64& job_time);
|
||||
virtual NodeValueTable RenderInternal(const NodeDependency& CurrentPath, const qint64& job_time);
|
||||
|
||||
virtual void RunNodeAccelerated(const Node *node, const TimeRange& range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
|
||||
|
||||
StreamPtr ResolveStreamFromInput(NodeInput* input);
|
||||
DecoderPtr ResolveDecoderFromInput(StreamPtr stream);
|
||||
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range, const QAtomicInt* cancelled) = 0;
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) = 0;
|
||||
|
||||
virtual void FrameToValue(StreamPtr stream, FramePtr frame, NodeValueTable* table) = 0;
|
||||
|
||||
@@ -51,6 +53,10 @@ protected:
|
||||
|
||||
NodeValueTable ProcessInput(const NodeInput* input, const TimeRange &range);
|
||||
|
||||
virtual void ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time);
|
||||
|
||||
const NodeDependency& CurrentPath() const;
|
||||
|
||||
private:
|
||||
NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range);
|
||||
|
||||
@@ -58,6 +64,8 @@ private:
|
||||
|
||||
DecoderCache decoder_cache_;
|
||||
|
||||
NodeDependency path_;
|
||||
|
||||
};
|
||||
|
||||
#endif // RENDERWORKER_H
|
||||
|
||||
@@ -38,7 +38,7 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path, con
|
||||
hasher.addData(reinterpret_cast<const char*>(&vfmt), sizeof(PixelFormat::Format));
|
||||
hasher.addData(reinterpret_cast<const char*>(&vmode), sizeof(RenderMode::Mode));
|
||||
|
||||
HashNodeRecursively(&hasher, path.node(), path.in(), &IsCancelled());
|
||||
HashNodeRecursively(&hasher, path.node(), path.in());
|
||||
hash = hasher.result();
|
||||
}
|
||||
|
||||
@@ -87,12 +87,12 @@ NodeValueTable VideoRenderWorker::RenderInternal(const NodeDependency& path, con
|
||||
return value;
|
||||
}
|
||||
|
||||
FramePtr VideoRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRange &range, const QAtomicInt* cancelled)
|
||||
FramePtr VideoRenderWorker::RetrieveFromDecoder(DecoderPtr decoder, const TimeRange &range)
|
||||
{
|
||||
return decoder->RetrieveVideo(range.in(), cancelled);
|
||||
return decoder->RetrieveVideo(range.in());
|
||||
}
|
||||
|
||||
void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node* n, const rational& time, const QAtomicInt* cancelled)
|
||||
void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node* n, const rational& time)
|
||||
{
|
||||
// Resolve BlockList
|
||||
if (n->IsTrack()) {
|
||||
@@ -140,7 +140,7 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node
|
||||
|
||||
if (input->IsConnected()) {
|
||||
// Traverse down this edge
|
||||
HashNodeRecursively(hash, input->get_connected_node(), input_time, cancelled);
|
||||
HashNodeRecursively(hash, input->get_connected_node(), input_time);
|
||||
} else {
|
||||
// Grab the value at this time
|
||||
QVariant value = input->get_value_at_time(input_time);
|
||||
@@ -150,32 +150,48 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node
|
||||
// We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer
|
||||
if (input->data_type() == NodeParam::kFootage) {
|
||||
StreamPtr stream = ResolveStreamFromInput(input);
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
|
||||
if (decoder != nullptr) {
|
||||
// Add footage details to hash
|
||||
if (stream) {
|
||||
DecoderPtr decoder = ResolveDecoderFromInput(stream);
|
||||
|
||||
// Footage filename
|
||||
hash->addData(stream->footage()->filename().toUtf8());
|
||||
if (decoder) {
|
||||
|
||||
// Footage last modified date
|
||||
hash->addData(stream->footage()->timestamp().toString().toUtf8());
|
||||
// Add footage details to hash
|
||||
|
||||
// Footage stream
|
||||
hash->addData(QString::number(stream->index()).toUtf8());
|
||||
// Footage filename
|
||||
hash->addData(stream->footage()->filename().toUtf8());
|
||||
|
||||
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
|
||||
ImageStreamPtr video_stream = std::static_pointer_cast<ImageStream>(stream);
|
||||
// Footage last modified date
|
||||
hash->addData(stream->footage()->timestamp().toString().toUtf8());
|
||||
|
||||
// Footage stream
|
||||
hash->addData(QString::number(stream->index()).toUtf8());
|
||||
|
||||
if (stream->type() == Stream::kImage || stream->type() == Stream::kVideo) {
|
||||
ImageStreamPtr image_stream = std::static_pointer_cast<ImageStream>(stream);
|
||||
|
||||
// Current color config and space
|
||||
hash->addData(image_stream->footage()->project()->ocio_config().toUtf8());
|
||||
hash->addData(image_stream->colorspace().toUtf8());
|
||||
|
||||
// Alpha associated setting
|
||||
hash->addData(QString::number(image_stream->premultiplied_alpha()).toUtf8());
|
||||
}
|
||||
|
||||
// Footage timestamp
|
||||
hash->addData(QString::number(decoder->GetTimestampFromTime(input_time, cancelled)).toUtf8());
|
||||
if (stream->type() == Stream::kVideo) {
|
||||
Decoder::RetrieveState state = decoder->GetRetrieveState(input_time);
|
||||
|
||||
// Current color config and space
|
||||
hash->addData(video_stream->footage()->project()->ocio_config().toUtf8());
|
||||
hash->addData(video_stream->colorspace().toUtf8());
|
||||
if (state == Decoder::kReady) {
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream);
|
||||
|
||||
// Alpha associated setting
|
||||
hash->addData(QString::number(video_stream->premultiplied_alpha()).toUtf8());
|
||||
int64_t timestamp_here = video_stream->get_closest_timestamp_in_frame_index(input_time);
|
||||
|
||||
hash->addData(QString::number(timestamp_here).toUtf8());
|
||||
} else {
|
||||
ReportUnavailableFootage(stream, state, input_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -258,13 +274,20 @@ NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const Ti
|
||||
NodeValueTable table;
|
||||
|
||||
if (active_block) {
|
||||
table = ProcessNode(NodeDependency(active_block,
|
||||
range));
|
||||
table = ProcessNode(NodeDependency(active_block, range));
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void VideoRenderWorker::ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational &stream_time)
|
||||
{
|
||||
emit FootageUnavailable(stream,
|
||||
state,
|
||||
TimeRange(CurrentPath().in(), CurrentPath().in() + video_params().time_base()),
|
||||
stream_time);
|
||||
}
|
||||
|
||||
ColorProcessorCache *VideoRenderWorker::color_cache()
|
||||
{
|
||||
return &color_cache_;
|
||||
|
||||
@@ -48,13 +48,13 @@ public:
|
||||
void SetOperatingMode(const OperatingMode& mode);
|
||||
|
||||
signals:
|
||||
void CompletedFrame(NodeDependency path, qint64 job_time, QByteArray hash, QVariant value);
|
||||
void CompletedFrame(NodeDependency CurrentPath, qint64 job_time, QByteArray hash, QVariant value);
|
||||
|
||||
void CompletedDownload(NodeDependency path, qint64 job_time, QByteArray hash, bool texture_existed);
|
||||
void CompletedDownload(NodeDependency CurrentPath, qint64 job_time, QByteArray hash, bool texture_existed);
|
||||
|
||||
void HashAlreadyBeingCached(NodeDependency path, qint64 job_time, QByteArray hash);
|
||||
void HashAlreadyBeingCached(NodeDependency CurrentPath, qint64 job_time, QByteArray hash);
|
||||
|
||||
void HashAlreadyExists(NodeDependency path, qint64 job_time, QByteArray hash);
|
||||
void HashAlreadyExists(NodeDependency CurrentPath, qint64 job_time, QByteArray hash);
|
||||
|
||||
void Aborted();
|
||||
|
||||
@@ -69,16 +69,18 @@ protected:
|
||||
|
||||
virtual void TextureToBuffer(const QVariant& texture, QByteArray& buffer) = 0;
|
||||
|
||||
virtual NodeValueTable RenderInternal(const NodeDependency& path, const qint64& job_time) override;
|
||||
virtual NodeValueTable RenderInternal(const NodeDependency& CurrentPath, const qint64& job_time) override;
|
||||
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range, const QAtomicInt *cancelled) override;
|
||||
virtual FramePtr RetrieveFromDecoder(DecoderPtr decoder, const TimeRange& range) override;
|
||||
|
||||
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
virtual void ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time) override;
|
||||
|
||||
ColorProcessorCache* color_cache();
|
||||
|
||||
private:
|
||||
void HashNodeRecursively(QCryptographicHash* hash, const Node *n, const rational &time, const QAtomicInt *cancelled);
|
||||
void HashNodeRecursively(QCryptographicHash* hash, const Node *n, const rational &time);
|
||||
|
||||
void Download(QVariant texture, QString filename);
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include "indexmanager.h"
|
||||
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
IndexManager* IndexManager::instance_ = nullptr;
|
||||
|
||||
IndexManager::IndexManager()
|
||||
{
|
||||
}
|
||||
|
||||
void IndexManager::CreateInstance()
|
||||
{
|
||||
instance_ = new IndexManager();
|
||||
}
|
||||
|
||||
IndexManager *IndexManager::instance()
|
||||
{
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void IndexManager::DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
void IndexManager::StartIndexingStream(StreamPtr stream)
|
||||
{
|
||||
if (IsIndexing(stream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IndexTask* index_task = new IndexTask(stream);
|
||||
threads_.append({stream, index_task});
|
||||
|
||||
connect(stream.get(), &Stream::IndexChanged, this, &IndexManager::StreamIndexUpdatedEvent, Qt::QueuedConnection);
|
||||
connect(index_task, &IndexTask::Succeeded, this, &IndexManager::IndexTaskFinished, Qt::QueuedConnection);
|
||||
|
||||
TaskManager::instance()->AddTask(index_task);
|
||||
}
|
||||
|
||||
bool IndexManager::IsIndexing(StreamPtr stream)
|
||||
{
|
||||
foreach (const StreamThreadPair& stp, threads_) {
|
||||
if (stp.stream == stream) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IndexManager::IndexTaskFinished()
|
||||
{
|
||||
for (int i=0;i<threads_.size();i++) {
|
||||
const StreamThreadPair& stp = threads_.at(i);
|
||||
|
||||
if (stp.task == sender()) {
|
||||
//emit StreamIndexUpdated(stp.stream.get());
|
||||
threads_.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IndexManager::StreamIndexUpdatedEvent()
|
||||
{
|
||||
emit StreamIndexUpdated(static_cast<Stream*>(sender()));
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef INDEXMANAGER_H
|
||||
#define INDEXMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "task/index/index.h"
|
||||
|
||||
class IndexManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IndexManager();
|
||||
|
||||
static void CreateInstance();
|
||||
static IndexManager* instance();
|
||||
static void DestroyInstance();
|
||||
|
||||
bool IsIndexing(StreamPtr stream);
|
||||
|
||||
public slots:
|
||||
void StartIndexingStream(StreamPtr stream);
|
||||
|
||||
signals:
|
||||
void StreamIndexUpdated(Stream* stream);
|
||||
|
||||
private:
|
||||
static IndexManager* instance_;
|
||||
|
||||
struct StreamThreadPair {
|
||||
StreamPtr stream;
|
||||
IndexTask* task;
|
||||
};
|
||||
|
||||
QList<StreamThreadPair> threads_;
|
||||
|
||||
private slots:
|
||||
void IndexTaskFinished();
|
||||
|
||||
void StreamIndexUpdatedEvent();
|
||||
|
||||
};
|
||||
|
||||
#endif // INDEXMANAGER_H
|
||||
Reference in New Issue
Block a user