finished merge of viewer and footage

This commit is contained in:
itsmattkc
2021-04-06 13:08:56 +10:00
parent 57ddae8920
commit 898ea25e89
58 changed files with 783 additions and 719 deletions
+4 -1
View File
@@ -1346,8 +1346,9 @@ void Node::ArrayResizeInternal(const QString &id, int size)
// equal subinputs_.size()
}
int old_sz = imm->array_size;
imm->array_size = size;
emit InputArraySizeChanged(id, size);
emit InputArraySizeChanged(id, old_sz, size);
ParameterValueChanged(id, -1, TimeRange(RATIONAL_MIN, RATIONAL_MAX));
}
}
@@ -1842,6 +1843,8 @@ void Node::ParameterValueChanged(const QString& input, int element, const TimeRa
void Node::LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled)
{
Q_UNUSED(xml_node_data)
NodeValue::Type data_type = GetInputDataType(input);
while (XMLReadNextStartElement(reader)) {
+1 -1
View File
@@ -876,7 +876,7 @@ signals:
void LinksChanged();
void InputArraySizeChanged(const QString& input, int new_size);
void InputArraySizeChanged(const QString& input, int old_size, int new_size);
void KeyframeAdded(NodeKeyframe* key);
+19
View File
@@ -673,4 +673,23 @@ uint qHash(const Track::Reference &r, uint seed)
seed);
}
QDataStream &operator<<(QDataStream &out, const Track::Reference &ref)
{
out << static_cast<int>(ref.type()) << ref.index();
return out;
}
QDataStream &operator>>(QDataStream &in, Track::Reference &ref)
{
int type;
int index;
in >> type >> index;
ref = Track::Reference(static_cast<Track::Type>(type), index);
return in;
}
}
+61
View File
@@ -134,6 +134,63 @@ public:
return !(*this == ref);
}
bool operator<(const Track::Reference& rhs) const
{
if (type_ != rhs.type_) {
return type_ < rhs.type_;
}
return index_ < rhs.index_;
}
QString ToString() const
{
QString type_string;
if (type_ == Track::kVideo) {
type_string = QStringLiteral("v");
} else if (type_ == Track::kAudio) {
type_string = QStringLiteral("a");
} else {
return QString();
}
return QStringLiteral("%1:%2").arg(type_string, QString::number(index_));
}
static Type TypeFromString(const QString& s)
{
if (s.at(1) == ':') {
if (s.at(0) == 'v') {
// Video stream
return Track::kVideo;
} else if (s.at(0) == 'a') {
// Audio stream
return Track::kAudio;
}
}
return Track::kNone;
}
static Reference FromString(const QString& s)
{
Reference ref;
Type parse_type = TypeFromString(s);
if (parse_type != Track::kNone) {
bool ok;
int parse_index = s.mid(2).toInt(&ok);
if (ok) {
ref.type_ = parse_type;
ref.index_ = parse_index;
}
}
return ref;
}
private:
Track::Type type_;
@@ -380,6 +437,10 @@ private slots:
uint qHash(const Track::Reference& r, uint seed = 0);
QDataStream &operator<<(QDataStream &out, const Track::Reference &ref);
QDataStream &operator>>(QDataStream &in, Track::Reference &ref);
}
#endif // TRACK_H
+281 -59
View File
@@ -36,18 +36,25 @@ const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight
#define super Node
ViewerOutput::ViewerOutput() :
ViewerOutput::ViewerOutput(bool create_default_streams) :
video_frame_cache_(this),
audio_playback_cache_(this),
operation_stack_(0)
{
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
connect(this, &Node::InputArraySizeChanged, this, &ViewerOutput::InputResized);
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
if (create_default_streams) {
AddStream(Track::kVideo, QVariant());
AddStream(Track::kAudio, QVariant());
}
}
ViewerOutput::~ViewerOutput()
@@ -85,16 +92,116 @@ QString ViewerOutput::Description() const
QString ViewerOutput::duration() const
{
rational timeline_length = GetLength();
/*rational timeline_length = GetLength();
int64_t timestamp = Timecode::time_to_timestamp(timeline_length, video_params().time_base());
rational timebase = GetVideoParams().time_base();
return Timecode::timestamp_to_timecode(timestamp, video_params().time_base(), Core::instance()->GetTimecodeDisplay());
int64_t timestamp = Timecode::time_to_timestamp(timeline_length, timebase);
return Timecode::timestamp_to_timecode(timestamp, timebase, Core::instance()->GetTimecodeDisplay());*/
// Try video first
VideoParams video = GetFirstEnabledVideoStream();
if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) {
int64_t duration = video.duration();
rational frame_rate_timebase = video.frame_rate().flipped();
if (frame_rate_timebase.isNull()) {
frame_rate_timebase = video.time_base();
}
if (video.time_base() != frame_rate_timebase) {
// Convert from timebase to frame rate
duration = Timecode::rescale_timestamp_ceil(duration, video.time_base(), frame_rate_timebase);
}
return Timecode::timestamp_to_timecode(duration,
frame_rate_timebase,
Core::instance()->GetTimecodeDisplay());
}
// Try audio second
AudioParams audio = GetFirstEnabledAudioStream();
if (audio.is_valid()) {
// If we're showing in a timecode, we prefer showing audio in seconds instead
Timecode::Display display = Core::instance()->GetTimecodeDisplay();
if (display == Timecode::kTimecodeDropFrame
|| display == Timecode::kTimecodeNonDropFrame) {
display = Timecode::kTimecodeSeconds;
}
return Timecode::timestamp_to_timecode(audio.duration(),
audio.time_base(),
display);
}
// Otherwise, return nothing
return QString();
}
QString ViewerOutput::rate() const
{
return tr("%1 FPS").arg(video_params().time_base().flipped().toDouble());
if (HasEnabledVideoStreams()) {
// This is a video editor, prioritize video streams
VideoParams video_stream = GetFirstEnabledVideoStream();
if (video_stream.video_type() != VideoParams::kVideoTypeStill) {
rational using_tb = video_stream.frame_rate();
if (using_tb.isNull()) {
using_tb = video_stream.time_base().flipped();
}
return tr("%1 FPS").arg(using_tb.toDouble());
}
} else if (HasEnabledAudioStreams()) {
// No video streams, return audio
AudioParams audio_stream = GetFirstEnabledAudioStream();
return tr("%1 Hz").arg(audio_stream.sample_rate());
}
return QString();
}
bool ViewerOutput::HasEnabledVideoStreams() const
{
return GetFirstEnabledVideoStream().is_valid();
}
bool ViewerOutput::HasEnabledAudioStreams() const
{
return GetFirstEnabledAudioStream().is_valid();
}
VideoParams ViewerOutput::GetFirstEnabledVideoStream() const
{
int sz = GetVideoStreamCount();
for (int i=0; i<sz; i++) {
VideoParams vp = GetVideoParams(i);
if (vp.enabled()) {
return vp;
}
}
return VideoParams();
}
AudioParams ViewerOutput::GetFirstEnabledAudioStream() const
{
int sz = GetAudioStreamCount();
for (int i=0; i<sz; i++) {
AudioParams ap = GetAudioParams(i);
if (ap.enabled()) {
return ap;
}
}
return AudioParams();
}
void ViewerOutput::set_default_parameters()
@@ -102,17 +209,21 @@ void ViewerOutput::set_default_parameters()
int width = Config::Current()["DefaultSequenceWidth"].toInt();
int height = Config::Current()["DefaultSequenceHeight"].toInt();
set_video_params(VideoParams(width,
height,
Config::Current()["DefaultSequenceFrameRate"].value<rational>(),
static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt()),
VideoParams::kInternalChannelCount,
Config::Current()["DefaultSequencePixelAspect"].value<rational>(),
Config::Current()["DefaultSequenceInterlacing"].value<VideoParams::Interlacing>(),
VideoParams::generate_auto_divider(width, height)));
set_audio_params(AudioParams(Config::Current()["DefaultSequenceAudioFrequency"].toInt(),
Config::Current()["DefaultSequenceAudioLayout"].toULongLong(),
AudioParams::kInternalFormat));
SetVideoParams(VideoParams(
width,
height,
Config::Current()["DefaultSequenceFrameRate"].value<rational>(),
static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt()),
VideoParams::kInternalChannelCount,
Config::Current()["DefaultSequencePixelAspect"].value<rational>(),
Config::Current()["DefaultSequenceInterlacing"].value<VideoParams::Interlacing>(),
VideoParams::generate_auto_divider(width, height)
));
SetAudioParams(AudioParams(
Config::Current()["DefaultSequenceAudioFrequency"].toInt(),
Config::Current()["DefaultSequenceAudioLayout"].toULongLong(),
AudioParams::kInternalFormat
));
}
void ViewerOutput::ShiftVideoCache(const rational &from, const rational &to)
@@ -165,6 +276,33 @@ const rational& ViewerOutput::GetLength() const
return last_length_;
}
QVector<Track::Reference> ViewerOutput::GetEnabledStreamsAsReferences() const
{
QVector<Track::Reference> refs;
{
int vp_sz = GetVideoStreamCount();
for (int i=0; i<vp_sz; i++) {
if (GetVideoParams(i).enabled()) {
refs.append(Track::Reference(Track::kVideo, i));
}
}
}
{
int ap_sz = GetAudioStreamCount();
for (int i=0; i<ap_sz; i++) {
if (GetAudioParams(i).enabled()) {
refs.append(Track::Reference(Track::kAudio, i));
}
}
}
return refs;
}
void ViewerOutput::Retranslate()
{
super::Retranslate();
@@ -258,44 +396,56 @@ void ViewerOutput::EndOperation()
super::EndOperation();
}
NodeOutput ViewerOutput::GetConnectedTextureOutput()
{
return GetConnectedOutput(kTextureInput);
}
NodeOutput ViewerOutput::GetConnectedSampleOutput()
{
return GetConnectedOutput(kSamplesInput);
}
void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
{
if (input == kVideoParamsInput) {
if (element == 0) {
if (input == kVideoParamsInput) {
VideoParams new_video_params = video_params();
VideoParams new_video_params = GetVideoParams();
bool size_changed = cached_video_params_.width() != new_video_params.width() || cached_video_params_.height() != new_video_params.height();
bool timebase_changed = cached_video_params_.time_base() != new_video_params.time_base();
bool pixel_aspect_changed = cached_video_params_.pixel_aspect_ratio() != new_video_params.pixel_aspect_ratio();
bool interlacing_changed = cached_video_params_.interlacing() != new_video_params.interlacing();
bool size_changed = cached_video_params_.width() != new_video_params.width() || cached_video_params_.height() != new_video_params.height();
bool timebase_changed = cached_video_params_.time_base() != new_video_params.time_base();
bool pixel_aspect_changed = cached_video_params_.pixel_aspect_ratio() != new_video_params.pixel_aspect_ratio();
bool interlacing_changed = cached_video_params_.interlacing() != new_video_params.interlacing();
if (size_changed) {
emit SizeChanged(new_video_params.width(), new_video_params.height());
}
if (pixel_aspect_changed) {
emit PixelAspectChanged(new_video_params.pixel_aspect_ratio());
}
if (interlacing_changed) {
emit InterlacingChanged(new_video_params.interlacing());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(new_video_params.time_base());
emit TimebaseChanged(new_video_params.time_base());
}
emit VideoParamsChanged();
cached_video_params_ = new_video_params;
} else if (input == kAudioParamsInput) {
emit AudioParamsChanged();
audio_playback_cache_.SetParameters(GetAudioParams());
if (size_changed) {
emit SizeChanged(new_video_params.width(), new_video_params.height());
}
if (pixel_aspect_changed) {
emit PixelAspectChanged(new_video_params.pixel_aspect_ratio());
}
if (interlacing_changed) {
emit InterlacingChanged(new_video_params.interlacing());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(new_video_params.time_base());
emit TimebaseChanged(new_video_params.time_base());
}
emit VideoParamsChanged();
cached_video_params_ = video_params();
} else if (input == kAudioParamsInput) {
emit AudioParamsChanged();
audio_playback_cache_.SetParameters(audio_params());
}
super::InputValueChangedEvent(input, element);
@@ -313,9 +463,9 @@ void ViewerOutput::ShiftAudioEvent(const rational &from, const rational &to)
Q_UNUSED(to)
}
void ViewerOutput::set_parameters_from_footage(const QVector<Footage *> footage)
void ViewerOutput::set_parameters_from_footage(const QVector<ViewerOutput *> footage)
{
foreach (Footage* f, footage) {
foreach (ViewerOutput* f, footage) {
QVector<VideoParams> video_streams = f->GetEnabledVideoStreams();
QVector<AudioParams> audio_streams = f->GetEnabledAudioStreams();
@@ -327,20 +477,20 @@ void ViewerOutput::set_parameters_from_footage(const QVector<Footage *> footage)
// If this is a still image, we'll use it's resolution but won't set
// `found_video_params` in case something with a frame rate comes along which we'll
// prioritize
using_timebase = video_params().time_base();
using_timebase = GetVideoParams().time_base();
} else {
using_timebase = s.frame_rate().flipped();
found_video_params = true;
}
set_video_params(VideoParams(s.width(),
SetVideoParams(VideoParams(s.width(),
s.height(),
using_timebase,
static_cast<VideoParams::Format>(Config::Current()[QStringLiteral("OfflinePixelFormat")].toInt()),
VideoParams::kInternalChannelCount,
s.pixel_aspect_ratio(),
s.interlacing(),
VideoParams::generate_auto_divider(s.width(), s.height())));
VideoParams::kInternalChannelCount,
s.pixel_aspect_ratio(),
s.interlacing(),
VideoParams::generate_auto_divider(s.width(), s.height())));
if (found_video_params) {
break;
@@ -349,7 +499,7 @@ void ViewerOutput::set_parameters_from_footage(const QVector<Footage *> footage)
if (!audio_streams.isEmpty()) {
const AudioParams& s = audio_streams.first();
set_audio_params(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat));
SetAudioParams(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat));
}
}
}
@@ -372,4 +522,76 @@ void ViewerOutput::SaveCustom(QXmlStreamWriter *writer) const
writer->writeEndElement(); // points
}
int ViewerOutput::AddStream(Track::Type type, const QVariant& value)
{
QString id;
if (type == Track::kVideo) {
id = kVideoParamsInput;
} else if (type == Track::kAudio) {
id = kAudioParamsInput;
} else {
return -1;
}
// Add another video/audio param to the array for this stream
int index = InputArraySize(id);
InputArrayAppend(id);
SetStandardValue(id, value, index);
return index;
}
void ViewerOutput::InputResized(const QString &input, int old_size, int new_size)
{
if (input == kVideoParamsInput || input == kAudioParamsInput) {
Track::Type type = (input == kVideoParamsInput) ? Track::kVideo : Track::kAudio;
if (new_size > old_size) {
for (int i=old_size; i<new_size; i++) {
AddOutput(Track::Reference(type, i).ToString());
}
} else if (new_size < old_size) {
for (int i=new_size; i<old_size; i++) {
RemoveOutput(Track::Reference(type, i).ToString());
}
}
}
}
QVector<VideoParams> ViewerOutput::GetEnabledVideoStreams() const
{
QVector<VideoParams> streams;
int vp_sz = GetVideoStreamCount();
for (int i=0; i<vp_sz; i++) {
VideoParams vp = GetVideoParams(i);
if (vp.enabled()) {
streams.append(vp);
}
}
return streams;
}
QVector<AudioParams> ViewerOutput::GetEnabledAudioStreams() const
{
QVector<AudioParams> streams;
int ap_sz = GetAudioStreamCount();
for (int i=0; i<ap_sz; i++) {
AudioParams ap = GetAudioParams(i);
if (ap.enabled()) {
streams.append(ap);
}
}
return streams;
}
}
+46 -10
View File
@@ -44,7 +44,7 @@ class ViewerOutput : public Node
{
Q_OBJECT
public:
ViewerOutput();
ViewerOutput(bool create_default_streams = true);
virtual ~ViewerOutput() override;
virtual Node* copy() const override;
@@ -59,7 +59,7 @@ public:
void set_default_parameters();
void set_parameters_from_footage(const QVector<Footage *> footage);
void set_parameters_from_footage(const QVector<ViewerOutput *> footage);
void ShiftVideoCache(const rational& from, const rational& to);
void ShiftAudioCache(const rational& from, const rational& to);
@@ -67,26 +67,47 @@ public:
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
VideoParams video_params() const
VideoParams GetVideoParams(int index = 0) const
{
return GetStandardValue(kVideoParamsInput).value<VideoParams>();
return GetStandardValue(kVideoParamsInput, index).value<VideoParams>();
}
AudioParams audio_params() const
AudioParams GetAudioParams(int index = 0) const
{
return GetStandardValue(kAudioParamsInput).value<AudioParams>();
return GetStandardValue(kAudioParamsInput, index).value<AudioParams>();
}
void set_video_params(const VideoParams &video)
void SetVideoParams(const VideoParams &video, int index = 0)
{
SetStandardValue(kVideoParamsInput, QVariant::fromValue(video));
SetStandardValue(kVideoParamsInput, QVariant::fromValue(video), index);
}
void set_audio_params(const AudioParams &audio)
void SetAudioParams(const AudioParams &audio, int index = 0)
{
SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio));
SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio), index);
}
int GetVideoStreamCount() const
{
return InputArraySize(kVideoParamsInput);
}
int GetAudioStreamCount() const
{
return InputArraySize(kAudioParamsInput);
}
int GetTotalStreamCount() const
{
return GetVideoStreamCount() + GetAudioStreamCount();
}
bool HasEnabledVideoStreams() const;
bool HasEnabledAudioStreams() const;
VideoParams GetFirstEnabledVideoStream() const;
AudioParams GetFirstEnabledAudioStream() const;
const rational &GetLength() const;
FrameHashCache* video_frame_cache()
@@ -104,12 +125,22 @@ public:
return &timeline_points_;
}
QVector<Track::Reference> GetEnabledStreamsAsReferences() const;
QVector<VideoParams> GetEnabledVideoStreams() const;
QVector<AudioParams> GetEnabledAudioStreams() const;
virtual void Retranslate() override;
virtual void BeginOperation() override;
virtual void EndOperation() override;
virtual NodeOutput GetConnectedTextureOutput();
virtual NodeOutput GetConnectedSampleOutput();
static const QString kVideoParamsInput;
static const QString kAudioParamsInput;
@@ -154,6 +185,8 @@ protected:
virtual void SaveCustom(QXmlStreamWriter *writer) const override;
int AddStream(Track::Type type, const QVariant &value);
private:
rational last_length_;
@@ -167,6 +200,9 @@ private:
TimelinePoints timeline_points_;
private slots:
void InputResized(const QString& input, int old_size, int new_size);
};
}
+2 -2
View File
@@ -234,7 +234,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
// Assume this is a VideoStream, we did a type check earlier in the function
FootageJob job = v.data().value<FootageJob>();
if (job.type() == Stream::kVideo) {
if (job.type() == Track::kVideo) {
QVariant value = ProcessVideoFootage(job, range.in());
if (!value.isNull()) {
@@ -267,7 +267,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
// Assume this is an AudioStream, we did a type check earlier in the function
FootageJob job = v.data().value<FootageJob>();
if (job.type() == Stream::kAudio) {
if (job.type() == Track::kAudio) {
QVariant value = ProcessAudioFootage(job, range);
if (!value.isNull()) {
-1
View File
@@ -27,7 +27,6 @@
#include <QVector4D>
#include "common/tohex.h"
#include "project/item/footage/stream.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
#include "render/color.h"