export: fixed audio exporting with new renderer system
This commit is contained in:
@@ -34,11 +34,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
RenderBackend::RenderBackend(QObject *parent) :
|
||||
QObject(parent),
|
||||
viewer_node_(nullptr),
|
||||
audio_mode_(kAudioPreview),
|
||||
divider_(1),
|
||||
render_mode_(RenderMode::kOnline),
|
||||
pix_fmt_(PixelFormat::PIX_FMT_RGBA32F),
|
||||
sample_fmt_(SampleFormat::SAMPLE_FMT_FLT),
|
||||
auto_audio_(true),
|
||||
ic_from_conform_(false)
|
||||
{
|
||||
// FIXME: Don't create in CLI mode
|
||||
@@ -91,7 +87,7 @@ void RenderBackend::SetViewerNode(ViewerOutput *viewer_node)
|
||||
this,
|
||||
&RenderBackend::NodeGraphChanged);
|
||||
|
||||
if (audio_mode_ == kAudioPreview) {
|
||||
if (auto_audio_) {
|
||||
// Listen for audio invalidation signals
|
||||
connect(viewer_node_->audio_playback_cache(),
|
||||
&AudioPlaybackCache::Invalidated,
|
||||
@@ -136,24 +132,23 @@ QFuture<FramePtr> RenderBackend::RenderFrame(const rational &time, bool clear_qu
|
||||
block_for_update);
|
||||
}
|
||||
|
||||
void RenderBackend::SetDivider(const int ÷r)
|
||||
QFuture<SampleBufferPtr> RenderBackend::RenderAudio(const TimeRange &r, bool block_for_update)
|
||||
{
|
||||
divider_ = divider;
|
||||
return QtConcurrent::run(&audio_pool_.threads,
|
||||
GetInstanceFromPool(audio_pool_),
|
||||
&RenderWorker::RenderAudio,
|
||||
r,
|
||||
block_for_update);
|
||||
}
|
||||
|
||||
void RenderBackend::SetMode(const RenderMode::Mode &mode)
|
||||
void RenderBackend::SetVideoParams(const VideoRenderingParams ¶ms)
|
||||
{
|
||||
render_mode_ = mode;
|
||||
video_params_ = params;
|
||||
}
|
||||
|
||||
void RenderBackend::SetPixelFormat(const PixelFormat::Format &pix_fmt)
|
||||
void RenderBackend::SetAudioParams(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
pix_fmt_ = pix_fmt;
|
||||
}
|
||||
|
||||
void RenderBackend::SetSampleFormat(const SampleFormat::Format &sample_fmt)
|
||||
{
|
||||
sample_fmt_ = sample_fmt;
|
||||
audio_params_ = params;
|
||||
}
|
||||
|
||||
void RenderBackend::SetVideoDownloadMatrix(const QMatrix4x4 &mat)
|
||||
@@ -161,9 +156,9 @@ void RenderBackend::SetVideoDownloadMatrix(const QMatrix4x4 &mat)
|
||||
video_download_matrix_ = mat;
|
||||
}
|
||||
|
||||
void RenderBackend::SetAudioMode(AudioMode e)
|
||||
void RenderBackend::SetAutomaticAudio(bool e)
|
||||
{
|
||||
audio_mode_ = e;
|
||||
auto_audio_ = e;
|
||||
}
|
||||
|
||||
void RenderBackend::WorkerStartedRenderingAudio(const TimeRange &r)
|
||||
@@ -173,6 +168,23 @@ void RenderBackend::WorkerStartedRenderingAudio(const TimeRange &r)
|
||||
queued_audio_lock_.unlock();
|
||||
}
|
||||
|
||||
QList<TimeRange> RenderBackend::SplitRangeIntoChunks(const TimeRange &r)
|
||||
{
|
||||
const int chunk_size = 2;
|
||||
|
||||
QList<TimeRange> split_ranges;
|
||||
|
||||
int start_time = qFloor(r.in().toDouble() / static_cast<double>(chunk_size)) * chunk_size;
|
||||
int end_time = qCeil(r.out().toDouble() / static_cast<double>(chunk_size)) * chunk_size;
|
||||
|
||||
for (int i=start_time; i<end_time; i+=chunk_size) {
|
||||
split_ranges.append(TimeRange(qMax(r.in(), rational(i)),
|
||||
qMin(r.out(), rational(i + chunk_size))));
|
||||
}
|
||||
|
||||
return split_ranges;
|
||||
}
|
||||
|
||||
void RenderBackend::NodeGraphChanged(NodeInput *source)
|
||||
{
|
||||
video_pool_.Queue(source);
|
||||
@@ -184,9 +196,10 @@ void RenderBackend::UpdateInstance(RenderWorker *instance)
|
||||
{
|
||||
instance->SetAvailable(false);
|
||||
instance->ProcessQueue();
|
||||
instance->SetVideoParams(video_params());
|
||||
instance->SetAudioParams(audio_params());
|
||||
instance->SetVideoParams(video_params_);
|
||||
instance->SetAudioParams(audio_params_);
|
||||
instance->SetVideoDownloadMatrix(video_download_matrix_);
|
||||
instance->SetAudioModeIsPreview(auto_audio_);
|
||||
}
|
||||
|
||||
void RenderBackend::Close()
|
||||
@@ -198,16 +211,6 @@ void RenderBackend::Close()
|
||||
hash_pool_.Destroy();
|
||||
}
|
||||
|
||||
VideoRenderingParams RenderBackend::video_params() const
|
||||
{
|
||||
return VideoRenderingParams(viewer_node_->video_params(), pix_fmt_, render_mode_, divider_);
|
||||
}
|
||||
|
||||
AudioRenderingParams RenderBackend::audio_params() const
|
||||
{
|
||||
return AudioRenderingParams(viewer_node_->audio_params(), sample_fmt_);
|
||||
}
|
||||
|
||||
RenderWorker *RenderBackend::GetInstanceFromPool(RenderPool& pool)
|
||||
{
|
||||
RenderWorker* instance = nullptr;
|
||||
@@ -361,15 +364,9 @@ void RenderBackend::AudioInvalidated(const TimeRange& r)
|
||||
}
|
||||
|
||||
// Split into 2 second chunks, one for each thread
|
||||
const int chunk_size = 2;
|
||||
QList<TimeRange> split_ranges = SplitRangeIntoChunks(r);
|
||||
|
||||
QList<TimeRange> split_ranges;
|
||||
|
||||
int start_time = qFloor(r.in().toDouble() / static_cast<double>(chunk_size)) * chunk_size;
|
||||
int end_time = qCeil(r.out().toDouble() / static_cast<double>(chunk_size)) * chunk_size;
|
||||
for (int i=start_time; i<end_time; i+=chunk_size) {
|
||||
TimeRange this_range(qMax(r.in(), rational(i)),
|
||||
qMin(r.out(), rational(i + chunk_size)));
|
||||
foreach (const TimeRange& this_range, split_ranges) {
|
||||
|
||||
{
|
||||
// Check if this range is already in the queue but hasn't started yet, in which case it'll
|
||||
@@ -388,11 +385,7 @@ void RenderBackend::AudioInvalidated(const TimeRange& r)
|
||||
|
||||
audio_jobs_.insert(watcher, this_range);
|
||||
|
||||
watcher->setFuture(QtConcurrent::run(&audio_pool_.threads,
|
||||
GetInstanceFromPool(audio_pool_),
|
||||
&RenderWorker::RenderAudio,
|
||||
this_range,
|
||||
audio_mode_ == kAudioPreview));
|
||||
watcher->setFuture(RenderAudio(this_range, auto_audio_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,26 +54,23 @@ public:
|
||||
*/
|
||||
QFuture<FramePtr> RenderFrame(const rational& time, bool clear_queue, bool block_for_update);
|
||||
|
||||
void SetDivider(const int& divider);
|
||||
/**
|
||||
* @brief Asynchronously generate a chunk of audio
|
||||
*/
|
||||
QFuture<SampleBufferPtr> RenderAudio(const TimeRange& r, bool block_for_update);
|
||||
|
||||
void SetMode(const RenderMode::Mode& mode);
|
||||
void SetVideoParams(const VideoRenderingParams& params);
|
||||
|
||||
void SetPixelFormat(const PixelFormat::Format& pix_fmt);
|
||||
|
||||
void SetSampleFormat(const SampleFormat::Format& sample_fmt);
|
||||
void SetAudioParams(const AudioRenderingParams& params);
|
||||
|
||||
void SetVideoDownloadMatrix(const QMatrix4x4& mat);
|
||||
|
||||
enum AudioMode {
|
||||
kAudioDisabled,
|
||||
kAudioPreview,
|
||||
kAudioRender
|
||||
};
|
||||
|
||||
void SetAudioMode(AudioMode e);
|
||||
void SetAutomaticAudio(bool e);
|
||||
|
||||
void WorkerStartedRenderingAudio(const TimeRange& r);
|
||||
|
||||
static QList<TimeRange> SplitRangeIntoChunks(const TimeRange& r);
|
||||
|
||||
public slots:
|
||||
void NodeGraphChanged(NodeInput *source);
|
||||
|
||||
@@ -84,10 +81,6 @@ protected:
|
||||
|
||||
void Close();
|
||||
|
||||
VideoRenderingParams video_params() const;
|
||||
|
||||
AudioRenderingParams audio_params() const;
|
||||
|
||||
private:
|
||||
struct RenderPool {
|
||||
QVector<RenderWorker*> instances;
|
||||
@@ -113,16 +106,14 @@ private:
|
||||
QMutex queued_audio_lock_;
|
||||
TimeRangeList queued_audio_;
|
||||
|
||||
AudioMode audio_mode_;
|
||||
bool auto_audio_;
|
||||
|
||||
// VIDEO MEMBERS
|
||||
int divider_;
|
||||
RenderMode::Mode render_mode_;
|
||||
PixelFormat::Format pix_fmt_;
|
||||
VideoRenderingParams video_params_;
|
||||
QMatrix4x4 video_download_matrix_;
|
||||
|
||||
// AUDIO MEMBERS
|
||||
SampleFormat::Format sample_fmt_;
|
||||
AudioRenderingParams audio_params_;
|
||||
QHash< QFutureWatcher<SampleBufferPtr>*, TimeRange > audio_jobs_;
|
||||
|
||||
struct ConformWaitInfo {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "audio/sumsamples.h"
|
||||
#include "config/config.h"
|
||||
#include "node/block/clip/clip.h"
|
||||
#include "task/conform/conform.h"
|
||||
#include "renderbackend.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -32,7 +33,8 @@ OLIVE_NAMESPACE_ENTER
|
||||
RenderWorker::RenderWorker(RenderBackend* parent) :
|
||||
parent_(parent),
|
||||
viewer_(nullptr),
|
||||
available_(true)
|
||||
available_(true),
|
||||
audio_mode_is_preview_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -359,8 +361,51 @@ NodeValue RenderWorker::GetDataFromStream(StreamPtr stream, const TimeRange &inp
|
||||
|
||||
if (decoder) {
|
||||
if (stream->type() == Stream::kVideo || stream->type() == Stream::kImage) {
|
||||
|
||||
// Return a texture from the derived class
|
||||
return FrameToTexture(decoder, stream, input_time);
|
||||
|
||||
} else if (stream->type() == Stream::kAudio) {
|
||||
|
||||
// See if we have a conformed version of this audio
|
||||
if (!decoder->HasConformedVersion(audio_params())) {
|
||||
|
||||
// If not, check what audio mode we're in
|
||||
if (audio_mode_is_preview_) {
|
||||
|
||||
// For preview, we report the conform is missing and finish the render without it
|
||||
// temporarily. The backend that picks up this signal will recache this section once the
|
||||
// conform is available.
|
||||
emit AudioConformUnavailable(decoder->stream(),
|
||||
audio_render_time_,
|
||||
input_time.out(),
|
||||
audio_params());
|
||||
|
||||
} else {
|
||||
|
||||
// For online rendering/export, it's a waste of time to render the audio until we have
|
||||
// all we need, so we try to handle the conform ourselves
|
||||
AudioStreamPtr as = std::static_pointer_cast<AudioStream>(stream);
|
||||
|
||||
// Check if any other threads are conforming this audio
|
||||
if (as->try_start_conforming(audio_params())) {
|
||||
|
||||
// If not, conform it ourselves
|
||||
decoder->ConformAudio(&IsCancelled(), audio_params());
|
||||
|
||||
} else {
|
||||
|
||||
// If another thread is conforming already, hackily try to wait until it's done.
|
||||
do {
|
||||
QThread::msleep(1000);
|
||||
} while (!as->has_conformed_version(audio_params()) && !IsCancelled());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (decoder->HasConformedVersion(audio_params())) {
|
||||
SampleBufferPtr frame = decoder->RetrieveAudio(input_time.in(), input_time.length(),
|
||||
audio_params());
|
||||
@@ -368,9 +413,6 @@ NodeValue RenderWorker::GetDataFromStream(StreamPtr stream, const TimeRange &inp
|
||||
if (frame) {
|
||||
return NodeValue(NodeParam::kSamples, QVariant::fromValue(frame));
|
||||
}
|
||||
} else {
|
||||
emit AudioConformUnavailable(decoder->stream(), audio_render_time_,
|
||||
input_time.out(), audio_params());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@ public:
|
||||
video_download_matrix_ = mat;
|
||||
}
|
||||
|
||||
void SetAudioModeIsPreview(bool audio_mode_is_preview)
|
||||
{
|
||||
audio_mode_is_preview_ = audio_mode_is_preview;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Return a unique ID for the image generated at this time
|
||||
*
|
||||
@@ -166,6 +171,8 @@ private:
|
||||
QHash<Node*, Node*> copy_map_;
|
||||
bool available_;
|
||||
|
||||
bool audio_mode_is_preview_;
|
||||
|
||||
QMutex lock_;
|
||||
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user