export: fixed audio exporting with new renderer system

This commit is contained in:
itsmattkc
2020-05-21 02:18:52 +10:00
parent a2d68f6d9f
commit d5766489e4
15 changed files with 246 additions and 142 deletions
+1 -1
View File
@@ -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;
+2 -14
View File
@@ -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;
}
}
+1 -1
View File
@@ -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
View File
@@ -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();
+37 -44
View File
@@ -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 &divider)
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 &params)
{
render_mode_ = mode;
video_params_ = params;
}
void RenderBackend::SetPixelFormat(const PixelFormat::Format &pix_fmt)
void RenderBackend::SetAudioParams(const AudioRenderingParams &params)
{
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_));
}
}
+12 -21
View File
@@ -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 {
+46 -4
View File
@@ -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());
}
}
}
+7
View File
@@ -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_;
};
+4 -5
View File
@@ -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;
}
+4 -3
View File
@@ -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
+25 -4
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
+20 -4
View File
@@ -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
+6 -6
View File
@@ -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)