moved sequence params into node graph too

Reduces code by reusing more of the existing node infrastructure
to synchronize sequence parameters the render backend.
This commit is contained in:
itsmattkc
2021-02-18 13:20:23 +11:00
parent d7c5ac4b44
commit 4827782331
46 changed files with 1375 additions and 1394 deletions
+2 -1
View File
@@ -19,7 +19,8 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
project/item/footage/footage.cpp
project/item/footage/footage.h
project/item/footage/stream.cpp
project/item/footage/footagedescription.cpp
project/item/footage/footagedescription.h
project/item/footage/stream.h
PARENT_SCOPE
)
+245 -327
View File
@@ -41,7 +41,6 @@ const QString Footage::kStreamPropertiesFormat = QStringLiteral("stream_properti
Footage::Footage(const QString &filename) :
super(true, false),
stream_count_(0),
cancelled_(nullptr)
{
AddInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
@@ -58,7 +57,7 @@ void Footage::Retranslate()
SetInputName(kFilenameInput, tr("Filename"));
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
StreamReference ref = GetReferenceFromRealIndex(it.key());
StreamReference ref = it.key();
SetInputName(it.value(), QStringLiteral("%1 %2").arg(GetStreamTypeName(ref.type()), QString::number(ref.index())));
}
@@ -111,12 +110,12 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
// Determine if we've already cached the metadata of this file
QString meta_cache_file = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).filePath(FileFunctions::GetUniqueFileIdentifier(filename()));
MetadataCache footage_info;
FootageDescription footage_info;
if (QFileInfo::exists(meta_cache_file)) {
// Load meta cache file
footage_info = LoadStreamCache(meta_cache_file);
footage_info.Load(meta_cache_file);
} else {
@@ -124,51 +123,28 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
QVector<DecoderPtr> decoder_list = Decoder::ReceiveListOfAllDecoders();
foreach (DecoderPtr decoder, decoder_list) {
footage_info.streams = decoder->Probe(filename(), cancelled_);
footage_info = decoder->Probe(filename(), cancelled_);
if (!footage_info.streams.isEmpty()) {
footage_info.decoder = decoder->id();
SetValid();
if (footage_info.IsValid()) {
break;
}
}
if (!SaveStreamCache(meta_cache_file, footage_info)) {
if (!footage_info.Save(meta_cache_file)) {
qWarning() << "Failed to save stream cache, footage will have to be re-probed";
}
}
stream_count_ = footage_info.streams.size();
if (footage_info.IsValid()) {
decoder_ = footage_info.decoder();
if (!footage_info.streams.isEmpty()) {
set_decoder(footage_info.decoder);
for (int i=0; i<footage_info.GetVideoStreams().size(); i++) {
AddStreamAsInput(Stream::kVideo, i, QVariant::fromValue(footage_info.GetVideoStreams().at(i)));
}
for (int i=0; i<footage_info.streams.size(); i++) {
const Stream& s = footage_info.streams.at(i);
if (s.type() == Stream::kVideo || s.type() == Stream::kAudio) {
QString id = GetInputIDOfIndex(i);
NodeValue::Type type;
if (s.type() == Stream::kVideo) {
type = NodeValue::kVideoStreamProperties;
} else {
type = NodeValue::kAudioStreamProperties;
}
// Create input for properties
AddInput(id, type, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetStandardValue(id, QVariant::fromValue(s));
inputs_for_stream_properties_.insert(i, id);
// Create output for stream
Footage::StreamReference ref = GetReferenceFromRealIndex(i);
QString out_id = GetStringFromReference(ref);
AddOutput(out_id);
outputs_for_streams_.insert(i, out_id);
}
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
AddStreamAsInput(Stream::kAudio, i, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
}
SetValid();
@@ -180,65 +156,109 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
}
}
QString Footage::DescribeStream(int index) const
{
Stream stream = GetStreamAt(index);
switch (stream.type()) {
case Stream::kVideo:
if (stream.video_type() == Stream::kVideoTypeStill) {
return tr("%1: Image - %2x%3").arg(QString::number(index),
QString::number(stream.width()),
QString::number(stream.height()));
} else {
return tr("%1: Video - %2x%3").arg(QString::number(index),
QString::number(stream.width()),
QString::number(stream.height()));
}
case Stream::kAudio:
return QCoreApplication::translate("Stream", "%1: Audio - %2 Channel(s), %3Hz")
.arg(QString::number(index),
QString::number(stream.channel_count()),
QString::number(stream.sample_rate()));
case Stream::kUnknown:
case Stream::kData:
case Stream::kSubtitle:
case Stream::kAttachment:
break;
}
return tr("%1: Unknown").arg(QString::number(index));
}
Footage::StreamReference Footage::GetReferenceFromOutput(const QString &s) const
{
Stream::Type type = GetTypeFromOutput(s);
Stream::Type type;
int index;
if (type != Stream::kUnknown) {
bool ok;
int index = s.mid(2).toInt(&ok);
if (ok) {
return StreamReference(this, type, index);
}
if (GetReferenceFromOutput(s, &type, &index)) {
return StreamReference(type, index);
} else {
return StreamReference();
}
return StreamReference();
}
int Footage::GetStreamTypeCount(Stream::Type type) const
bool Footage::GetReferenceFromOutput(const QString &s, Stream::Type *type, int *index)
{
int count = 0;
Stream::Type parse_type = GetTypeFromOutput(s);
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
Stream s = GetStreamAt(it.key());
if (parse_type != Stream::kUnknown) {
bool ok;
int parse_index = s.mid(2).toInt(&ok);
if (s.type() == type) {
count++;
if (ok) {
*type = parse_type;
*index = parse_index;
return true;
}
}
return count;
return false;
}
VideoParams Footage::GetFirstEnabledVideoStream() const
{
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key().type() == Stream::kVideo) {
VideoParams vp = GetVideoParams(it.key().index());
if (vp.enabled()) {
return vp;
}
}
}
return VideoParams();
}
AudioParams Footage::GetFirstEnabledAudioStream() const
{
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key().type() == Stream::kAudio) {
AudioParams ap = GetAudioParams(it.key().index());
if (ap.enabled()) {
return ap;
}
}
}
return AudioParams();
}
QVector<VideoParams> Footage::GetEnabledVideoStreams() const
{
QVector<VideoParams> list;
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key().type() == Stream::kVideo) {
VideoParams vp = GetVideoParams(it.key().index());
if (vp.enabled()) {
list.append(vp);
}
}
}
return list;
}
QVector<AudioParams> Footage::GetEnabledAudioStreams() const
{
QVector<AudioParams> list;
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key().type() == Stream::kAudio) {
AudioParams ap = GetAudioParams(it.key().index());
if (ap.enabled()) {
list.append(ap);
}
}
}
return list;
}
QVector<Footage::StreamReference> Footage::GetEnabledStreamsAsReferences() const
{
QVector<Footage::StreamReference> refs;
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
refs.append(StreamReference(it.key().type(), it.key().index()));
}
return refs;
}
void Footage::Clear()
@@ -255,9 +275,6 @@ void Footage::Clear()
}
outputs_for_streams_.clear();
// Reset stream count
stream_count_ = 0;
// Clear decoder link
decoder_.clear();
@@ -290,36 +307,6 @@ void Footage::set_timestamp(const qint64 &t)
timestamp_ = t;
}
int64_t Footage::GetTimeInTimebaseUnits(int index, const rational &time) const
{
Stream s = GetStreamAt(index);
if (!s.IsValid()) {
return AV_NOPTS_VALUE;
}
return Timecode::time_to_timestamp(time, s.timebase()) + s.start_time();
}
int Footage::GetRealStreamIndex(Stream::Type type, int index) const
{
int lookup_index = 0;
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
Stream stream = GetStandardValue(it.value()).value<Stream>();
if (stream.type() == type) {
if (lookup_index == index) {
return it.key();
} else {
lookup_index++;
}
}
}
return -1;
}
QString Footage::GetStringFromReference(Stream::Type type, int index)
{
QString type_string;
@@ -335,32 +322,18 @@ QString Footage::GetStringFromReference(Stream::Type type, int index)
return QStringLiteral("%1:%2").arg(type_string, QString::number(index));
}
Footage::StreamReference Footage::GetReferenceFromRealIndex(int real_index) const
int Footage::GetStreamIndex(Stream::Type type, int index) const
{
Stream s = GetStreamAt(real_index);
if (!s.IsValid()) {
// Return invalid/null reference
return StreamReference();
if (type == Stream::kVideo) {
return GetVideoParams(index).stream_index();
} else if (type == Stream::kAudio) {
return GetAudioParams(index).stream_index();
} else {
return -1;
}
int index_in_type = 0;
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key() == real_index) {
break;
} else {
Stream temp = GetStreamAt(it.key());
if (temp.type() == s.type()) {
index_in_type++;
}
}
}
return StreamReference(this, s.type(), index_in_type);
}
Stream::Type Footage::GetTypeFromOutput(const QString &s) const
Stream::Type Footage::GetTypeFromOutput(const QString &s)
{
if (s.at(1) == ':') {
if (s.at(0) == 'v') {
@@ -375,55 +348,22 @@ Stream::Type Footage::GetTypeFromOutput(const QString &s) const
return Stream::kUnknown;
}
Stream Footage::GetFirstEnabledStreamOfType(Stream::Type type) const
{
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
Stream stream = GetStandardValue(it.value()).value<Stream>();
if (stream.enabled() && stream.type() == type) {
return stream;
}
}
return Stream();
}
QVector<int> Footage::GetStreamIndexesOfType(Stream::Type type) const
{
QVector<int> indexes;
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
Stream stream = GetStandardValue(it.value()).value<Stream>();
if (stream.enabled() && stream.type() == type) {
indexes.append(it.key());
}
}
return indexes;
}
const QString &Footage::decoder() const
{
return decoder_;
}
void Footage::set_decoder(const QString &id)
{
decoder_ = id;
}
QIcon Footage::icon() const
{
if (valid_ && !inputs_for_stream_properties_.isEmpty()) {
// Prioritize video > audio > image
Stream s = GetFirstEnabledStreamOfType(Stream::kVideo);
VideoParams s = GetFirstEnabledVideoStream();
if (s.IsValid() && s.video_type() != Stream::kVideoTypeStill) {
if (s.is_valid() && s.video_type() != VideoParams::kVideoTypeStill) {
return icon::Video;
} else if (HasEnabledStreamsOfType(Stream::kAudio)) {
} else if (HasEnabledAudioStreams()) {
return icon::Audio;
} else if (s.IsValid() && s.video_type() == Stream::kVideoTypeStill) {
} else if (s.is_valid() && s.video_type() == VideoParams::kVideoTypeStill) {
return icon::Image;
}
}
@@ -433,53 +373,40 @@ QIcon Footage::icon() const
QString Footage::duration()
{
// Find longest stream duration
Stream longest_stream;
rational longest;
// Try video first
VideoParams video = GetFirstEnabledVideoStream();
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
Stream s = GetStandardValue(it.value()).value<Stream>();
if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) {
int64_t duration = video.duration();
rational frame_rate_timebase = video.frame_rate().flipped();
if (s.enabled() && (s.type() == Stream::kVideo || s.type() == Stream::kAudio)) {
rational this_stream_dur = Timecode::timestamp_to_time(s.duration(), s.timebase());
if (this_stream_dur > longest) {
longest_stream = s;
longest = this_stream_dur;
}
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());
}
if (longest_stream.IsValid()) {
if (longest_stream.type() == Stream::kVideo) {
if (longest_stream.video_type() != Stream::kVideoTypeStill) {
int64_t duration = longest_stream.duration();
rational frame_rate_timebase = longest_stream.frame_rate().flipped();
// Try audio second
AudioParams audio = GetFirstEnabledAudioStream();
if (longest_stream.timebase() != frame_rate_timebase) {
// Convert from timebase to frame rate
rational duration_time = Timecode::timestamp_to_time(duration, longest_stream.timebase());
duration = Timecode::time_to_timestamp(duration_time, frame_rate_timebase);
}
return Timecode::timestamp_to_timecode(duration,
frame_rate_timebase,
Core::instance()->GetTimecodeDisplay());
}
} else if (longest_stream.type() == Stream::kAudio) {
// 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(longest_stream.duration(),
longest_stream.timebase(),
display);
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();
}
@@ -489,47 +416,51 @@ QString Footage::rate()
return QString();
}
if (HasEnabledStreamsOfType(Stream::kVideo)) {
if (HasEnabledVideoStreams()) {
// This is a video editor, prioritize video streams
Stream video_stream = GetFirstEnabledStreamOfType(Stream::kVideo);
VideoParams video_stream = GetFirstEnabledVideoStream();
if (video_stream.video_type() != Stream::kVideoTypeStill) {
if (video_stream.video_type() != VideoParams::kVideoTypeStill) {
return QCoreApplication::translate("Footage", "%1 FPS").arg(video_stream.frame_rate().toDouble());
}
} else if (HasEnabledStreamsOfType(Stream::kAudio)) {
} else if (HasEnabledAudioStreams()) {
// No video streams, return audio
Stream audio_stream = GetStreamAt(0);
AudioParams audio_stream = GetFirstEnabledAudioStream();
return QCoreApplication::translate("Footage", "%1 Hz").arg(audio_stream.sample_rate());
}
return QString();
}
quint64 Footage::get_enabled_stream_flags() const
bool Footage::HasEnabledVideoStreams() const
{
quint64 enabled_streams = 0;
for (int i=0; i<stream_count_; i++) {
if (IsStreamEnabled(i)) {
enabled_streams |= (1 << i);
}
}
return enabled_streams;
return GetFirstEnabledVideoStream().is_valid();
}
bool Footage::HasEnabledStreamsOfType(const Stream::Type &type) const
bool Footage::HasEnabledAudioStreams() const
{
// Return true if any streams are video streams
for (int i=0; i<stream_count_; i++) {
Stream s = GetStreamAt(i);
return GetFirstEnabledAudioStream().is_valid();
}
if (s.enabled() && s.type() == type) {
return true;
}
QString Footage::DescribeVideoStream(const VideoParams &params)
{
if (params.video_type() == VideoParams::kVideoTypeStill) {
return tr("%1: Image - %2x%3").arg(QString::number(params.stream_index()),
QString::number(params.width()),
QString::number(params.height()));
} else {
return tr("%1: Video - %2x%3").arg(QString::number(params.stream_index()),
QString::number(params.width()),
QString::number(params.height()));
}
}
return false;
QString Footage::DescribeAudioStream(const AudioParams &params)
{
return tr("%1: Audio - %2 Channel(s), %3Hz")
.arg(QString::number(params.stream_index()),
QString::number(params.channel_count()),
QString::number(params.sample_rate()));
}
bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
@@ -552,6 +483,7 @@ bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
return true;
}
}*/
Q_UNUSED(footage)
// Simplified, since our footage node is much more tolerant, we'll try this
return true;
@@ -576,9 +508,9 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
QString fn = filename();
if (!fn.isEmpty()) {
Stream stream = GetStreamAt(GetReferenceFromOutput(output));
VideoParams params = GetVideoParams(ref.index());
if (stream.IsValid()) {
if (params.is_valid()) {
// Add footage details to hash
// Footage filename
@@ -593,23 +525,23 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
if (ref.type() == Stream::kVideo) {
// Current color config and space
hash.addData(project()->color_manager()->GetConfigFilename().toUtf8());
hash.addData(stream.colorspace().toUtf8());
hash.addData(params.colorspace().toUtf8());
// Alpha associated setting
hash.addData(QString::number(stream.premultiplied_alpha()).toUtf8());
hash.addData(QString::number(params.premultiplied_alpha()).toUtf8());
// Pixel aspect ratio
hash.addData(reinterpret_cast<const char*>(&stream.pixel_aspect_ratio()), sizeof(stream.pixel_aspect_ratio()));
hash.addData(reinterpret_cast<const char*>(&params.pixel_aspect_ratio()), sizeof(params.pixel_aspect_ratio()));
// Footage timestamp
if (stream.video_type() != Stream::kVideoTypeStill) {
int64_t video_ts = Timecode::time_to_timestamp(time, stream.timebase());
if (params.video_type() != VideoParams::kVideoTypeStill) {
int64_t video_ts = Timecode::time_to_timestamp(time, params.time_base());
// Add timestamp in units of the video stream's timebase
hash.addData(reinterpret_cast<const char*>(&video_ts), sizeof(int64_t));
// Add start time - used for both image sequences and video streams
hash.addData(QString::number(stream.start_time()).toUtf8());
hash.addData(QString::number(params.start_time()).toUtf8());
}
}
}
@@ -627,8 +559,23 @@ NodeValueTable Footage::Value(const QString &output, NodeValueDatabase &value) c
NodeValueTable table = value.Merge();
// If the file exists and the reference is valid, push a footage job to the renderer
if (QFileInfo(file).exists() && ref.IsValid()) {
table.Push(NodeValue::kFootageJob, QVariant::fromValue(ref), this);
if (QFileInfo(file).exists()) {
FootageJob job(decoder_, filename(), ref.type());
if (ref.type() == Stream::kVideo) {
VideoParams vp = GetVideoParams(ref.index());
if (vp.colorspace().isEmpty()) {
vp.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace());
}
job.set_video_params(vp);
} else {
job.set_audio_params(GetAudioParams(ref.index()));
job.set_cache_path(project()->cache_path());
}
table.Push(NodeValue::kFootageJob, QVariant::fromValue(job), this);
}
return table;
@@ -659,13 +606,20 @@ void Footage::UpdateTooltip()
if (valid_) {
QString tip = QCoreApplication::translate("Footage", "Filename: %1").arg(filename());
if (!inputs_for_stream_properties_.isEmpty()) {
for (int i=0; i<stream_count_; i++) {
Stream s = GetStreamAt(i);
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key().type() == Stream::kVideo) {
VideoParams p = GetVideoParams(it.key().index());
if (s.enabled()) {
if (p.enabled()) {
tip.append("\n");
tip.append(DescribeStream(i));
tip.append(DescribeVideoStream(p));
}
} else if (it.key().type() == Stream::kAudio) {
AudioParams p = GetAudioParams(it.key().index());
if (p.enabled()) {
tip.append("\n");
tip.append(DescribeAudioStream(p));
}
}
}
@@ -676,77 +630,22 @@ void Footage::UpdateTooltip()
}
}
Footage::MetadataCache Footage::LoadStreamCache(const QString &filename)
void Footage::AddStreamAsInput(Stream::Type type, int index, QVariant value)
{
MetadataCache cache;
QFile file(filename);
QString input_id = GetInputIDOfIndex(type, index);
if (file.open(QFile::ReadOnly)) {
QXmlStreamReader reader(&file);
StreamReference ref(type, index);
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("streamcache")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("decoder")) {
cache.decoder = reader.readElementText();
} else if (reader.name() == QStringLiteral("streams")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("stream")) {
Stream s;
s.Load(&reader);
cache.streams.append(s);
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
// Create input for parameters
AddInput(input_id, type == Stream::kVideo ? NodeValue::kVideoParams : NodeValue::kAudioParams,
InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetStandardValue(input_id, value);
inputs_for_stream_properties_.insert(ref, input_id);
file.close();
}
return cache;
}
bool Footage::SaveStreamCache(const QString &filename, const Footage::MetadataCache &data)
{
QFile file(filename);
if (!file.open(QFile::WriteOnly)) {
return false;
}
QXmlStreamWriter writer(&file);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("streamcache"));
writer.writeTextElement(QStringLiteral("decoder"), data.decoder);
writer.writeStartElement(QStringLiteral("streams"));
foreach (const Stream& s, data.streams) {
writer.writeStartElement(QStringLiteral("stream"));
s.Save(&writer);
writer.writeEndElement(); // stream
}
writer.writeEndElement(); // streams
writer.writeEndElement(); // streamcache
writer.writeEndDocument();
file.close();
return true;
// Create output for stream
QString output_id = GetStringFromReference(type, index);
AddOutput(output_id);
outputs_for_streams_.insert(ref, output_id);
}
void Footage::CheckFootage()
@@ -766,26 +665,45 @@ void Footage::CheckFootage()
}
}
QString Footage::StreamReference::video_colorspace(bool default_if_empty) const
/*QString Footage::StreamReference::video_colorspace(bool default_if_empty) const
{
if (IsValid()) {
Stream stream = footage_->GetStreamAt(type_, index_);
VideoParams params = footage_->GetVideoParams(index_);
if (params.is_valid()) {
if (params.colorspace().isEmpty() && default_if_empty) {
if (stream.IsValid()) {
if (stream.colorspace().isEmpty() && default_if_empty) {
return footage_->project()->color_manager()->GetDefaultInputColorSpace();
} else {
return stream.colorspace();
return params.colorspace();
}
}
}
return QString();
}
}*/
uint qHash(const Footage::StreamReference &ref, uint seed)
{
return qHash(ref.footage(), seed) ^ qHash(ref.type(), seed) ^ qHash(ref.index(), seed);
return qHash(ref.type(), seed) ^ qHash(ref.index(), seed);
}
QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref)
{
out << static_cast<int>(ref.type()) << ref.index();
return out;
}
QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref)
{
int type;
int index;
in >> type >> index;
ref = Footage::StreamReference(static_cast<Stream::Type>(type), index);
return in;
}
}
+63 -145
View File
@@ -25,8 +25,11 @@
#include <QDateTime>
#include "common/rational.h"
#include "footagedescription.h"
#include "node/node.h"
#include "project/item/item.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
#include "stream.h"
#include "timeline/timelinepoints.h"
@@ -143,26 +146,33 @@ public:
public:
StreamReference()
{
footage_ = nullptr;
type_ = Stream::kUnknown;
index_ = -1;
}
StreamReference(const Footage* footage, Stream::Type type, int index)
StreamReference(Stream::Type type, int index)
{
footage_ = footage;
type_ = type;
index_ = index;
}
bool operator==(const StreamReference& rhs) const
{
return footage_ == rhs.footage_ && type_ == rhs.type_ && index_ == rhs.index_;
return type_ == rhs.type_ && index_ == rhs.index_;
}
bool operator<(const StreamReference& rhs) const
{
if (type_ != rhs.type_) {
return type_ < rhs.type_;
}
return index_ < rhs.index_;
}
bool IsValid() const
{
return footage_ && index_ >= 0;
return type_ != Stream::kUnknown && index_ >= 0;
}
void Reset()
@@ -170,11 +180,6 @@ public:
*this = StreamReference();
}
const Footage* footage() const
{
return footage_;
}
Stream::Type type() const
{
return type_;
@@ -185,138 +190,68 @@ public:
return index_;
}
Stream GetStream() const
{
if (IsValid()) {
return footage_->GetStreamAt(*this);
} else {
return Stream();
}
}
int64_t GetTimeInTimebaseUnits(const rational& timecode) const
{
if (IsValid()) {
return footage_->GetTimeInTimebaseUnits(type_, index_, timecode);
} else {
return -1;
}
}
int GetRealStreamIndex() const
{
if (IsValid()) {
return footage_->GetRealStreamIndex(*this);
} else {
return -1;
}
}
QString filename() const
{
if (footage_) {
return footage_->filename();
} else {
return QString();
}
}
VideoParams video_params() const
{
return GetStream().video_params();
}
AudioParams audio_params() const
{
return GetStream().audio_params();
}
int64_t duration() const
{
return GetStream().duration();
}
QString video_colorspace(bool default_if_empty = true) const;
private:
const Footage* footage_;
Stream::Type type_;
int index_;
};
Stream GetStreamAt(int index) const
{
return GetStandardValue(GetInputIDOfIndex(index)).value<Stream>();
}
Stream GetStreamAt(Stream::Type type, int index_within_type) const
{
return GetStreamAt(GetRealStreamIndex(type, index_within_type));
}
Stream GetStreamAt(const StreamReference& ref) const
{
return GetStreamAt(ref.type(), ref.index());
}
void SetStreamAt(int index, const Stream& stream)
{
SetStandardValue(GetInputIDOfIndex(index), QVariant::fromValue(stream));
}
void SetStreamAt(Stream::Type type, int index_within_type, const Stream& stream)
{
SetStreamAt(GetRealStreamIndex(type, index_within_type), stream);
}
void SetStreamAt(const StreamReference& ref, const Stream& stream)
{
SetStreamAt(ref.type(), ref.index(), stream);
}
int64_t GetTimeInTimebaseUnits(int index, const rational& time) const;
int64_t GetTimeInTimebaseUnits(Stream::Type type, int index_within_type, const rational& time) const
{
return GetTimeInTimebaseUnits(GetRealStreamIndex(type, index_within_type), time);
}
int GetRealStreamIndex(Stream::Type type, int index_within_type) const;
int GetRealStreamIndex(const StreamReference& ref) const
{
return GetRealStreamIndex(ref.type(), ref.index());
}
static QString GetStringFromReference(Stream::Type type, int index);
static QString GetStringFromReference(const StreamReference& ref)
{
return GetStringFromReference(ref.type(), ref.index());
}
int GetStreamIndex(Stream::Type type, int index) const;
int GetStreamIndex(const StreamReference& ref) const
{
return GetStreamIndex(ref.type(), ref.index());
}
int GetTotalStreamCount() const
{
return inputs_for_stream_properties_.size();
}
StreamReference GetReferenceFromRealIndex(int real_index) const;
Stream::Type GetTypeFromOutput(const QString& output) const;
static Stream::Type GetTypeFromOutput(const QString& output);
StreamReference GetReferenceFromOutput(const QString& s) const;
static bool GetReferenceFromOutput(const QString& s, Stream::Type* type, int* index);
int GetStreamCount() const
VideoParams GetVideoParams(int index) const
{
return stream_count_;
return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index))).value<VideoParams>();
}
int GetStreamTypeCount(Stream::Type type) const;
bool IsStreamEnabled(int index) const
void SetVideoParams(int index, const VideoParams& p)
{
return GetStreamAt(index).enabled();
SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index)), QVariant::fromValue(p));
}
Stream GetFirstEnabledStreamOfType(Stream::Type type) const;
VideoParams GetFirstEnabledVideoStream() const;
QVector<int> GetStreamIndexesOfType(Stream::Type type) const;
AudioParams GetAudioParams(int index) const
{
return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index))).value<AudioParams>();
}
void SetAudioParams(int index, const AudioParams& p)
{
SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index)), QVariant::fromValue(p));
}
AudioParams GetFirstEnabledAudioStream() const;
QVector<VideoParams> GetEnabledVideoStreams() const;
QVector<AudioParams> GetEnabledAudioStreams() const;
Stream::Type GetStreamType(int index);
QVector<StreamReference> GetEnabledStreamsAsReferences() const;
/**
* @brief Get the Decoder ID set when this Footage was probed
*
@@ -326,27 +261,17 @@ public:
*/
const QString& decoder() const;
/**
* @brief Used by decoders when they Probe to attach itself to this Footage
*/
void set_decoder(const QString& id);
virtual QIcon icon() const override;
virtual QString duration() override;
virtual QString rate() override;
quint64 get_enabled_stream_flags() const;
bool HasEnabledVideoStreams() const;
bool HasEnabledAudioStreams() const;
/**
* @brief Check if this footage has streams of a certain type
*
* @param type
*
* The stream type to check for
*/
bool HasEnabledStreamsOfType(const Stream::Type& type) const;
static QString DescribeVideoStream(const VideoParams& params);
static QString DescribeAudioStream(const AudioParams& params);
static bool CompareFootageToFile(Footage* footage, const QString& filename);
static bool CompareFootageToItsFilename(Footage* footage);
@@ -374,13 +299,6 @@ protected:
virtual void InputValueChangedEvent(const QString &input, int element) override;
private:
QString DescribeStream(int index) const;
struct MetadataCache {
QString decoder;
Streams streams;
};
/**
* @brief Update the icon based on the Footage status
*
@@ -397,24 +315,22 @@ private:
*/
void UpdateTooltip();
MetadataCache LoadStreamCache(const QString& filename);
void AddStreamAsInput(Stream::Type type, int index, QVariant value);
bool SaveStreamCache(const QString& filename, const MetadataCache& data);
static QString GetInputIDOfIndex(int index)
static QString GetInputIDOfIndex(Stream::Type type, int index)
{
return kStreamPropertiesFormat.arg(index);
return kStreamPropertiesFormat.arg(GetStringFromReference(type, index));
}
/**
* @brief List of dynamic inputs added for stream properties
*/
QMap<int, QString> inputs_for_stream_properties_;
QMap<StreamReference, QString> inputs_for_stream_properties_;
/**
* @brief List of dynamic outputs added for streams
*/
QMap<int, QString> outputs_for_streams_;
QMap<StreamReference, QString> outputs_for_streams_;
/**
* @brief Internal timestamp object
@@ -426,8 +342,6 @@ private:
*/
QString decoder_;
int stream_count_;
bool valid_;
const QAtomicInt* cancelled_;
@@ -439,6 +353,10 @@ private slots:
uint qHash(const Footage::StreamReference& ref, uint seed = 0);
QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref);
QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref);
}
Q_DECLARE_METATYPE(olive::Footage::StreamReference)
@@ -0,0 +1,118 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "footagedescription.h"
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "common/xmlutils.h"
namespace olive {
bool FootageDescription::Load(const QString &filename)
{
// Reset self
*this = FootageDescription();
QFile file(filename);
if (file.open(QFile::ReadOnly)) {
QXmlStreamReader reader(&file);
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("streamcache")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("decoder")) {
decoder_ = reader.readElementText();
} else if (reader.name() == QStringLiteral("streams")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("video")) {
VideoParams vp;
vp.Load(&reader);
AddVideoStream(vp);
} else if (reader.name() == QStringLiteral("audio")) {
AudioParams ap;
ap.Load(&reader);
AddAudioStream(ap);
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
file.close();
return true;
}
return false;
}
bool FootageDescription::Save(const QString &filename) const
{
QFile file(filename);
if (!file.open(QFile::WriteOnly)) {
return false;
}
QXmlStreamWriter writer(&file);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("streamcache"));
writer.writeTextElement(QStringLiteral("decoder"), decoder_);
writer.writeStartElement(QStringLiteral("streams"));
foreach (const VideoParams& vp, video_streams_) {
writer.writeStartElement(QStringLiteral("video"));
vp.Save(&writer);
writer.writeEndElement(); // video
}
foreach (const AudioParams& ap, audio_streams_) {
writer.writeStartElement(QStringLiteral("audio"));
ap.Save(&writer);
writer.writeEndElement(); // audio
}
writer.writeEndElement(); // streams
writer.writeEndElement(); // streamcache
writer.writeEndDocument();
file.close();
return true;
}
}
@@ -0,0 +1,125 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef FOOTAGEDESCRIPTION_H
#define FOOTAGEDESCRIPTION_H
#include "render/audioparams.h"
#include "render/videoparams.h"
#include "stream.h"
namespace olive {
class FootageDescription
{
public:
FootageDescription(const QString& decoder = QString()) :
decoder_(decoder)
{
}
bool IsValid() const
{
return !decoder_.isEmpty() && (!video_streams_.isEmpty() || !audio_streams_.isEmpty());
}
const QString& decoder() const
{
return decoder_;
}
void AddVideoStream(const VideoParams& video_params)
{
Q_ASSERT(!HasStreamIndex(video_params.stream_index()));
video_streams_.append(video_params);
}
void AddAudioStream(const AudioParams& audio_params)
{
Q_ASSERT(!HasStreamIndex(audio_params.stream_index()));
audio_streams_.append(audio_params);
}
Stream::Type GetTypeOfStream(int index)
{
if (StreamIsVideo(index)) {
return Stream::kVideo;
} else if (StreamIsAudio(index)) {
return Stream::kAudio;
} else {
return Stream::kUnknown;
}
}
bool StreamIsVideo(int index) const
{
foreach (const VideoParams& vp, video_streams_) {
if (vp.stream_index() == index) {
return true;
}
}
return false;
}
bool StreamIsAudio(int index) const
{
foreach (const AudioParams& ap, audio_streams_) {
if (ap.stream_index() == index) {
return true;
}
}
return false;
}
bool HasStreamIndex(int index) const
{
return StreamIsVideo(index) || StreamIsAudio(index);
}
bool Load(const QString& filename);
bool Save(const QString& filename) const;
const QVector<VideoParams>& GetVideoStreams() const
{
return video_streams_;
}
const QVector<AudioParams>& GetAudioStreams() const
{
return audio_streams_;
}
private:
QString decoder_;
QVector<VideoParams> video_streams_;
QVector<AudioParams> audio_streams_;
};
}
#endif // FOOTAGEDESCRIPTION_H
-131
View File
@@ -1,131 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "stream.h"
#include "common/xmlutils.h"
namespace olive {
void Stream::Load(QXmlStreamReader *reader)
{
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("type")) {
*this = Stream(static_cast<Type>(attr.value().toInt()));
break;
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("global")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("timebase")) {
timebase_ = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("duration")) {
duration_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("channelcount")) {
channel_count_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("enabled")) {
enabled_ = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("video")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("width")) {
width_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("height")) {
height_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixelaspectratio")) {
pixel_aspect_ratio_ = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("videotype")) {
video_type_ = static_cast<VideoType>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("interlacing")) {
interlacing_ = static_cast<VideoParams::Interlacing>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("pixelformat")) {
pixel_format_ = static_cast<VideoParams::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("framerate")) {
frame_rate_ = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("starttime")) {
start_time_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("premultipliedalpha")) {
premultiplied_alpha_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("colorspace")) {
colorspace_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("audio")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("samplerate")) {
sample_rate_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("channellayout")) {
channel_layout_ = reader->readElementText().toULongLong();
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
}
void Stream::Save(QXmlStreamWriter *writer) const
{
writer->writeAttribute(QStringLiteral("type"), QString::number(type_));
writer->writeStartElement(QStringLiteral("global"));
writer->writeTextElement(QStringLiteral("timebase"), timebase_.toString());
writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_));
writer->writeTextElement(QStringLiteral("channelcount"), QString::number(channel_count_));
writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_));
writer->writeEndElement(); // global
writer->writeStartElement(QStringLiteral("video"));
writer->writeTextElement(QStringLiteral("width"), QString::number(width_));
writer->writeTextElement(QStringLiteral("height"), QString::number(height_));
writer->writeTextElement(QStringLiteral("pixelaspectratio"), pixel_aspect_ratio_.toString());
writer->writeTextElement(QStringLiteral("videotype"), QString::number(video_type_));
writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_));
writer->writeTextElement(QStringLiteral("pixelformat"), QString::number(pixel_format_));
writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString());
writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_));
writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_));
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
writer->writeEndElement(); // video
writer->writeStartElement(QStringLiteral("audio"));
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_));
writer->writeTextElement(QStringLiteral("channellayout"), QString::number(channel_layout_));
writer->writeEndElement(); // audio
}
}
+2 -285
View File
@@ -21,17 +21,10 @@
#ifndef STREAM_H
#define STREAM_H
#include <QVector>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "common/rational.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
namespace olive {
class Stream {
class Stream
{
public:
enum Type {
kUnknown = -1,
@@ -41,284 +34,8 @@ public:
kSubtitle,
kAttachment
};
enum VideoType {
kVideoTypeVideo,
kVideoTypeStill,
kVideoTypeImageSequence
};
Stream(Type type = kUnknown) :
type_(type)
{
Init();
}
bool IsValid() const
{
return type_ != kUnknown;
}
Type type() const
{
return type_;
}
const rational& timebase() const
{
return timebase_;
}
void set_timebase(const rational& timebase)
{
timebase_ = timebase;
}
int64_t duration() const
{
return duration_;
}
void set_duration(int64_t duration)
{
duration_ = duration;
}
int channel_count() const
{
return channel_count_;
}
void set_channel_count(int c)
{
channel_count_ = c;
}
bool enabled() const
{
return enabled_;
}
void set_enabled(bool e)
{
enabled_ = e;
}
int width() const
{
return width_;
}
void set_width(int w)
{
width_ = w;
}
int height() const
{
return height_;
}
void set_height(int h)
{
height_ = h;
}
const rational& pixel_aspect_ratio() const
{
return pixel_aspect_ratio_;
}
void set_pixel_aspect_ratio(const rational& pixel_aspect_ratio)
{
pixel_aspect_ratio_ = pixel_aspect_ratio;
}
VideoType video_type() const
{
return video_type_;
}
void set_video_type(VideoType t)
{
video_type_ = t;
}
VideoParams::Interlacing interlacing() const
{
return interlacing_;
}
void set_interlacing(VideoParams::Interlacing interlacing)
{
interlacing_ = interlacing;
}
VideoParams::Format pixel_format() const
{
return pixel_format_;
}
void set_pixel_format(VideoParams::Format pixel_format)
{
pixel_format_ = pixel_format;
}
const rational& frame_rate() const
{
return frame_rate_;
}
void set_frame_rate(const rational& frame_rate)
{
frame_rate_ = frame_rate;
}
int64_t start_time() const
{
return start_time_;
}
void set_start_time(int64_t start_time)
{
start_time_ = start_time;
}
bool premultiplied_alpha() const
{
return premultiplied_alpha_;
}
void set_premultiplied_alpha(bool premultiplied_alpha)
{
premultiplied_alpha_ = premultiplied_alpha;
}
const QString& colorspace() const
{
return colorspace_;
}
void set_colorspace(const QString& c)
{
colorspace_ = c;
}
int sample_rate() const
{
return sample_rate_;
}
void set_sample_rate(int sample_rate)
{
sample_rate_ = sample_rate;
}
uint64_t channel_layout() const
{
return channel_layout_;
}
void set_channel_layout(uint64_t channel_layout)
{
channel_layout_ = channel_layout;
}
VideoParams video_params() const
{
if (type_ == kVideo) {
return VideoParams(width_, height_, timebase_,
pixel_format_, channel_count_, pixel_aspect_ratio_,
interlacing_);
} else {
return VideoParams();
}
}
AudioParams audio_params() const
{
if (type_ == kAudio) {
return AudioParams(sample_rate_, channel_layout_, AudioParams::kInternalFormat);
} else {
return AudioParams();
}
}
void Load(QXmlStreamReader* reader);
void Save(QXmlStreamWriter* writer) const;
QByteArray toBytes() const
{
QByteArray arr;
arr.append(reinterpret_cast<const char*>(&type_), sizeof(type_));
arr.append(reinterpret_cast<const char*>(&timebase_), sizeof(timebase_));
arr.append(reinterpret_cast<const char*>(&duration_), sizeof(duration_));
arr.append(reinterpret_cast<const char*>(&channel_count_), sizeof(channel_count_));
arr.append(reinterpret_cast<const char*>(&enabled_), sizeof(enabled_));
arr.append(reinterpret_cast<const char*>(&width_), sizeof(width_));
arr.append(reinterpret_cast<const char*>(&height_), sizeof(height_));
arr.append(reinterpret_cast<const char*>(&pixel_aspect_ratio_), sizeof(pixel_aspect_ratio_));
arr.append(reinterpret_cast<const char*>(&video_type_), sizeof(video_type_));
arr.append(reinterpret_cast<const char*>(&interlacing_), sizeof(interlacing_));
arr.append(reinterpret_cast<const char*>(&pixel_format_), sizeof(pixel_format_));
arr.append(reinterpret_cast<const char*>(&frame_rate_), sizeof(frame_rate_));
arr.append(reinterpret_cast<const char*>(&start_time_), sizeof(start_time_));
arr.append(reinterpret_cast<const char*>(&premultiplied_alpha_), sizeof(premultiplied_alpha_));
arr.append(colorspace_.toUtf8());
arr.append(reinterpret_cast<const char*>(&sample_rate_), sizeof(sample_rate_));
arr.append(reinterpret_cast<const char*>(&channel_layout_), sizeof(channel_layout_));
return arr;
}
private:
void Init()
{
duration_ = AV_NOPTS_VALUE;
channel_count_ = 0;
enabled_ = true;
width_ = 0;
height_ = 0;
video_type_ = VideoType::kVideoTypeVideo;
interlacing_ = VideoParams::kInterlaceNone;
pixel_format_ = VideoParams::kFormatInvalid;
start_time_ = 0;
premultiplied_alpha_ = false;
sample_rate_ = 0;
channel_layout_ = 0;
}
// Global members
Type type_;
rational timebase_;
int64_t duration_;
int channel_count_;
bool enabled_;
// Video members
int width_;
int height_;
rational pixel_aspect_ratio_;
VideoType video_type_;
VideoParams::Interlacing interlacing_;
VideoParams::Format pixel_format_;
rational frame_rate_;
int64_t start_time_;
bool premultiplied_alpha_;
QString colorspace_;
// Audio members
int sample_rate_;
uint64_t channel_layout_;
};
using Streams = QVector<Stream>;
}
Q_DECLARE_METATYPE(olive::Stream)
#endif // STREAM_H
+2
View File
@@ -34,6 +34,8 @@ Item::Item(bool create_folder_input, bool create_default_output) :
if (create_folder_input) {
// Hierarchy input for items
AddInput(kParentInput, NodeValue::kNone);
IgnoreHashingFrom(kParentInput);
IgnoreInvalidationsFrom(kParentInput);
}
}
+80 -95
View File
@@ -37,6 +37,8 @@
namespace olive {
const QString Sequence::kVideoParamsInput = QStringLiteral("video_param_in");
const QString Sequence::kAudioParamsInput = QStringLiteral("audio_param_in");
const QString Sequence::kTextureInput = QStringLiteral("tex_in");
const QString Sequence::kSamplesInput = QStringLiteral("samples_in");
const QString Sequence::kTrackInputFormat = QStringLiteral("track_in_%1");
@@ -49,8 +51,10 @@ Sequence::Sequence(bool viewer_only_mode) :
audio_playback_cache_(this),
operation_stack_(0)
{
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
if (!viewer_only_mode) {
@@ -129,66 +133,42 @@ void Sequence::set_default_parameters()
void Sequence::set_parameters_from_footage(const QVector<Footage *> footage)
{
bool found_video_params = false;
bool found_audio_params = false;
foreach (Footage* f, footage) {
for (int i=0; i<f->GetStreamCount(); i++) {
if (!f->IsStreamEnabled(i)) {
continue;
QVector<VideoParams> video_streams = f->GetEnabledVideoStreams();
QVector<AudioParams> audio_streams = f->GetEnabledAudioStreams();
foreach (const VideoParams& s, video_streams) {
bool found_video_params = false;
rational using_timebase;
if (s.video_type() == VideoParams::kVideoTypeStill) {
// 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();
} else {
using_timebase = s.frame_rate().flipped();
found_video_params = true;
}
Stream s = f->GetStreamAt(i);
set_video_params(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())));
if (!s.IsValid()) {
continue;
}
switch (s.type()) {
case Stream::kVideo:
{
// If this is a video stream, use these parameters
if (!found_video_params) {
rational using_timebase;
if (s.video_type() == Stream::kVideoTypeStill) {
// 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();
} else {
using_timebase = s.frame_rate().flipped();
found_video_params = true;
}
set_video_params(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())));
}
break;
}
case Stream::kAudio:
if (!found_audio_params) {
set_audio_params(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat));
found_audio_params = true;
}
break;
case Stream::kUnknown:
case Stream::kData:
case Stream::kSubtitle:
case Stream::kAttachment:
// Ignore these types
if (found_video_params) {
break;
}
}
if (found_video_params && found_audio_params) {
return;
}
if (!audio_streams.isEmpty()) {
const AudioParams& s = audio_streams.first();
set_audio_params(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat));
}
}
}
@@ -211,8 +191,10 @@ void Sequence::Retranslate()
{
super::Retranslate();
SetInputName(kTextureInput, tr("Texture"));
SetInputName(kVideoParamsInput, tr("Video Parameters"));
SetInputName(kAudioParamsInput, tr("Audio Parameters"));
SetInputName(kTextureInput, tr("Texture"));
SetInputName(kSamplesInput, tr("Samples"));
for (int i=0;i<Track::kCount;i++) {
@@ -352,47 +334,6 @@ void Sequence::InvalidateCache(const TimeRange& range, const QString& from, int
super::InvalidateCache(range, from);
}
void Sequence::set_video_params(const VideoParams &video)
{
bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height();
bool timebase_changed = video_params_.time_base() != video.time_base();
bool pixel_aspect_changed = video_params_.pixel_aspect_ratio() != video.pixel_aspect_ratio();
bool interlacing_changed = video_params_.interlacing() != video.interlacing();
video_params_ = video;
if (size_changed) {
emit SizeChanged(video_params_.width(), video_params_.height());
}
if (pixel_aspect_changed) {
emit PixelAspectChanged(video_params_.pixel_aspect_ratio());
}
if (interlacing_changed) {
emit InterlacingChanged(video_params_.interlacing());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(video_params_.time_base());
emit TimebaseChanged(video_params_.time_base());
}
emit VideoParamsChanged();
video_frame_cache_.InvalidateAll();
}
void Sequence::set_audio_params(const AudioParams &audio)
{
audio_params_ = audio;
emit AudioParamsChanged();
// This will automatically InvalidateAll
audio_playback_cache_.SetParameters(audio_params());
}
rational Sequence::GetLength()
{
return last_length_;
@@ -479,6 +420,50 @@ void Sequence::SaveInternal(QXmlStreamWriter *writer) const
writer->writeEndElement(); // points
}
void Sequence::InputValueChangedEvent(const QString &input, int element)
{
if (input == kVideoParamsInput) {
VideoParams new_video_params = video_params();
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();
video_frame_cache_.InvalidateAll();
cached_video_params_ = video_params();
} else if (input == kAudioParamsInput) {
emit AudioParamsChanged();
// This will automatically InvalidateAll
audio_playback_cache_.SetParameters(audio_params());
}
}
void Sequence::ShiftVideoEvent(const rational &from, const rational &to)
{
Q_UNUSED(from)
+19 -8
View File
@@ -116,18 +116,25 @@ public:
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
const VideoParams& video_params() const
VideoParams video_params() const
{
return video_params_;
return GetStandardValue(kVideoParamsInput).value<VideoParams>();
}
const AudioParams& audio_params() const
AudioParams audio_params() const
{
return audio_params_;
return GetStandardValue(kAudioParamsInput).value<AudioParams>();
}
void set_video_params(const VideoParams &video);
void set_audio_params(const AudioParams &audio);
void set_video_params(const VideoParams &video)
{
SetStandardValue(kVideoParamsInput, QVariant::fromValue(video));
}
void set_audio_params(const AudioParams &audio)
{
SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio));
}
rational GetLength();
@@ -147,6 +154,9 @@ public:
virtual void EndOperation() override;
static const QString kVideoParamsInput;
static const QString kAudioParamsInput;
static const QString kTextureInput;
static const QString kSamplesInput;
static const QString kTrackInputFormat;
@@ -198,6 +208,8 @@ protected:
virtual void SaveInternal(QXmlStreamWriter *writer) const override;
virtual void InputValueChangedEvent(const QString& input, int element) override;
private:
QVector<TrackList*> track_lists_;
@@ -213,8 +225,7 @@ private:
int operation_stack_;
VideoParams video_params_;
AudioParams audio_params_;
VideoParams cached_video_params_;
TimelinePoints timeline_points_;
+10 -2
View File
@@ -83,13 +83,15 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
bool is_root = false;
QString id;
{
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
id = attr.value().toString();
break;
} else if (attr.name() == QStringLiteral("root") && attr.value() == QStringLiteral("1")) {
is_root = true;
}
}
}
@@ -97,7 +99,13 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
if (id.isEmpty()) {
qWarning() << "Failed to load node with empty ID";
} else {
Node* node = NodeFactory::CreateFromID(id);
Node* node;
if (is_root) {
node = &root_;
} else {
node = NodeFactory::CreateFromID(id);
}
if (!node) {
qWarning() << "Failed to find node with ID" << id;
+23 -21
View File
@@ -196,13 +196,17 @@ bool ProjectViewModel::setData(const QModelIndex &index, const QVariant &value,
if (index.isValid() && columns_.at(index.column()) == kName && role == Qt::EditRole) {
Item* item = GetItemObjectFromIndex(index);
NodeRenameCommand* nrc = new NodeRenameCommand();
QString new_name = value.toString();
nrc->AddNode(item, value.toString());
if (!new_name.isEmpty()) {
NodeRenameCommand* nrc = new NodeRenameCommand();
Core::instance()->undo_stack()->push(nrc);
nrc->AddNode(item, value.toString());
return true;
Core::instance()->undo_stack()->push(nrc);
return true;
}
}
return false;
@@ -238,7 +242,7 @@ Qt::ItemFlags ProjectViewModel::flags(const QModelIndex &index) const
QStringList ProjectViewModel::mimeTypes() const
{
// Allow data from this model and a file list from external sources
return {"application/x-oliveprojectitemdata", "text/uri-list"};
return {QStringLiteral("application/x-oliveprojectitemdata"), QStringLiteral("text/uri-list")};
}
QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const
@@ -264,22 +268,21 @@ QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const
// Check if we've dragged this item before
if (!dragged_items.contains(index.internalPointer())) {
// If not, add it to the stream (and also keep track of it in the vector)
quint64 stream_flags;
Footage* footage = dynamic_cast<Footage*>(static_cast<Item*>(index.internalPointer()));
if (dynamic_cast<Folder*>(static_cast<Item*>(index.internalPointer()))) {
stream_flags = static_cast<Footage*>(index.internalPointer())->get_enabled_stream_flags();
} else {
stream_flags = UINT64_MAX;
if (footage) {
QVector<Footage::StreamReference> streams = footage->GetEnabledStreamsAsReferences();
stream << streams << reinterpret_cast<quintptr>(footage);
dragged_items.append(footage);
}
stream << stream_flags << index.row() << reinterpret_cast<quintptr>(index.internalPointer());
dragged_items.append(index.internalPointer());
}
}
}
// Set byte array as the mime data and return the mime data
data->setData("application/x-oliveprojectitemdata", encoded_data);
data->setData(QStringLiteral("application/x-oliveprojectitemdata"), encoded_data);
return data;
}
@@ -298,9 +301,9 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
// Probe mime data for its format
QStringList mime_formats = data->formats();
if (mime_formats.contains("application/x-oliveprojectitemdata")) {
if (mime_formats.contains(QStringLiteral("application/x-oliveprojectitemdata"))) {
// Data is drag/drop data from this model
QByteArray model_data = data->data("application/x-oliveprojectitemdata");
QByteArray model_data = data->data(QStringLiteral("application/x-oliveprojectitemdata"));
// Use QDataStream to deserialize the data
QDataStream stream(&model_data, QIODevice::ReadOnly);
@@ -315,8 +318,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
// Variables to deserialize into
quintptr item_ptr;
int r;
quint64 enabled_streams;
QList<Footage::StreamReference> streams;
// Loop through all data
MultiUndoCommand* move_command = new MultiUndoCommand();
@@ -324,7 +326,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
move_command->set_name(tr("Move Items"));
while (!stream.atEnd()) {
stream >> enabled_streams >> r >> item_ptr;
stream >> streams >> item_ptr;
Item* item = reinterpret_cast<Item*>(item_ptr);
@@ -343,9 +345,9 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
return true;
} else if (mime_formats.contains("text/uri-list")) {
} else if (mime_formats.contains(QStringLiteral("text/uri-list"))) {
// We received a list of files
QByteArray file_data = data->data("text/uri-list");
QByteArray file_data = data->data(QStringLiteral("text/uri-list"));
// Use text stream to parse (just an easy way of sifting through line breaks
QTextStream stream(&file_data);