moved sequence params into node graph too

Reduces code by reusing more of the existing node infrastructure
to synchronize sequence parameters the render backend.
This commit is contained in:
itsmattkc
2021-02-18 13:20:23 +11:00
parent d7c5ac4b44
commit 4827782331
46 changed files with 1375 additions and 1394 deletions
-5
View File
@@ -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
View File
@@ -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
+20 -44
View File
@@ -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)
+2 -2
View File
@@ -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;
+17 -16
View File
@@ -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()
+2 -2
View File
@@ -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
View File
@@ -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
+6 -6
View File
@@ -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()) {
+3 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -19,7 +19,8 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
project/item/footage/footage.cpp
project/item/footage/footage.h
project/item/footage/stream.cpp
project/item/footage/footagedescription.cpp
project/item/footage/footagedescription.h
project/item/footage/stream.h
PARENT_SCOPE
)
+232 -314
View File
@@ -41,7 +41,6 @@ const QString Footage::kStreamPropertiesFormat = QStringLiteral("stream_properti
Footage::Footage(const QString &filename) :
super(true, false),
stream_count_(0),
cancelled_(nullptr)
{
AddInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
@@ -58,7 +57,7 @@ void Footage::Retranslate()
SetInputName(kFilenameInput, tr("Filename"));
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
StreamReference ref = GetReferenceFromRealIndex(it.key());
StreamReference ref = it.key();
SetInputName(it.value(), QStringLiteral("%1 %2").arg(GetStreamTypeName(ref.type()), QString::number(ref.index())));
}
@@ -111,12 +110,12 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
// Determine if we've already cached the metadata of this file
QString meta_cache_file = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)).filePath(FileFunctions::GetUniqueFileIdentifier(filename()));
MetadataCache footage_info;
FootageDescription footage_info;
if (QFileInfo::exists(meta_cache_file)) {
// Load meta cache file
footage_info = LoadStreamCache(meta_cache_file);
footage_info.Load(meta_cache_file);
} else {
@@ -124,51 +123,28 @@ void Footage::InputValueChangedEvent(const QString &input, int element)
QVector<DecoderPtr> decoder_list = Decoder::ReceiveListOfAllDecoders();
foreach (DecoderPtr decoder, decoder_list) {
footage_info.streams = decoder->Probe(filename(), cancelled_);
footage_info = decoder->Probe(filename(), cancelled_);
if (!footage_info.streams.isEmpty()) {
footage_info.decoder = decoder->id();
SetValid();
if (footage_info.IsValid()) {
break;
}
}
if (!SaveStreamCache(meta_cache_file, footage_info)) {
if (!footage_info.Save(meta_cache_file)) {
qWarning() << "Failed to save stream cache, footage will have to be re-probed";
}
}
stream_count_ = footage_info.streams.size();
if (footage_info.IsValid()) {
decoder_ = footage_info.decoder();
if (!footage_info.streams.isEmpty()) {
set_decoder(footage_info.decoder);
for (int i=0; i<footage_info.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 enabled_streams;
return GetFirstEnabledVideoStream().is_valid();
}
bool Footage::HasEnabledStreamsOfType(const Stream::Type &type) const
bool Footage::HasEnabledAudioStreams() const
{
// Return true if any streams are video streams
for (int i=0; i<stream_count_; i++) {
Stream s = GetStreamAt(i);
return GetFirstEnabledAudioStream().is_valid();
}
if (s.enabled() && s.type() == type) {
return true;
}
QString Footage::DescribeVideoStream(const VideoParams &params)
{
if (params.video_type() == VideoParams::kVideoTypeStill) {
return tr("%1: Image - %2x%3").arg(QString::number(params.stream_index()),
QString::number(params.width()),
QString::number(params.height()));
} else {
return tr("%1: Video - %2x%3").arg(QString::number(params.stream_index()),
QString::number(params.width()),
QString::number(params.height()));
}
}
return false;
QString Footage::DescribeAudioStream(const AudioParams &params)
{
return tr("%1: Audio - %2 Channel(s), %3Hz")
.arg(QString::number(params.stream_index()),
QString::number(params.channel_count()),
QString::number(params.sample_rate()));
}
bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
@@ -552,6 +483,7 @@ bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
return true;
}
}*/
Q_UNUSED(footage)
// Simplified, since our footage node is much more tolerant, we'll try this
return true;
@@ -576,9 +508,9 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
QString fn = filename();
if (!fn.isEmpty()) {
Stream stream = GetStreamAt(GetReferenceFromOutput(output));
VideoParams params = GetVideoParams(ref.index());
if (stream.IsValid()) {
if (params.is_valid()) {
// Add footage details to hash
// Footage filename
@@ -593,23 +525,23 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
if (ref.type() == Stream::kVideo) {
// Current color config and space
hash.addData(project()->color_manager()->GetConfigFilename().toUtf8());
hash.addData(stream.colorspace().toUtf8());
hash.addData(params.colorspace().toUtf8());
// Alpha associated setting
hash.addData(QString::number(stream.premultiplied_alpha()).toUtf8());
hash.addData(QString::number(params.premultiplied_alpha()).toUtf8());
// Pixel aspect ratio
hash.addData(reinterpret_cast<const char*>(&stream.pixel_aspect_ratio()), sizeof(stream.pixel_aspect_ratio()));
hash.addData(reinterpret_cast<const char*>(&params.pixel_aspect_ratio()), sizeof(params.pixel_aspect_ratio()));
// Footage timestamp
if (stream.video_type() != Stream::kVideoTypeStill) {
int64_t video_ts = Timecode::time_to_timestamp(time, stream.timebase());
if (params.video_type() != VideoParams::kVideoTypeStill) {
int64_t video_ts = Timecode::time_to_timestamp(time, params.time_base());
// Add timestamp in units of the video stream's timebase
hash.addData(reinterpret_cast<const char*>(&video_ts), sizeof(int64_t));
// Add start time - used for both image sequences and video streams
hash.addData(QString::number(stream.start_time()).toUtf8());
hash.addData(QString::number(params.start_time()).toUtf8());
}
}
}
@@ -627,8 +559,23 @@ NodeValueTable Footage::Value(const QString &output, NodeValueDatabase &value) c
NodeValueTable table = value.Merge();
// If the file exists and the reference is valid, push a footage job to the renderer
if (QFileInfo(file).exists() && ref.IsValid()) {
table.Push(NodeValue::kFootageJob, QVariant::fromValue(ref), this);
if (QFileInfo(file).exists()) {
FootageJob job(decoder_, filename(), ref.type());
if (ref.type() == Stream::kVideo) {
VideoParams vp = GetVideoParams(ref.index());
if (vp.colorspace().isEmpty()) {
vp.set_colorspace(project()->color_manager()->GetDefaultInputColorSpace());
}
job.set_video_params(vp);
} else {
job.set_audio_params(GetAudioParams(ref.index()));
job.set_cache_path(project()->cache_path());
}
table.Push(NodeValue::kFootageJob, QVariant::fromValue(job), this);
}
return table;
@@ -659,13 +606,20 @@ void Footage::UpdateTooltip()
if (valid_) {
QString tip = QCoreApplication::translate("Footage", "Filename: %1").arg(filename());
if (!inputs_for_stream_properties_.isEmpty()) {
for (int i=0; i<stream_count_; i++) {
Stream s = GetStreamAt(i);
for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) {
if (it.key().type() == Stream::kVideo) {
VideoParams p = GetVideoParams(it.key().index());
if (s.enabled()) {
if (p.enabled()) {
tip.append("\n");
tip.append(DescribeStream(i));
tip.append(DescribeVideoStream(p));
}
} else if (it.key().type() == Stream::kAudio) {
AudioParams p = GetAudioParams(it.key().index());
if (p.enabled()) {
tip.append("\n");
tip.append(DescribeAudioStream(p));
}
}
}
@@ -676,77 +630,22 @@ void Footage::UpdateTooltip()
}
}
Footage::MetadataCache Footage::LoadStreamCache(const QString &filename)
void Footage::AddStreamAsInput(Stream::Type type, int index, QVariant value)
{
MetadataCache cache;
QFile file(filename);
QString input_id = GetInputIDOfIndex(type, index);
if (file.open(QFile::ReadOnly)) {
QXmlStreamReader reader(&file);
StreamReference ref(type, index);
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("streamcache")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("decoder")) {
cache.decoder = reader.readElementText();
} else if (reader.name() == QStringLiteral("streams")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("stream")) {
Stream s;
s.Load(&reader);
cache.streams.append(s);
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
// Create input for parameters
AddInput(input_id, type == Stream::kVideo ? NodeValue::kVideoParams : NodeValue::kAudioParams,
InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetStandardValue(input_id, value);
inputs_for_stream_properties_.insert(ref, input_id);
file.close();
}
return cache;
}
bool Footage::SaveStreamCache(const QString &filename, const Footage::MetadataCache &data)
{
QFile file(filename);
if (!file.open(QFile::WriteOnly)) {
return false;
}
QXmlStreamWriter writer(&file);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("streamcache"));
writer.writeTextElement(QStringLiteral("decoder"), data.decoder);
writer.writeStartElement(QStringLiteral("streams"));
foreach (const Stream& s, data.streams) {
writer.writeStartElement(QStringLiteral("stream"));
s.Save(&writer);
writer.writeEndElement(); // stream
}
writer.writeEndElement(); // streams
writer.writeEndElement(); // streamcache
writer.writeEndDocument();
file.close();
return true;
// Create output for stream
QString output_id = GetStringFromReference(type, index);
AddOutput(output_id);
outputs_for_streams_.insert(ref, output_id);
}
void Footage::CheckFootage()
@@ -766,26 +665,45 @@ void Footage::CheckFootage()
}
}
QString Footage::StreamReference::video_colorspace(bool default_if_empty) const
/*QString Footage::StreamReference::video_colorspace(bool default_if_empty) const
{
if (IsValid()) {
Stream stream = footage_->GetStreamAt(type_, index_);
VideoParams params = footage_->GetVideoParams(index_);
if (params.is_valid()) {
if (params.colorspace().isEmpty() && default_if_empty) {
if (stream.IsValid()) {
if (stream.colorspace().isEmpty() && default_if_empty) {
return footage_->project()->color_manager()->GetDefaultInputColorSpace();
} else {
return stream.colorspace();
return params.colorspace();
}
}
}
return QString();
}
}*/
uint qHash(const Footage::StreamReference &ref, uint seed)
{
return qHash(ref.footage(), seed) ^ qHash(ref.type(), seed) ^ qHash(ref.index(), seed);
return qHash(ref.type(), seed) ^ qHash(ref.index(), seed);
}
QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref)
{
out << static_cast<int>(ref.type()) << ref.index();
return out;
}
QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref)
{
int type;
int index;
in >> type >> index;
ref = Footage::StreamReference(static_cast<Stream::Type>(type), index);
return in;
}
}
+63 -145
View File
@@ -25,8 +25,11 @@
#include <QDateTime>
#include "common/rational.h"
#include "footagedescription.h"
#include "node/node.h"
#include "project/item/item.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
#include "stream.h"
#include "timeline/timelinepoints.h"
@@ -143,26 +146,33 @@ public:
public:
StreamReference()
{
footage_ = nullptr;
type_ = Stream::kUnknown;
index_ = -1;
}
StreamReference(const Footage* footage, Stream::Type type, int index)
StreamReference(Stream::Type type, int index)
{
footage_ = footage;
type_ = type;
index_ = index;
}
bool operator==(const StreamReference& rhs) const
{
return footage_ == rhs.footage_ && type_ == rhs.type_ && index_ == rhs.index_;
return type_ == rhs.type_ && index_ == rhs.index_;
}
bool operator<(const StreamReference& rhs) const
{
if (type_ != rhs.type_) {
return type_ < rhs.type_;
}
return index_ < rhs.index_;
}
bool IsValid() const
{
return footage_ && index_ >= 0;
return type_ != Stream::kUnknown && index_ >= 0;
}
void Reset()
@@ -170,11 +180,6 @@ public:
*this = StreamReference();
}
const Footage* footage() const
{
return footage_;
}
Stream::Type type() const
{
return type_;
@@ -185,138 +190,68 @@ public:
return index_;
}
Stream GetStream() const
{
if (IsValid()) {
return footage_->GetStreamAt(*this);
} else {
return Stream();
}
}
int64_t GetTimeInTimebaseUnits(const rational& timecode) const
{
if (IsValid()) {
return footage_->GetTimeInTimebaseUnits(type_, index_, timecode);
} else {
return -1;
}
}
int GetRealStreamIndex() const
{
if (IsValid()) {
return footage_->GetRealStreamIndex(*this);
} else {
return -1;
}
}
QString filename() const
{
if (footage_) {
return footage_->filename();
} else {
return QString();
}
}
VideoParams video_params() const
{
return GetStream().video_params();
}
AudioParams audio_params() const
{
return GetStream().audio_params();
}
int64_t duration() const
{
return GetStream().duration();
}
QString video_colorspace(bool default_if_empty = true) const;
private:
const Footage* footage_;
Stream::Type type_;
int index_;
};
Stream GetStreamAt(int index) const
{
return GetStandardValue(GetInputIDOfIndex(index)).value<Stream>();
}
Stream GetStreamAt(Stream::Type type, int index_within_type) const
{
return GetStreamAt(GetRealStreamIndex(type, index_within_type));
}
Stream GetStreamAt(const StreamReference& ref) const
{
return GetStreamAt(ref.type(), ref.index());
}
void SetStreamAt(int index, const Stream& stream)
{
SetStandardValue(GetInputIDOfIndex(index), QVariant::fromValue(stream));
}
void SetStreamAt(Stream::Type type, int index_within_type, const Stream& stream)
{
SetStreamAt(GetRealStreamIndex(type, index_within_type), stream);
}
void SetStreamAt(const StreamReference& ref, const Stream& stream)
{
SetStreamAt(ref.type(), ref.index(), stream);
}
int64_t GetTimeInTimebaseUnits(int index, const rational& time) const;
int64_t GetTimeInTimebaseUnits(Stream::Type type, int index_within_type, const rational& time) const
{
return GetTimeInTimebaseUnits(GetRealStreamIndex(type, index_within_type), time);
}
int GetRealStreamIndex(Stream::Type type, int index_within_type) const;
int GetRealStreamIndex(const StreamReference& ref) const
{
return GetRealStreamIndex(ref.type(), ref.index());
}
static QString GetStringFromReference(Stream::Type type, int index);
static QString GetStringFromReference(const StreamReference& ref)
{
return GetStringFromReference(ref.type(), ref.index());
}
int GetStreamIndex(Stream::Type type, int index) const;
int GetStreamIndex(const StreamReference& ref) const
{
return GetStreamIndex(ref.type(), ref.index());
}
int GetTotalStreamCount() const
{
return inputs_for_stream_properties_.size();
}
StreamReference GetReferenceFromRealIndex(int real_index) const;
Stream::Type GetTypeFromOutput(const QString& output) const;
static Stream::Type GetTypeFromOutput(const QString& output);
StreamReference GetReferenceFromOutput(const QString& s) const;
static bool GetReferenceFromOutput(const QString& s, Stream::Type* type, int* index);
int GetStreamCount() const
VideoParams GetVideoParams(int index) const
{
return stream_count_;
return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index))).value<VideoParams>();
}
int GetStreamTypeCount(Stream::Type type) const;
bool IsStreamEnabled(int index) const
void SetVideoParams(int index, const VideoParams& p)
{
return GetStreamAt(index).enabled();
SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index)), QVariant::fromValue(p));
}
Stream GetFirstEnabledStreamOfType(Stream::Type type) const;
VideoParams GetFirstEnabledVideoStream() const;
QVector<int> GetStreamIndexesOfType(Stream::Type type) const;
AudioParams GetAudioParams(int index) const
{
return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index))).value<AudioParams>();
}
void SetAudioParams(int index, const AudioParams& p)
{
SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index)), QVariant::fromValue(p));
}
AudioParams GetFirstEnabledAudioStream() const;
QVector<VideoParams> GetEnabledVideoStreams() const;
QVector<AudioParams> GetEnabledAudioStreams() const;
Stream::Type GetStreamType(int index);
QVector<StreamReference> GetEnabledStreamsAsReferences() const;
/**
* @brief Get the Decoder ID set when this Footage was probed
*
@@ -326,27 +261,17 @@ public:
*/
const QString& decoder() const;
/**
* @brief Used by decoders when they Probe to attach itself to this Footage
*/
void set_decoder(const QString& id);
virtual QIcon icon() const override;
virtual QString duration() override;
virtual QString rate() override;
quint64 get_enabled_stream_flags() const;
bool HasEnabledVideoStreams() const;
bool HasEnabledAudioStreams() const;
/**
* @brief Check if this footage has streams of a certain type
*
* @param type
*
* The stream type to check for
*/
bool HasEnabledStreamsOfType(const Stream::Type& type) const;
static QString DescribeVideoStream(const VideoParams& params);
static QString DescribeAudioStream(const AudioParams& params);
static bool CompareFootageToFile(Footage* footage, const QString& filename);
static bool CompareFootageToItsFilename(Footage* footage);
@@ -374,13 +299,6 @@ protected:
virtual void InputValueChangedEvent(const QString &input, int element) override;
private:
QString DescribeStream(int index) const;
struct MetadataCache {
QString decoder;
Streams streams;
};
/**
* @brief Update the icon based on the Footage status
*
@@ -397,24 +315,22 @@ private:
*/
void UpdateTooltip();
MetadataCache LoadStreamCache(const QString& filename);
void AddStreamAsInput(Stream::Type type, int index, QVariant value);
bool SaveStreamCache(const QString& filename, const MetadataCache& data);
static QString GetInputIDOfIndex(int index)
static QString GetInputIDOfIndex(Stream::Type type, int index)
{
return kStreamPropertiesFormat.arg(index);
return kStreamPropertiesFormat.arg(GetStringFromReference(type, index));
}
/**
* @brief List of dynamic inputs added for stream properties
*/
QMap<int, QString> inputs_for_stream_properties_;
QMap<StreamReference, QString> inputs_for_stream_properties_;
/**
* @brief List of dynamic outputs added for streams
*/
QMap<int, QString> outputs_for_streams_;
QMap<StreamReference, QString> outputs_for_streams_;
/**
* @brief Internal timestamp object
@@ -426,8 +342,6 @@ private:
*/
QString decoder_;
int stream_count_;
bool valid_;
const QAtomicInt* cancelled_;
@@ -439,6 +353,10 @@ private slots:
uint qHash(const Footage::StreamReference& ref, uint seed = 0);
QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref);
QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref);
}
Q_DECLARE_METATYPE(olive::Footage::StreamReference)
@@ -0,0 +1,118 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "footagedescription.h"
#include <QFile>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "common/xmlutils.h"
namespace olive {
bool FootageDescription::Load(const QString &filename)
{
// Reset self
*this = FootageDescription();
QFile file(filename);
if (file.open(QFile::ReadOnly)) {
QXmlStreamReader reader(&file);
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("streamcache")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("decoder")) {
decoder_ = reader.readElementText();
} else if (reader.name() == QStringLiteral("streams")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("video")) {
VideoParams vp;
vp.Load(&reader);
AddVideoStream(vp);
} else if (reader.name() == QStringLiteral("audio")) {
AudioParams ap;
ap.Load(&reader);
AddAudioStream(ap);
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
} else {
reader.skipCurrentElement();
}
}
file.close();
return true;
}
return false;
}
bool FootageDescription::Save(const QString &filename) const
{
QFile file(filename);
if (!file.open(QFile::WriteOnly)) {
return false;
}
QXmlStreamWriter writer(&file);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("streamcache"));
writer.writeTextElement(QStringLiteral("decoder"), decoder_);
writer.writeStartElement(QStringLiteral("streams"));
foreach (const VideoParams& vp, video_streams_) {
writer.writeStartElement(QStringLiteral("video"));
vp.Save(&writer);
writer.writeEndElement(); // video
}
foreach (const AudioParams& ap, audio_streams_) {
writer.writeStartElement(QStringLiteral("audio"));
ap.Save(&writer);
writer.writeEndElement(); // audio
}
writer.writeEndElement(); // streams
writer.writeEndElement(); // streamcache
writer.writeEndDocument();
file.close();
return true;
}
}
@@ -0,0 +1,125 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef FOOTAGEDESCRIPTION_H
#define FOOTAGEDESCRIPTION_H
#include "render/audioparams.h"
#include "render/videoparams.h"
#include "stream.h"
namespace olive {
class FootageDescription
{
public:
FootageDescription(const QString& decoder = QString()) :
decoder_(decoder)
{
}
bool IsValid() const
{
return !decoder_.isEmpty() && (!video_streams_.isEmpty() || !audio_streams_.isEmpty());
}
const QString& decoder() const
{
return decoder_;
}
void AddVideoStream(const VideoParams& video_params)
{
Q_ASSERT(!HasStreamIndex(video_params.stream_index()));
video_streams_.append(video_params);
}
void AddAudioStream(const AudioParams& audio_params)
{
Q_ASSERT(!HasStreamIndex(audio_params.stream_index()));
audio_streams_.append(audio_params);
}
Stream::Type GetTypeOfStream(int index)
{
if (StreamIsVideo(index)) {
return Stream::kVideo;
} else if (StreamIsAudio(index)) {
return Stream::kAudio;
} else {
return Stream::kUnknown;
}
}
bool StreamIsVideo(int index) const
{
foreach (const VideoParams& vp, video_streams_) {
if (vp.stream_index() == index) {
return true;
}
}
return false;
}
bool StreamIsAudio(int index) const
{
foreach (const AudioParams& ap, audio_streams_) {
if (ap.stream_index() == index) {
return true;
}
}
return false;
}
bool HasStreamIndex(int index) const
{
return StreamIsVideo(index) || StreamIsAudio(index);
}
bool Load(const QString& filename);
bool Save(const QString& filename) const;
const QVector<VideoParams>& GetVideoStreams() const
{
return video_streams_;
}
const QVector<AudioParams>& GetAudioStreams() const
{
return audio_streams_;
}
private:
QString decoder_;
QVector<VideoParams> video_streams_;
QVector<AudioParams> audio_streams_;
};
}
#endif // FOOTAGEDESCRIPTION_H
-131
View File
@@ -1,131 +0,0 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "stream.h"
#include "common/xmlutils.h"
namespace olive {
void Stream::Load(QXmlStreamReader *reader)
{
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("type")) {
*this = Stream(static_cast<Type>(attr.value().toInt()));
break;
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("global")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("timebase")) {
timebase_ = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("duration")) {
duration_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("channelcount")) {
channel_count_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("enabled")) {
enabled_ = reader->readElementText().toInt();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("video")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("width")) {
width_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("height")) {
height_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("pixelaspectratio")) {
pixel_aspect_ratio_ = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("videotype")) {
video_type_ = static_cast<VideoType>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("interlacing")) {
interlacing_ = static_cast<VideoParams::Interlacing>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("pixelformat")) {
pixel_format_ = static_cast<VideoParams::Format>(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("framerate")) {
frame_rate_ = rational::fromString(reader->readElementText());
} else if (reader->name() == QStringLiteral("starttime")) {
start_time_ = reader->readElementText().toLongLong();
} else if (reader->name() == QStringLiteral("premultipliedalpha")) {
premultiplied_alpha_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("colorspace")) {
colorspace_ = reader->readElementText();
} else {
reader->skipCurrentElement();
}
}
} else if (reader->name() == QStringLiteral("audio")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("samplerate")) {
sample_rate_ = reader->readElementText().toInt();
} else if (reader->name() == QStringLiteral("channellayout")) {
channel_layout_ = reader->readElementText().toULongLong();
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
}
}
void Stream::Save(QXmlStreamWriter *writer) const
{
writer->writeAttribute(QStringLiteral("type"), QString::number(type_));
writer->writeStartElement(QStringLiteral("global"));
writer->writeTextElement(QStringLiteral("timebase"), timebase_.toString());
writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_));
writer->writeTextElement(QStringLiteral("channelcount"), QString::number(channel_count_));
writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_));
writer->writeEndElement(); // global
writer->writeStartElement(QStringLiteral("video"));
writer->writeTextElement(QStringLiteral("width"), QString::number(width_));
writer->writeTextElement(QStringLiteral("height"), QString::number(height_));
writer->writeTextElement(QStringLiteral("pixelaspectratio"), pixel_aspect_ratio_.toString());
writer->writeTextElement(QStringLiteral("videotype"), QString::number(video_type_));
writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_));
writer->writeTextElement(QStringLiteral("pixelformat"), QString::number(pixel_format_));
writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString());
writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_));
writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_));
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
writer->writeEndElement(); // video
writer->writeStartElement(QStringLiteral("audio"));
writer->writeTextElement(QStringLiteral("samplerate"), QString::number(sample_rate_));
writer->writeTextElement(QStringLiteral("channellayout"), QString::number(channel_layout_));
writer->writeEndElement(); // audio
}
}
+2 -285
View File
@@ -21,17 +21,10 @@
#ifndef STREAM_H
#define STREAM_H
#include <QVector>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "common/rational.h"
#include "render/audioparams.h"
#include "render/videoparams.h"
namespace olive {
class Stream {
class Stream
{
public:
enum Type {
kUnknown = -1,
@@ -41,284 +34,8 @@ public:
kSubtitle,
kAttachment
};
enum VideoType {
kVideoTypeVideo,
kVideoTypeStill,
kVideoTypeImageSequence
};
Stream(Type type = kUnknown) :
type_(type)
{
Init();
}
bool IsValid() const
{
return type_ != kUnknown;
}
Type type() const
{
return type_;
}
const rational& timebase() const
{
return timebase_;
}
void set_timebase(const rational& timebase)
{
timebase_ = timebase;
}
int64_t duration() const
{
return duration_;
}
void set_duration(int64_t duration)
{
duration_ = duration;
}
int channel_count() const
{
return channel_count_;
}
void set_channel_count(int c)
{
channel_count_ = c;
}
bool enabled() const
{
return enabled_;
}
void set_enabled(bool e)
{
enabled_ = e;
}
int width() const
{
return width_;
}
void set_width(int w)
{
width_ = w;
}
int height() const
{
return height_;
}
void set_height(int h)
{
height_ = h;
}
const rational& pixel_aspect_ratio() const
{
return pixel_aspect_ratio_;
}
void set_pixel_aspect_ratio(const rational& pixel_aspect_ratio)
{
pixel_aspect_ratio_ = pixel_aspect_ratio;
}
VideoType video_type() const
{
return video_type_;
}
void set_video_type(VideoType t)
{
video_type_ = t;
}
VideoParams::Interlacing interlacing() const
{
return interlacing_;
}
void set_interlacing(VideoParams::Interlacing interlacing)
{
interlacing_ = interlacing;
}
VideoParams::Format pixel_format() const
{
return pixel_format_;
}
void set_pixel_format(VideoParams::Format pixel_format)
{
pixel_format_ = pixel_format;
}
const rational& frame_rate() const
{
return frame_rate_;
}
void set_frame_rate(const rational& frame_rate)
{
frame_rate_ = frame_rate;
}
int64_t start_time() const
{
return start_time_;
}
void set_start_time(int64_t start_time)
{
start_time_ = start_time;
}
bool premultiplied_alpha() const
{
return premultiplied_alpha_;
}
void set_premultiplied_alpha(bool premultiplied_alpha)
{
premultiplied_alpha_ = premultiplied_alpha;
}
const QString& colorspace() const
{
return colorspace_;
}
void set_colorspace(const QString& c)
{
colorspace_ = c;
}
int sample_rate() const
{
return sample_rate_;
}
void set_sample_rate(int sample_rate)
{
sample_rate_ = sample_rate;
}
uint64_t channel_layout() const
{
return channel_layout_;
}
void set_channel_layout(uint64_t channel_layout)
{
channel_layout_ = channel_layout;
}
VideoParams video_params() const
{
if (type_ == kVideo) {
return VideoParams(width_, height_, timebase_,
pixel_format_, channel_count_, pixel_aspect_ratio_,
interlacing_);
} else {
return VideoParams();
}
}
AudioParams audio_params() const
{
if (type_ == kAudio) {
return AudioParams(sample_rate_, channel_layout_, AudioParams::kInternalFormat);
} else {
return AudioParams();
}
}
void Load(QXmlStreamReader* reader);
void Save(QXmlStreamWriter* writer) const;
QByteArray toBytes() const
{
QByteArray arr;
arr.append(reinterpret_cast<const char*>(&type_), sizeof(type_));
arr.append(reinterpret_cast<const char*>(&timebase_), sizeof(timebase_));
arr.append(reinterpret_cast<const char*>(&duration_), sizeof(duration_));
arr.append(reinterpret_cast<const char*>(&channel_count_), sizeof(channel_count_));
arr.append(reinterpret_cast<const char*>(&enabled_), sizeof(enabled_));
arr.append(reinterpret_cast<const char*>(&width_), sizeof(width_));
arr.append(reinterpret_cast<const char*>(&height_), sizeof(height_));
arr.append(reinterpret_cast<const char*>(&pixel_aspect_ratio_), sizeof(pixel_aspect_ratio_));
arr.append(reinterpret_cast<const char*>(&video_type_), sizeof(video_type_));
arr.append(reinterpret_cast<const char*>(&interlacing_), sizeof(interlacing_));
arr.append(reinterpret_cast<const char*>(&pixel_format_), sizeof(pixel_format_));
arr.append(reinterpret_cast<const char*>(&frame_rate_), sizeof(frame_rate_));
arr.append(reinterpret_cast<const char*>(&start_time_), sizeof(start_time_));
arr.append(reinterpret_cast<const char*>(&premultiplied_alpha_), sizeof(premultiplied_alpha_));
arr.append(colorspace_.toUtf8());
arr.append(reinterpret_cast<const char*>(&sample_rate_), sizeof(sample_rate_));
arr.append(reinterpret_cast<const char*>(&channel_layout_), sizeof(channel_layout_));
return arr;
}
private:
void Init()
{
duration_ = AV_NOPTS_VALUE;
channel_count_ = 0;
enabled_ = true;
width_ = 0;
height_ = 0;
video_type_ = VideoType::kVideoTypeVideo;
interlacing_ = VideoParams::kInterlaceNone;
pixel_format_ = VideoParams::kFormatInvalid;
start_time_ = 0;
premultiplied_alpha_ = false;
sample_rate_ = 0;
channel_layout_ = 0;
}
// Global members
Type type_;
rational timebase_;
int64_t duration_;
int channel_count_;
bool enabled_;
// Video members
int width_;
int height_;
rational pixel_aspect_ratio_;
VideoType video_type_;
VideoParams::Interlacing interlacing_;
VideoParams::Format pixel_format_;
rational frame_rate_;
int64_t start_time_;
bool premultiplied_alpha_;
QString colorspace_;
// Audio members
int sample_rate_;
uint64_t channel_layout_;
};
using Streams = QVector<Stream>;
}
Q_DECLARE_METATYPE(olive::Stream)
#endif // STREAM_H
+2
View File
@@ -34,6 +34,8 @@ Item::Item(bool create_folder_input, bool create_default_output) :
if (create_folder_input) {
// Hierarchy input for items
AddInput(kParentInput, NodeValue::kNone);
IgnoreHashingFrom(kParentInput);
IgnoreInvalidationsFrom(kParentInput);
}
}
+62 -77
View File
@@ -37,6 +37,8 @@
namespace olive {
const QString Sequence::kVideoParamsInput = QStringLiteral("video_param_in");
const QString Sequence::kAudioParamsInput = QStringLiteral("audio_param_in");
const QString Sequence::kTextureInput = QStringLiteral("tex_in");
const QString Sequence::kSamplesInput = QStringLiteral("samples_in");
const QString Sequence::kTrackInputFormat = QStringLiteral("track_in_%1");
@@ -49,8 +51,10 @@ Sequence::Sequence(bool viewer_only_mode) :
audio_playback_cache_(this),
operation_stack_(0)
{
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
if (!viewer_only_mode) {
@@ -129,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)
+19 -8
View File
@@ -116,18 +116,25 @@ public:
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
const VideoParams& video_params() const
VideoParams video_params() const
{
return video_params_;
return GetStandardValue(kVideoParamsInput).value<VideoParams>();
}
const AudioParams& audio_params() const
AudioParams audio_params() const
{
return audio_params_;
return GetStandardValue(kAudioParamsInput).value<AudioParams>();
}
void set_video_params(const VideoParams &video);
void set_audio_params(const AudioParams &audio);
void set_video_params(const VideoParams &video)
{
SetStandardValue(kVideoParamsInput, QVariant::fromValue(video));
}
void set_audio_params(const AudioParams &audio)
{
SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio));
}
rational GetLength();
@@ -147,6 +154,9 @@ public:
virtual void EndOperation() override;
static const QString kVideoParamsInput;
static const QString kAudioParamsInput;
static const QString kTextureInput;
static const QString kSamplesInput;
static const QString kTrackInputFormat;
@@ -198,6 +208,8 @@ protected:
virtual void SaveInternal(QXmlStreamWriter *writer) const override;
virtual void InputValueChangedEvent(const QString& input, int element) override;
private:
QVector<TrackList*> track_lists_;
@@ -213,8 +225,7 @@ private:
int operation_stack_;
VideoParams video_params_;
AudioParams audio_params_;
VideoParams cached_video_params_;
TimelinePoints timeline_points_;
+10 -2
View File
@@ -83,13 +83,15 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("node")) {
bool is_root = false;
QString id;
{
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
id = attr.value().toString();
break;
} else if (attr.name() == QStringLiteral("root") && attr.value() == QStringLiteral("1")) {
is_root = true;
}
}
}
@@ -97,7 +99,13 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, uint
if (id.isEmpty()) {
qWarning() << "Failed to load node with empty ID";
} else {
Node* node = NodeFactory::CreateFromID(id);
Node* node;
if (is_root) {
node = &root_;
} else {
node = NodeFactory::CreateFromID(id);
}
if (!node) {
qWarning() << "Failed to find node with ID" << id;
+19 -17
View File
@@ -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);
+49
View File
@@ -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);
+82 -5
View File
@@ -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
View File
@@ -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_;
};
+2 -2
View File
@@ -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:
+2 -59
View File
@@ -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,
+1 -9
View File
@@ -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();
/**
+26 -19
View File
@@ -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) {
+2 -2
View File
@@ -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;
+3 -2
View File
@@ -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
View File
@@ -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*>(&divider_), 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_);
}
}
+111
View File
@@ -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_;
};
}
+9 -16
View File
@@ -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();
}
+9 -6
View File
@@ -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();
+5 -3
View File
@@ -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);
}
}
+40 -54
View File
@@ -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());
}
PlaceAt(refs, start, insert);
}
void ImportTool::PlaceAt(const QVector<DraggedFootage> &footage, const rational &start, bool 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;
}
}
+3 -35
View File
@@ -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_;
+37 -19
View File
@@ -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;
@@ -148,7 +167,6 @@ void FootageViewerWidget::TryConnectingType(Footage *footage, Stream::Type type)
Node::ConnectEdge(NodeOutput(footage, s), NodeInput(&sequence_, input_param));
}
}
}
void FootageViewerWidget::StartFootageDrag()
+2 -2
View File
@@ -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());
+1 -1
View File
@@ -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;