export: fixed audio exporting with new renderer system
This commit is contained in:
+1
-1
@@ -108,7 +108,7 @@ public:
|
||||
|
||||
virtual bool WriteFrame(OLIVE_NAMESPACE::FramePtr frame, OLIVE_NAMESPACE::rational time) = 0;
|
||||
virtual void WriteAudio(OLIVE_NAMESPACE::AudioRenderingParams pcm_info,
|
||||
const QString& pcm_filename, OLIVE_NAMESPACE::TimeRange range) = 0;
|
||||
const QString& pcm_filename) = 0;
|
||||
|
||||
virtual void Close() = 0;
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ fail:
|
||||
return success;
|
||||
}
|
||||
|
||||
void FFmpegEncoder::WriteAudio(AudioRenderingParams pcm_info, const QString &pcm_filename, TimeRange range)
|
||||
void FFmpegEncoder::WriteAudio(AudioRenderingParams pcm_info, const QString &pcm_filename)
|
||||
{
|
||||
QFile pcm(pcm_filename);
|
||||
if (pcm.open(QFile::ReadOnly)) {
|
||||
@@ -213,13 +213,6 @@ void FFmpegEncoder::WriteAudio(AudioRenderingParams pcm_info, const QString &pcm
|
||||
// Keep track of sample count to use as each frame's timebase
|
||||
int sample_counter = 0;
|
||||
|
||||
// Go to start range if one was set
|
||||
if (range.in() > 0) {
|
||||
pcm.seek(pcm_info.time_to_bytes(range.in()));
|
||||
}
|
||||
|
||||
int end_in_bytes = pcm_info.time_to_bytes(range.out());
|
||||
|
||||
while (true) {
|
||||
// Calculate how many samples should input this frame
|
||||
int64_t samples_needed = av_rescale_rnd(maximum_frame_samples + swr_get_delay(swr_ctx, pcm_info.sample_rate()),
|
||||
@@ -230,11 +223,6 @@ void FFmpegEncoder::WriteAudio(AudioRenderingParams pcm_info, const QString &pcm
|
||||
// Calculate how many bytes this is
|
||||
int max_read = pcm_info.samples_to_bytes(samples_needed);
|
||||
|
||||
// Limit read to end time if necessary
|
||||
if (pcm.pos() + max_read > end_in_bytes) {
|
||||
max_read = end_in_bytes - pcm.pos();
|
||||
}
|
||||
|
||||
// Read bytes from PCM
|
||||
QByteArray input_data = pcm.read(max_read);
|
||||
|
||||
@@ -270,7 +258,7 @@ void FFmpegEncoder::WriteAudio(AudioRenderingParams pcm_info, const QString &pcm
|
||||
}
|
||||
|
||||
// Break if we've reached the end point
|
||||
if (pcm.atEnd() || pcm.pos() == end_in_bytes) {
|
||||
if (pcm.atEnd()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
virtual bool WriteFrame(OLIVE_NAMESPACE::FramePtr frame, OLIVE_NAMESPACE::rational time) override;
|
||||
|
||||
virtual void WriteAudio(OLIVE_NAMESPACE::AudioRenderingParams pcm_info,
|
||||
const QString& pcm_filename, OLIVE_NAMESPACE::TimeRange range) override;
|
||||
const QString& pcm_filename) override;
|
||||
|
||||
virtual void Close() override;
|
||||
|
||||
|
||||
+12
-1
@@ -1121,7 +1121,18 @@ void Core::CacheActiveSequence(bool in_out_only)
|
||||
if (p && p->GetConnectedViewer()) {
|
||||
// FIXME: Hardcoded divider...
|
||||
// FIXME: Consider preventing caching the footage viewer
|
||||
CacheTask* task = new CacheTask(p->GetConnectedViewer(), 2, in_out_only);
|
||||
VideoRenderingParams vrp(p->GetConnectedViewer()->video_params(),
|
||||
PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline),
|
||||
RenderMode::kOffline,
|
||||
2);
|
||||
|
||||
AudioRenderingParams arp(p->GetConnectedViewer()->audio_params(),
|
||||
SampleFormat::kInternalFormat);
|
||||
|
||||
CacheTask* task = new CacheTask(p->GetConnectedViewer(),
|
||||
vrp,
|
||||
arp,
|
||||
in_out_only);
|
||||
|
||||
TaskDialog* dialog = new TaskDialog(task, tr("Caching Sequence"), main_window_);
|
||||
dialog->open();
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
Vendored
+4
-5
@@ -27,10 +27,9 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
CacheTask::CacheTask(ViewerOutput* viewer, int divider, bool in_out_only) :
|
||||
RenderTask(viewer),
|
||||
in_out_only_(in_out_only),
|
||||
divider_(divider)
|
||||
CacheTask::CacheTask(ViewerOutput* viewer, const VideoRenderingParams& vparams, const AudioRenderingParams &aparams, bool in_out_only) :
|
||||
RenderTask(viewer, vparams, aparams),
|
||||
in_out_only_(in_out_only)
|
||||
{
|
||||
SetTitle(tr("Caching \"%1\"").arg(viewer->media_name()));
|
||||
}
|
||||
@@ -49,7 +48,7 @@ bool CacheTask::Run()
|
||||
}
|
||||
}
|
||||
|
||||
Render(range_to_cache, RenderMode::kOffline, QMatrix4x4(), false, divider_);
|
||||
Render(range_to_cache, QMatrix4x4(), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Vendored
+4
-3
@@ -31,7 +31,10 @@ class CacheTask : public RenderTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CacheTask(ViewerOutput* viewer, int divider, bool in_out_only);
|
||||
CacheTask(ViewerOutput* viewer,
|
||||
const VideoRenderingParams &vparams,
|
||||
const AudioRenderingParams &aparams,
|
||||
bool in_out_only);
|
||||
|
||||
public slots:
|
||||
virtual bool Run() override;
|
||||
@@ -44,8 +47,6 @@ protected:
|
||||
private:
|
||||
bool in_out_only_;
|
||||
|
||||
int divider_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
ExportTask::ExportTask(ViewerOutput* viewer_node,
|
||||
ColorManager* color_manager,
|
||||
const ExportParams& params) :
|
||||
RenderTask(viewer_node),
|
||||
RenderTask(viewer_node, params.video_params(), params.audio_params()),
|
||||
color_manager_(color_manager),
|
||||
params_(params)
|
||||
{
|
||||
@@ -80,9 +80,14 @@ bool ExportTask::Run()
|
||||
params_.color_transform());
|
||||
}
|
||||
|
||||
if (params_.audio_enabled()) {
|
||||
audio_data_.SetParameters(audio_params());
|
||||
}
|
||||
|
||||
// Start render process
|
||||
TimeRangeList ranges;
|
||||
ranges.InsertTimeRange(range);
|
||||
Render(ranges, RenderMode::kOnline, mat, true, 1);
|
||||
Render(ranges, mat, params_.audio_enabled());
|
||||
|
||||
bool success = true;
|
||||
|
||||
@@ -95,6 +100,11 @@ bool ExportTask::Run()
|
||||
}
|
||||
}
|
||||
|
||||
if (params_.audio_enabled()) {
|
||||
// Write audio data now
|
||||
encoder_->WriteAudio(audio_params(), audio_data_.GetCacheFilename());
|
||||
}
|
||||
|
||||
encoder_->Close();
|
||||
|
||||
encoder_->deleteLater();
|
||||
@@ -104,8 +114,6 @@ bool ExportTask::Run()
|
||||
|
||||
void FrameColorConvert(ColorProcessorPtr processor, FramePtr frame)
|
||||
{
|
||||
qDebug() << "Converting" << frame->timestamp() << "from" << frame->format();
|
||||
|
||||
// OCIO conversion requires a frame in 32F format
|
||||
if (frame->format() != PixelFormat::PIX_FMT_RGBA32F) {
|
||||
frame = PixelFormat::ConvertPixelFormat(frame, PixelFormat::PIX_FMT_RGBA32F);
|
||||
@@ -153,4 +161,17 @@ void ExportTask::FrameDownloaded(const QByteArray &hash, const QLinkedList<ratio
|
||||
}
|
||||
}
|
||||
|
||||
void ExportTask::AudioDownloaded(const TimeRange &range, SampleBufferPtr samples)
|
||||
{
|
||||
TimeRange adjusted_range = range;
|
||||
|
||||
if (params_.has_custom_range()) {
|
||||
adjusted_range -= params_.custom_range().in();
|
||||
}
|
||||
|
||||
qDebug() << "Downloaded audio" << adjusted_range;
|
||||
|
||||
audio_data_.WritePCM(adjusted_range, samples);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -43,6 +43,8 @@ protected:
|
||||
|
||||
virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList<rational>& times) override;
|
||||
|
||||
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples) override;
|
||||
|
||||
private:
|
||||
QHash<QByteArray, FramePtr> rendered_frame_;
|
||||
|
||||
@@ -60,6 +62,8 @@ private:
|
||||
|
||||
int64_t frame_time_;
|
||||
|
||||
AudioPlaybackCache audio_data_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
+65
-34
@@ -25,10 +25,11 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
RenderTask::RenderTask(ViewerOutput* viewer) :
|
||||
viewer_(viewer)
|
||||
RenderTask::RenderTask(ViewerOutput* viewer, const VideoRenderingParams &vparams, const AudioRenderingParams &aparams) :
|
||||
viewer_(viewer),
|
||||
video_params_(vparams),
|
||||
audio_params_(aparams)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
struct TimeHashFuturePair {
|
||||
@@ -46,50 +47,52 @@ struct HashFrameFuturePair {
|
||||
QFuture<FramePtr> frame_future;
|
||||
};
|
||||
|
||||
struct RangeSampleFuturePair {
|
||||
TimeRange range;
|
||||
QFuture<SampleBufferPtr> sample_future;
|
||||
};
|
||||
|
||||
struct HashDownloadFuturePair {
|
||||
QByteArray hash;
|
||||
QFuture<void> download_future;
|
||||
};
|
||||
|
||||
void RenderTask::Render(TimeRangeList range_to_cache,
|
||||
RenderMode::Mode mode,
|
||||
void RenderTask::Render(const TimeRangeList& range_to_cache,
|
||||
const QMatrix4x4& mat,
|
||||
bool audio_enabled,
|
||||
int divider)
|
||||
bool audio_enabled)
|
||||
{
|
||||
OpenGLBackend backend;
|
||||
|
||||
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
|
||||
|
||||
backend.SetAudioMode(audio_enabled ? RenderBackend::kAudioRender : RenderBackend::kAudioDisabled);
|
||||
backend.SetAutomaticAudio(false);
|
||||
backend.SetViewerNode(viewer_);
|
||||
backend.SetPixelFormat(format);
|
||||
backend.SetMode(mode);
|
||||
backend.SetDivider(divider);
|
||||
backend.SetSampleFormat(SampleFormat::kInternalFormat);
|
||||
backend.SetVideoParams(video_params_);
|
||||
backend.SetAudioParams(audio_params_);
|
||||
backend.SetVideoDownloadMatrix(mat);
|
||||
|
||||
// Get hashes for each frame
|
||||
QLinkedList<TimeHashFuturePair> hash_list;
|
||||
|
||||
while (!range_to_cache.isEmpty()) {
|
||||
const TimeRange& range = range_to_cache.first();
|
||||
{
|
||||
TimeRangeList range_list = range_to_cache;
|
||||
while (!range_list.isEmpty()) {
|
||||
const TimeRange& range = range_list.first();
|
||||
|
||||
const rational& timebase = viewer_->video_params().time_base();
|
||||
const rational& timebase = viewer_->video_params().time_base();
|
||||
|
||||
rational time = range.in();
|
||||
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
|
||||
rational next;
|
||||
rational time = range.in();
|
||||
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
|
||||
rational next;
|
||||
|
||||
if (snapped > time) {
|
||||
next = snapped;
|
||||
snapped -= timebase;
|
||||
} else {
|
||||
next = snapped + timebase;
|
||||
if (snapped > time) {
|
||||
next = snapped;
|
||||
snapped -= timebase;
|
||||
} else {
|
||||
next = snapped + timebase;
|
||||
}
|
||||
|
||||
hash_list.append({snapped, backend.Hash(snapped, false)});
|
||||
range_list.RemoveTimeRange(TimeRange(snapped, next));
|
||||
}
|
||||
|
||||
hash_list.append({snapped, backend.Hash(snapped, false)});
|
||||
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
|
||||
}
|
||||
|
||||
// Determine any duplicates
|
||||
@@ -130,12 +133,18 @@ void RenderTask::Render(TimeRangeList range_to_cache,
|
||||
}
|
||||
}
|
||||
|
||||
QLinkedList<RangeSampleFuturePair> audio_lookup_table;
|
||||
if (audio_enabled) {
|
||||
foreach (const TimeRange& r, range_to_cache) {
|
||||
QList<TimeRange> ranges = RenderBackend::SplitRangeIntoChunks(r);
|
||||
|
||||
foreach (const TimeRange& split, ranges) {
|
||||
audio_lookup_table.append({split, backend.RenderAudio(split, false)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start downloading frames that have finished
|
||||
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
|
||||
OIIO::ImageSpec output_spec(viewer()->video_params().width() / divider,
|
||||
viewer()->video_params().height() / divider,
|
||||
PixelFormat::ChannelCount(format),
|
||||
output_desc);
|
||||
|
||||
int counter = 0;
|
||||
int nb_frames = render_lookup_table.size();
|
||||
@@ -145,8 +154,12 @@ void RenderTask::Render(TimeRangeList range_to_cache,
|
||||
// Iterators
|
||||
QLinkedList<HashFrameFuturePair>::iterator i;
|
||||
QLinkedList<HashDownloadFuturePair>::iterator j;
|
||||
QLinkedList<RangeSampleFuturePair>::iterator k;
|
||||
|
||||
while (!render_lookup_table.isEmpty()
|
||||
|| !download_futures.isEmpty()
|
||||
|| !audio_lookup_table.isEmpty()) {
|
||||
|
||||
while (!render_lookup_table.isEmpty() || !download_futures.isEmpty()) {
|
||||
i = render_lookup_table.begin();
|
||||
|
||||
while (i != render_lookup_table.end()) {
|
||||
@@ -178,7 +191,25 @@ void RenderTask::Render(TimeRangeList range_to_cache,
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
if (audio_enabled) {
|
||||
k = audio_lookup_table.begin();
|
||||
|
||||
while (k != audio_lookup_table.end()) {
|
||||
if (k->sample_future.isFinished()) {
|
||||
AudioDownloaded(k->range, k->sample_future.result());
|
||||
|
||||
k = audio_lookup_table.erase(k);
|
||||
} else {
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTask::AudioDownloaded(const TimeRange &, SampleBufferPtr)
|
||||
{
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -31,23 +31,39 @@ OLIVE_NAMESPACE_ENTER
|
||||
class RenderTask : public Task
|
||||
{
|
||||
public:
|
||||
RenderTask(ViewerOutput* viewer);
|
||||
RenderTask(ViewerOutput* viewer, const VideoRenderingParams &vparams, const AudioRenderingParams &aparams);
|
||||
|
||||
protected:
|
||||
void Render(TimeRangeList range_to_cache, RenderMode::Mode mode, const QMatrix4x4 &mat, bool audio_enabled, int divider);
|
||||
void Render(const TimeRangeList &range_to_cache, const QMatrix4x4 &mat, bool audio_enabled);
|
||||
|
||||
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) = 0;
|
||||
|
||||
virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList<rational>& times) = 0;
|
||||
|
||||
virtual void AudioDownloaded(const TimeRange& range, SampleBufferPtr samples);
|
||||
|
||||
ViewerOutput* viewer() const
|
||||
{
|
||||
return viewer_;
|
||||
}
|
||||
|
||||
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) = 0;
|
||||
VideoRenderingParams video_params() const
|
||||
{
|
||||
return video_params_;
|
||||
}
|
||||
|
||||
virtual void FrameDownloaded(const QByteArray& hash, const QLinkedList<rational>& times) = 0;
|
||||
AudioRenderingParams audio_params() const
|
||||
{
|
||||
return audio_params_;
|
||||
}
|
||||
|
||||
private:
|
||||
ViewerOutput* viewer_;
|
||||
|
||||
VideoRenderingParams video_params_;
|
||||
|
||||
AudioRenderingParams audio_params_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -723,15 +723,15 @@ void ViewerWidget::RendererGeneratedFrameForQueue()
|
||||
|
||||
void ViewerWidget::UpdateRendererParameters()
|
||||
{
|
||||
RenderMode::Mode render_mode = RenderMode::kOffline;
|
||||
renderer_->SetVideoParams(VideoRenderingParams(GetConnectedNode()->video_params(),
|
||||
GetCurrentPixelFormat(),
|
||||
RenderMode::kOffline,
|
||||
divider_));
|
||||
|
||||
renderer_->SetDivider(divider_);
|
||||
renderer_->SetMode(render_mode);
|
||||
renderer_->SetPixelFormat(GetCurrentPixelFormat());
|
||||
renderer_->SetAudioParams(AudioRenderingParams(GetConnectedNode()->audio_params(),
|
||||
SampleFormat::kInternalFormat));
|
||||
|
||||
display_widget_->SetVideoParams(GetConnectedNode()->video_params());
|
||||
|
||||
renderer_->SetSampleFormat(SampleFormat::kInternalFormat);
|
||||
}
|
||||
|
||||
void ViewerWidget::ShowContextMenu(const QPoint &pos)
|
||||
|
||||
Reference in New Issue
Block a user