Merge branch 'master' into hashremoval

This commit is contained in:
itsmattkc
2022-05-29 11:18:58 -07:00
30 changed files with 346 additions and 157 deletions
+2 -2
View File
@@ -199,14 +199,14 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
return nullptr;
}
int64_t Decoder::GetTimeInTimebaseUnits(const rational &time, const rational &timebase, int64_t start_time, VideoParams::Interlacing interlacing)
int64_t Decoder::GetTimeInTimebaseUnits(const rational &time, const rational &timebase, int64_t start_time)
{
int64_t t = Timecode::time_to_timestamp(time, timebase);
t += start_time;
return t;
}
rational Decoder::GetTimestampInTimeUnits(int64_t time, const rational &timebase, int64_t start_time, VideoParams::Interlacing interlacing)
rational Decoder::GetTimestampInTimeUnits(int64_t time, const rational &timebase, int64_t start_time)
{
time -= start_time;
return Timecode::timestamp_to_time(time, timebase);
+3 -7
View File
@@ -150,13 +150,9 @@ public:
RetrieveVideoParams()
{
divider = 1;
src_interlacing = VideoParams::kInterlaceNone;
dst_interlacing = VideoParams::kInterlaceNone;
}
int divider;
VideoParams::Interlacing src_interlacing;
VideoParams::Interlacing dst_interlacing;
void reset()
{
@@ -165,7 +161,7 @@ public:
bool operator==(const RetrieveVideoParams& rhs) const
{
return divider == rhs.divider && src_interlacing == rhs.src_interlacing && dst_interlacing == rhs.dst_interlacing;
return divider == rhs.divider;
}
bool operator!=(const RetrieveVideoParams& rhs) const
@@ -295,8 +291,8 @@ protected:
return stream_;
}
static int64_t GetTimeInTimebaseUnits(const rational& time, const rational& timebase, int64_t start_time, VideoParams::Interlacing interlacing);
static rational GetTimestampInTimeUnits(int64_t time, const rational& timebase, int64_t start_time, VideoParams::Interlacing interlacing);
static int64_t GetTimeInTimebaseUnits(const rational& time, const rational& timebase, int64_t start_time);
static rational GetTimestampInTimeUnits(int64_t time, const rational& timebase, int64_t start_time);
signals:
/**
+3 -18
View File
@@ -271,7 +271,6 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicIn
frame);
compatible_pix_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast<AVPixelFormat>(avstream->codecpar->format));
qDebug() << "GOT IT FROM FRAME" << compatible_pix_fmt;
}
// Read second frame
@@ -654,7 +653,7 @@ void FFmpegDecoder::ClearFrameCache()
AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, const QAtomicInt *cancelled)
{
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time, filter_params_.src_interlacing);
int64_t target_ts = GetTimeInTimebaseUnits(time, instance_.avstream()->time_base, instance_.avstream()->start_time);
const int64_t min_seek = -instance_.avstream()->start_time;
int64_t seek_ts = target_ts;
@@ -843,19 +842,6 @@ bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params
// Link filters as necessary
AVFilterContext *last_filter = buffersrc_ctx_;
// Add interlacing filter if necessary
if (filter_params_.src_interlacing != VideoParams::kInterlaceNone) {
// Footage is interlaced, our renderer works in progressive so we'll need to de-interlace
AVFilterContext* interlace_filter;
snprintf(filter_args, kFilterArgSz, "mode=1:parity=%s",
GetInterlacingModeInFFmpeg(filter_params_.src_interlacing));
avfilter_graph_create_filter(&interlace_filter, avfilter_get_by_name("yadif"), "yadif", filter_args, nullptr, filter_graph_);
avfilter_link(last_filter, 0, interlace_filter, 0);
last_filter = interlace_filter;
}
// Add scale filter if necessary
int dst_width, dst_height;
if (filter_params_.divider > 1) {
@@ -864,10 +850,9 @@ bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params
dst_width = VideoParams::GetScaledDimension(src_width, filter_params_.divider);
dst_height = VideoParams::GetScaledDimension(src_height, filter_params_.divider);
snprintf(filter_args, kFilterArgSz, "w=%d:h=%d:flags=fast_bilinear:interl=%d",
snprintf(filter_args, kFilterArgSz, "w=%d:h=%d:flags=fast_bilinear:interl=-1",
dst_width,
dst_height,
params.dst_interlacing != VideoParams::kInterlaceNone);
dst_height);
avfilter_graph_create_filter(&scale_filter, avfilter_get_by_name("scale"), "scale", filter_args, nullptr, filter_graph_);
+46 -42
View File
@@ -20,7 +20,6 @@
#include "oiiodecoder.h"
#include <OpenImageIO/imagebufalgo.h>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
@@ -36,8 +35,7 @@ namespace olive {
QStringList OIIODecoder::supported_formats_;
OIIODecoder::OIIODecoder() :
image_(nullptr),
buffer_(nullptr)
image_(nullptr)
{
}
@@ -75,17 +73,11 @@ FootageDescription OIIODecoder::Probe(const QString &filename, const QAtomicInt*
bool stream_enabled = true;
for (int i=0; in->seek_subimage(i, 0); i++) {
VideoParams video_params;
OIIO::ImageSpec spec = in->spec();
VideoParams video_params = GetVideoParamsFromImageSpec(spec);
video_params.set_stream_index(i);
video_params.set_width(spec.width);
video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
if (i > 1) {
// This is a multilayer image and this image might have an offset
@@ -128,31 +120,40 @@ bool OIIODecoder::RetrieveVideoInternal(TexturePtr destination, const rational &
Q_UNUSED(timecode)
Q_UNUSED(cancelled)
VideoParams vp(buffer_->spec().width,
buffer_->spec().height,
pix_fmt_,
channel_count_,
OIIOUtils::GetPixelAspectRatioFromOIIO(buffer_->spec()),
VideoParams::kInterlaceNone, // FIXME: Does OIIO deinterlace for us?
params.divider);
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(params.divider);
if (params.divider == 1) {
if (!buffer_.is_allocated() || last_params_ != params) {
last_params_ = params;
destination->Upload(buffer_->localpixels(), buffer_->scanline_stride() / vp.GetBytesPerPixel());
buffer_.destroy();
buffer_.set_video_params(vp);
buffer_.allocate();
} else {
if (params.divider == 1) {
// Just upload straight to the buffer
image_->read_image(oiio_pix_fmt_, buffer_.data(), OIIO::AutoStride, buffer_.linesize_bytes());
} else {
OIIO::ImageBuf buf(image_->spec());
image_->read_image(image_->spec().format, buf.localpixels(), buf.pixel_stride(), buf.scanline_stride(), buf.z_stride());
// Will need to resize the image
OIIO::ImageBuf dst(OIIO::ImageSpec(vp.effective_width(), vp.effective_height(), buffer_->spec().nchannels, buffer_->spec().format));
// Roughly downsample image for divider (for some reason OIIO::ImageBufAlgo::resample failed here)
int px_sz = vp.GetBytesPerPixel();
for (int dst_y=0; dst_y<buffer_.height(); dst_y++) {
int src_y = dst_y * buf.spec().height / buffer_.height();
if (!OIIO::ImageBufAlgo::resample(dst, *buffer_)) {
qWarning() << "OIIO resize failed";
for (int dst_x=0; dst_x<buffer_.width(); dst_x++) {
int src_x = dst_x * buf.spec().width / buffer_.width();
memcpy(buffer_.data() + buffer_.linesize_bytes() * dst_y + px_sz * dst_x,
static_cast<uint8_t*>(buf.localpixels()) + buf.scanline_stride() * src_y + px_sz * src_x,
px_sz);
}
}
}
destination->Upload(dst.localpixels(), dst.scanline_stride() / vp.GetBytesPerPixel());
}
destination->Upload(buffer_.data(), buffer_.linesize_pixels());
return true;
}
@@ -201,9 +202,6 @@ bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
// Check if we can work with this pixel format
const OIIO::ImageSpec& spec = image_->spec();
// Store channel count
channel_count_ = spec.nchannels;
// We use RGBA frames because that tends to be the native format of GPUs
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
@@ -212,18 +210,13 @@ bool OIIODecoder::OpenImageHandler(const QString &fn, int subimage)
return false;
}
OIIO::TypeDesc::BASETYPE type = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
oiio_pix_fmt_ = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
if (type == OIIO::TypeDesc::UNKNOWN) {
if (oiio_pix_fmt_ == OIIO::TypeDesc::UNKNOWN) {
qCritical() << "Failed to determine appropriate OIIO basetype from native format";
return false;
}
buffer_ = new OIIO::ImageBuf(OIIO::ImageSpec(spec.width, spec.height, spec.nchannels, type),
OIIO::InitializePixels::No);
image_->read_image(type, buffer_->localpixels());
return true;
}
@@ -234,10 +227,21 @@ void OIIODecoder::CloseImageHandle()
image_ = nullptr;
}
if (buffer_) {
delete buffer_;
buffer_ = nullptr;
}
buffer_.destroy();
}
VideoParams OIIODecoder::GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec)
{
VideoParams video_params;
video_params.set_width(spec.width);
video_params.set_height(spec.height);
video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype)));
video_params.set_channel_count(spec.nchannels);
video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(spec));
video_params.set_video_type(VideoParams::kVideoTypeStill);
return video_params;
}
}
+5 -3
View File
@@ -56,11 +56,13 @@ private:
void CloseImageHandle();
static VideoParams GetVideoParamsFromImageSpec(const OIIO::ImageSpec &spec);
VideoParams::Format pix_fmt_;
OIIO::TypeDesc::BASETYPE oiio_pix_fmt_;
int channel_count_;
OIIO::ImageBuf* buffer_;
Frame buffer_;
RetrieveVideoParams last_params_;
static QStringList supported_formats_;
+37
View File
@@ -191,6 +191,13 @@ void SampleBuffer::transform_volume_for_sample_on_channel(int sample_index, int
data_[channel][sample_index] *= volume;
}
void SampleBuffer::clamp()
{
for (int i=0; i<channel_count(); i++) {
clamp_channel(i);
}
}
void SampleBuffer::silence()
{
silence(0, sample_count_per_channel_);
@@ -223,4 +230,34 @@ void SampleBuffer::set(int channel, const float *data, int sample_offset, int sa
memcpy(&data_[channel].data()[sample_offset], data, sizeof(float) * sample_length);
}
void SampleBuffer::clamp_channel(int channel)
{
const float min = -1.0f;
const float max = 1.0f;
float *cdat = data_[channel].data();
int unopt_start = 0;
#if defined(Q_PROCESSOR_X86) || defined(Q_PROCESSOR_ARM)
__m128 min_sse = _mm_load1_ps(&min);
__m128 max_sse = _mm_load1_ps(&max);
unopt_start = (sample_count_per_channel_ / 4) * 4;
for (int j=0; j<unopt_start; j+=4) {
float *here = cdat + j;
__m128 samples = _mm_loadu_ps(here);
samples = _mm_max_ps(samples, min_sse);
samples = _mm_min_ps(samples, max_sse);
_mm_storeu_ps(here, samples);
}
#endif
for (int sample=unopt_start; sample<sample_count(); sample++) {
float &s = data(channel)[sample];
s = std::clamp(s, min, max);
}
}
}
+6
View File
@@ -71,6 +71,8 @@ public:
return r;
}
int channel_count() const { return data_.size(); }
bool is_allocated() const;
void allocate();
void destroy();
@@ -82,6 +84,8 @@ public:
void transform_volume_for_sample(int sample_index, float volume);
void transform_volume_for_sample_on_channel(int sample_index, int channel, float volume);
void clamp();
void silence();
void silence(int start_sample, int end_sample);
void silence_bytes(int start_byte, int end_byte);
@@ -93,6 +97,8 @@ public:
}
private:
void clamp_channel(int channel);
AudioParams audio_params_;
int sample_count_per_channel_;
+12 -2
View File
@@ -304,14 +304,24 @@ int64_t Timecode::time_to_timestamp(const double &time, const rational &timebase
return 0;
}
const double eps = 0.000000000001;
switch (floor) {
case kRound:
default:
return qRound64(d);
case kFloor:
return qFloor(d);
if (d > qCeil(d)-eps) {
return qCeil(d);
} else {
return qFloor(d);
}
case kCeil:
return qCeil(d);
if (d < qFloor(d)+eps) {
return qFloor(d);
} else {
return qCeil(d);
}
}
}
+1
View File
@@ -102,6 +102,7 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("StopPlaybackOnLastFrame"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("UseLegacyColorInInputTab"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("AutoCacheDelay"), NodeValue::kInt, 1000);
+9 -5
View File
@@ -390,10 +390,12 @@ void Core::DialogProjectPropertiesShow()
void Core::DialogExportShow()
{
ViewerOutput* viewer = GetSequenceToExport();
ViewerOutput* viewer;
rational time;
if (viewer) {
if (GetSequenceToExport(&viewer, &time)) {
ExportDialog* ed = new ExportDialog(viewer, main_window_);
ed->SetTime(time);
connect(ed, &ExportDialog::finished, ed, &ExportDialog::deleteLater);
ed->open();
}
@@ -857,7 +859,7 @@ void Core::SaveProjectInternal(Project* project, const QString& override_filenam
psm->deleteLater();
}
ViewerOutput* Core::GetSequenceToExport()
bool Core::GetSequenceToExport(ViewerOutput **viewer, rational *time)
{
// First try the most recently focused time based window
TimeBasedPanel* time_panel = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
@@ -875,7 +877,9 @@ ViewerOutput* Core::GetSequenceToExport()
tr("This Sequence is empty. There is nothing to export."),
QMessageBox::Ok);
} else {
return time_panel->GetConnectedViewer();
*viewer = time_panel->GetConnectedViewer();
*time = time_panel->GetTime();
return true;
}
} else {
QMessageBox::critical(main_window_,
@@ -884,7 +888,7 @@ ViewerOutput* Core::GetSequenceToExport()
QMessageBox::Ok);
}
return nullptr;
return false;
}
QString Core::GetAutoRecoveryIndexFilename()
+1 -1
View File
@@ -545,7 +545,7 @@ private:
/**
* @brief Retrieves the currently most active sequence for exporting
*/
ViewerOutput *GetSequenceToExport();
bool GetSequenceToExport(ViewerOutput **viewer, rational *time);
static QString GetAutoRecoveryIndexFilename();
+1 -1
View File
@@ -526,7 +526,7 @@ ExportParams ExportDialog::GenerateParams() const
if (ExportCodec::IsCodecAStillImage(video_tab_->GetSelectedCodec()) && !video_tab_->IsImageSequenceSet()) {
// Exporting as image without exporting image sequence, only export one frame
rational export_time = video_tab_->GetStillImageTime();
params.set_custom_range(TimeRange(export_time, export_time));
params.set_custom_range(TimeRange(export_time, export_time + GetSelectedTimebase()));
} else if (range_combobox_->currentIndex() == kRangeInToOut) {
// Assume if this combobox is enabled, workarea is enabled - a check that we make in this dialog's constructor
params.set_custom_range(viewer_node_->GetTimelinePoints()->workarea()->range());
+8
View File
@@ -46,6 +46,14 @@ public:
rational GetSelectedTimebase() const;
void SetTime(const rational &time)
{
preview_viewer_->SetAudioScrubbingEnabled(false);
preview_viewer_->SetTime(time);
video_tab_->SetTime(time);
preview_viewer_->SetAudioScrubbingEnabled(true);
}
protected:
virtual void closeEvent(QCloseEvent *e) override;
+23
View File
@@ -106,6 +106,29 @@ void SequenceDialog::accept()
return;
}
if (!VideoParams::FormatIsFloat(parameter_tab_->GetSelectedPreviewFormat())
&& !OLIVE_CONFIG("PreviewNonFloatDontAskAgain").toBool()) {
QMessageBox b(this);
QCheckBox *dont_show_again_ = new QCheckBox(tr("Don't ask me again"));
b.setIcon(QMessageBox::Warning);
b.setWindowTitle(tr("Low Quality Preview"));
b.setText(tr("The preview resolution has been set to a non-float format. This may cause banding and clipping artifacts in the preview.\n\n"
"Do you wish to continue?"));
b.setCheckBox(dont_show_again_);
b.addButton(QMessageBox::Yes);
b.addButton(QMessageBox::No);
if (b.exec() == QMessageBox::No) {
return;
}
if (dont_show_again_->isChecked()) {
OLIVE_CONFIG("PreviewNonFloatDontAskAgain") = true;
}
}
// Generate video and audio parameter structs from data
VideoParams video_params = VideoParams(parameter_tab_->GetSelectedVideoWidth(),
parameter_tab_->GetSelectedVideoHeight(),
@@ -71,7 +71,7 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
preview_layout->addWidget(preview_resolution_label_, row, 2);
row++;
preview_layout->addWidget(new QLabel(tr("Quality:")), row, 0);
preview_format_field_ = new PixelFormatComboBox(true);
preview_format_field_ = new PixelFormatComboBox(false);
preview_layout->addWidget(preview_format_field_, row, 1, 1, 2);
row++;
preview_layout->addWidget(new QLabel(tr("Auto-Cache:")), row, 0);
+2
View File
@@ -990,6 +990,8 @@ public:
InputFlags GetInputFlags(const QString& input) const;
void SetInputFlags(const QString &input, const InputFlags &f);
virtual void LoadFinishedEvent(){}
static void SetValueAtTime(const NodeInput &input, const rational &time, const QVariant &value, int track, MultiUndoCommand *command, bool insert_on_all_tracks_if_no_key);
static std::list<Node*> FindPath(Node *from, Node *to, int path_index = 0);
+10 -2
View File
@@ -465,6 +465,11 @@ void ViewerOutput::set_parameters_from_footage(const QVector<ViewerOutput *> foo
}
int ViewerOutput::AddStream(Track::Type type, const QVariant& value)
{
return SetStream(type, value, -1);
}
int ViewerOutput::SetStream(Track::Type type, const QVariant &value, int index_in)
{
QString id;
@@ -479,8 +484,11 @@ int ViewerOutput::AddStream(Track::Type type, const QVariant& value)
}
// Add another video/audio param to the array for this stream
int index = InputArraySize(id);
InputArrayAppend(id);
int index = (index_in == -1) ? InputArraySize(id) : index_in;
if (index >= InputArraySize(id)) {
InputArrayResize(id, index+1);
}
SetStandardValue(id, value, index);
+1
View File
@@ -211,6 +211,7 @@ protected:
virtual void InputValueChangedEvent(const QString& input, int element) override;
int AddStream(Track::Type type, const QVariant &value);
int SetStream(Track::Type type, const QVariant &value, int index);
private:
rational last_length_;
+106 -55
View File
@@ -43,6 +43,8 @@ const QString Footage::kLoopModeInput = QStringLiteral("loop_in");
Footage::Footage(const QString &filename) :
ViewerOutput(false, false),
timestamp_(0),
valid_(false),
cancelled_(nullptr)
{
SetCacheTextures(true);
@@ -53,7 +55,9 @@ Footage::Footage(const QString &filename) :
Clear();
set_filename(filename);
if (!filename.isEmpty()) {
set_filename(filename);
}
QTimer *check_timer = new QTimer(this);
check_timer->setInterval(5000);
@@ -76,59 +80,7 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
// Reset internal stream cache
Clear();
// Determine if file still exists
QFileInfo info(filename());
if (info.exists()) {
// Grab timestamp
set_timestamp(info.lastModified().toMSecsSinceEpoch());
// Determine if we've already cached the metadata of this file
QString meta_cache_file = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).filePath(FileFunctions::GetUniqueFileIdentifier(filename()));
FootageDescription footage_info;
// Try to load footage info from cache
if (!QFileInfo::exists(meta_cache_file) || !footage_info.Load(meta_cache_file)) {
// Probe and create cache
QVector<DecoderPtr> decoder_list = Decoder::ReceiveListOfAllDecoders();
foreach (DecoderPtr decoder, decoder_list) {
footage_info = decoder->Probe(filename(), cancelled_);
if (footage_info.IsValid()) {
break;
}
}
if (!footage_info.Save(meta_cache_file)) {
qWarning() << "Failed to save stream cache, footage will have to be re-probed";
}
}
if (footage_info.IsValid()) {
decoder_ = footage_info.decoder();
for (int i=0; i<footage_info.GetVideoStreams().size(); i++) {
AddStream(Track::kVideo, QVariant::fromValue(footage_info.GetVideoStreams().at(i)));
}
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
AddStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
}
for (int i=0; i<footage_info.GetSubtitleStreams().size(); i++) {
AddStream(Track::kSubtitle, QVariant::fromValue(footage_info.GetSubtitleStreams().at(i)));
}
SetValid();
}
} else {
set_timestamp(0);
}
Reprobe();
} else {
super::InputValueChangedEvent(input, element);
}
@@ -419,6 +371,13 @@ rational Footage::AdjustTimeByLoopMode(rational time, Footage::LoopMode loop_mod
return time;
}
void Footage::LoadFinishedEvent()
{
if (!filename().isEmpty()) {
Reprobe();
}
}
qint64 Footage::creation_time() const
{
QFileInfo info(filename());
@@ -482,6 +441,98 @@ void Footage::UpdateTooltip()
}
}
void Footage::Reprobe()
{
// Determine if file still exists
QString filename = this->filename();
// In case of failure to load file, set timestamp to a value that will always be invalid so we
// continuously reprobe
set_timestamp(0);
if (!filename.isEmpty()) {
QFileInfo info(filename);
if (info.exists()) {
// Grab timestamp
set_timestamp(info.lastModified().toMSecsSinceEpoch());
// Determine if we've already cached the metadata of this file
QString meta_cache_file = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).filePath(FileFunctions::GetUniqueFileIdentifier(filename));
FootageDescription footage_info;
// Try to load footage info from cache
if (!QFileInfo::exists(meta_cache_file) || !footage_info.Load(meta_cache_file)) {
// Probe and create cache
QVector<DecoderPtr> decoder_list = Decoder::ReceiveListOfAllDecoders();
foreach (DecoderPtr decoder, decoder_list) {
footage_info = decoder->Probe(filename, cancelled_);
if (footage_info.IsValid()) {
break;
}
}
if (!footage_info.Save(meta_cache_file)) {
qWarning() << "Failed to save stream cache, footage will have to be re-probed";
}
}
if (footage_info.IsValid()) {
decoder_ = footage_info.decoder();
InputArrayResize(kVideoParamsInput, footage_info.GetVideoStreams().size());
for (int i=0; i<footage_info.GetVideoStreams().size(); i++) {
VideoParams video_stream = footage_info.GetVideoStreams().at(i);
if (i < InputArraySize(kVideoParamsInput)) {
VideoParams existing = this->GetVideoParams(i);
if (existing.is_valid()) {
video_stream = MergeVideoStream(video_stream, existing);
}
}
SetStream(Track::kVideo, QVariant::fromValue(video_stream), i);
}
InputArrayResize(kAudioParamsInput, footage_info.GetAudioStreams().size());
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
SetStream(Track::kAudio, QVariant::fromValue(footage_info.GetAudioStreams().at(i)), i);
}
InputArrayResize(kSubtitleParamsInput, footage_info.GetSubtitleStreams().size());
for (int i=0; i<footage_info.GetSubtitleStreams().size(); i++) {
SetStream(Track::kSubtitle, QVariant::fromValue(footage_info.GetSubtitleStreams().at(i)), i);
}
SetValid();
}
}
}
}
VideoParams Footage::MergeVideoStream(const VideoParams &base, const VideoParams &over)
{
VideoParams merged = base;
merged.set_pixel_aspect_ratio(over.pixel_aspect_ratio());
merged.set_interlacing(over.interlacing());
merged.set_colorspace(over.colorspace());
merged.set_premultiplied_alpha(over.premultiplied_alpha());
if (merged.video_type() == VideoParams::kVideoTypeImageSequence && over.video_type() == VideoParams::kVideoTypeImageSequence) {
merged.set_start_time(over.start_time());
merged.set_duration(over.duration());
merged.set_frame_rate(over.frame_rate());
}
return merged;
}
void Footage::CheckFootage()
{
// Don't check files if not the active window
@@ -495,7 +546,7 @@ void Footage::CheckFootage()
if (current_file_timestamp != timestamp()) {
// File has changed!
set_timestamp(current_file_timestamp);
Reprobe();
InvalidateAll(kFilenameInput);
}
}
+6
View File
@@ -185,6 +185,8 @@ public:
static rational AdjustTimeByLoopMode(rational time, LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase);
virtual void LoadFinishedEvent() override;
virtual qint64 creation_time() const override;
virtual qint64 mod_time() const override;
@@ -215,6 +217,10 @@ private:
*/
void UpdateTooltip();
void Reprobe();
VideoParams MergeVideoStream(const VideoParams &base, const VideoParams &over);
/**
* @brief Internal timestamp object
*/
@@ -243,6 +243,8 @@ void ProjectSerializer210528::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
reader->skipCurrentElement();
}
}
node->LoadFinishedEvent();
}
void ProjectSerializer210528::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
@@ -240,6 +240,8 @@ void ProjectSerializer210907::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
reader->skipCurrentElement();
}
}
node->LoadFinishedEvent();
}
void ProjectSerializer210907::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
@@ -290,6 +290,8 @@ void ProjectSerializer211228::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
reader->skipCurrentElement();
}
}
node->LoadFinishedEvent();
}
void ProjectSerializer211228::LoadInput(Node *node, QXmlStreamReader *reader, XMLNodeData &xml_node_data) const
@@ -548,6 +548,8 @@ void ProjectSerializer220403::LoadNode(Node *node, XMLNodeData &xml_node_data, Q
reader->skipCurrentElement();
}
}
node->LoadFinishedEvent();
}
void ProjectSerializer220403::SaveNode(Node *node, QXmlStreamWriter *writer) const
+2
View File
@@ -783,12 +783,14 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
if (!video_tasks_.isEmpty()) {
// Cancel any video tasks and wait for them to finish
CancelVideoTasks(true);
video_tasks_.clear();
}
// Handle audio rendering tasks
if (!audio_tasks_.isEmpty()) {
// Cancel any audio tasks and wait for them to finish
CancelAudioTasks(true);
audio_tasks_.clear();
}
// Clear any single frame render that might be queued
+19 -15
View File
@@ -215,11 +215,15 @@ void RenderProcessor::Run()
ResolveJobs(sample_val, time);
SampleBuffer samples = sample_val.toSamples();
if (samples.is_allocated() && ticket_->property("enablewaveforms").toBool()) {
AudioVisualWaveform vis;
vis.set_channel_count(samples.audio_params().channel_count());
vis.OverwriteSamples(samples, samples.audio_params().sample_rate());
ticket_->setProperty("waveform", QVariant::fromValue(vis));
if (samples.is_allocated()) {
samples.clamp();
if (ticket_->property("enablewaveforms").toBool()) {
AudioVisualWaveform vis;
vis.set_channel_count(samples.audio_params().channel_count());
vis.OverwriteSamples(samples, samples.audio_params().sample_rate());
ticket_->setProperty("waveform", QVariant::fromValue(vis));
}
}
if (HeardCancel()) {
@@ -404,30 +408,30 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
DecoderPtr decoder = nullptr;
if (stream_data.video_type() == VideoParams::kVideoTypeVideo) {
switch (stream_data.video_type()) {
case VideoParams::kVideoTypeVideo:
case VideoParams::kVideoTypeStill:
decoder = ResolveDecoderFromInput(decoder_id, default_codec_stream);
} else {
break;
case VideoParams::kVideoTypeImageSequence:
{
// Since image sequences involve multiple files, we don't engage the decoder cache
decoder = Decoder::CreateFromID(decoder_id);
QString frame_filename;
if (stream_data.video_type() == VideoParams::kVideoTypeImageSequence) {
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
} else {
frame_filename = stream.filename();
}
int64_t frame_number = stream_data.get_time_in_timebase_units(input_time);
frame_filename = Decoder::TransformImageSequenceFileName(stream.filename(), frame_number);
// Decoder will close automatically since it's a stream_ptr
decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index()));
break;
}
}
if (decoder) {
Decoder::RetrieveVideoParams p;
p.divider = stream.video_params().divider();
p.src_interlacing = stream_data.interlacing();
p.dst_interlacing = GetCacheVideoParams().interlacing();
if (!IsCancelled()) {
@@ -83,7 +83,10 @@ void NodeParamViewItem::Retranslate()
void NodeParamViewItem::RecreateBody()
{
delete body_;
if (body_) {
body_->setParent(nullptr);
body_->deleteLater();
}
body_ = new NodeParamViewItemBody(node_, create_checkboxes_);
connect(body_, &NodeParamViewItemBody::RequestSelectNode, this, &NodeParamViewItem::RequestSelectNode);
+3 -2
View File
@@ -68,7 +68,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
prequeuing_video_(false),
prequeuing_audio_(0),
record_armed_(false),
recording_(false)
recording_(false),
enable_audio_scrubbing_(true)
{
// Set up main layout
QVBoxLayout* layout = new QVBoxLayout(this);
@@ -834,7 +835,7 @@ void ViewerWidget::PauseInternal()
void ViewerWidget::PushScrubbedAudio()
{
if (!IsPlaying() && GetConnectedNode() && OLIVE_CONFIG("AudioScrubbing").toBool()) {
if (!IsPlaying() && GetConnectedNode() && OLIVE_CONFIG("AudioScrubbing").toBool() && enable_audio_scrubbing_) {
// Get audio src device from renderer
const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters();
+7
View File
@@ -89,6 +89,11 @@ public:
void StartCapture(TimelineWidget *source, const TimeRange &time, const Track::Reference &track);
void SetAudioScrubbingEnabled(bool e)
{
enable_audio_scrubbing_ = e;
}
public slots:
void Play(bool in_to_out_only);
@@ -276,6 +281,8 @@ private:
qint64 queue_starved_start_;
bool enable_audio_scrubbing_;
private slots:
void PlaybackTimerUpdate();
+20
View File
@@ -105,4 +105,24 @@ OLIVE_ADD_TEST(TimeRangeListFrameIteratorSize)
OLIVE_TEST_END;
}
OLIVE_ADD_TEST(TimeRangeListFrameIteratorSize2)
{
const rational timebase(1001, 30000);
TimeRangeList ranges;
ranges.insert(TimeRange(rational(247247, 30000), rational(31031, 3750))); // 1
TimeRange tr(rational(247247, 30000), rational(31031, 3750));
TimeRangeListFrameIterator iterator(ranges, timebase);
QVector<rational> vec = iterator.ToVector();
OLIVE_ASSERT_EQUAL(vec.size(), 1);
OLIVE_ASSERT_EQUAL(iterator.size(), vec.size());
OLIVE_TEST_END;
}
}