From 4827782331648c262ef24af88b48c761fb06271f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 18 Feb 2021 13:20:23 +1100 Subject: [PATCH] moved sequence params into node graph too Reduces code by reusing more of the existing node infrastructure to synchronize sequence parameters the render backend. --- app/codec/decoder.cpp | 5 - app/codec/decoder.h | 7 +- app/codec/ffmpeg/ffmpegdecoder.cpp | 60 +- app/codec/ffmpeg/ffmpegdecoder.h | 4 +- app/codec/oiio/oiiodecoder.cpp | 33 +- app/codec/oiio/oiiodecoder.h | 4 +- app/node/node.cpp | 32 +- app/node/traverser.cpp | 12 +- app/node/traverser.h | 5 +- app/node/value.cpp | 18 +- app/node/value.h | 12 +- app/project/item/footage/CMakeLists.txt | 3 +- app/project/item/footage/footage.cpp | 572 ++++++++---------- app/project/item/footage/footage.h | 208 ++----- .../item/footage/footagedescription.cpp | 118 ++++ app/project/item/footage/footagedescription.h | 125 ++++ app/project/item/footage/stream.cpp | 131 ---- app/project/item/footage/stream.h | 287 +-------- app/project/item/item.cpp | 2 + app/project/item/sequence/sequence.cpp | 175 +++--- app/project/item/sequence/sequence.h | 27 +- app/project/project.cpp | 12 +- app/project/projectviewmodel.cpp | 44 +- app/render/audioparams.cpp | 49 ++ app/render/audioparams.h | 87 ++- app/render/job/footagejob.h | 69 ++- app/render/opengl/openglrenderer.cpp | 4 +- app/render/previewautocacher.cpp | 61 +- app/render/previewautocacher.h | 10 +- app/render/renderprocessor.cpp | 45 +- app/render/renderprocessor.h | 4 +- app/render/stillimagecache.h | 5 +- app/render/videoparams.cpp | 115 +++- app/render/videoparams.h | 111 ++++ app/task/project/import/import.cpp | 25 +- app/task/project/load/load.cpp | 35 +- app/task/project/save/save.cpp | 8 +- .../nodeparamviewwidgetbridge.cpp | 12 +- .../nodetableview/nodetabletraverser.cpp | 4 +- app/widget/nodetableview/nodetabletraverser.h | 4 +- .../projectexplorer/projectexplorer.cpp | 9 +- app/widget/timelinewidget/tool/import.cpp | 94 ++- app/widget/timelinewidget/tool/import.h | 38 +- app/widget/viewer/footageviewer.cpp | 78 ++- app/widget/viewer/gizmotraverser.cpp | 4 +- app/widget/viewer/gizmotraverser.h | 2 +- 46 files changed, 1375 insertions(+), 1394 deletions(-) create mode 100644 app/project/item/footage/footagedescription.cpp create mode 100644 app/project/item/footage/footagedescription.h delete mode 100644 app/project/item/footage/stream.cpp diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index 7da4bd0d8..67b481700 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -235,11 +235,6 @@ int64_t Decoder::GetTimeInTimebaseUnits(const rational &time, const rational &ti return Timecode::time_to_timestamp(time, timebase) + start_time; } -Decoder::CodecStream Decoder::GetCodecStreamFromStreamReference(const Footage::StreamReference &ref) -{ - return CodecStream(ref.footage()->filename(), ref.footage()->GetRealStreamIndex(ref)); -} - void Decoder::SignalProcessingProgress(int64_t ts, int64_t duration) { if (duration != AV_NOPTS_VALUE && duration != 0) { diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 37a084450..477206a2c 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -36,6 +36,7 @@ extern "C" { #include "codec/waveoutput.h" #include "common/rational.h" #include "project/item/footage/footage.h" +#include "project/item/footage/footagedescription.h" #include "project/item/footage/stream.h" namespace olive { @@ -74,7 +75,7 @@ public: /** * @brief Unique decoder ID */ - virtual QString id() = 0; + virtual QString id() const = 0; virtual bool SupportsVideo(){return false;} virtual bool SupportsAudio(){return false;} @@ -176,7 +177,7 @@ public: * * This function is re-entrant. */ - virtual Streams Probe(const QString& filename, const QAtomicInt* cancelled) const = 0; + virtual FootageDescription Probe(const QString& filename, const QAtomicInt* cancelled) const = 0; /** * @brief Closes media/deallocates memory @@ -202,8 +203,6 @@ public: static QVector ReceiveListOfAllDecoders(); - static CodecStream GetCodecStreamFromStreamReference(const Footage::StreamReference& ref); - protected: /** * @brief Internal open function diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 76c033f50..e71edf118 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -202,15 +202,15 @@ void FFmpegDecoder::CloseInternal() FreeScaler(); } -QString FFmpegDecoder::id() +QString FFmpegDecoder::id() const { return QStringLiteral("ffmpeg"); } -Streams FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelled) const +FootageDescription FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelled) const { // Return value - Streams streams; + FootageDescription desc(id()); // Variable for receiving errors from FFmpeg int error_code; @@ -236,9 +236,6 @@ Streams FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelle // FFmpeg AVStream AVStream* avstream = fmt_ctx->streams[i]; - // Our native stream class - Stream stream; - // Find decoder for this stream, if it exists we can proceed AVCodec* decoder = avcodec_find_decoder(avstream->codecpar->codec_id); @@ -320,20 +317,25 @@ Streams FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelle AVPixelFormat compatible_pix_fmt = FFmpegUtils::GetCompatiblePixelFormat(static_cast(avstream->codecpar->format)); - stream = Stream(Stream::kVideo); + VideoParams stream; + stream.set_stream_index(i); stream.set_width(avstream->codecpar->width); stream.set_height(avstream->codecpar->height); - stream.set_video_type((image_is_still) ? Stream::kVideoTypeStill : Stream::kVideoTypeVideo); - stream.set_pixel_format(GetNativePixelFormat(compatible_pix_fmt)); + stream.set_video_type((image_is_still) ? VideoParams::kVideoTypeStill : VideoParams::kVideoTypeVideo); + stream.set_format(GetNativePixelFormat(compatible_pix_fmt)); stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt)); stream.set_interlacing(interlacing); stream.set_pixel_aspect_ratio(pixel_aspect_ratio); stream.set_frame_rate(frame_rate); stream.set_start_time(avstream->start_time); + stream.set_time_base(avstream->time_base); + stream.set_duration(avstream->duration); // Defaults to false, requires user intervention if incorrect stream.set_premultiplied_alpha(false); + desc.AddVideoStream(stream); + } else { // Create an audio stream object @@ -370,45 +372,19 @@ Streams FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelle } } - stream = Stream(Stream::kAudio); + AudioParams stream; + stream.set_stream_index(i); stream.set_channel_layout(channel_layout); - stream.set_channel_count(avstream->codecpar->channels); stream.set_sample_rate(avstream->codecpar->sample_rate); + stream.set_format(AudioParams::kInternalFormat); + stream.set_time_base(avstream->time_base); + stream.set_duration(avstream->duration); + desc.AddAudioStream(stream); } - } else { - - // This is data we can't utilize at the moment, but we make a Stream object anyway to keep parity with the file - Stream::Type type; - - // Set the correct codec type based on FFmpeg's result - switch (avstream->codecpar->codec_type) { - case AVMEDIA_TYPE_DATA: - type = Stream::kData; - break; - case AVMEDIA_TYPE_SUBTITLE: - type = Stream::kSubtitle; - break; - case AVMEDIA_TYPE_ATTACHMENT: - type = Stream::kAttachment; - break; - case AVMEDIA_TYPE_UNKNOWN: - default: - // Fallback to an unknown stream - type = Stream::kUnknown; - break; - } - - stream = Stream(type); - } - stream.set_timebase(avstream->time_base); - stream.set_duration(avstream->duration); - - streams.append(stream); - } } @@ -416,7 +392,7 @@ Streams FFmpegDecoder::Probe(const QString &filename, const QAtomicInt *cancelle // Free all memory avformat_close_input(&fmt_ctx); - return streams; + return desc; } QString FFmpegDecoder::FFmpegError(int error_code) diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index e2b8f91aa..1d3d73420 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -51,12 +51,12 @@ public: // Destructor virtual ~FFmpegDecoder() override; - virtual QString id() override; + virtual QString id() const override; virtual bool SupportsVideo() override{return true;} virtual bool SupportsAudio() override{return true;} - virtual Streams Probe(const QString &filename, const QAtomicInt *cancelled) const override; + virtual FootageDescription Probe(const QString &filename, const QAtomicInt *cancelled) const override; protected: virtual bool OpenInternal() override; diff --git a/app/codec/oiio/oiiodecoder.cpp b/app/codec/oiio/oiiodecoder.cpp index 70d78e9b8..43712e9a1 100644 --- a/app/codec/oiio/oiiodecoder.cpp +++ b/app/codec/oiio/oiiodecoder.cpp @@ -46,21 +46,21 @@ OIIODecoder::~OIIODecoder() CloseInternal(); } -QString OIIODecoder::id() +QString OIIODecoder::id() const { return QStringLiteral("oiio"); } -Streams OIIODecoder::Probe(const QString &filename, const QAtomicInt* cancelled) const +FootageDescription OIIODecoder::Probe(const QString &filename, const QAtomicInt* cancelled) const { Q_UNUSED(cancelled) - Streams streams; + FootageDescription desc(id()); // Filter out any file extensions that aren't expected to work - sometimes OIIO will crash trying // to open a file that it can't if it's given one if (!FileTypeIsSupported(filename)) { - return streams; + return desc; } std::string std_filename = filename.toStdString(); @@ -68,35 +68,36 @@ Streams OIIODecoder::Probe(const QString &filename, const QAtomicInt* cancelled) auto in = OIIO::ImageInput::open(std_filename); if (!in) { - return streams; + return desc; } // Filter out OIIO detecting an "FFmpeg movie", we have a native FFmpeg decoder that can handle // it better if (!strcmp(in->format_name(), "FFmpeg movie")) { - return streams; + return desc; } - Stream stream(Stream::kVideo); + VideoParams video_params; - stream.set_width(in->spec().width); - stream.set_height(in->spec().height); - stream.set_pixel_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast(in->spec().format.basetype))); - stream.set_channel_count(in->spec().nchannels); - stream.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(in->spec())); - stream.set_video_type(Stream::kVideoTypeStill); + video_params.set_stream_index(0); + video_params.set_width(in->spec().width); + video_params.set_height(in->spec().height); + video_params.set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast(in->spec().format.basetype))); + video_params.set_channel_count(in->spec().nchannels); + video_params.set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(in->spec())); + video_params.set_video_type(VideoParams::kVideoTypeStill); // OIIO automatically premultiplies alpha // FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this // likely reduces the fidelity? - stream.set_premultiplied_alpha(true); + video_params.set_premultiplied_alpha(true); - streams.append(stream); + desc.AddVideoStream(video_params); // If we're here, we have a successful image open in->close(); - return streams; + return desc; } bool OIIODecoder::OpenInternal() diff --git a/app/codec/oiio/oiiodecoder.h b/app/codec/oiio/oiiodecoder.h index 63774a38d..c4c2cd91b 100644 --- a/app/codec/oiio/oiiodecoder.h +++ b/app/codec/oiio/oiiodecoder.h @@ -36,11 +36,11 @@ public: virtual ~OIIODecoder() override; - virtual QString id() override; + virtual QString id() const override; virtual bool SupportsVideo() override{return true;} - virtual Streams Probe(const QString& filename, const QAtomicInt* cancelled) const override; + virtual FootageDescription Probe(const QString& filename, const QAtomicInt* cancelled) const override; protected: virtual bool OpenInternal() override; diff --git a/app/node/node.cpp b/app/node/node.cpp index bc763d12f..0bec9ee3c 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -1456,7 +1456,8 @@ void GetDependenciesRecursively(QVector& list, const Node* node, bool tra for (auto it=node->input_connections().cbegin(); it!=node->input_connections().cend(); it++) { Node* connected_node = it->second.node(); - if (connected_node->outputs().size() == 1 || !exclusive_only) { + if (!exclusive_only + || (connected_node->outputs().size() == 1 && !dynamic_cast(connected_node))) { if (!list.contains(connected_node)) { list.append(connected_node); @@ -1764,11 +1765,22 @@ void Node::LoadImmediate(QXmlStreamReader *reader, const QString& input, int ele } if (reader->name() == QStringLiteral("track")) { - QString value_text = reader->readElementText(); QVariant value_on_track; - if (!value_text.isEmpty()) { - value_on_track = NodeValue::StringToValue(data_type, value_text, element); + if (data_type == NodeValue::kVideoParams) { + VideoParams vp; + vp.Load(reader); + value_on_track = QVariant::fromValue(vp); + } else if (data_type == NodeValue::kAudioParams) { + AudioParams ap; + ap.Load(reader); + value_on_track = QVariant::fromValue(ap); + } else { + QString value_text = reader->readElementText(); + + if (!value_text.isEmpty()) { + value_on_track = NodeValue::StringToValue(data_type, value_text, element); + } } SetSplitStandardValueOnTrack(input, val_index, value_on_track, element); @@ -1865,7 +1877,17 @@ void Node::SaveImmediate(QXmlStreamWriter *writer, const QString& input, int ele writer->writeStartElement(QStringLiteral("standard")); foreach (const QVariant& v, GetSplitStandardValue(input, element)) { - writer->writeTextElement(QStringLiteral("track"), NodeValue::ValueToString(data_type, v, true)); + writer->writeStartElement(QStringLiteral("track")); + + if (data_type == NodeValue::kVideoParams) { + v.value().Save(writer); + } else if (data_type == NodeValue::kAudioParams) { + v.value().Save(writer); + } else { + writer->writeCharacters(NodeValue::ValueToString(data_type, v, true)); + } + + writer->writeEndElement(); // track } writer->writeEndElement(); // standard diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index c3503babd..f184dde30 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -128,7 +128,7 @@ NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeR return table; } -QVariant NodeTraverser::ProcessVideoFootage(const Footage::StreamReference& stream, const rational &input_time) +QVariant NodeTraverser::ProcessVideoFootage(const FootageJob &stream, const rational &input_time) { Q_UNUSED(stream) Q_UNUSED(input_time) @@ -136,7 +136,7 @@ QVariant NodeTraverser::ProcessVideoFootage(const Footage::StreamReference& stre return QVariant(); } -QVariant NodeTraverser::ProcessAudioFootage(const Footage::StreamReference& stream, const TimeRange &input_time) +QVariant NodeTraverser::ProcessAudioFootage(const FootageJob& stream, const TimeRange &input_time) { Q_UNUSED(stream) Q_UNUSED(input_time) @@ -232,9 +232,9 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N // Retrieve video frames foreach (const NodeValue& v, footage_jobs_to_run) { // Assume this is a VideoStream, we did a type check earlier in the function - Footage::StreamReference job = v.data().value(); + FootageJob job = v.data().value(); - if (job.IsValid() && job.type() == Stream::kVideo && job.footage()->IsValid()) { + if (job.type() == Stream::kVideo) { QVariant value = ProcessVideoFootage(job, range.in()); if (!value.isNull()) { @@ -265,9 +265,9 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N // Retrieve audio samples foreach (const NodeValue& v, footage_jobs_to_run) { // Assume this is an AudioStream, we did a type check earlier in the function - Footage::StreamReference job = v.data().value(); + FootageJob job = v.data().value(); - if (job.IsValid() && job.type() == Stream::kAudio && job.footage()->IsValid()) { + if (job.type() == Stream::kAudio) { QVariant value = ProcessAudioFootage(job, range); if (!value.isNull()) { diff --git a/app/node/traverser.h b/app/node/traverser.h index e597aac4a..462267e86 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -26,6 +26,7 @@ #include "codec/decoder.h" #include "common/cancelableobject.h" #include "node/output/track/track.h" +#include "render/job/footagejob.h" #include "value.h" namespace olive { @@ -48,9 +49,9 @@ protected: virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range); - virtual QVariant ProcessVideoFootage(const Footage::StreamReference &stream, const rational &input_time); + virtual QVariant ProcessVideoFootage(const FootageJob &stream, const rational &input_time); - virtual QVariant ProcessAudioFootage(const Footage::StreamReference &stream, const TimeRange &input_time); + virtual QVariant ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time); virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job); diff --git a/app/node/value.cpp b/app/node/value.cpp index 1755f9227..fd7215384 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -28,6 +28,8 @@ #include "common/tohex.h" #include "project/item/footage/stream.h" +#include "render/audioparams.h" +#include "render/videoparams.h" #include "render/color.h" namespace olive { @@ -115,10 +117,10 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value) case kVec4: return ValueToBytesInternal(value); case kCombo: return ValueToBytesInternal(value); - case kVideoStreamProperties: - case kAudioStreamProperties: - return value.value().toBytes(); - + case kVideoParams: + return value.value().toBytes(); + case kAudioParams: + return value.value().toBytes(); // These types have no persistent input case kNone: @@ -307,10 +309,10 @@ QString NodeValue::GetPrettyDataTypeName(Type type) return QCoreApplication::translate("NodeValue", "Vector 3D"); case kVec4: return QCoreApplication::translate("NodeValue", "Vector 4D"); - case kVideoStreamProperties: - return QCoreApplication::translate("NodeValue", "Video Stream Properties"); - case kAudioStreamProperties: - return QCoreApplication::translate("NodeValue", "Audio Stream Properties"); + case kVideoParams: + return QCoreApplication::translate("NodeValue", "Video Parameters"); + case kAudioParams: + return QCoreApplication::translate("NodeValue", "Audio Parameters"); case kFootageJob: case kShaderJob: diff --git a/app/node/value.h b/app/node/value.h index c1c2ff7c1..723e84b2d 100644 --- a/app/node/value.h +++ b/app/node/value.h @@ -150,18 +150,18 @@ public: kCombo, /** - * Properties pertaining to the video stream of a footage file + * Video Parameters type * - * Resolves to a `Stream` object. + * Resolves to `VideoParams` */ - kVideoStreamProperties, + kVideoParams, /** - * Properties pertaining to the audio stream of a footage file + * Audio Parameters type * - * Resolves to a `Stream` object. + * Resolves to `AudioParams` */ - kAudioStreamProperties, + kAudioParams, /** * Job type diff --git a/app/project/item/footage/CMakeLists.txt b/app/project/item/footage/CMakeLists.txt index d868e0c15..8c3e24a27 100644 --- a/app/project/item/footage/CMakeLists.txt +++ b/app/project/item/footage/CMakeLists.txt @@ -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 ) diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index ad6fb51c9..3a06193a7 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -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 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::GetEnabledVideoStreams() const +{ + QVector 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 Footage::GetEnabledAudioStreams() const +{ + QVector 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::GetEnabledStreamsAsReferences() const +{ + QVector 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(); - - 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(); - - if (stream.enabled() && stream.type() == type) { - return stream; - } - } - - return Stream(); -} - -QVector Footage::GetStreamIndexesOfType(Stream::Type type) const -{ - QVector indexes; - - for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { - Stream stream = GetStandardValue(it.value()).value(); - - 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(); + 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; icolor_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(&stream.pixel_aspect_ratio()), sizeof(stream.pixel_aspect_ratio())); + hash.addData(reinterpret_cast(¶ms.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(&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; iGetStreamAt(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(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(type), index); + + return in; } } diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index 1e08463e9..405f7848c 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -25,8 +25,11 @@ #include #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 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(); } - 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 GetStreamIndexesOfType(Stream::Type type) const; + AudioParams GetAudioParams(int index) const + { + return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index))).value(); + } + + void SetAudioParams(int index, const AudioParams& p) + { + SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index)), QVariant::fromValue(p)); + } + + AudioParams GetFirstEnabledAudioStream() const; + + QVector GetEnabledVideoStreams() const; + + QVector GetEnabledAudioStreams() const; Stream::Type GetStreamType(int index); + QVector 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 inputs_for_stream_properties_; + QMap inputs_for_stream_properties_; /** * @brief List of dynamic outputs added for streams */ - QMap outputs_for_streams_; + QMap 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) diff --git a/app/project/item/footage/footagedescription.cpp b/app/project/item/footage/footagedescription.cpp new file mode 100644 index 000000000..8afce0d7b --- /dev/null +++ b/app/project/item/footage/footagedescription.cpp @@ -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 . + +***/ + +#include "footagedescription.h" + +#include +#include +#include + +#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; +} + +} diff --git a/app/project/item/footage/footagedescription.h b/app/project/item/footage/footagedescription.h new file mode 100644 index 000000000..4e0c50b26 --- /dev/null +++ b/app/project/item/footage/footagedescription.h @@ -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 . + +***/ + +#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& GetVideoStreams() const + { + return video_streams_; + } + + const QVector& GetAudioStreams() const + { + return audio_streams_; + } + +private: + QString decoder_; + + QVector video_streams_; + + QVector audio_streams_; + +}; + +} + +#endif // FOOTAGEDESCRIPTION_H diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp deleted file mode 100644 index f3173aec1..000000000 --- a/app/project/item/footage/stream.cpp +++ /dev/null @@ -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 . - -***/ - -#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(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(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("interlacing")) { - interlacing_ = static_cast(reader->readElementText().toInt()); - } else if (reader->name() == QStringLiteral("pixelformat")) { - pixel_format_ = static_cast(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 -} - -} diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 3dc323144..0d9251ec7 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -21,17 +21,10 @@ #ifndef STREAM_H #define STREAM_H -#include -#include -#include - -#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(&type_), sizeof(type_)); - arr.append(reinterpret_cast(&timebase_), sizeof(timebase_)); - arr.append(reinterpret_cast(&duration_), sizeof(duration_)); - arr.append(reinterpret_cast(&channel_count_), sizeof(channel_count_)); - arr.append(reinterpret_cast(&enabled_), sizeof(enabled_)); - arr.append(reinterpret_cast(&width_), sizeof(width_)); - arr.append(reinterpret_cast(&height_), sizeof(height_)); - arr.append(reinterpret_cast(&pixel_aspect_ratio_), sizeof(pixel_aspect_ratio_)); - arr.append(reinterpret_cast(&video_type_), sizeof(video_type_)); - arr.append(reinterpret_cast(&interlacing_), sizeof(interlacing_)); - arr.append(reinterpret_cast(&pixel_format_), sizeof(pixel_format_)); - arr.append(reinterpret_cast(&frame_rate_), sizeof(frame_rate_)); - arr.append(reinterpret_cast(&start_time_), sizeof(start_time_)); - arr.append(reinterpret_cast(&premultiplied_alpha_), sizeof(premultiplied_alpha_)); - arr.append(colorspace_.toUtf8()); - arr.append(reinterpret_cast(&sample_rate_), sizeof(sample_rate_)); - arr.append(reinterpret_cast(&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; - } -Q_DECLARE_METATYPE(olive::Stream) - #endif // STREAM_H diff --git a/app/project/item/item.cpp b/app/project/item/item.cpp index c2925f822..729d2dc22 100644 --- a/app/project/item/item.cpp +++ b/app/project/item/item.cpp @@ -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); } } diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index b70d912f1..56f81d86d 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -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) { - bool found_video_params = false; - bool found_audio_params = false; foreach (Footage* f, footage) { - for (int i=0; iGetStreamCount(); i++) { - if (!f->IsStreamEnabled(i)) { - continue; + QVector video_streams = f->GetEnabledVideoStreams(); + QVector 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(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(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;iwriteEndElement(); // 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) diff --git a/app/project/item/sequence/sequence.h b/app/project/item/sequence/sequence.h index 83600998e..30d22c31e 100644 --- a/app/project/item/sequence/sequence.h +++ b/app/project/item/sequence/sequence.h @@ -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(); } - const AudioParams& audio_params() const + AudioParams audio_params() const { - return audio_params_; + return GetStandardValue(kAudioParamsInput).value(); } - 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 track_lists_; @@ -213,8 +225,7 @@ private: int operation_stack_; - VideoParams video_params_; - AudioParams audio_params_; + VideoParams cached_video_params_; TimelinePoints timeline_points_; diff --git a/app/project/project.cpp b/app/project/project.cpp index 4847e11f1..0725198a7 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -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; diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index 94c7e2ac7..ed6cd47ba 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -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(static_cast(index.internalPointer())); - if (dynamic_cast(static_cast(index.internalPointer()))) { - stream_flags = static_cast(index.internalPointer())->get_enabled_stream_flags(); - } else { - stream_flags = UINT64_MAX; + if (footage) { + QVector streams = footage->GetEnabledStreamsAsReferences(); + + stream << streams << reinterpret_cast(footage); + + dragged_items.append(footage); } - - stream << stream_flags << index.row() << reinterpret_cast(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 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_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); diff --git a/app/render/audioparams.cpp b/app/render/audioparams.cpp index fa16a4838..d0efef430 100644 --- a/app/render/audioparams.cpp +++ b/app/render/audioparams.cpp @@ -25,6 +25,9 @@ extern "C" { } #include +#include + +#include "common/xmlutils.h" namespace olive { @@ -172,6 +175,52 @@ bool AudioParams::is_valid() const && format_ < kFormatCount); } +QByteArray AudioParams::toBytes() const +{ + QCryptographicHash hasher(QCryptographicHash::Sha1); + + hasher.addData(reinterpret_cast(&sample_rate_), sizeof(sample_rate_)); + hasher.addData(reinterpret_cast(&channel_layout_), sizeof(channel_layout_)); + hasher.addData(reinterpret_cast(&format_), sizeof(format_)); + hasher.addData(reinterpret_cast(&timebase_), sizeof(timebase_)); + + return hasher.result(); +} + +void AudioParams::Load(QXmlStreamReader *reader) +{ + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("samplerate")) { + set_sample_rate(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("channellayout")) { + set_channel_layout(reader->readElementText().toULongLong()); + } else if (reader->name() == QStringLiteral("format")) { + set_format(static_cast(reader->readElementText().toInt())); + } else if (reader->name() == QStringLiteral("enabled")) { + set_enabled(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("streamindex")) { + set_stream_index(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("duration")) { + set_duration(reader->readElementText().toLongLong()); + } else if (reader->name() == QStringLiteral("timebase")) { + set_time_base(rational::fromString(reader->readElementText())); + } else { + reader->skipCurrentElement(); + } + } +} + +void AudioParams::Save(QXmlStreamWriter *writer) const +{ + writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_)); + writer->writeTextElement(QStringLiteral("channellayout"), QString::number(channel_layout_)); + writer->writeTextElement(QStringLiteral("format"), QString::number(format_)); + writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_)); + writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index_)); + writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_)); + writer->writeTextElement(QStringLiteral("timebase"), timebase_.toString()); +} + QString AudioParams::SampleRateToString(const int &sample_rate) { return QCoreApplication::translate("AudioParams", "%1 Hz").arg(sample_rate); diff --git a/app/render/audioparams.h b/app/render/audioparams.h index 989fbf959..dc6ba7b57 100644 --- a/app/render/audioparams.h +++ b/app/render/audioparams.h @@ -23,6 +23,8 @@ #include #include +#include +#include #include "common/rational.h" @@ -63,6 +65,7 @@ public: channel_layout_(0), format_(kFormatInvalid) { + set_default_footage_parameters(); } AudioParams(const int& sample_rate, const uint64_t& channel_layout, const Format& format) : @@ -70,28 +73,83 @@ public: channel_layout_(channel_layout), format_(format) { + set_default_footage_parameters(); } - const int& sample_rate() const + int sample_rate() const { return sample_rate_; } - const uint64_t& channel_layout() const + void set_sample_rate(int sample_rate) + { + sample_rate_ = sample_rate; + } + + uint64_t channel_layout() const { return channel_layout_; } - rational time_base() const + void set_channel_layout(uint64_t channel_layout) { - return rational(1, sample_rate()); + channel_layout_ = channel_layout; } - const Format &format() const + rational time_base() const + { + if (timebase_.isNull()) { + return rational(1, sample_rate()); + } else { + return timebase_; + } + } + + void set_time_base(const rational& timebase) + { + timebase_ = timebase; + } + + Format format() const { return format_; } + void set_format(Format format) + { + format_ = format; + } + + bool enabled() const + { + return enabled_; + } + + void set_enabled(bool e) + { + enabled_ = e; + } + + int stream_index() const + { + return stream_index_; + } + + void set_stream_index(int s) + { + stream_index_ = s; + } + + int64_t duration() const + { + return duration_; + } + + void set_duration(int64_t duration) + { + duration_ = duration; + } + qint64 time_to_bytes(const double& time) const; qint64 time_to_bytes(const rational& time) const; qint64 time_to_samples(const double& time) const; @@ -105,6 +163,12 @@ public: int bits_per_sample() const; bool is_valid() const; + QByteArray toBytes() const; + + void Load(QXmlStreamReader* reader); + + void Save(QXmlStreamWriter* writer) const; + bool operator==(const AudioParams& other) const; bool operator!=(const AudioParams& other) const; @@ -124,12 +188,25 @@ public: static QString ChannelLayoutToString(const uint64_t &layout); private: + void set_default_footage_parameters() + { + enabled_ = true; + stream_index_ = 0; + duration_ = 0; + } + int sample_rate_; uint64_t channel_layout_; Format format_; + // Footage-specific + bool enabled_; + int stream_index_; + int64_t duration_; + rational timebase_; + }; } diff --git a/app/render/job/footagejob.h b/app/render/job/footagejob.h index 3602251c3..72dbd54a6 100644 --- a/app/render/job/footagejob.h +++ b/app/render/job/footagejob.h @@ -28,28 +28,75 @@ namespace olive { class FootageJob { public: - FootageJob() = default; - - FootageJob(const Footage::StreamReference& ref, const TimeRange& range) : - footage_(ref), - range_(range) + FootageJob() : + type_(Stream::kUnknown) { } - const Footage::StreamReference& footage() const + FootageJob(const QString& decoder, const QString& filename, Stream::Type type) : + decoder_(decoder), + filename_(filename), + type_(type) { - return footage_; } - const TimeRange& range() const + const QString& decoder() const { - return range_; + return decoder_; + } + + const QString& filename() const + { + return filename_; + } + + Stream::Type type() const + { + return type_; + } + + const VideoParams& video_params() const + { + return video_params_; + } + + void set_video_params(const VideoParams& p) + { + video_params_ = p; + } + + const AudioParams& audio_params() const + { + return audio_params_; + } + + void set_audio_params(const AudioParams& p) + { + audio_params_ = p; + } + + const QString& cache_path() const + { + return cache_path_; + } + + void set_cache_path(const QString& p) + { + cache_path_ = p; } private: - Footage::StreamReference footage_; + QString decoder_; - TimeRange range_; + QString filename_; + + Stream::Type type_; + + VideoParams video_params_; + + AudioParams audio_params_; + + QString cache_path_; }; diff --git a/app/render/opengl/openglrenderer.cpp b/app/render/opengl/openglrenderer.cpp index b7220c733..9e3aab53f 100644 --- a/app/render/opengl/openglrenderer.cpp +++ b/app/render/opengl/openglrenderer.cpp @@ -437,8 +437,8 @@ void OpenGLRenderer::Blit(QVariant s, ShaderJob job, Texture *destination, Video case NodeValue::kRational: case NodeValue::kFont: case NodeValue::kFile: - case NodeValue::kVideoStreamProperties: - case NodeValue::kAudioStreamProperties: + case NodeValue::kVideoParams: + case NodeValue::kAudioParams: case NodeValue::kShaderJob: case NodeValue::kSampleJob: case NodeValue::kGenerateJob: diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 34ab1497c..41a532746 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -65,7 +65,7 @@ void PreviewAutoCacher::GenerateHashes(Sequence *viewer, FrameHashCache* cache, foreach (const rational& time, times) { // See if hash already exists in disk cache - QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(ViewerOutput::kTextureInput), viewer->video_params(), time); + QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(Sequence::kTextureInput), viewer->video_params(), time); // Check memory list since disk checking is slow bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), hash) != existing_hashes.end()); @@ -264,12 +264,6 @@ void PreviewAutoCacher::ProcessUpdateQueue() case QueuedJob::kValueChanged: CopyValue(job.input); break; - case QueuedJob::kVideoParamsChanged: - UpdateVideoParams(); - break; - case QueuedJob::kAudioParamsChanged: - UpdateAudioParams(); - break; } } graph_update_queue_.clear(); @@ -330,16 +324,6 @@ void PreviewAutoCacher::CopyValue(const NodeInput &input) Node::CopyValuesOfElement(input.node(), our_input, input.input(), input.element()); } -void PreviewAutoCacher::UpdateVideoParams() -{ - copied_viewer_node_->set_video_params(viewer_node_->video_params()); -} - -void PreviewAutoCacher::UpdateAudioParams() -{ - copied_viewer_node_->set_audio_params(viewer_node_->audio_params()); -} - void PreviewAutoCacher::SetPlayhead(const rational &playhead) { cache_range_ = TimeRange(playhead - Config::Current()[QStringLiteral("DiskCacheBehind")].value(), @@ -449,23 +433,6 @@ void PreviewAutoCacher::ValueChanged(const NodeInput &input) graph_update_queue_.append({QueuedJob::kValueChanged, nullptr, input, NodeOutput()}); } -void PreviewAutoCacher::VideoParamsChanged() -{ - // In case the user is pressing the mouse at this exact moment - IgnoreNextMouseButton(); - - graph_update_queue_.append({QueuedJob::kVideoParamsChanged, nullptr, NodeInput(), NodeOutput()}); - ClearVideoQueue(); - TryRender(); -} - -void PreviewAutoCacher::AudioParamsChanged() -{ - graph_update_queue_.append({QueuedJob::kAudioParamsChanged, nullptr, NodeInput(), NodeOutput()}); - ClearAudioQueue(); - TryRender(); -} - void PreviewAutoCacher::TryRender() { if (!graph_update_queue_.isEmpty()) { @@ -639,16 +606,6 @@ void PreviewAutoCacher::SetViewerNode(Sequence *viewer_node) disconnect(graph, &NodeGraph::ValueChanged, this, &PreviewAutoCacher::ValueChanged); // Disconnect signal (will be a no-op if the signal was never connected) - disconnect(viewer_node_, - &ViewerOutput::VideoParamsChanged, - this, - &PreviewAutoCacher::VideoParamsChanged); - - disconnect(viewer_node_, - &ViewerOutput::AudioParamsChanged, - this, - &PreviewAutoCacher::AudioParamsChanged); - disconnect(viewer_node_->video_frame_cache(), &PlaybackCache::Invalidated, this, @@ -672,7 +629,7 @@ void PreviewAutoCacher::SetViewerNode(Sequence *viewer_node) } // Find copied viewer node - copied_viewer_node_ = static_cast(copy_map_.value(viewer_node_)); + copied_viewer_node_ = static_cast(copy_map_.value(viewer_node_)); // Copy parameters copied_viewer_node_->set_video_params(viewer_node_->video_params()); @@ -698,20 +655,6 @@ void PreviewAutoCacher::SetViewerNode(Sequence *viewer_node) invalidated_video_ = viewer_node_->video_frame_cache()->GetInvalidatedRanges(); invalidated_audio_ = viewer_node_->audio_playback_cache()->GetInvalidatedRanges(); - // We begin an operation and never end it which prevents the copy from unnecessarily - // invalidating its own cache - copied_viewer_node_->BeginOperation(); - - connect(viewer_node_, - &ViewerOutput::VideoParamsChanged, - this, - &PreviewAutoCacher::VideoParamsChanged); - - connect(viewer_node_, - &ViewerOutput::AudioParamsChanged, - this, - &PreviewAutoCacher::AudioParamsChanged); - connect(viewer_node_->video_frame_cache(), &PlaybackCache::Invalidated, this, diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index e4b730ea2..36523edf7 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -111,8 +111,6 @@ private: void AddEdge(const NodeOutput& output, const NodeInput& input); void RemoveEdge(const NodeOutput& output, const NodeInput& input); void CopyValue(const NodeInput& input); - void UpdateVideoParams(); - void UpdateAudioParams(); class QueuedJob { public: @@ -121,9 +119,7 @@ private: kNodeRemoved, kEdgeAdded, kEdgeRemoved, - kValueChanged, - kVideoParamsChanged, - kAudioParamsChanged + kValueChanged }; Type type; @@ -210,10 +206,6 @@ private slots: void ValueChanged(const NodeInput& input); - void VideoParamsChanged(); - - void AudioParamsChanged(); - void SingleFrameFinished(); /** diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index adf90aefc..994faac6a 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -262,7 +262,7 @@ NodeValueTable RenderProcessor::GenerateBlockTable(const Track *track, const Tim } } -QVariant RenderProcessor::ProcessVideoFootage(const Footage::StreamReference &stream, const rational &input_time) +QVariant RenderProcessor::ProcessVideoFootage(const FootageJob &stream, const rational &input_time) { TexturePtr value = nullptr; @@ -270,27 +270,34 @@ QVariant RenderProcessor::ProcessVideoFootage(const Footage::StreamReference &st // and color managing them for every frame is a waste of time, so we implement a small cache here // to optimize such a situation const VideoParams& render_params = ticket_->property("vparam").value(); - VideoParams stream_params = stream.video_params(); + VideoParams stream_data = stream.video_params(); ColorManager* color_manager = Node::ValueToPtr(ticket_->property("colormanager")); // See if we can make this divider larger (i.e. if the fooage is smaller) int footage_divider = render_params.divider(); while (footage_divider > 1 - && VideoParams::GetScaledDimension(stream_params.width(), footage_divider-1) < render_params.effective_width() - && VideoParams::GetScaledDimension(stream_params.height(), footage_divider-1) < render_params.effective_height()) { + && VideoParams::GetScaledDimension(stream_data.width(), footage_divider-1) < render_params.effective_width() + && VideoParams::GetScaledDimension(stream_data.height(), footage_divider-1) < render_params.effective_height()) { footage_divider--; } - Stream stream_data = stream.GetStream(); + QString using_colorspace = stream_data.colorspace(); + + if (using_colorspace.isEmpty()) { + // FIXME: + qWarning() << "HAVEN'T GOTTEN DEFAULT INPUT COLORSPACE"; + } + + Decoder::CodecStream default_codec_stream(stream.filename(), stream_data.stream_index()); StillImageCache::EntryPtr want_entry = std::make_shared( nullptr, - stream, - ColorProcessor::GenerateID(color_manager, stream.video_colorspace(), color_manager->GetReferenceColorSpace()), + default_codec_stream, + ColorProcessor::GenerateID(color_manager, using_colorspace, color_manager->GetReferenceColorSpace()), stream_data.premultiplied_alpha(), footage_divider, - (stream_data.video_type() == Stream::kVideoTypeStill) ? 0 : input_time, + (stream_data.video_type() == VideoParams::kVideoTypeStill) ? 0 : input_time, true); bool found_existing = false; @@ -327,31 +334,31 @@ QVariant RenderProcessor::ProcessVideoFootage(const Footage::StreamReference &st still_image_cache_->mutex()->unlock(); - QString decoder_id = stream.footage()->decoder(); + QString decoder_id = stream.decoder(); DecoderPtr decoder = nullptr; - if (stream_data.video_type() == Stream::kVideoTypeVideo) { - decoder = ResolveDecoderFromInput(decoder_id, Decoder::GetCodecStreamFromStreamReference(stream)); + if (stream_data.video_type() == VideoParams::kVideoTypeVideo) { + decoder = ResolveDecoderFromInput(decoder_id, default_codec_stream); } else { // 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() == Stream::kVideoTypeImageSequence) { - int64_t frame_number = stream.GetTimeInTimebaseUnits(input_time); + 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(); } // Decoder will close automatically since it's a stream_ptr - decoder->Open(Decoder::CodecStream(frame_filename, stream.index())); + decoder->Open(Decoder::CodecStream(frame_filename, stream_data.stream_index())); } if (decoder) { - FramePtr frame = decoder->RetrieveVideo((stream_data.video_type() == Stream::kVideoTypeVideo) ? input_time : Decoder::kAnyTimecode, footage_divider); + FramePtr frame = decoder->RetrieveVideo((stream_data.video_type() == VideoParams::kVideoTypeVideo) ? input_time : Decoder::kAnyTimecode, footage_divider); if (frame) { // Return a texture from the derived class @@ -368,7 +375,7 @@ QVariant RenderProcessor::ProcessVideoFootage(const Footage::StreamReference &st value = render_ctx_->CreateTexture(managed_params); ColorProcessorPtr processor = ColorProcessor::Create(color_manager, - stream.video_colorspace(), + using_colorspace, color_manager->GetReferenceColorSpace()); render_ctx_->BlitColorManaged(processor, unmanaged_texture, @@ -391,17 +398,17 @@ QVariant RenderProcessor::ProcessVideoFootage(const Footage::StreamReference &st return QVariant::fromValue(value); } -QVariant RenderProcessor::ProcessAudioFootage(const Footage::StreamReference &stream, const TimeRange &input_time) +QVariant RenderProcessor::ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time) { QVariant value; - DecoderPtr decoder = ResolveDecoderFromInput(stream.footage()->decoder(), Decoder::GetCodecStreamFromStreamReference(stream)); + DecoderPtr decoder = ResolveDecoderFromInput(stream.decoder(), Decoder::CodecStream(stream.filename(), stream.audio_params().stream_index())); if (decoder) { const AudioParams& audio_params = ticket_->property("aparam").value(); SampleBufferPtr frame = decoder->RetrieveAudio(input_time, audio_params, - stream.footage()->project()->cache_path(), + stream.cache_path(), &IsCancelled()); if (frame) { diff --git a/app/render/renderprocessor.h b/app/render/renderprocessor.h index ba0cab7d4..0e5754b02 100644 --- a/app/render/renderprocessor.h +++ b/app/render/renderprocessor.h @@ -43,9 +43,9 @@ public: protected: virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange &range) override; - virtual QVariant ProcessVideoFootage(const Footage::StreamReference &stream, const rational &input_time) override; + virtual QVariant ProcessVideoFootage(const FootageJob &stream, const rational &input_time) override; - virtual QVariant ProcessAudioFootage(const Footage::StreamReference &stream, const TimeRange &input_time) override; + virtual QVariant ProcessAudioFootage(const FootageJob &stream, const TimeRange &input_time) override; virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override; diff --git a/app/render/stillimagecache.h b/app/render/stillimagecache.h index 96516755c..1f8abd5da 100644 --- a/app/render/stillimagecache.h +++ b/app/render/stillimagecache.h @@ -4,6 +4,7 @@ #include #include +#include "codec/decoder.h" #include "common/rational.h" #include "project/item/footage/footage.h" #include "render/texture.h" @@ -14,7 +15,7 @@ class StillImageCache { public: struct Entry { - Entry(TexturePtr t, const Footage::StreamReference& s, const QString& cs, bool a, int d, const rational& i, bool w) + Entry(TexturePtr t, const Decoder::CodecStream& s, const QString& cs, bool a, int d, const rational& i, bool w) { texture = t; stream = s; @@ -26,7 +27,7 @@ public: } TexturePtr texture; - Footage::StreamReference stream; + Decoder::CodecStream stream; QString colorspace; bool alpha_is_associated; int divider; diff --git a/app/render/videoparams.cpp b/app/render/videoparams.cpp index 02f736344..66adb10ae 100644 --- a/app/render/videoparams.cpp +++ b/app/render/videoparams.cpp @@ -67,8 +67,10 @@ VideoParams::VideoParams() : depth_(0), format_(kFormatInvalid), channel_count_(0), - interlacing_(Interlacing::kInterlaceNone) + interlacing_(Interlacing::kInterlaceNone), + divider_(1) { + set_defaults_for_footage(); } VideoParams::VideoParams(int width, int height, Format format, int nb_channels, const rational& pixel_aspect_ratio, Interlacing interlacing, int divider) : @@ -83,6 +85,7 @@ VideoParams::VideoParams(int width, int height, Format format, int nb_channels, { calculate_effective_size(); validate_pixel_aspect_ratio(); + set_defaults_for_footage(); } VideoParams::VideoParams(int width, int height, int depth, Format format, int nb_channels, const rational &pixel_aspect_ratio, VideoParams::Interlacing interlacing, int divider) : @@ -97,6 +100,7 @@ VideoParams::VideoParams(int width, int height, int depth, Format format, int nb { calculate_effective_size(); validate_pixel_aspect_ratio(); + set_defaults_for_footage(); } VideoParams::VideoParams(int width, int height, const rational &time_base, Format format, int nb_channels, const rational& pixel_aspect_ratio, Interlacing interlacing, int divider) : @@ -112,6 +116,7 @@ VideoParams::VideoParams(int width, int height, const rational &time_base, Forma { calculate_effective_size(); validate_pixel_aspect_ratio(); + set_defaults_for_footage(); } int VideoParams::generate_auto_divider(qint64 width, qint64 height) @@ -237,6 +242,16 @@ void VideoParams::validate_pixel_aspect_ratio() } } +void VideoParams::set_defaults_for_footage() +{ + enabled_ = true; + stream_index_ = 0; + video_type_ = kVideoTypeVideo; + start_time_ = 0; + duration_ = 0; + premultiplied_alpha_ = false; +} + bool VideoParams::is_valid() const { return (width() > 0 @@ -280,4 +295,102 @@ int VideoParams::GetScaledDimension(int dim, int divider) return dim / divider; } +QByteArray VideoParams::toBytes() const +{ + QCryptographicHash hasher(QCryptographicHash::Sha1); + + hasher.addData(reinterpret_cast(&width_), sizeof(width_)); + hasher.addData(reinterpret_cast(&height_), sizeof(height_)); + hasher.addData(reinterpret_cast(&depth_), sizeof(depth_)); + hasher.addData(reinterpret_cast(&time_base_), sizeof(time_base_)); + hasher.addData(reinterpret_cast(&format_), sizeof(format_)); + hasher.addData(reinterpret_cast(&channel_count_), sizeof(channel_count_)); + hasher.addData(reinterpret_cast(&pixel_aspect_ratio_), sizeof(pixel_aspect_ratio_)); + hasher.addData(reinterpret_cast(&interlacing_), sizeof(interlacing_)); + hasher.addData(reinterpret_cast(÷r_), sizeof(divider_)); + hasher.addData(reinterpret_cast(&enabled_), sizeof(enabled_)); + hasher.addData(reinterpret_cast(&stream_index_), sizeof(stream_index_)); + hasher.addData(reinterpret_cast(&video_type_), sizeof(video_type_)); + hasher.addData(reinterpret_cast(&frame_rate_), sizeof(frame_rate_)); + hasher.addData(reinterpret_cast(&start_time_), sizeof(start_time_)); + hasher.addData(reinterpret_cast(&duration_), sizeof(duration_)); + hasher.addData(reinterpret_cast(&premultiplied_alpha_), sizeof(premultiplied_alpha_)); + hasher.addData(colorspace_.toUtf8()); + + return hasher.result(); +} + +int64_t VideoParams::get_time_in_timebase_units(const rational &time) const +{ + if (time_base_.isNull()) { + return AV_NOPTS_VALUE; + } + + return Timecode::time_to_timestamp(time, time_base_) + start_time_; +} + +void VideoParams::Load(QXmlStreamReader *reader) +{ + while (XMLReadNextStartElement(reader)) { + if (reader->name() == QStringLiteral("width")) { + set_width(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("height")) { + set_height(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("depth")) { + set_depth(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("timebase")) { + set_time_base(rational::fromString(reader->readElementText())); + } else if (reader->name() == QStringLiteral("format")) { + set_format(static_cast(reader->readElementText().toInt())); + } else if (reader->name() == QStringLiteral("channelcount")) { + set_channel_count(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("pixelaspectratio")) { + set_pixel_aspect_ratio(rational::fromString(reader->readElementText())); + } else if (reader->name() == QStringLiteral("interlacing")) { + set_interlacing(static_cast(reader->readElementText().toInt())); + } else if (reader->name() == QStringLiteral("divider")) { + set_divider(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("enabled")) { + set_enabled(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("streamindex")) { + set_stream_index(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("videotype")) { + set_video_type(static_cast(reader->readElementText().toInt())); + } else if (reader->name() == QStringLiteral("framerate")) { + set_frame_rate(rational::fromString(reader->readElementText())); + } else if (reader->name() == QStringLiteral("starttime")) { + set_start_time(reader->readElementText().toLongLong()); + } else if (reader->name() == QStringLiteral("duration")) { + set_duration(reader->readElementText().toLongLong()); + } else if (reader->name() == QStringLiteral("premultipliedalpha")) { + set_premultiplied_alpha(reader->readElementText().toInt()); + } else if (reader->name() == QStringLiteral("colorspace")) { + set_colorspace(reader->readElementText()); + } else { + reader->skipCurrentElement(); + } + } +} + +void VideoParams::Save(QXmlStreamWriter *writer) const +{ + writer->writeTextElement(QStringLiteral("width"), QString::number(width_)); + writer->writeTextElement(QStringLiteral("height"), QString::number(height_)); + writer->writeTextElement(QStringLiteral("depth"), QString::number(depth_)); + writer->writeTextElement(QStringLiteral("timebase"), time_base_.toString()); + writer->writeTextElement(QStringLiteral("format"), QString::number(format_)); + writer->writeTextElement(QStringLiteral("channelcount"), QString::number(channel_count_)); + writer->writeTextElement(QStringLiteral("pixelaspectratio"), pixel_aspect_ratio_.toString()); + writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_)); + writer->writeTextElement(QStringLiteral("divider"), QString::number(divider_)); + writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_)); + writer->writeTextElement(QStringLiteral("streamindex"), QString::number(stream_index_)); + writer->writeTextElement(QStringLiteral("videotype"), QString::number(video_type_)); + writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString()); + writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_)); + writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_)); + writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_)); + writer->writeTextElement(QStringLiteral("colorspace"), colorspace_); +} + } diff --git a/app/render/videoparams.h b/app/render/videoparams.h index d38626491..ba51a2256 100644 --- a/app/render/videoparams.h +++ b/app/render/videoparams.h @@ -21,6 +21,9 @@ #ifndef VIDEOPARAMS_H #define VIDEOPARAMS_H +#include +#include + #include "common/rational.h" #include "rendermodes.h" @@ -57,6 +60,12 @@ public: kInterlacedBottomFirst }; + enum Type { + kVideoTypeVideo, + kVideoTypeStill, + kVideoTypeImageSequence + }; + VideoParams(); VideoParams(int width, int height, Format format, int nb_channels, const rational& pixel_aspect_ratio = 1, @@ -239,11 +248,101 @@ public: static int GetScaledDimension(int dim, int divider); + QByteArray toBytes() const; + + bool enabled() const + { + return enabled_; + } + + void set_enabled(bool e) + { + enabled_ = e; + } + + int stream_index() const + { + return stream_index_; + } + + void set_stream_index(int s) + { + stream_index_ = s; + } + + Type video_type() const + { + return video_type_; + } + + void set_video_type(Type t) + { + video_type_ = t; + } + + 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; + } + + int64_t duration() const + { + return duration_; + } + + void set_duration(int64_t duration) + { + duration_ = duration; + } + + 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; + } + + int64_t get_time_in_timebase_units(const rational& time) const; + + void Load(QXmlStreamReader* reader); + + void Save(QXmlStreamWriter* writer) const; + private: void calculate_effective_size(); void validate_pixel_aspect_ratio(); + void set_defaults_for_footage(); + int width_; int height_; int depth_; @@ -258,9 +357,21 @@ private: Interlacing interlacing_; int divider_; + + // Cached values int effective_width_; int effective_height_; int effective_depth_; + + bool enabled_; + int stream_index_; + Type video_type_; + rational frame_rate_; + int64_t start_time_; + int64_t duration_; + bool premultiplied_alpha_; + QString colorspace_; + }; } diff --git a/app/task/project/import/import.cpp b/app/task/project/import/import.cpp index 0fbcfec40..210596b43 100644 --- a/app/task/project/import/import.cpp +++ b/app/task/project/import/import.cpp @@ -147,7 +147,7 @@ void ProjectImportTask::ValidateImageSequence(Footage *footage, QFileInfoList& i // see if it ends with numbers. if (Decoder::GetImageSequenceDigitCount(footage->filename()) > 0 && !image_sequence_ignore_files_.contains(footage->filename())) { - Stream video_stream = footage->GetStreamAt(Stream::kVideo, 0); + VideoParams video_stream = footage->GetVideoParams(0); QSize dim(video_stream.width(), video_stream.height()); int64_t ind = Decoder::GetImageSequenceIndex(footage->filename()); @@ -205,16 +205,16 @@ void ProjectImportTask::ValidateImageSequence(Footage *footage, QFileInfoList& i if (is_sequence) { // User has confirmed it is a still image, let's set it accordingly. - video_stream.set_video_type(Stream::kVideoTypeImageSequence); + video_stream.set_video_type(VideoParams::kVideoTypeImageSequence); rational default_timebase = Config::Current()[QStringLiteral("DefaultSequenceFrameRate")].value(); - video_stream.set_timebase(default_timebase); + video_stream.set_time_base(default_timebase); video_stream.set_frame_rate(default_timebase.flipped()); video_stream.set_start_time(start_index); video_stream.set_duration(end_index - start_index + 1); - footage->SetStreamAt(Stream::kVideo, 0, video_stream); + footage->SetVideoParams(0, video_stream); } } @@ -225,22 +225,15 @@ void ProjectImportTask::ValidateImageSequence(Footage *footage, QFileInfoList& i bool ProjectImportTask::ItemIsStillImageFootageOnly(Footage* footage) { - if (footage->GetStreamCount() != 1) { + if (footage->GetTotalStreamCount() != 1) { // Footage with more than one stream (usually video+audio) most likely isn't an image sequence return false; } - if (footage->GetStreamAt(0).type() != Stream::kVideo) { - // Footage with no video stream definitely isn't an image sequence - return false; - } + VideoParams vp = footage->GetVideoParams(0); - if (footage->GetStreamAt(0).video_type() != Stream::kVideoTypeStill) { - // If video type is not a still, this definitely isn't a video stream - return false; - } - - return true; + // Footage must be valid and video stream must be a still image to be an image sequence + return vp.is_valid() && vp.video_type() == VideoParams::kVideoTypeStill; } bool ProjectImportTask::CompareStillImageSize(Footage* footage, const QSize &sz) @@ -249,7 +242,7 @@ bool ProjectImportTask::CompareStillImageSize(Footage* footage, const QSize &sz) return false; } - Stream stream = footage->GetStreamAt(Stream::kVideo, 0); + VideoParams stream = footage->GetVideoParams(0); return stream.width() == sz.width() && stream.height() == sz.height(); } diff --git a/app/task/project/load/load.cpp b/app/task/project/load/load.cpp index 27e387229..717815e9f 100644 --- a/app/task/project/load/load.cpp +++ b/app/task/project/load/load.cpp @@ -41,26 +41,29 @@ bool ProjectLoadTask::Run() if (project_file.open(QFile::ReadOnly | QFile::Text)) { QXmlStreamReader reader(&project_file); - bool ok; - uint project_version = reader.documentVersion().toUInt(&ok); - - if (!ok) { - SetError(tr("Failed to determine project's version identifier.")); - return false; - } else if (project_version > Core::kProjectVersion) { - // Project is newer than we support - SetError(tr("This project is newer than this version of Olive and cannot be opened.")); - return false; - } else if (project_version < 210122) { // Change this if we drop support for a project version - // Project is older than we support - SetError(tr("This project is from a version of Olive that is no longer supported in this version.")); - return false; - } + uint project_version; while (XMLReadNextStartElement(&reader)) { if (reader.name() == QStringLiteral("olive")) { while(XMLReadNextStartElement(&reader)) { - if (reader.name() == QStringLiteral("url")) { + if (reader.name() == QStringLiteral("version")) { + bool ok; + + project_version = reader.readElementText().toUInt(&ok); + + if (!ok) { + SetError(tr("Failed to determine project's version identifier.")); + return false; + } else if (project_version > Core::kProjectVersion) { + // Project is newer than we support + SetError(tr("This project is newer than this version of Olive and cannot be opened.")); + return false; + } else if (project_version < 210122) { // Change this if we drop support for a project version + // Project is older than we support + SetError(tr("This project is from a version of Olive that is no longer supported in this version.")); + return false; + } + } else if (reader.name() == QStringLiteral("url")) { project_saved_url_ = reader.readElementText(); } else if (reader.name() == QStringLiteral("project")) { project_ = new Project(); diff --git a/app/task/project/save/save.cpp b/app/task/project/save/save.cpp index 940655e19..2d50c8d29 100644 --- a/app/task/project/save/save.cpp +++ b/app/task/project/save/save.cpp @@ -46,12 +46,14 @@ bool ProjectSaveTask::Run() QXmlStreamWriter writer(&project_file); writer.setAutoFormatting(true); - // Version is stored in YYMMDD from whenever the project format was last changed - // Allows easy integer math for checking project versions. - writer.writeStartDocument(QString::number(Core::kProjectVersion)); + writer.writeStartDocument(); writer.writeStartElement("olive"); + // Version is stored in YYMMDD from whenever the project format was last changed + // Allows easy integer math for checking project versions. + writer.writeTextElement(QStringLiteral("version"), QString::number(Core::kProjectVersion)); + writer.writeTextElement("url", project_->filename()); writer.writeStartElement(QStringLiteral("project")); diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index ba1f75059..e89753534 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -81,8 +81,8 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeValue::kShaderJob: case NodeValue::kSampleJob: case NodeValue::kGenerateJob: - case NodeValue::kVideoStreamProperties: - case NodeValue::kAudioStreamProperties: + case NodeValue::kVideoParams: + case NodeValue::kAudioParams: break; case NodeValue::kInt: { @@ -252,8 +252,8 @@ void NodeParamViewWidgetBridge::WidgetCallback() case NodeValue::kShaderJob: case NodeValue::kSampleJob: case NodeValue::kGenerateJob: - case NodeValue::kVideoStreamProperties: - case NodeValue::kAudioStreamProperties: + case NodeValue::kVideoParams: + case NodeValue::kAudioParams: break; case NodeValue::kInt: { @@ -402,8 +402,8 @@ void NodeParamViewWidgetBridge::UpdateWidgetValues() case NodeValue::kShaderJob: case NodeValue::kSampleJob: case NodeValue::kGenerateJob: - case NodeValue::kVideoStreamProperties: - case NodeValue::kAudioStreamProperties: + case NodeValue::kVideoParams: + case NodeValue::kAudioParams: break; case NodeValue::kInt: { diff --git a/app/widget/nodetableview/nodetabletraverser.cpp b/app/widget/nodetableview/nodetabletraverser.cpp index 2eead5b81..41b8adb2e 100644 --- a/app/widget/nodetableview/nodetabletraverser.cpp +++ b/app/widget/nodetableview/nodetabletraverser.cpp @@ -22,12 +22,12 @@ namespace olive { -QVariant NodeTableTraverser::ProcessVideoFootage(const Footage::StreamReference &video_stream, const rational &input_time) +QVariant NodeTableTraverser::ProcessVideoFootage(const FootageJob &video_stream, const rational &input_time) { return QVariant::fromValue(video_stream.video_params()); } -QVariant NodeTableTraverser::ProcessAudioFootage(const Footage::StreamReference &audio_stream, const TimeRange &input_time) +QVariant NodeTableTraverser::ProcessAudioFootage(const FootageJob &audio_stream, const TimeRange &input_time) { return QVariant::fromValue(audio_stream.audio_params()); } diff --git a/app/widget/nodetableview/nodetabletraverser.h b/app/widget/nodetableview/nodetabletraverser.h index 571b105a8..66b46b9e6 100644 --- a/app/widget/nodetableview/nodetabletraverser.h +++ b/app/widget/nodetableview/nodetabletraverser.h @@ -31,9 +31,9 @@ public: NodeTableTraverser() = default; protected: - virtual QVariant ProcessVideoFootage(const Footage::StreamReference&video_stream, const rational &input_time); + virtual QVariant ProcessVideoFootage(const FootageJob &video_stream, const rational &input_time); - virtual QVariant ProcessAudioFootage(const Footage::StreamReference& audio_stream, const TimeRange &input_time); + virtual QVariant ProcessAudioFootage(const FootageJob &audio_stream, const TimeRange &input_time); }; diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 8e20a776f..f14770fb2 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -67,6 +67,7 @@ ProjectExplorer::ProjectExplorer(QWidget *parent) : // Add tree view to stacked widget tree_view_ = new ProjectExplorerTreeView(stacked_widget_); tree_view_->setSortingEnabled(true); + tree_view_->sortByColumn(0, Qt::AscendingOrder); tree_view_->setContextMenuPolicy(Qt::CustomContextMenu); AddView(tree_view_); @@ -307,7 +308,7 @@ void ProjectExplorer::ShowContextMenu() Footage* footage_cast_test = dynamic_cast(i); Sequence* sequence_cast_test = dynamic_cast(i); - if (footage_cast_test && !footage_cast_test->HasEnabledStreamsOfType(Stream::kVideo)) { + if (footage_cast_test && !footage_cast_test->HasEnabledVideoStreams()) { all_items_have_video_streams = false; } @@ -416,11 +417,11 @@ void ProjectExplorer::ContextMenuStartProxy(QAction *a) foreach (Item* i, context_menu_items_) { Footage* f = static_cast(i); - QVector video_streams = f->GetStreamIndexesOfType(Stream::kVideo); + QVector enabled_streams = f->GetEnabledVideoStreams(); - foreach (int stream, video_streams) { + foreach (const VideoParams& stream, enabled_streams) { // Start a background task for proxying - PreCacheTask* proxy_task = new PreCacheTask(f, stream, sequence); + PreCacheTask* proxy_task = new PreCacheTask(f, stream.stream_index(), sequence); TaskManager::instance()->AddTask(proxy_task); } } diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index ed4fb1f81..b8ec5d26a 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -71,18 +71,17 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event) QStringList mime_formats = event->GetMimeData()->formats(); // Listen for MIME data from a ProjectViewModel - if (mime_formats.contains("application/x-oliveprojectitemdata")) { + if (mime_formats.contains(QStringLiteral("application/x-oliveprojectitemdata"))) { // Data is drag/drop data from a ProjectViewModel - QByteArray model_data = event->GetMimeData()->data("application/x-oliveprojectitemdata"); + QByteArray model_data = event->GetMimeData()->data(QStringLiteral("application/x-oliveprojectitemdata")); // Use QDataStream to deserialize the data QDataStream stream(&model_data, QIODevice::ReadOnly); // Variables to deserialize into quintptr item_ptr; - int r; - quint64 enabled_streams; + QVector enabled_streams; // Set drag start position drag_start_ = event->GetCoordinates(); @@ -90,7 +89,7 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event) snap_points_.clear(); while (!stream.atEnd()) { - stream >> enabled_streams >> r >> item_ptr; + stream >> enabled_streams >> item_ptr; // Get Item object Item* item = reinterpret_cast(item_ptr); @@ -102,7 +101,7 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event) if (f->IsValid()) { // If the Item is Footage, we can create a Ghost from it - dragged_footage_.append(DraggedFootage(f, enabled_streams)); + dragged_footage_.insert(f, enabled_streams); } } @@ -194,10 +193,16 @@ void ImportTool::DragDrop(TimelineViewMouseEvent *event) void ImportTool::PlaceAt(const QVector &footage, const rational &start, bool insert) { - PlaceAt(FootageToDraggedFootage(footage), start, insert); + QMap > refs; + + foreach (Footage* f, footage) { + refs.insert(f, f->GetEnabledStreamsAsReferences()); + } + + PlaceAt(refs, start, insert); } -void ImportTool::PlaceAt(const QVector &footage, const rational &start, bool insert) +void ImportTool::PlaceAt(const QMap > &footage, const rational &start, bool insert) { dragged_footage_ = footage; @@ -209,10 +214,9 @@ void ImportTool::PlaceAt(const QVector &footage, const rational DropGhosts(insert); } -void ImportTool::FootageToGhosts(rational ghost_start, const QVector &footage_list, const rational& dest_tb, const int& track_start) +void ImportTool::FootageToGhosts(rational ghost_start, const QMap > &sorted, const rational& dest_tb, const int& track_start) { - foreach (const DraggedFootage& footage, footage_list) { - + for (auto it=sorted.cbegin(); it!=sorted.cend(); it++) { // Each stream is offset by one track per track "type", we keep track of them in this vector QVector track_offsets(Track::kCount); track_offsets.fill(track_start); @@ -221,40 +225,36 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVectoroutputs()) { - Footage::StreamReference ref = footage.footage()->GetReferenceFromOutput(output); - + foreach (const Footage::StreamReference& ref, it.value()) { Track::Type track_type = TrackTypeFromStreamType(ref.type()); - quint64 cached_enabled_streams = enabled_streams; - enabled_streams >>= 1; - - // Check if this stream has a compatible TrackList - if (track_type == Track::kNone - || !(cached_enabled_streams & 0x1)) { - continue; - } - TimelineViewGhostItem* ghost = new TimelineViewGhostItem(); - Stream stream = ref.GetStream(); - - if (ref.type() == Stream::kVideo - && stream.video_type() == Stream::kVideoTypeStill) { + if (ref.type() == Stream::kVideo && it.key()->GetVideoParams(ref.index()).video_type() == VideoParams::kVideoTypeStill) { // Stream is essentially length-less - we may use the default still image length in config, // or we may use another stream's length depending on the circumstance contains_image_stream = true; } else { // Rescale stream duration to timeline timebase // Convert to rational time - if (footage.footage()->workarea()->enabled()) { - footage_duration = qMax(footage_duration, footage.footage()->workarea()->range().length()); - ghost->SetMediaIn(footage.footage()->workarea()->in()); + if (it.key()->workarea()->enabled()) { + footage_duration = qMax(footage_duration, it.key()->workarea()->range().length()); + ghost->SetMediaIn(it.key()->workarea()->in()); } else { - int64_t stream_duration = Timecode::rescale_timestamp_ceil(stream.duration(), stream.timebase(), dest_tb); + int64_t dur; + rational tb; + + if (ref.type() == Stream::kVideo) { + VideoParams vp = it.key()->GetVideoParams(ref.index()); + dur = vp.duration(); + tb = vp.time_base(); + } else { + AudioParams ap = it.key()->GetAudioParams(ref.index()); + dur = ap.duration(); + tb = ap.time_base(); + } + + int64_t stream_duration = Timecode::rescale_timestamp_ceil(dur, tb, dest_tb); footage_duration = qMax(footage_duration, Timecode::timestamp_to_time(stream_duration, dest_tb)); } } @@ -264,11 +264,11 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVectorSetData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(TimelineViewGhostItem::AttachedFootage({footage.footage(), output}))); + TimelineViewGhostItem::AttachedFootage af = {it.key(), it.key()->GetStringFromReference(ref)}; + ghost->SetData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(af)); ghost->SetMode(Timeline::kMove); footage_ghosts.append(ghost); - } if (contains_image_stream && footage_duration.isNull()) { @@ -367,8 +367,10 @@ void ImportTool::DropGhosts(bool insert) QVector footage_only; - foreach (const DraggedFootage& df, dragged_footage_) { - footage_only.append(df.footage()); + for (auto it=dragged_footage_.cbegin(); it!=dragged_footage_.cend(); it++) { + if (!footage_only.contains(it.key())) { + footage_only.append(it.key()); + } } new_sequence->set_parameters_from_footage(footage_only); @@ -479,20 +481,4 @@ void ImportTool::DropGhosts(bool insert) dragged_footage_.clear(); } -ImportTool::DraggedFootage ImportTool::FootageToDraggedFootage(Footage *f) -{ - return DraggedFootage(f, f->get_enabled_stream_flags()); -} - -QVector ImportTool::FootageToDraggedFootage(QVector footage) -{ - QVector df; - - foreach (Footage* f, footage) { - df.append(FootageToDraggedFootage(f)); - } - - return df; -} - } diff --git a/app/widget/timelinewidget/tool/import.h b/app/widget/timelinewidget/tool/import.h index 38f22448d..9c15592d0 100644 --- a/app/widget/timelinewidget/tool/import.h +++ b/app/widget/timelinewidget/tool/import.h @@ -35,37 +35,8 @@ public: virtual void DragLeave(QDragLeaveEvent *event) override; virtual void DragDrop(TimelineViewMouseEvent *event) override; - class DraggedFootage { - public: - DraggedFootage() : - footage_(nullptr), - streams_(0) - { - } - - DraggedFootage(Footage* f, quint64 streams) : - footage_(f), - streams_(streams) - { - } - - Footage* footage() const { - return footage_; - } - - const quint64& streams() const { - return streams_; - } - - private: - Footage* footage_; - - quint64 streams_; - - }; - void PlaceAt(const QVector &footage, const rational& start, bool insert); - void PlaceAt(const QVector &footage, const rational& start, bool insert); + void PlaceAt(const QMap > &footage, const rational& start, bool insert); enum DropWithoutSequenceBehavior { kDWSAsk, @@ -75,16 +46,13 @@ public: }; private: - static DraggedFootage FootageToDraggedFootage(Footage* f); - static QVector FootageToDraggedFootage(QVector footage); - - void FootageToGhosts(rational ghost_start, const QVector &footage, const rational &dest_tb, const int &track_start); + void FootageToGhosts(rational ghost_start, const QMap > &footage, const rational &dest_tb, const int &track_start); void PrepGhosts(const rational &frame, const int &track_index); void DropGhosts(bool insert); - QVector dragged_footage_; + QMap > dragged_footage_; int import_pre_buffer_; diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 78f7a2869..a28d3f05d 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -99,56 +99,74 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl QByteArray encoded_data; QDataStream data_stream(&encoded_data, QIODevice::WriteOnly); - quint64 enabled_stream_flags = GetFootage()->get_enabled_stream_flags(); + QVector streams = GetFootage()->GetEnabledStreamsAsReferences(); // Disable streams that have been disabled if (!enable_video || !enable_audio) { - quint64 stream_disabler = 0x1; + for (int i=0; iGetStreamCount(); i++) { - Stream stream = GetFootage()->GetStreamAt(i); - - if ((stream.type() == Stream::kVideo && !enable_video) - || (stream.type() == Stream::kAudio && !enable_audio)) { - enabled_stream_flags &= ~stream_disabler; + if ((ref.type() == Stream::kVideo && !enable_video) + || (ref.type() == Stream::kAudio && !enable_audio)) { + streams.removeAt(i); + i--; } - - stream_disabler <<= 1; } } - data_stream << enabled_stream_flags << -1 << reinterpret_cast(GetFootage()); + if (!streams.isEmpty()) { + data_stream << streams << reinterpret_cast(GetFootage()); - mimedata->setData("application/x-oliveprojectitemdata", encoded_data); - drag->setMimeData(mimedata); + mimedata->setData(QStringLiteral("application/x-oliveprojectitemdata"), encoded_data); + drag->setMimeData(mimedata); - drag->exec(); + drag->exec(); + } } void FootageViewerWidget::TryConnectingType(Footage *footage, Stream::Type type) { + int index = -1; + for (int i=0; ; i++) { - Stream stream = footage_->GetStreamAt(type, i); + if (type == Stream::kVideo) { + VideoParams vp = footage->GetVideoParams(i); - // Found end of stream list - if (!stream.IsValid()) { - break; - } - - if (stream.enabled()) { - QString s = Footage::GetStringFromReference(type, i); - - QString input_param; - - if (type == Stream::kVideo) { - input_param = ViewerOutput::kTextureInput; - } else { - input_param = ViewerOutput::kSamplesInput; + if (!vp.is_valid()) { + break; } - Node::ConnectEdge(NodeOutput(footage, s), NodeInput(&sequence_, input_param)); + if (vp.enabled()) { + index = i; + break; + } + } else if (type == Stream::kAudio) { + AudioParams vp = footage->GetAudioParams(i); + + if (!vp.is_valid()) { + break; + } + + if (vp.enabled()) { + index = i; + break; + } } } + + if (index != -1) { + QString s = Footage::GetStringFromReference(type, index); + + QString input_param; + + if (type == Stream::kVideo) { + input_param = ViewerOutput::kTextureInput; + } else { + input_param = ViewerOutput::kSamplesInput; + } + + Node::ConnectEdge(NodeOutput(footage, s), NodeInput(&sequence_, input_param)); + } } void FootageViewerWidget::StartFootageDrag() diff --git a/app/widget/viewer/gizmotraverser.cpp b/app/widget/viewer/gizmotraverser.cpp index cd8a9b2a2..01b5c67c3 100644 --- a/app/widget/viewer/gizmotraverser.cpp +++ b/app/widget/viewer/gizmotraverser.cpp @@ -22,11 +22,11 @@ namespace olive { -QVariant GizmoTraverser::ProcessVideoFootage(const Footage::StreamReference &ref, const rational &input_time) +QVariant GizmoTraverser::ProcessVideoFootage(const FootageJob &ref, const rational &input_time) { Q_UNUSED(input_time) - Stream stream = ref.GetStream(); + VideoParams stream = ref.video_params(); return QVector2D(stream.width() * stream.pixel_aspect_ratio().toDouble(), stream.height()); diff --git a/app/widget/viewer/gizmotraverser.h b/app/widget/viewer/gizmotraverser.h index 5330a74e1..223a380cb 100644 --- a/app/widget/viewer/gizmotraverser.h +++ b/app/widget/viewer/gizmotraverser.h @@ -34,7 +34,7 @@ public: } protected: - virtual QVariant ProcessVideoFootage(const Footage::StreamReference& stream, const rational &input_time) override; + virtual QVariant ProcessVideoFootage(const FootageJob& stream, const rational &input_time) override; virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job) override;