moved sequence params into node graph too
Reduces code by reusing more of the existing node infrastructure to synchronize sequence parameters the render backend.
This commit is contained in:
@@ -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) {
|
||||
|
||||
+3
-4
@@ -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<DecoderPtr> ReceiveListOfAllDecoders();
|
||||
|
||||
static CodecStream GetCodecStreamFromStreamReference(const Footage::StreamReference& ref);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Internal open function
|
||||
|
||||
@@ -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<AVPixelFormat>(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,44 +372,18 @@ 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);
|
||||
|
||||
}
|
||||
|
||||
} 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_format(AudioParams::kInternalFormat);
|
||||
stream.set_time_base(avstream->time_base);
|
||||
stream.set_duration(avstream->duration);
|
||||
desc.AddAudioStream(stream);
|
||||
|
||||
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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<OIIO::TypeDesc::BASETYPE>(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<OIIO::TypeDesc::BASETYPE>(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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
+25
-3
@@ -1456,7 +1456,8 @@ void GetDependenciesRecursively(QVector<Node*>& 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<Item*>(connected_node))) {
|
||||
if (!list.contains(connected_node)) {
|
||||
list.append(connected_node);
|
||||
|
||||
@@ -1764,12 +1765,23 @@ 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 (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<VideoParams>().Save(writer);
|
||||
} else if (data_type == NodeValue::kAudioParams) {
|
||||
v.value<AudioParams>().Save(writer);
|
||||
} else {
|
||||
writer->writeCharacters(NodeValue::ValueToString(data_type, v, true));
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // track
|
||||
}
|
||||
|
||||
writer->writeEndElement(); // standard
|
||||
|
||||
@@ -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<Footage::StreamReference>();
|
||||
FootageJob job = v.data().value<FootageJob>();
|
||||
|
||||
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<Footage::StreamReference>();
|
||||
FootageJob job = v.data().value<FootageJob>();
|
||||
|
||||
if (job.IsValid() && job.type() == Stream::kAudio && job.footage()->IsValid()) {
|
||||
if (job.type() == Stream::kAudio) {
|
||||
QVariant value = ProcessAudioFootage(job, range);
|
||||
|
||||
if (!value.isNull()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+10
-8
@@ -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<QVector4D>(value);
|
||||
case kCombo: return ValueToBytesInternal<int>(value);
|
||||
|
||||
case kVideoStreamProperties:
|
||||
case kAudioStreamProperties:
|
||||
return value.value<Stream>().toBytes();
|
||||
|
||||
case kVideoParams:
|
||||
return value.value<VideoParams>().toBytes();
|
||||
case kAudioParams:
|
||||
return value.value<AudioParams>().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:
|
||||
|
||||
+6
-6
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -41,7 +41,6 @@ const QString Footage::kStreamPropertiesFormat = QStringLiteral("stream_properti
|
||||
|
||||
Footage::Footage(const QString &filename) :
|
||||
super(true, false),
|
||||
stream_count_(0),
|
||||
cancelled_(nullptr)
|
||||
{
|
||||
AddInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
@@ -58,7 +57,7 @@ void Footage::Retranslate()
|
||||
SetInputName(kFilenameInput, tr("Filename"));
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
StreamReference ref = GetReferenceFromRealIndex(it.key());
|
||||
StreamReference ref = it.key();
|
||||
|
||||
SetInputName(it.value(), QStringLiteral("%1 %2").arg(GetStreamTypeName(ref.type()), QString::number(ref.index())));
|
||||
}
|
||||
@@ -111,12 +110,12 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
// Determine if we've already cached the metadata of this file
|
||||
QString meta_cache_file = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).filePath(FileFunctions::GetUniqueFileIdentifier(filename()));
|
||||
|
||||
MetadataCache footage_info;
|
||||
FootageDescription footage_info;
|
||||
|
||||
if (QFileInfo::exists(meta_cache_file)) {
|
||||
|
||||
// Load meta cache file
|
||||
footage_info = LoadStreamCache(meta_cache_file);
|
||||
footage_info.Load(meta_cache_file);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -124,51 +123,28 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
QVector<DecoderPtr> decoder_list = Decoder::ReceiveListOfAllDecoders();
|
||||
|
||||
foreach (DecoderPtr decoder, decoder_list) {
|
||||
footage_info.streams = decoder->Probe(filename(), cancelled_);
|
||||
footage_info = decoder->Probe(filename(), cancelled_);
|
||||
|
||||
if (!footage_info.streams.isEmpty()) {
|
||||
footage_info.decoder = decoder->id();
|
||||
SetValid();
|
||||
if (footage_info.IsValid()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!SaveStreamCache(meta_cache_file, footage_info)) {
|
||||
if (!footage_info.Save(meta_cache_file)) {
|
||||
qWarning() << "Failed to save stream cache, footage will have to be re-probed";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stream_count_ = footage_info.streams.size();
|
||||
if (footage_info.IsValid()) {
|
||||
decoder_ = footage_info.decoder();
|
||||
|
||||
if (!footage_info.streams.isEmpty()) {
|
||||
set_decoder(footage_info.decoder);
|
||||
|
||||
for (int i=0; i<footage_info.streams.size(); i++) {
|
||||
const Stream& s = footage_info.streams.at(i);
|
||||
|
||||
if (s.type() == Stream::kVideo || s.type() == Stream::kAudio) {
|
||||
QString id = GetInputIDOfIndex(i);
|
||||
|
||||
NodeValue::Type type;
|
||||
|
||||
if (s.type() == Stream::kVideo) {
|
||||
type = NodeValue::kVideoStreamProperties;
|
||||
} else {
|
||||
type = NodeValue::kAudioStreamProperties;
|
||||
for (int i=0; i<footage_info.GetVideoStreams().size(); i++) {
|
||||
AddStreamAsInput(Stream::kVideo, i, QVariant::fromValue(footage_info.GetVideoStreams().at(i)));
|
||||
}
|
||||
|
||||
// Create input for properties
|
||||
AddInput(id, type, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
SetStandardValue(id, QVariant::fromValue(s));
|
||||
inputs_for_stream_properties_.insert(i, id);
|
||||
|
||||
// Create output for stream
|
||||
Footage::StreamReference ref = GetReferenceFromRealIndex(i);
|
||||
QString out_id = GetStringFromReference(ref);
|
||||
AddOutput(out_id);
|
||||
outputs_for_streams_.insert(i, out_id);
|
||||
}
|
||||
for (int i=0; i<footage_info.GetAudioStreams().size(); i++) {
|
||||
AddStreamAsInput(Stream::kAudio, i, QVariant::fromValue(footage_info.GetAudioStreams().at(i)));
|
||||
}
|
||||
|
||||
SetValid();
|
||||
@@ -180,65 +156,109 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
|
||||
}
|
||||
}
|
||||
|
||||
QString Footage::DescribeStream(int index) const
|
||||
{
|
||||
Stream stream = GetStreamAt(index);
|
||||
|
||||
switch (stream.type()) {
|
||||
case Stream::kVideo:
|
||||
if (stream.video_type() == Stream::kVideoTypeStill) {
|
||||
return tr("%1: Image - %2x%3").arg(QString::number(index),
|
||||
QString::number(stream.width()),
|
||||
QString::number(stream.height()));
|
||||
} else {
|
||||
return tr("%1: Video - %2x%3").arg(QString::number(index),
|
||||
QString::number(stream.width()),
|
||||
QString::number(stream.height()));
|
||||
}
|
||||
case Stream::kAudio:
|
||||
return QCoreApplication::translate("Stream", "%1: Audio - %2 Channel(s), %3Hz")
|
||||
.arg(QString::number(index),
|
||||
QString::number(stream.channel_count()),
|
||||
QString::number(stream.sample_rate()));
|
||||
case Stream::kUnknown:
|
||||
case Stream::kData:
|
||||
case Stream::kSubtitle:
|
||||
case Stream::kAttachment:
|
||||
break;
|
||||
}
|
||||
|
||||
return tr("%1: Unknown").arg(QString::number(index));
|
||||
}
|
||||
|
||||
Footage::StreamReference Footage::GetReferenceFromOutput(const QString &s) const
|
||||
{
|
||||
Stream::Type type = GetTypeFromOutput(s);
|
||||
|
||||
if (type != Stream::kUnknown) {
|
||||
bool ok;
|
||||
int index = s.mid(2).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
return StreamReference(this, type, index);
|
||||
}
|
||||
}
|
||||
Stream::Type type;
|
||||
int index;
|
||||
|
||||
if (GetReferenceFromOutput(s, &type, &index)) {
|
||||
return StreamReference(type, index);
|
||||
} else {
|
||||
return StreamReference();
|
||||
}
|
||||
}
|
||||
|
||||
int Footage::GetStreamTypeCount(Stream::Type type) const
|
||||
bool Footage::GetReferenceFromOutput(const QString &s, Stream::Type *type, int *index)
|
||||
{
|
||||
int count = 0;
|
||||
Stream::Type parse_type = GetTypeFromOutput(s);
|
||||
|
||||
if (parse_type != Stream::kUnknown) {
|
||||
bool ok;
|
||||
int parse_index = s.mid(2).toInt(&ok);
|
||||
|
||||
if (ok) {
|
||||
*type = parse_type;
|
||||
*index = parse_index;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
VideoParams Footage::GetFirstEnabledVideoStream() const
|
||||
{
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
if (it.key().type() == Stream::kVideo) {
|
||||
VideoParams vp = GetVideoParams(it.key().index());
|
||||
|
||||
if (vp.enabled()) {
|
||||
return vp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VideoParams();
|
||||
}
|
||||
|
||||
AudioParams Footage::GetFirstEnabledAudioStream() const
|
||||
{
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
if (it.key().type() == Stream::kAudio) {
|
||||
AudioParams ap = GetAudioParams(it.key().index());
|
||||
|
||||
if (ap.enabled()) {
|
||||
return ap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AudioParams();
|
||||
}
|
||||
|
||||
QVector<VideoParams> Footage::GetEnabledVideoStreams() const
|
||||
{
|
||||
QVector<VideoParams> list;
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
Stream s = GetStreamAt(it.key());
|
||||
if (it.key().type() == Stream::kVideo) {
|
||||
VideoParams vp = GetVideoParams(it.key().index());
|
||||
|
||||
if (s.type() == type) {
|
||||
count++;
|
||||
if (vp.enabled()) {
|
||||
list.append(vp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
return list;
|
||||
}
|
||||
|
||||
QVector<AudioParams> Footage::GetEnabledAudioStreams() const
|
||||
{
|
||||
QVector<AudioParams> list;
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
if (it.key().type() == Stream::kAudio) {
|
||||
AudioParams ap = GetAudioParams(it.key().index());
|
||||
|
||||
if (ap.enabled()) {
|
||||
list.append(ap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QVector<Footage::StreamReference> Footage::GetEnabledStreamsAsReferences() const
|
||||
{
|
||||
QVector<Footage::StreamReference> refs;
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
refs.append(StreamReference(it.key().type(), it.key().index()));
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
void Footage::Clear()
|
||||
@@ -255,9 +275,6 @@ void Footage::Clear()
|
||||
}
|
||||
outputs_for_streams_.clear();
|
||||
|
||||
// Reset stream count
|
||||
stream_count_ = 0;
|
||||
|
||||
// Clear decoder link
|
||||
decoder_.clear();
|
||||
|
||||
@@ -290,36 +307,6 @@ void Footage::set_timestamp(const qint64 &t)
|
||||
timestamp_ = t;
|
||||
}
|
||||
|
||||
int64_t Footage::GetTimeInTimebaseUnits(int index, const rational &time) const
|
||||
{
|
||||
Stream s = GetStreamAt(index);
|
||||
|
||||
if (!s.IsValid()) {
|
||||
return AV_NOPTS_VALUE;
|
||||
}
|
||||
|
||||
return Timecode::time_to_timestamp(time, s.timebase()) + s.start_time();
|
||||
}
|
||||
|
||||
int Footage::GetRealStreamIndex(Stream::Type type, int index) const
|
||||
{
|
||||
int lookup_index = 0;
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
Stream stream = GetStandardValue(it.value()).value<Stream>();
|
||||
|
||||
if (stream.type() == type) {
|
||||
if (lookup_index == index) {
|
||||
return it.key();
|
||||
} else {
|
||||
lookup_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString Footage::GetStringFromReference(Stream::Type type, int index)
|
||||
{
|
||||
QString type_string;
|
||||
@@ -335,32 +322,18 @@ QString Footage::GetStringFromReference(Stream::Type type, int index)
|
||||
return QStringLiteral("%1:%2").arg(type_string, QString::number(index));
|
||||
}
|
||||
|
||||
Footage::StreamReference Footage::GetReferenceFromRealIndex(int real_index) const
|
||||
int Footage::GetStreamIndex(Stream::Type type, int index) const
|
||||
{
|
||||
Stream s = GetStreamAt(real_index);
|
||||
|
||||
if (!s.IsValid()) {
|
||||
// Return invalid/null reference
|
||||
return StreamReference();
|
||||
}
|
||||
|
||||
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;
|
||||
if (type == Stream::kVideo) {
|
||||
return GetVideoParams(index).stream_index();
|
||||
} else if (type == Stream::kAudio) {
|
||||
return GetAudioParams(index).stream_index();
|
||||
} else {
|
||||
Stream temp = GetStreamAt(it.key());
|
||||
|
||||
if (temp.type() == s.type()) {
|
||||
index_in_type++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return StreamReference(this, s.type(), index_in_type);
|
||||
}
|
||||
|
||||
Stream::Type Footage::GetTypeFromOutput(const QString &s) const
|
||||
Stream::Type Footage::GetTypeFromOutput(const QString &s)
|
||||
{
|
||||
if (s.at(1) == ':') {
|
||||
if (s.at(0) == 'v') {
|
||||
@@ -375,55 +348,22 @@ Stream::Type Footage::GetTypeFromOutput(const QString &s) const
|
||||
return Stream::kUnknown;
|
||||
}
|
||||
|
||||
Stream Footage::GetFirstEnabledStreamOfType(Stream::Type type) const
|
||||
{
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
Stream stream = GetStandardValue(it.value()).value<Stream>();
|
||||
|
||||
if (stream.enabled() && stream.type() == type) {
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
return Stream();
|
||||
}
|
||||
|
||||
QVector<int> Footage::GetStreamIndexesOfType(Stream::Type type) const
|
||||
{
|
||||
QVector<int> indexes;
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
Stream stream = GetStandardValue(it.value()).value<Stream>();
|
||||
|
||||
if (stream.enabled() && stream.type() == type) {
|
||||
indexes.append(it.key());
|
||||
}
|
||||
}
|
||||
|
||||
return indexes;
|
||||
}
|
||||
|
||||
const QString &Footage::decoder() const
|
||||
{
|
||||
return decoder_;
|
||||
}
|
||||
|
||||
void Footage::set_decoder(const QString &id)
|
||||
{
|
||||
decoder_ = id;
|
||||
}
|
||||
|
||||
QIcon Footage::icon() const
|
||||
{
|
||||
if (valid_ && !inputs_for_stream_properties_.isEmpty()) {
|
||||
// Prioritize video > audio > image
|
||||
Stream s = GetFirstEnabledStreamOfType(Stream::kVideo);
|
||||
VideoParams s = GetFirstEnabledVideoStream();
|
||||
|
||||
if (s.IsValid() && s.video_type() != Stream::kVideoTypeStill) {
|
||||
if (s.is_valid() && s.video_type() != VideoParams::kVideoTypeStill) {
|
||||
return icon::Video;
|
||||
} else if (HasEnabledStreamsOfType(Stream::kAudio)) {
|
||||
} else if (HasEnabledAudioStreams()) {
|
||||
return icon::Audio;
|
||||
} else if (s.IsValid() && s.video_type() == Stream::kVideoTypeStill) {
|
||||
} else if (s.is_valid() && s.video_type() == VideoParams::kVideoTypeStill) {
|
||||
return icon::Image;
|
||||
}
|
||||
}
|
||||
@@ -433,40 +373,27 @@ QIcon Footage::icon() const
|
||||
|
||||
QString Footage::duration()
|
||||
{
|
||||
// Find longest stream duration
|
||||
Stream longest_stream;
|
||||
rational longest;
|
||||
// Try video first
|
||||
VideoParams video = GetFirstEnabledVideoStream();
|
||||
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
Stream s = GetStandardValue(it.value()).value<Stream>();
|
||||
if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) {
|
||||
int64_t duration = video.duration();
|
||||
rational frame_rate_timebase = video.frame_rate().flipped();
|
||||
|
||||
if (s.enabled() && (s.type() == Stream::kVideo || s.type() == Stream::kAudio)) {
|
||||
rational this_stream_dur = Timecode::timestamp_to_time(s.duration(), s.timebase());
|
||||
|
||||
if (this_stream_dur > longest) {
|
||||
longest_stream = s;
|
||||
longest = this_stream_dur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (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();
|
||||
|
||||
if (longest_stream.timebase() != frame_rate_timebase) {
|
||||
if (video.time_base() != 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);
|
||||
duration = Timecode::rescale_timestamp_ceil(duration, video.time_base(), frame_rate_timebase);
|
||||
}
|
||||
|
||||
return Timecode::timestamp_to_timecode(duration,
|
||||
frame_rate_timebase,
|
||||
Core::instance()->GetTimecodeDisplay());
|
||||
}
|
||||
} else if (longest_stream.type() == Stream::kAudio) {
|
||||
|
||||
// Try audio second
|
||||
AudioParams audio = GetFirstEnabledAudioStream();
|
||||
|
||||
if (audio.is_valid()) {
|
||||
// If we're showing in a timecode, we prefer showing audio in seconds instead
|
||||
Timecode::Display display = Core::instance()->GetTimecodeDisplay();
|
||||
if (display == Timecode::kTimecodeDropFrame
|
||||
@@ -474,12 +401,12 @@ QString Footage::duration()
|
||||
display = Timecode::kTimecodeSeconds;
|
||||
}
|
||||
|
||||
return Timecode::timestamp_to_timecode(longest_stream.duration(),
|
||||
longest_stream.timebase(),
|
||||
return Timecode::timestamp_to_timecode(audio.duration(),
|
||||
audio.time_base(),
|
||||
display);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, return nothing
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -489,47 +416,51 @@ QString Footage::rate()
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (HasEnabledStreamsOfType(Stream::kVideo)) {
|
||||
if (HasEnabledVideoStreams()) {
|
||||
// This is a video editor, prioritize video streams
|
||||
Stream video_stream = GetFirstEnabledStreamOfType(Stream::kVideo);
|
||||
VideoParams video_stream = GetFirstEnabledVideoStream();
|
||||
|
||||
if (video_stream.video_type() != Stream::kVideoTypeStill) {
|
||||
if (video_stream.video_type() != VideoParams::kVideoTypeStill) {
|
||||
return QCoreApplication::translate("Footage", "%1 FPS").arg(video_stream.frame_rate().toDouble());
|
||||
}
|
||||
} else if (HasEnabledStreamsOfType(Stream::kAudio)) {
|
||||
} else if (HasEnabledAudioStreams()) {
|
||||
// No video streams, return audio
|
||||
Stream audio_stream = GetStreamAt(0);
|
||||
AudioParams audio_stream = GetFirstEnabledAudioStream();
|
||||
return QCoreApplication::translate("Footage", "%1 Hz").arg(audio_stream.sample_rate());
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
quint64 Footage::get_enabled_stream_flags() const
|
||||
bool Footage::HasEnabledVideoStreams() const
|
||||
{
|
||||
quint64 enabled_streams = 0;
|
||||
|
||||
for (int i=0; i<stream_count_; i++) {
|
||||
if (IsStreamEnabled(i)) {
|
||||
enabled_streams |= (1 << i);
|
||||
}
|
||||
return GetFirstEnabledVideoStream().is_valid();
|
||||
}
|
||||
|
||||
return enabled_streams;
|
||||
}
|
||||
|
||||
bool Footage::HasEnabledStreamsOfType(const Stream::Type &type) const
|
||||
bool Footage::HasEnabledAudioStreams() const
|
||||
{
|
||||
// Return true if any streams are video streams
|
||||
for (int i=0; i<stream_count_; i++) {
|
||||
Stream s = GetStreamAt(i);
|
||||
return GetFirstEnabledAudioStream().is_valid();
|
||||
}
|
||||
|
||||
if (s.enabled() && s.type() == type) {
|
||||
return true;
|
||||
QString Footage::DescribeVideoStream(const VideoParams ¶ms)
|
||||
{
|
||||
if (params.video_type() == VideoParams::kVideoTypeStill) {
|
||||
return tr("%1: Image - %2x%3").arg(QString::number(params.stream_index()),
|
||||
QString::number(params.width()),
|
||||
QString::number(params.height()));
|
||||
} else {
|
||||
return tr("%1: Video - %2x%3").arg(QString::number(params.stream_index()),
|
||||
QString::number(params.width()),
|
||||
QString::number(params.height()));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
QString Footage::DescribeAudioStream(const AudioParams ¶ms)
|
||||
{
|
||||
return tr("%1: Audio - %2 Channel(s), %3Hz")
|
||||
.arg(QString::number(params.stream_index()),
|
||||
QString::number(params.channel_count()),
|
||||
QString::number(params.sample_rate()));
|
||||
}
|
||||
|
||||
bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
|
||||
@@ -552,6 +483,7 @@ bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
|
||||
return true;
|
||||
}
|
||||
}*/
|
||||
Q_UNUSED(footage)
|
||||
|
||||
// Simplified, since our footage node is much more tolerant, we'll try this
|
||||
return true;
|
||||
@@ -576,9 +508,9 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
|
||||
QString fn = filename();
|
||||
|
||||
if (!fn.isEmpty()) {
|
||||
Stream stream = GetStreamAt(GetReferenceFromOutput(output));
|
||||
VideoParams params = GetVideoParams(ref.index());
|
||||
|
||||
if (stream.IsValid()) {
|
||||
if (params.is_valid()) {
|
||||
// Add footage details to hash
|
||||
|
||||
// Footage filename
|
||||
@@ -593,23 +525,23 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
|
||||
if (ref.type() == Stream::kVideo) {
|
||||
// Current color config and space
|
||||
hash.addData(project()->color_manager()->GetConfigFilename().toUtf8());
|
||||
hash.addData(stream.colorspace().toUtf8());
|
||||
hash.addData(params.colorspace().toUtf8());
|
||||
|
||||
// Alpha associated setting
|
||||
hash.addData(QString::number(stream.premultiplied_alpha()).toUtf8());
|
||||
hash.addData(QString::number(params.premultiplied_alpha()).toUtf8());
|
||||
|
||||
// Pixel aspect ratio
|
||||
hash.addData(reinterpret_cast<const char*>(&stream.pixel_aspect_ratio()), sizeof(stream.pixel_aspect_ratio()));
|
||||
hash.addData(reinterpret_cast<const char*>(¶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<const char*>(&video_ts), sizeof(int64_t));
|
||||
|
||||
// Add start time - used for both image sequences and video streams
|
||||
hash.addData(QString::number(stream.start_time()).toUtf8());
|
||||
hash.addData(QString::number(params.start_time()).toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,8 +559,23 @@ NodeValueTable Footage::Value(const QString &output, NodeValueDatabase &value) c
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
// If the file exists and the reference is valid, push a footage job to the renderer
|
||||
if (QFileInfo(file).exists() && ref.IsValid()) {
|
||||
table.Push(NodeValue::kFootageJob, QVariant::fromValue(ref), this);
|
||||
if (QFileInfo(file).exists()) {
|
||||
FootageJob job(decoder_, filename(), ref.type());
|
||||
|
||||
if (ref.type() == Stream::kVideo) {
|
||||
VideoParams vp = GetVideoParams(ref.index());
|
||||
|
||||
if (vp.colorspace().isEmpty()) {
|
||||
vp.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace());
|
||||
}
|
||||
|
||||
job.set_video_params(vp);
|
||||
} else {
|
||||
job.set_audio_params(GetAudioParams(ref.index()));
|
||||
job.set_cache_path(project()->cache_path());
|
||||
}
|
||||
|
||||
table.Push(NodeValue::kFootageJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
|
||||
return table;
|
||||
@@ -659,13 +606,20 @@ void Footage::UpdateTooltip()
|
||||
if (valid_) {
|
||||
QString tip = QCoreApplication::translate("Footage", "Filename: %1").arg(filename());
|
||||
|
||||
if (!inputs_for_stream_properties_.isEmpty()) {
|
||||
for (int i=0; i<stream_count_; i++) {
|
||||
Stream s = GetStreamAt(i);
|
||||
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
|
||||
if (it.key().type() == Stream::kVideo) {
|
||||
VideoParams p = GetVideoParams(it.key().index());
|
||||
|
||||
if (s.enabled()) {
|
||||
if (p.enabled()) {
|
||||
tip.append("\n");
|
||||
tip.append(DescribeStream(i));
|
||||
tip.append(DescribeVideoStream(p));
|
||||
}
|
||||
} else if (it.key().type() == Stream::kAudio) {
|
||||
AudioParams p = GetAudioParams(it.key().index());
|
||||
|
||||
if (p.enabled()) {
|
||||
tip.append("\n");
|
||||
tip.append(DescribeAudioStream(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -676,77 +630,22 @@ void Footage::UpdateTooltip()
|
||||
}
|
||||
}
|
||||
|
||||
Footage::MetadataCache Footage::LoadStreamCache(const QString &filename)
|
||||
void Footage::AddStreamAsInput(Stream::Type type, int index, QVariant value)
|
||||
{
|
||||
MetadataCache cache;
|
||||
QFile file(filename);
|
||||
QString input_id = GetInputIDOfIndex(type, index);
|
||||
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QXmlStreamReader reader(&file);
|
||||
StreamReference ref(type, index);
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("streamcache")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("decoder")) {
|
||||
cache.decoder = reader.readElementText();
|
||||
} else if (reader.name() == QStringLiteral("streams")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("stream")) {
|
||||
Stream s;
|
||||
s.Load(&reader);
|
||||
cache.streams.append(s);
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
// Create input for parameters
|
||||
AddInput(input_id, type == Stream::kVideo ? NodeValue::kVideoParams : NodeValue::kAudioParams,
|
||||
InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
|
||||
SetStandardValue(input_id, value);
|
||||
inputs_for_stream_properties_.insert(ref, input_id);
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
bool Footage::SaveStreamCache(const QString &filename, const Footage::MetadataCache &data)
|
||||
{
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamWriter writer(&file);
|
||||
|
||||
writer.writeStartDocument();
|
||||
|
||||
writer.writeStartElement(QStringLiteral("streamcache"));
|
||||
|
||||
writer.writeTextElement(QStringLiteral("decoder"), data.decoder);
|
||||
|
||||
writer.writeStartElement(QStringLiteral("streams"));
|
||||
|
||||
foreach (const Stream& s, data.streams) {
|
||||
writer.writeStartElement(QStringLiteral("stream"));
|
||||
s.Save(&writer);
|
||||
writer.writeEndElement(); // stream
|
||||
}
|
||||
|
||||
writer.writeEndElement(); // streams
|
||||
|
||||
writer.writeEndElement(); // streamcache
|
||||
|
||||
writer.writeEndDocument();
|
||||
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
// Create output for stream
|
||||
QString output_id = GetStringFromReference(type, index);
|
||||
AddOutput(output_id);
|
||||
outputs_for_streams_.insert(ref, output_id);
|
||||
}
|
||||
|
||||
void Footage::CheckFootage()
|
||||
@@ -766,26 +665,45 @@ void Footage::CheckFootage()
|
||||
}
|
||||
}
|
||||
|
||||
QString Footage::StreamReference::video_colorspace(bool default_if_empty) const
|
||||
/*QString Footage::StreamReference::video_colorspace(bool default_if_empty) const
|
||||
{
|
||||
if (IsValid()) {
|
||||
Stream stream = footage_->GetStreamAt(type_, index_);
|
||||
VideoParams params = footage_->GetVideoParams(index_);
|
||||
|
||||
if (params.is_valid()) {
|
||||
if (params.colorspace().isEmpty() && default_if_empty) {
|
||||
|
||||
if (stream.IsValid()) {
|
||||
if (stream.colorspace().isEmpty() && default_if_empty) {
|
||||
return footage_->project()->color_manager()->GetDefaultInputColorSpace();
|
||||
} else {
|
||||
return stream.colorspace();
|
||||
return params.colorspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
}*/
|
||||
|
||||
uint qHash(const Footage::StreamReference &ref, uint seed)
|
||||
{
|
||||
return qHash(ref.footage(), seed) ^ qHash(ref.type(), seed) ^ qHash(ref.index(), seed);
|
||||
return qHash(ref.type(), seed) ^ qHash(ref.index(), seed);
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref)
|
||||
{
|
||||
out << static_cast<int>(ref.type()) << ref.index();
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref)
|
||||
{
|
||||
int type;
|
||||
int index;
|
||||
|
||||
in >> type >> index;
|
||||
|
||||
ref = Footage::StreamReference(static_cast<Stream::Type>(type), index);
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,11 @@
|
||||
#include <QDateTime>
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "footagedescription.h"
|
||||
#include "node/node.h"
|
||||
#include "project/item/item.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "stream.h"
|
||||
#include "timeline/timelinepoints.h"
|
||||
|
||||
@@ -143,26 +146,33 @@ public:
|
||||
public:
|
||||
StreamReference()
|
||||
{
|
||||
footage_ = nullptr;
|
||||
type_ = Stream::kUnknown;
|
||||
index_ = -1;
|
||||
}
|
||||
|
||||
StreamReference(const Footage* footage, Stream::Type type, int index)
|
||||
StreamReference(Stream::Type type, int index)
|
||||
{
|
||||
footage_ = footage;
|
||||
type_ = type;
|
||||
index_ = index;
|
||||
}
|
||||
|
||||
bool operator==(const StreamReference& rhs) const
|
||||
{
|
||||
return footage_ == rhs.footage_ && type_ == rhs.type_ && index_ == rhs.index_;
|
||||
return type_ == rhs.type_ && index_ == rhs.index_;
|
||||
}
|
||||
|
||||
bool operator<(const StreamReference& rhs) const
|
||||
{
|
||||
if (type_ != rhs.type_) {
|
||||
return type_ < rhs.type_;
|
||||
}
|
||||
|
||||
return index_ < rhs.index_;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return footage_ && index_ >= 0;
|
||||
return type_ != Stream::kUnknown && index_ >= 0;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
@@ -170,11 +180,6 @@ public:
|
||||
*this = StreamReference();
|
||||
}
|
||||
|
||||
const Footage* footage() const
|
||||
{
|
||||
return footage_;
|
||||
}
|
||||
|
||||
Stream::Type type() const
|
||||
{
|
||||
return type_;
|
||||
@@ -185,138 +190,68 @@ public:
|
||||
return index_;
|
||||
}
|
||||
|
||||
Stream GetStream() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return footage_->GetStreamAt(*this);
|
||||
} else {
|
||||
return Stream();
|
||||
}
|
||||
}
|
||||
|
||||
int64_t GetTimeInTimebaseUnits(const rational& timecode) const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return footage_->GetTimeInTimebaseUnits(type_, index_, timecode);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int GetRealStreamIndex() const
|
||||
{
|
||||
if (IsValid()) {
|
||||
return footage_->GetRealStreamIndex(*this);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
QString filename() const
|
||||
{
|
||||
if (footage_) {
|
||||
return footage_->filename();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
VideoParams video_params() const
|
||||
{
|
||||
return GetStream().video_params();
|
||||
}
|
||||
|
||||
AudioParams audio_params() const
|
||||
{
|
||||
return GetStream().audio_params();
|
||||
}
|
||||
|
||||
int64_t duration() const
|
||||
{
|
||||
return GetStream().duration();
|
||||
}
|
||||
|
||||
QString video_colorspace(bool default_if_empty = true) const;
|
||||
|
||||
private:
|
||||
const Footage* footage_;
|
||||
Stream::Type type_;
|
||||
int index_;
|
||||
|
||||
};
|
||||
|
||||
Stream GetStreamAt(int index) const
|
||||
{
|
||||
return GetStandardValue(GetInputIDOfIndex(index)).value<Stream>();
|
||||
}
|
||||
|
||||
Stream GetStreamAt(Stream::Type type, int index_within_type) const
|
||||
{
|
||||
return GetStreamAt(GetRealStreamIndex(type, index_within_type));
|
||||
}
|
||||
|
||||
Stream GetStreamAt(const StreamReference& ref) const
|
||||
{
|
||||
return GetStreamAt(ref.type(), ref.index());
|
||||
}
|
||||
|
||||
void SetStreamAt(int index, const Stream& stream)
|
||||
{
|
||||
SetStandardValue(GetInputIDOfIndex(index), QVariant::fromValue(stream));
|
||||
}
|
||||
|
||||
void SetStreamAt(Stream::Type type, int index_within_type, const Stream& stream)
|
||||
{
|
||||
SetStreamAt(GetRealStreamIndex(type, index_within_type), stream);
|
||||
}
|
||||
|
||||
void SetStreamAt(const StreamReference& ref, const Stream& stream)
|
||||
{
|
||||
SetStreamAt(ref.type(), ref.index(), stream);
|
||||
}
|
||||
|
||||
int64_t GetTimeInTimebaseUnits(int index, const rational& time) const;
|
||||
int64_t GetTimeInTimebaseUnits(Stream::Type type, int index_within_type, const rational& time) const
|
||||
{
|
||||
return GetTimeInTimebaseUnits(GetRealStreamIndex(type, index_within_type), time);
|
||||
}
|
||||
|
||||
int GetRealStreamIndex(Stream::Type type, int index_within_type) const;
|
||||
int GetRealStreamIndex(const StreamReference& ref) const
|
||||
{
|
||||
return GetRealStreamIndex(ref.type(), ref.index());
|
||||
}
|
||||
|
||||
static QString GetStringFromReference(Stream::Type type, int index);
|
||||
static QString GetStringFromReference(const StreamReference& ref)
|
||||
{
|
||||
return GetStringFromReference(ref.type(), ref.index());
|
||||
}
|
||||
|
||||
int GetStreamIndex(Stream::Type type, int index) const;
|
||||
int GetStreamIndex(const StreamReference& ref) const
|
||||
{
|
||||
return GetStreamIndex(ref.type(), ref.index());
|
||||
}
|
||||
|
||||
int GetTotalStreamCount() const
|
||||
{
|
||||
return inputs_for_stream_properties_.size();
|
||||
}
|
||||
|
||||
StreamReference GetReferenceFromRealIndex(int real_index) const;
|
||||
|
||||
Stream::Type GetTypeFromOutput(const QString& output) const;
|
||||
static Stream::Type GetTypeFromOutput(const QString& output);
|
||||
|
||||
StreamReference GetReferenceFromOutput(const QString& s) const;
|
||||
static bool GetReferenceFromOutput(const QString& s, Stream::Type* type, int* index);
|
||||
|
||||
int GetStreamCount() const
|
||||
VideoParams GetVideoParams(int index) const
|
||||
{
|
||||
return stream_count_;
|
||||
return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index))).value<VideoParams>();
|
||||
}
|
||||
|
||||
int GetStreamTypeCount(Stream::Type type) const;
|
||||
|
||||
bool IsStreamEnabled(int index) const
|
||||
void SetVideoParams(int index, const VideoParams& p)
|
||||
{
|
||||
return GetStreamAt(index).enabled();
|
||||
SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index)), QVariant::fromValue(p));
|
||||
}
|
||||
|
||||
Stream GetFirstEnabledStreamOfType(Stream::Type type) const;
|
||||
VideoParams GetFirstEnabledVideoStream() const;
|
||||
|
||||
QVector<int> GetStreamIndexesOfType(Stream::Type type) const;
|
||||
AudioParams GetAudioParams(int index) const
|
||||
{
|
||||
return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index))).value<AudioParams>();
|
||||
}
|
||||
|
||||
void SetAudioParams(int index, const AudioParams& p)
|
||||
{
|
||||
SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index)), QVariant::fromValue(p));
|
||||
}
|
||||
|
||||
AudioParams GetFirstEnabledAudioStream() const;
|
||||
|
||||
QVector<VideoParams> GetEnabledVideoStreams() const;
|
||||
|
||||
QVector<AudioParams> GetEnabledAudioStreams() const;
|
||||
|
||||
Stream::Type GetStreamType(int index);
|
||||
|
||||
QVector<StreamReference> GetEnabledStreamsAsReferences() const;
|
||||
|
||||
/**
|
||||
* @brief Get the Decoder ID set when this Footage was probed
|
||||
*
|
||||
@@ -326,27 +261,17 @@ public:
|
||||
*/
|
||||
const QString& decoder() const;
|
||||
|
||||
/**
|
||||
* @brief Used by decoders when they Probe to attach itself to this Footage
|
||||
*/
|
||||
void set_decoder(const QString& id);
|
||||
|
||||
virtual QIcon icon() const override;
|
||||
|
||||
virtual QString duration() override;
|
||||
|
||||
virtual QString rate() override;
|
||||
|
||||
quint64 get_enabled_stream_flags() const;
|
||||
bool HasEnabledVideoStreams() const;
|
||||
bool HasEnabledAudioStreams() const;
|
||||
|
||||
/**
|
||||
* @brief Check if this footage has streams of a certain type
|
||||
*
|
||||
* @param type
|
||||
*
|
||||
* The stream type to check for
|
||||
*/
|
||||
bool HasEnabledStreamsOfType(const Stream::Type& type) const;
|
||||
static QString DescribeVideoStream(const VideoParams& params);
|
||||
static QString DescribeAudioStream(const AudioParams& params);
|
||||
|
||||
static bool CompareFootageToFile(Footage* footage, const QString& filename);
|
||||
static bool CompareFootageToItsFilename(Footage* footage);
|
||||
@@ -374,13 +299,6 @@ protected:
|
||||
virtual void InputValueChangedEvent(const QString &input, int element) override;
|
||||
|
||||
private:
|
||||
QString DescribeStream(int index) const;
|
||||
|
||||
struct MetadataCache {
|
||||
QString decoder;
|
||||
Streams streams;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Update the icon based on the Footage status
|
||||
*
|
||||
@@ -397,24 +315,22 @@ private:
|
||||
*/
|
||||
void UpdateTooltip();
|
||||
|
||||
MetadataCache LoadStreamCache(const QString& filename);
|
||||
void AddStreamAsInput(Stream::Type type, int index, QVariant value);
|
||||
|
||||
bool SaveStreamCache(const QString& filename, const MetadataCache& data);
|
||||
|
||||
static QString GetInputIDOfIndex(int index)
|
||||
static QString GetInputIDOfIndex(Stream::Type type, int index)
|
||||
{
|
||||
return kStreamPropertiesFormat.arg(index);
|
||||
return kStreamPropertiesFormat.arg(GetStringFromReference(type, index));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief List of dynamic inputs added for stream properties
|
||||
*/
|
||||
QMap<int, QString> inputs_for_stream_properties_;
|
||||
QMap<StreamReference, QString> inputs_for_stream_properties_;
|
||||
|
||||
/**
|
||||
* @brief List of dynamic outputs added for streams
|
||||
*/
|
||||
QMap<int, QString> outputs_for_streams_;
|
||||
QMap<StreamReference, QString> outputs_for_streams_;
|
||||
|
||||
/**
|
||||
* @brief Internal timestamp object
|
||||
@@ -426,8 +342,6 @@ private:
|
||||
*/
|
||||
QString decoder_;
|
||||
|
||||
int stream_count_;
|
||||
|
||||
bool valid_;
|
||||
|
||||
const QAtomicInt* cancelled_;
|
||||
@@ -439,6 +353,10 @@ private slots:
|
||||
|
||||
uint qHash(const Footage::StreamReference& ref, uint seed = 0);
|
||||
|
||||
QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref);
|
||||
|
||||
QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref);
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::Footage::StreamReference)
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "footagedescription.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
bool FootageDescription::Load(const QString &filename)
|
||||
{
|
||||
// Reset self
|
||||
*this = FootageDescription();
|
||||
|
||||
QFile file(filename);
|
||||
|
||||
if (file.open(QFile::ReadOnly)) {
|
||||
QXmlStreamReader reader(&file);
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("streamcache")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("decoder")) {
|
||||
decoder_ = reader.readElementText();
|
||||
} else if (reader.name() == QStringLiteral("streams")) {
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("video")) {
|
||||
VideoParams vp;
|
||||
vp.Load(&reader);
|
||||
AddVideoStream(vp);
|
||||
} else if (reader.name() == QStringLiteral("audio")) {
|
||||
AudioParams ap;
|
||||
ap.Load(&reader);
|
||||
AddAudioStream(ap);
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FootageDescription::Save(const QString &filename) const
|
||||
{
|
||||
QFile file(filename);
|
||||
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamWriter writer(&file);
|
||||
|
||||
writer.writeStartDocument();
|
||||
|
||||
writer.writeStartElement(QStringLiteral("streamcache"));
|
||||
|
||||
writer.writeTextElement(QStringLiteral("decoder"), decoder_);
|
||||
|
||||
writer.writeStartElement(QStringLiteral("streams"));
|
||||
|
||||
foreach (const VideoParams& vp, video_streams_) {
|
||||
writer.writeStartElement(QStringLiteral("video"));
|
||||
vp.Save(&writer);
|
||||
writer.writeEndElement(); // video
|
||||
}
|
||||
|
||||
foreach (const AudioParams& ap, audio_streams_) {
|
||||
writer.writeStartElement(QStringLiteral("audio"));
|
||||
ap.Save(&writer);
|
||||
writer.writeEndElement(); // audio
|
||||
}
|
||||
|
||||
writer.writeEndElement(); // streams
|
||||
|
||||
writer.writeEndElement(); // streamcache
|
||||
|
||||
writer.writeEndDocument();
|
||||
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef FOOTAGEDESCRIPTION_H
|
||||
#define FOOTAGEDESCRIPTION_H
|
||||
|
||||
#include "render/audioparams.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "stream.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class FootageDescription
|
||||
{
|
||||
public:
|
||||
FootageDescription(const QString& decoder = QString()) :
|
||||
decoder_(decoder)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return !decoder_.isEmpty() && (!video_streams_.isEmpty() || !audio_streams_.isEmpty());
|
||||
}
|
||||
|
||||
const QString& decoder() const
|
||||
{
|
||||
return decoder_;
|
||||
}
|
||||
|
||||
void AddVideoStream(const VideoParams& video_params)
|
||||
{
|
||||
Q_ASSERT(!HasStreamIndex(video_params.stream_index()));
|
||||
|
||||
video_streams_.append(video_params);
|
||||
}
|
||||
|
||||
void AddAudioStream(const AudioParams& audio_params)
|
||||
{
|
||||
Q_ASSERT(!HasStreamIndex(audio_params.stream_index()));
|
||||
|
||||
audio_streams_.append(audio_params);
|
||||
}
|
||||
|
||||
Stream::Type GetTypeOfStream(int index)
|
||||
{
|
||||
if (StreamIsVideo(index)) {
|
||||
return Stream::kVideo;
|
||||
} else if (StreamIsAudio(index)) {
|
||||
return Stream::kAudio;
|
||||
} else {
|
||||
return Stream::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
bool StreamIsVideo(int index) const
|
||||
{
|
||||
foreach (const VideoParams& vp, video_streams_) {
|
||||
if (vp.stream_index() == index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StreamIsAudio(int index) const
|
||||
{
|
||||
foreach (const AudioParams& ap, audio_streams_) {
|
||||
if (ap.stream_index() == index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasStreamIndex(int index) const
|
||||
{
|
||||
return StreamIsVideo(index) || StreamIsAudio(index);
|
||||
}
|
||||
|
||||
bool Load(const QString& filename);
|
||||
|
||||
bool Save(const QString& filename) const;
|
||||
|
||||
const QVector<VideoParams>& GetVideoStreams() const
|
||||
{
|
||||
return video_streams_;
|
||||
}
|
||||
|
||||
const QVector<AudioParams>& GetAudioStreams() const
|
||||
{
|
||||
return audio_streams_;
|
||||
}
|
||||
|
||||
private:
|
||||
QString decoder_;
|
||||
|
||||
QVector<VideoParams> video_streams_;
|
||||
|
||||
QVector<AudioParams> audio_streams_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FOOTAGEDESCRIPTION_H
|
||||
@@ -1,131 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2020 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "stream.h"
|
||||
|
||||
#include "common/xmlutils.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
void Stream::Load(QXmlStreamReader *reader)
|
||||
{
|
||||
XMLAttributeLoop(reader, attr) {
|
||||
if (attr.name() == QStringLiteral("type")) {
|
||||
*this = Stream(static_cast<Type>(attr.value().toInt()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("global")) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("timebase")) {
|
||||
timebase_ = rational::fromString(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("duration")) {
|
||||
duration_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("channelcount")) {
|
||||
channel_count_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("enabled")) {
|
||||
enabled_ = reader->readElementText().toInt();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader->name() == QStringLiteral("video")) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("width")) {
|
||||
width_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("height")) {
|
||||
height_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("pixelaspectratio")) {
|
||||
pixel_aspect_ratio_ = rational::fromString(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("videotype")) {
|
||||
video_type_ = static_cast<VideoType>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("interlacing")) {
|
||||
interlacing_ = static_cast<VideoParams::Interlacing>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("pixelformat")) {
|
||||
pixel_format_ = static_cast<VideoParams::Format>(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("framerate")) {
|
||||
frame_rate_ = rational::fromString(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("starttime")) {
|
||||
start_time_ = reader->readElementText().toLongLong();
|
||||
} else if (reader->name() == QStringLiteral("premultipliedalpha")) {
|
||||
premultiplied_alpha_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("colorspace")) {
|
||||
colorspace_ = reader->readElementText();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (reader->name() == QStringLiteral("audio")) {
|
||||
|
||||
while (XMLReadNextStartElement(reader)) {
|
||||
if (reader->name() == QStringLiteral("samplerate")) {
|
||||
sample_rate_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("channellayout")) {
|
||||
channel_layout_ = reader->readElementText().toULongLong();
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
reader->skipCurrentElement();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::Save(QXmlStreamWriter *writer) const
|
||||
{
|
||||
writer->writeAttribute(QStringLiteral("type"), QString::number(type_));
|
||||
|
||||
writer->writeStartElement(QStringLiteral("global"));
|
||||
writer->writeTextElement(QStringLiteral("timebase"), timebase_.toString());
|
||||
writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_));
|
||||
writer->writeTextElement(QStringLiteral("channelcount"), QString::number(channel_count_));
|
||||
writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_));
|
||||
writer->writeEndElement(); // global
|
||||
|
||||
writer->writeStartElement(QStringLiteral("video"));
|
||||
writer->writeTextElement(QStringLiteral("width"), QString::number(width_));
|
||||
writer->writeTextElement(QStringLiteral("height"), QString::number(height_));
|
||||
writer->writeTextElement(QStringLiteral("pixelaspectratio"), pixel_aspect_ratio_.toString());
|
||||
writer->writeTextElement(QStringLiteral("videotype"), QString::number(video_type_));
|
||||
writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_));
|
||||
writer->writeTextElement(QStringLiteral("pixelformat"), QString::number(pixel_format_));
|
||||
writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString());
|
||||
writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_));
|
||||
writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_));
|
||||
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
|
||||
writer->writeEndElement(); // video
|
||||
|
||||
writer->writeStartElement(QStringLiteral("audio"));
|
||||
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_));
|
||||
writer->writeTextElement(QStringLiteral("channellayout"), QString::number(channel_layout_));
|
||||
writer->writeEndElement(); // audio
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,17 +21,10 @@
|
||||
#ifndef STREAM_H
|
||||
#define STREAM_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
namespace olive {
|
||||
|
||||
class Stream {
|
||||
class Stream
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
kUnknown = -1,
|
||||
@@ -41,284 +34,8 @@ public:
|
||||
kSubtitle,
|
||||
kAttachment
|
||||
};
|
||||
|
||||
enum VideoType {
|
||||
kVideoTypeVideo,
|
||||
kVideoTypeStill,
|
||||
kVideoTypeImageSequence
|
||||
};
|
||||
|
||||
Stream(Type type = kUnknown) :
|
||||
type_(type)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return type_ != kUnknown;
|
||||
}
|
||||
|
||||
Type type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
const rational& timebase() const
|
||||
{
|
||||
return timebase_;
|
||||
}
|
||||
|
||||
void set_timebase(const rational& timebase)
|
||||
{
|
||||
timebase_ = timebase;
|
||||
}
|
||||
|
||||
int64_t duration() const
|
||||
{
|
||||
return duration_;
|
||||
}
|
||||
|
||||
void set_duration(int64_t duration)
|
||||
{
|
||||
duration_ = duration;
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return channel_count_;
|
||||
}
|
||||
|
||||
void set_channel_count(int c)
|
||||
{
|
||||
channel_count_ = c;
|
||||
}
|
||||
|
||||
bool enabled() const
|
||||
{
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
void set_enabled(bool e)
|
||||
{
|
||||
enabled_ = e;
|
||||
}
|
||||
|
||||
int width() const
|
||||
{
|
||||
return width_;
|
||||
}
|
||||
|
||||
void set_width(int w)
|
||||
{
|
||||
width_ = w;
|
||||
}
|
||||
|
||||
int height() const
|
||||
{
|
||||
return height_;
|
||||
}
|
||||
|
||||
void set_height(int h)
|
||||
{
|
||||
height_ = h;
|
||||
}
|
||||
|
||||
const rational& pixel_aspect_ratio() const
|
||||
{
|
||||
return pixel_aspect_ratio_;
|
||||
}
|
||||
|
||||
void set_pixel_aspect_ratio(const rational& pixel_aspect_ratio)
|
||||
{
|
||||
pixel_aspect_ratio_ = pixel_aspect_ratio;
|
||||
}
|
||||
|
||||
VideoType video_type() const
|
||||
{
|
||||
return video_type_;
|
||||
}
|
||||
|
||||
void set_video_type(VideoType t)
|
||||
{
|
||||
video_type_ = t;
|
||||
}
|
||||
|
||||
VideoParams::Interlacing interlacing() const
|
||||
{
|
||||
return interlacing_;
|
||||
}
|
||||
|
||||
void set_interlacing(VideoParams::Interlacing interlacing)
|
||||
{
|
||||
interlacing_ = interlacing;
|
||||
}
|
||||
|
||||
VideoParams::Format pixel_format() const
|
||||
{
|
||||
return pixel_format_;
|
||||
}
|
||||
|
||||
void set_pixel_format(VideoParams::Format pixel_format)
|
||||
{
|
||||
pixel_format_ = pixel_format;
|
||||
}
|
||||
|
||||
const rational& frame_rate() const
|
||||
{
|
||||
return frame_rate_;
|
||||
}
|
||||
|
||||
void set_frame_rate(const rational& frame_rate)
|
||||
{
|
||||
frame_rate_ = frame_rate;
|
||||
}
|
||||
|
||||
int64_t start_time() const
|
||||
{
|
||||
return start_time_;
|
||||
}
|
||||
|
||||
void set_start_time(int64_t start_time)
|
||||
{
|
||||
start_time_ = start_time;
|
||||
}
|
||||
|
||||
bool premultiplied_alpha() const
|
||||
{
|
||||
return premultiplied_alpha_;
|
||||
}
|
||||
|
||||
void set_premultiplied_alpha(bool premultiplied_alpha)
|
||||
{
|
||||
premultiplied_alpha_ = premultiplied_alpha;
|
||||
}
|
||||
|
||||
const QString& colorspace() const
|
||||
{
|
||||
return colorspace_;
|
||||
}
|
||||
|
||||
void set_colorspace(const QString& c)
|
||||
{
|
||||
colorspace_ = c;
|
||||
}
|
||||
|
||||
int sample_rate() const
|
||||
{
|
||||
return sample_rate_;
|
||||
}
|
||||
|
||||
void set_sample_rate(int sample_rate)
|
||||
{
|
||||
sample_rate_ = sample_rate;
|
||||
}
|
||||
|
||||
uint64_t channel_layout() const
|
||||
{
|
||||
return channel_layout_;
|
||||
}
|
||||
|
||||
void set_channel_layout(uint64_t channel_layout)
|
||||
{
|
||||
channel_layout_ = channel_layout;
|
||||
}
|
||||
|
||||
VideoParams video_params() const
|
||||
{
|
||||
if (type_ == kVideo) {
|
||||
return VideoParams(width_, height_, timebase_,
|
||||
pixel_format_, channel_count_, pixel_aspect_ratio_,
|
||||
interlacing_);
|
||||
} else {
|
||||
return VideoParams();
|
||||
}
|
||||
}
|
||||
|
||||
AudioParams audio_params() const
|
||||
{
|
||||
if (type_ == kAudio) {
|
||||
return AudioParams(sample_rate_, channel_layout_, AudioParams::kInternalFormat);
|
||||
} else {
|
||||
return AudioParams();
|
||||
}
|
||||
}
|
||||
|
||||
void Load(QXmlStreamReader* reader);
|
||||
|
||||
void Save(QXmlStreamWriter* writer) const;
|
||||
|
||||
QByteArray toBytes() const
|
||||
{
|
||||
QByteArray arr;
|
||||
|
||||
arr.append(reinterpret_cast<const char*>(&type_), sizeof(type_));
|
||||
arr.append(reinterpret_cast<const char*>(&timebase_), sizeof(timebase_));
|
||||
arr.append(reinterpret_cast<const char*>(&duration_), sizeof(duration_));
|
||||
arr.append(reinterpret_cast<const char*>(&channel_count_), sizeof(channel_count_));
|
||||
arr.append(reinterpret_cast<const char*>(&enabled_), sizeof(enabled_));
|
||||
arr.append(reinterpret_cast<const char*>(&width_), sizeof(width_));
|
||||
arr.append(reinterpret_cast<const char*>(&height_), sizeof(height_));
|
||||
arr.append(reinterpret_cast<const char*>(&pixel_aspect_ratio_), sizeof(pixel_aspect_ratio_));
|
||||
arr.append(reinterpret_cast<const char*>(&video_type_), sizeof(video_type_));
|
||||
arr.append(reinterpret_cast<const char*>(&interlacing_), sizeof(interlacing_));
|
||||
arr.append(reinterpret_cast<const char*>(&pixel_format_), sizeof(pixel_format_));
|
||||
arr.append(reinterpret_cast<const char*>(&frame_rate_), sizeof(frame_rate_));
|
||||
arr.append(reinterpret_cast<const char*>(&start_time_), sizeof(start_time_));
|
||||
arr.append(reinterpret_cast<const char*>(&premultiplied_alpha_), sizeof(premultiplied_alpha_));
|
||||
arr.append(colorspace_.toUtf8());
|
||||
arr.append(reinterpret_cast<const char*>(&sample_rate_), sizeof(sample_rate_));
|
||||
arr.append(reinterpret_cast<const char*>(&channel_layout_), sizeof(channel_layout_));
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
private:
|
||||
void Init()
|
||||
{
|
||||
duration_ = AV_NOPTS_VALUE;
|
||||
channel_count_ = 0;
|
||||
enabled_ = true;
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
video_type_ = VideoType::kVideoTypeVideo;
|
||||
interlacing_ = VideoParams::kInterlaceNone;
|
||||
pixel_format_ = VideoParams::kFormatInvalid;
|
||||
start_time_ = 0;
|
||||
premultiplied_alpha_ = false;
|
||||
sample_rate_ = 0;
|
||||
channel_layout_ = 0;
|
||||
}
|
||||
|
||||
// Global members
|
||||
Type type_;
|
||||
rational timebase_;
|
||||
int64_t duration_;
|
||||
int channel_count_;
|
||||
bool enabled_;
|
||||
|
||||
// Video members
|
||||
int width_;
|
||||
int height_;
|
||||
rational pixel_aspect_ratio_;
|
||||
VideoType video_type_;
|
||||
VideoParams::Interlacing interlacing_;
|
||||
VideoParams::Format pixel_format_;
|
||||
rational frame_rate_;
|
||||
int64_t start_time_;
|
||||
bool premultiplied_alpha_;
|
||||
QString colorspace_;
|
||||
|
||||
// Audio members
|
||||
int sample_rate_;
|
||||
uint64_t channel_layout_;
|
||||
|
||||
};
|
||||
|
||||
using Streams = QVector<Stream>;
|
||||
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(olive::Stream)
|
||||
|
||||
#endif // STREAM_H
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,29 +133,16 @@ void Sequence::set_default_parameters()
|
||||
|
||||
void Sequence::set_parameters_from_footage(const QVector<Footage *> footage)
|
||||
{
|
||||
bool found_video_params = false;
|
||||
bool found_audio_params = false;
|
||||
|
||||
foreach (Footage* f, footage) {
|
||||
for (int i=0; i<f->GetStreamCount(); i++) {
|
||||
if (!f->IsStreamEnabled(i)) {
|
||||
continue;
|
||||
}
|
||||
QVector<VideoParams> video_streams = f->GetEnabledVideoStreams();
|
||||
QVector<AudioParams> audio_streams = f->GetEnabledAudioStreams();
|
||||
|
||||
Stream s = f->GetStreamAt(i);
|
||||
|
||||
if (!s.IsValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (s.type()) {
|
||||
case Stream::kVideo:
|
||||
{
|
||||
// If this is a video stream, use these parameters
|
||||
if (!found_video_params) {
|
||||
foreach (const VideoParams& s, video_streams) {
|
||||
bool found_video_params = false;
|
||||
rational using_timebase;
|
||||
|
||||
if (s.video_type() == Stream::kVideoTypeStill) {
|
||||
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
|
||||
@@ -169,26 +160,15 @@ void Sequence::set_parameters_from_footage(const QVector<Footage *> footage)
|
||||
s.pixel_aspect_ratio(),
|
||||
s.interlacing(),
|
||||
VideoParams::generate_auto_divider(s.width(), s.height())));
|
||||
}
|
||||
|
||||
if (found_video_params) {
|
||||
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
|
||||
break;
|
||||
}
|
||||
|
||||
if (found_video_params && found_audio_params) {
|
||||
return;
|
||||
}
|
||||
if (!audio_streams.isEmpty()) {
|
||||
const AudioParams& s = audio_streams.first();
|
||||
set_audio_params(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,8 +191,10 @@ void Sequence::Retranslate()
|
||||
{
|
||||
super::Retranslate();
|
||||
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kVideoParamsInput, tr("Video Parameters"));
|
||||
SetInputName(kAudioParamsInput, tr("Audio Parameters"));
|
||||
|
||||
SetInputName(kTextureInput, tr("Texture"));
|
||||
SetInputName(kSamplesInput, tr("Samples"));
|
||||
|
||||
for (int i=0;i<Track::kCount;i++) {
|
||||
@@ -352,47 +334,6 @@ void Sequence::InvalidateCache(const TimeRange& range, const QString& from, int
|
||||
super::InvalidateCache(range, from);
|
||||
}
|
||||
|
||||
void Sequence::set_video_params(const VideoParams &video)
|
||||
{
|
||||
bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height();
|
||||
bool timebase_changed = video_params_.time_base() != video.time_base();
|
||||
bool pixel_aspect_changed = video_params_.pixel_aspect_ratio() != video.pixel_aspect_ratio();
|
||||
bool interlacing_changed = video_params_.interlacing() != video.interlacing();
|
||||
|
||||
video_params_ = video;
|
||||
|
||||
if (size_changed) {
|
||||
emit SizeChanged(video_params_.width(), video_params_.height());
|
||||
}
|
||||
|
||||
if (pixel_aspect_changed) {
|
||||
emit PixelAspectChanged(video_params_.pixel_aspect_ratio());
|
||||
}
|
||||
|
||||
if (interlacing_changed) {
|
||||
emit InterlacingChanged(video_params_.interlacing());
|
||||
}
|
||||
|
||||
if (timebase_changed) {
|
||||
video_frame_cache_.SetTimebase(video_params_.time_base());
|
||||
emit TimebaseChanged(video_params_.time_base());
|
||||
}
|
||||
|
||||
emit VideoParamsChanged();
|
||||
|
||||
video_frame_cache_.InvalidateAll();
|
||||
}
|
||||
|
||||
void Sequence::set_audio_params(const AudioParams &audio)
|
||||
{
|
||||
audio_params_ = audio;
|
||||
|
||||
emit AudioParamsChanged();
|
||||
|
||||
// This will automatically InvalidateAll
|
||||
audio_playback_cache_.SetParameters(audio_params());
|
||||
}
|
||||
|
||||
rational Sequence::GetLength()
|
||||
{
|
||||
return last_length_;
|
||||
@@ -479,6 +420,50 @@ void Sequence::SaveInternal(QXmlStreamWriter *writer) const
|
||||
writer->writeEndElement(); // points
|
||||
}
|
||||
|
||||
void Sequence::InputValueChangedEvent(const QString &input, int element)
|
||||
{
|
||||
if (input == kVideoParamsInput) {
|
||||
|
||||
VideoParams new_video_params = video_params();
|
||||
|
||||
bool size_changed = cached_video_params_.width() != new_video_params.width() || cached_video_params_.height() != new_video_params.height();
|
||||
bool timebase_changed = cached_video_params_.time_base() != new_video_params.time_base();
|
||||
bool pixel_aspect_changed = cached_video_params_.pixel_aspect_ratio() != new_video_params.pixel_aspect_ratio();
|
||||
bool interlacing_changed = cached_video_params_.interlacing() != new_video_params.interlacing();
|
||||
|
||||
if (size_changed) {
|
||||
emit SizeChanged(new_video_params.width(), new_video_params.height());
|
||||
}
|
||||
|
||||
if (pixel_aspect_changed) {
|
||||
emit PixelAspectChanged(new_video_params.pixel_aspect_ratio());
|
||||
}
|
||||
|
||||
if (interlacing_changed) {
|
||||
emit InterlacingChanged(new_video_params.interlacing());
|
||||
}
|
||||
|
||||
if (timebase_changed) {
|
||||
video_frame_cache_.SetTimebase(new_video_params.time_base());
|
||||
emit TimebaseChanged(new_video_params.time_base());
|
||||
}
|
||||
|
||||
emit VideoParamsChanged();
|
||||
|
||||
video_frame_cache_.InvalidateAll();
|
||||
|
||||
cached_video_params_ = video_params();
|
||||
|
||||
} else if (input == kAudioParamsInput) {
|
||||
|
||||
emit AudioParamsChanged();
|
||||
|
||||
// This will automatically InvalidateAll
|
||||
audio_playback_cache_.SetParameters(audio_params());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Sequence::ShiftVideoEvent(const rational &from, const rational &to)
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
@@ -116,18 +116,25 @@ public:
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
|
||||
|
||||
const VideoParams& video_params() const
|
||||
VideoParams video_params() const
|
||||
{
|
||||
return video_params_;
|
||||
return GetStandardValue(kVideoParamsInput).value<VideoParams>();
|
||||
}
|
||||
|
||||
const AudioParams& audio_params() const
|
||||
AudioParams audio_params() const
|
||||
{
|
||||
return audio_params_;
|
||||
return GetStandardValue(kAudioParamsInput).value<AudioParams>();
|
||||
}
|
||||
|
||||
void set_video_params(const VideoParams &video);
|
||||
void set_audio_params(const AudioParams &audio);
|
||||
void set_video_params(const VideoParams &video)
|
||||
{
|
||||
SetStandardValue(kVideoParamsInput, QVariant::fromValue(video));
|
||||
}
|
||||
|
||||
void set_audio_params(const AudioParams &audio)
|
||||
{
|
||||
SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio));
|
||||
}
|
||||
|
||||
rational GetLength();
|
||||
|
||||
@@ -147,6 +154,9 @@ public:
|
||||
|
||||
virtual void EndOperation() override;
|
||||
|
||||
static const QString kVideoParamsInput;
|
||||
static const QString kAudioParamsInput;
|
||||
|
||||
static const QString kTextureInput;
|
||||
static const QString kSamplesInput;
|
||||
static const QString kTrackInputFormat;
|
||||
@@ -198,6 +208,8 @@ protected:
|
||||
|
||||
virtual void SaveInternal(QXmlStreamWriter *writer) const override;
|
||||
|
||||
virtual void InputValueChangedEvent(const QString& input, int element) override;
|
||||
|
||||
private:
|
||||
QVector<TrackList*> track_lists_;
|
||||
|
||||
@@ -213,8 +225,7 @@ private:
|
||||
|
||||
int operation_stack_;
|
||||
|
||||
VideoParams video_params_;
|
||||
AudioParams audio_params_;
|
||||
VideoParams cached_video_params_;
|
||||
|
||||
TimelinePoints timeline_points_;
|
||||
|
||||
|
||||
+10
-2
@@ -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;
|
||||
|
||||
@@ -196,6 +196,9 @@ bool ProjectViewModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
if (index.isValid() && columns_.at(index.column()) == kName && role == Qt::EditRole) {
|
||||
Item* item = GetItemObjectFromIndex(index);
|
||||
|
||||
QString new_name = value.toString();
|
||||
|
||||
if (!new_name.isEmpty()) {
|
||||
NodeRenameCommand* nrc = new NodeRenameCommand();
|
||||
|
||||
nrc->AddNode(item, value.toString());
|
||||
@@ -204,6 +207,7 @@ bool ProjectViewModel::setData(const QModelIndex &index, const QVariant &value,
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -238,7 +242,7 @@ Qt::ItemFlags ProjectViewModel::flags(const QModelIndex &index) const
|
||||
QStringList ProjectViewModel::mimeTypes() const
|
||||
{
|
||||
// Allow data from this model and a file list from external sources
|
||||
return {"application/x-oliveprojectitemdata", "text/uri-list"};
|
||||
return {QStringLiteral("application/x-oliveprojectitemdata"), QStringLiteral("text/uri-list")};
|
||||
}
|
||||
|
||||
QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const
|
||||
@@ -264,22 +268,21 @@ QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const
|
||||
// Check if we've dragged this item before
|
||||
if (!dragged_items.contains(index.internalPointer())) {
|
||||
// If not, add it to the stream (and also keep track of it in the vector)
|
||||
quint64 stream_flags;
|
||||
Footage* footage = dynamic_cast<Footage*>(static_cast<Item*>(index.internalPointer()));
|
||||
|
||||
if (dynamic_cast<Folder*>(static_cast<Item*>(index.internalPointer()))) {
|
||||
stream_flags = static_cast<Footage*>(index.internalPointer())->get_enabled_stream_flags();
|
||||
} else {
|
||||
stream_flags = UINT64_MAX;
|
||||
if (footage) {
|
||||
QVector<Footage::StreamReference> streams = footage->GetEnabledStreamsAsReferences();
|
||||
|
||||
stream << streams << reinterpret_cast<quintptr>(footage);
|
||||
|
||||
dragged_items.append(footage);
|
||||
}
|
||||
|
||||
stream << stream_flags << index.row() << reinterpret_cast<quintptr>(index.internalPointer());
|
||||
dragged_items.append(index.internalPointer());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set byte array as the mime data and return the mime data
|
||||
data->setData("application/x-oliveprojectitemdata", encoded_data);
|
||||
data->setData(QStringLiteral("application/x-oliveprojectitemdata"), encoded_data);
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -298,9 +301,9 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
// Probe mime data for its format
|
||||
QStringList mime_formats = data->formats();
|
||||
|
||||
if (mime_formats.contains("application/x-oliveprojectitemdata")) {
|
||||
if (mime_formats.contains(QStringLiteral("application/x-oliveprojectitemdata"))) {
|
||||
// Data is drag/drop data from this model
|
||||
QByteArray model_data = data->data("application/x-oliveprojectitemdata");
|
||||
QByteArray model_data = data->data(QStringLiteral("application/x-oliveprojectitemdata"));
|
||||
|
||||
// Use QDataStream to deserialize the data
|
||||
QDataStream stream(&model_data, QIODevice::ReadOnly);
|
||||
@@ -315,8 +318,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
|
||||
// Variables to deserialize into
|
||||
quintptr item_ptr;
|
||||
int r;
|
||||
quint64 enabled_streams;
|
||||
QList<Footage::StreamReference> streams;
|
||||
|
||||
// Loop through all data
|
||||
MultiUndoCommand* move_command = new MultiUndoCommand();
|
||||
@@ -324,7 +326,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
move_command->set_name(tr("Move Items"));
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
stream >> enabled_streams >> r >> item_ptr;
|
||||
stream >> streams >> item_ptr;
|
||||
|
||||
Item* item = reinterpret_cast<Item*>(item_ptr);
|
||||
|
||||
@@ -343,9 +345,9 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action
|
||||
|
||||
return true;
|
||||
|
||||
} else if (mime_formats.contains("text/uri-list")) {
|
||||
} else if (mime_formats.contains(QStringLiteral("text/uri-list"))) {
|
||||
// We received a list of files
|
||||
QByteArray file_data = data->data("text/uri-list");
|
||||
QByteArray file_data = data->data(QStringLiteral("text/uri-list"));
|
||||
|
||||
// Use text stream to parse (just an easy way of sifting through line breaks
|
||||
QTextStream stream(&file_data);
|
||||
|
||||
@@ -25,6 +25,9 @@ extern "C" {
|
||||
}
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#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<const char*>(&sample_rate_), sizeof(sample_rate_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&channel_layout_), sizeof(channel_layout_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&format_), sizeof(format_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&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<AudioParams::Format>(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);
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
|
||||
#include <QAudioFormat>
|
||||
#include <QtMath>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+58
-11
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<rational>(),
|
||||
@@ -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<ViewerOutput*>(copy_map_.value(viewer_node_));
|
||||
copied_viewer_node_ = static_cast<Sequence*>(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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>();
|
||||
VideoParams stream_params = stream.video_params();
|
||||
VideoParams stream_data = stream.video_params();
|
||||
|
||||
ColorManager* color_manager = Node::ValueToPtr<ColorManager>(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<StillImageCache::Entry>(
|
||||
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<AudioParams>();
|
||||
|
||||
SampleBufferPtr frame = decoder->RetrieveAudio(input_time, audio_params,
|
||||
stream.footage()->project()->cache_path(),
|
||||
stream.cache_path(),
|
||||
&IsCancelled());
|
||||
|
||||
if (frame) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <QHash>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#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;
|
||||
|
||||
+114
-1
@@ -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<const char*>(&width_), sizeof(width_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&height_), sizeof(height_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&depth_), sizeof(depth_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&time_base_), sizeof(time_base_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&format_), sizeof(format_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&channel_count_), sizeof(channel_count_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&pixel_aspect_ratio_), sizeof(pixel_aspect_ratio_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&interlacing_), sizeof(interlacing_));
|
||||
hasher.addData(reinterpret_cast<const char*>(÷r_), sizeof(divider_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&enabled_), sizeof(enabled_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&stream_index_), sizeof(stream_index_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&video_type_), sizeof(video_type_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&frame_rate_), sizeof(frame_rate_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&start_time_), sizeof(start_time_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&duration_), sizeof(duration_));
|
||||
hasher.addData(reinterpret_cast<const char*>(&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<VideoParams::Format>(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<VideoParams::Interlacing>(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<VideoParams::Type>(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_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
#ifndef VIDEOPARAMS_H
|
||||
#define VIDEOPARAMS_H
|
||||
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#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_;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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<rational>();
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -41,8 +41,15 @@ bool ProjectLoadTask::Run()
|
||||
if (project_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QXmlStreamReader reader(&project_file);
|
||||
|
||||
uint project_version;
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("olive")) {
|
||||
while(XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("version")) {
|
||||
bool ok;
|
||||
uint project_version = reader.documentVersion().toUInt(&ok);
|
||||
|
||||
project_version = reader.readElementText().toUInt(&ok);
|
||||
|
||||
if (!ok) {
|
||||
SetError(tr("Failed to determine project's version identifier."));
|
||||
@@ -56,11 +63,7 @@ bool ProjectLoadTask::Run()
|
||||
SetError(tr("This project is from a version of Olive that is no longer supported in this version."));
|
||||
return false;
|
||||
}
|
||||
|
||||
while (XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("olive")) {
|
||||
while(XMLReadNextStartElement(&reader)) {
|
||||
if (reader.name() == QStringLiteral("url")) {
|
||||
} else if (reader.name() == QStringLiteral("url")) {
|
||||
project_saved_url_ = reader.readElementText();
|
||||
} else if (reader.name() == QStringLiteral("project")) {
|
||||
project_ = new Project();
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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:
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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<Footage*>(i);
|
||||
Sequence* sequence_cast_test = dynamic_cast<Sequence*>(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<Footage*>(i);
|
||||
|
||||
QVector<int> video_streams = f->GetStreamIndexesOfType(Stream::kVideo);
|
||||
QVector<VideoParams> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Footage::StreamReference> 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*>(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 *> &footage, const rational &start, bool insert)
|
||||
{
|
||||
PlaceAt(FootageToDraggedFootage(footage), start, insert);
|
||||
QMap<Footage*, QVector<Footage::StreamReference> > refs;
|
||||
|
||||
foreach (Footage* f, footage) {
|
||||
refs.insert(f, f->GetEnabledStreamsAsReferences());
|
||||
}
|
||||
|
||||
void ImportTool::PlaceAt(const QVector<DraggedFootage> &footage, const rational &start, bool insert)
|
||||
PlaceAt(refs, start, insert);
|
||||
}
|
||||
|
||||
void ImportTool::PlaceAt(const QMap<Footage*, QVector<Footage::StreamReference> > &footage, const rational &start, bool insert)
|
||||
{
|
||||
dragged_footage_ = footage;
|
||||
|
||||
@@ -209,10 +214,9 @@ void ImportTool::PlaceAt(const QVector<DraggedFootage> &footage, const rational
|
||||
DropGhosts(insert);
|
||||
}
|
||||
|
||||
void ImportTool::FootageToGhosts(rational ghost_start, const QVector<DraggedFootage> &footage_list, const rational& dest_tb, const int& track_start)
|
||||
void ImportTool::FootageToGhosts(rational ghost_start, const QMap<Footage *, QVector<Footage::StreamReference> > &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<int> track_offsets(Track::kCount);
|
||||
track_offsets.fill(track_start);
|
||||
@@ -221,40 +225,36 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QVector<DraggedFoot
|
||||
rational footage_duration;
|
||||
bool contains_image_stream = false;
|
||||
|
||||
quint64 enabled_streams = footage.streams();
|
||||
|
||||
// Loop through all streams in footage
|
||||
foreach (const QString& output, footage.footage()->outputs()) {
|
||||
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 QVector<DraggedFoot
|
||||
// Increment track count for this track type
|
||||
track_offsets[track_type]++;
|
||||
|
||||
ghost->SetData(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*> 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::DraggedFootage> ImportTool::FootageToDraggedFootage(QVector<Footage *> footage)
|
||||
{
|
||||
QVector<DraggedFootage> df;
|
||||
|
||||
foreach (Footage* f, footage) {
|
||||
df.append(FootageToDraggedFootage(f));
|
||||
}
|
||||
|
||||
return df;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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*> &footage, const rational& start, bool insert);
|
||||
void PlaceAt(const QVector<DraggedFootage> &footage, const rational& start, bool insert);
|
||||
void PlaceAt(const QMap<Footage *, QVector<Footage::StreamReference> > &footage, const rational& start, bool insert);
|
||||
|
||||
enum DropWithoutSequenceBehavior {
|
||||
kDWSAsk,
|
||||
@@ -75,16 +46,13 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
static DraggedFootage FootageToDraggedFootage(Footage* f);
|
||||
static QVector<DraggedFootage> FootageToDraggedFootage(QVector<Footage*> footage);
|
||||
|
||||
void FootageToGhosts(rational ghost_start, const QVector<DraggedFootage> &footage, const rational &dest_tb, const int &track_start);
|
||||
void FootageToGhosts(rational ghost_start, const QMap<Footage*, QVector<Footage::StreamReference> > &footage, const rational &dest_tb, const int &track_start);
|
||||
|
||||
void PrepGhosts(const rational &frame, const int &track_index);
|
||||
|
||||
void DropGhosts(bool insert);
|
||||
|
||||
QVector<DraggedFootage> dragged_footage_;
|
||||
QMap<Footage*, QVector<Footage::StreamReference> > dragged_footage_;
|
||||
|
||||
int import_pre_buffer_;
|
||||
|
||||
|
||||
@@ -99,44 +99,63 @@ 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<Footage::StreamReference> streams = GetFootage()->GetEnabledStreamsAsReferences();
|
||||
|
||||
// Disable streams that have been disabled
|
||||
if (!enable_video || !enable_audio) {
|
||||
quint64 stream_disabler = 0x1;
|
||||
for (int i=0; i<streams.size(); i++) {
|
||||
const Footage::StreamReference& ref = streams.at(i);
|
||||
|
||||
for (int i=0; i<GetFootage()->GetStreamCount(); 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<quintptr>(GetFootage());
|
||||
if (!streams.isEmpty()) {
|
||||
data_stream << streams << reinterpret_cast<quintptr>(GetFootage());
|
||||
|
||||
mimedata->setData("application/x-oliveprojectitemdata", encoded_data);
|
||||
mimedata->setData(QStringLiteral("application/x-oliveprojectitemdata"), encoded_data);
|
||||
drag->setMimeData(mimedata);
|
||||
|
||||
drag->exec();
|
||||
}
|
||||
}
|
||||
|
||||
void FootageViewerWidget::TryConnectingType(Footage *footage, Stream::Type type)
|
||||
{
|
||||
for (int i=0; ; i++) {
|
||||
Stream stream = footage_->GetStreamAt(type, i);
|
||||
int index = -1;
|
||||
|
||||
// Found end of stream list
|
||||
if (!stream.IsValid()) {
|
||||
for (int i=0; ; i++) {
|
||||
if (type == Stream::kVideo) {
|
||||
VideoParams vp = footage->GetVideoParams(i);
|
||||
|
||||
if (!vp.is_valid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream.enabled()) {
|
||||
QString s = Footage::GetStringFromReference(type, i);
|
||||
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;
|
||||
|
||||
@@ -149,7 +168,6 @@ void FootageViewerWidget::TryConnectingType(Footage *footage, Stream::Type type)
|
||||
Node::ConnectEdge(NodeOutput(footage, s), NodeInput(&sequence_, input_param));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FootageViewerWidget::StartFootageDrag()
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user