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