renderer/decoder: simplified audio PCM transcode

Turned the two-step PCM transcode into one step and simplified/removed much of
the unnecessary infrastructure that supported it. This makes the code cleaner
and generally improves the code paths.
This commit is contained in:
itsmattkc
2020-05-03 16:07:10 +10:00
parent a925476c3f
commit e4c3b6bf7b
28 changed files with 275 additions and 763 deletions
-2
View File
@@ -34,8 +34,6 @@ set(OLIVE_SOURCES
render/backend/audiorenderbackend.cpp
render/backend/audiorenderworker.h
render/backend/audiorenderworker.cpp
render/backend/indexmanager.h
render/backend/indexmanager.cpp
render/backend/videorenderbackend.h
render/backend/videorenderbackend.cpp
+45 -7
View File
@@ -25,7 +25,8 @@
#include "audiorenderworker.h"
#include "common/filefunctions.h"
#include "render/backend/indexmanager.h"
#include "task/conform/conform.h"
#include "task/taskmanager.h"
OLIVE_NAMESPACE_ENTER
@@ -33,7 +34,6 @@ AudioRenderBackend::AudioRenderBackend(QObject *parent) :
RenderBackend(parent),
ic_from_conform_(false)
{
connect(IndexManager::instance(), &IndexManager::StreamConformAppended, this, &AudioRenderBackend::ConformUpdated);
connect(this, &AudioRenderBackend::QueueComplete, this, &AudioRenderBackend::FilterQueueCompleteSignal);
}
@@ -154,6 +154,30 @@ void AudioRenderBackend::InvalidateCacheInternal(const rational &start_range, co
RenderBackend::InvalidateCacheInternal(start_range, end_range);
}
void AudioRenderBackend::ListenForConformSignal(AudioStreamPtr s)
{
foreach (const ConformWaitInfo& info, conform_wait_info_) {
if (info.stream == s) {
// We've probably already connected to this one
return;
}
}
connect(s.get(), &AudioStream::ConformAppended, this, &AudioRenderBackend::ConformUpdated);
}
void AudioRenderBackend::StopListeningForConformSignal(AudioStream* s)
{
foreach (const ConformWaitInfo& info, conform_wait_info_) {
if (info.stream.get() == s) {
// There are still conforms we're waiting for, don't disconnect
return;
}
}
disconnect(s, &AudioStream::ConformAppended, this, &AudioRenderBackend::ConformUpdated);
}
void AudioRenderBackend::ConformUnavailable(StreamPtr stream, TimeRange range, rational stream_time, AudioRenderingParams params)
{
ConformWaitInfo info = {stream, params, range, stream_time};
@@ -164,26 +188,38 @@ void AudioRenderBackend::ConformUnavailable(StreamPtr stream, TimeRange range, r
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(stream);
if (IndexManager::instance()->IsConforming(audio_stream, params)) {
if (audio_stream->try_start_conforming(params)) {
// Start indexing process
ListenForConformSignal(audio_stream);
conform_wait_info_.append(info);
ConformTask* conform_task = new ConformTask(audio_stream, params);
TaskManager::instance()->AddTask(conform_task);
} else if (audio_stream->has_conformed_version(params)) {
// Index JUST finished, requeue this time
// Conform JUST finished, requeue this time
ic_from_conform_ = true;
InvalidateCache(range, nullptr);
ic_from_conform_ = false;
} else {
// Start indexing process
// A conform task is already running, so we'll just wait for it
ListenForConformSignal(audio_stream);
conform_wait_info_.append(info);
IndexManager::instance()->StartConformingStream(audio_stream, params);
}
}
void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams params)
void AudioRenderBackend::ConformUpdated(AudioRenderingParams params)
{
AudioStream *stream = static_cast<AudioStream*>(sender());
for (int i=0;i<conform_wait_info_.size();i++) {
const ConformWaitInfo& info = conform_wait_info_.at(i);
@@ -204,6 +240,8 @@ void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams par
}
}
StopListeningForConformSignal(stream);
}
void AudioRenderBackend::TruncateCache(const rational &r)
+5 -1
View File
@@ -79,6 +79,10 @@ private:
bool operator==(const ConformWaitInfo& rhs) const;
};
void ListenForConformSignal(AudioStreamPtr s);
void StopListeningForConformSignal(AudioStream *s);
QList<ConformWaitInfo> conform_wait_info_;
AudioRenderingParams params_;
@@ -88,7 +92,7 @@ private:
private slots:
void ConformUnavailable(StreamPtr stream, TimeRange range, rational stream_time, AudioRenderingParams params);
void ConformUpdated(Stream *stream, AudioRenderingParams params);
void ConformUpdated(OLIVE_NAMESPACE::AudioRenderingParams params);
void TruncateCache(const rational& r);
-119
View File
@@ -1,119 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "indexmanager.h"
#include "task/taskmanager.h"
OLIVE_NAMESPACE_ENTER
IndexManager* IndexManager::instance_ = nullptr;
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);
indexing_.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);
}
void IndexManager::StartConformingStream(AudioStreamPtr stream, AudioRenderingParams params)
{
if (IsConforming(stream, params)) {
return;
}
ConformTask* conform_task = new ConformTask(stream, params);
conforming_.append({stream, params, conform_task});
connect(stream.get(), &AudioStream::ConformAppended, this, &IndexManager::StreamConformAppendedEvent, Qt::QueuedConnection);
connect(conform_task, &ConformTask::Succeeded, this, &IndexManager::IndexTaskFinished, Qt::QueuedConnection);
TaskManager::instance()->AddTask(conform_task);
}
bool IndexManager::IsIndexing(StreamPtr stream) const
{
foreach (const IndexPair& stp, indexing_) {
if (stp.stream == stream) {
return true;
}
}
return false;
}
bool IndexManager::IsConforming(AudioStreamPtr stream, const AudioRenderingParams &params) const
{
foreach (const ConformPair& cfp, conforming_) {
if (cfp.stream == stream && cfp.params == params) {
return true;
}
}
return false;
}
void IndexManager::IndexTaskFinished()
{
for (int i=0;i<indexing_.size();i++) {
const IndexPair& stp = indexing_.at(i);
if (stp.task == sender()) {
indexing_.removeAt(i);
return;
}
}
}
void IndexManager::StreamIndexUpdatedEvent()
{
emit StreamIndexUpdated(static_cast<Stream*>(sender()));
}
void IndexManager::StreamConformAppendedEvent(const AudioRenderingParams &params)
{
emit StreamConformAppended(static_cast<Stream*>(sender()), params);
}
OLIVE_NAMESPACE_EXIT
-82
View File
@@ -1,82 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 Olive Team
This program 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.
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef INDEXMANAGER_H
#define INDEXMANAGER_H
#include <QObject>
#include "project/item/footage/stream.h"
#include "task/conform/conform.h"
#include "task/index/index.h"
OLIVE_NAMESPACE_ENTER
class IndexManager : public QObject
{
Q_OBJECT
public:
IndexManager() = default;
static void CreateInstance();
static IndexManager* instance();
static void DestroyInstance();
bool IsIndexing(StreamPtr stream) const;
bool IsConforming(AudioStreamPtr stream, const AudioRenderingParams& params) const;
public slots:
void StartIndexingStream(OLIVE_NAMESPACE::StreamPtr stream);
void StartConformingStream(OLIVE_NAMESPACE::AudioStreamPtr stream, OLIVE_NAMESPACE::AudioRenderingParams params);
signals:
void StreamIndexUpdated(Stream* stream);
void StreamConformAppended(Stream* stream, OLIVE_NAMESPACE::AudioRenderingParams params);
private:
static IndexManager* instance_;
struct IndexPair {
StreamPtr stream;
IndexTask* task;
};
struct ConformPair {
StreamPtr stream;
AudioRenderingParams params;
ConformTask* task;
};
QList<IndexPair> indexing_;
QList<ConformPair> conforming_;
private slots:
void IndexTaskFinished();
void StreamIndexUpdatedEvent();
void StreamConformAppendedEvent(const AudioRenderingParams& params);
};
OLIVE_NAMESPACE_EXIT
#endif // INDEXMANAGER_H
-81
View File
@@ -24,7 +24,6 @@
#include <QThread>
#include "core.h"
#include "render/backend/indexmanager.h"
#include "window/mainwindow/mainwindow.h"
OLIVE_NAMESPACE_ENTER
@@ -37,8 +36,6 @@ 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()
@@ -473,7 +470,6 @@ 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);
@@ -486,83 +482,6 @@ void RenderBackend::InitWorkers()
processor_busy_state_.fill(false);
}
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};
if (footage_wait_info_.contains(info)) {
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, nullptr);
} 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, nullptr);
footage_wait_info_.removeAt(i);
i--;
}
}
}
}
bool RenderBackend::FootageWaitInfo::operator==(const RenderBackend::FootageWaitInfo &rhs) const
{
return rhs.stream == stream
-5
View File
@@ -167,11 +167,6 @@ private:
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);
};
OLIVE_NAMESPACE_EXIT
+1 -11
View File
@@ -108,11 +108,6 @@ bool RenderWorker::IsStarted()
return started_;
}
void RenderWorker::ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time)
{
emit FootageUnavailable(stream, state, path_.range(), stream_time);
}
void RenderWorker::InputProcessingEvent(NodeInput* input, const TimeRange& input_time, NodeValueTable *table)
{
// Exception for Footage types where we actually retrieve some Footage data from a decoder
@@ -124,13 +119,8 @@ void RenderWorker::InputProcessingEvent(NodeInput* input, const TimeRange& input
if (decoder) {
Decoder::RetrieveState state = decoder->GetRetrieveState(input_time.out());
FrameToValue(decoder, stream, input_time, table);
if (state == Decoder::kReady) {
FrameToValue(decoder, stream, input_time, table);
} else {
ReportUnavailableFootage(stream, state, input_time.out());
}
}
}
}
-4
View File
@@ -48,8 +48,6 @@ public slots:
signals:
void CompletedCache(OLIVE_NAMESPACE::NodeDependency dep, OLIVE_NAMESPACE::NodeValueTable data, qint64 job_time);
void FootageUnavailable(OLIVE_NAMESPACE::StreamPtr stream, OLIVE_NAMESPACE::Decoder::RetrieveState state, const OLIVE_NAMESPACE::TimeRange& range, const OLIVE_NAMESPACE::rational& stream_time);
protected:
virtual bool InitInternal() = 0;
@@ -61,8 +59,6 @@ protected:
virtual void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table) = 0;
virtual void ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time);
virtual void InputProcessingEvent(NodeInput *input, const TimeRange &input_time, NodeValueTable* table) override;
virtual void ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params, NodeValueTable &output_params) override;
-8
View File
@@ -295,14 +295,6 @@ NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const Ti
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_;
-2
View File
@@ -100,8 +100,6 @@ protected:
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: