Merge branch 'render-singleton'
This commit is contained in:
+1
-1
@@ -41,7 +41,7 @@ endif()
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
find_package(OpenColorIO REQUIRED)
|
||||
find_package(OpenColorIO 2.0.0 REQUIRED)
|
||||
|
||||
find_package(OpenImageIO 1.6 REQUIRED)
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ add_subdirectory(project)
|
||||
add_subdirectory(render)
|
||||
add_subdirectory(shaders)
|
||||
add_subdirectory(task)
|
||||
add_subdirectory(threading)
|
||||
add_subdirectory(timeline)
|
||||
add_subdirectory(tool)
|
||||
add_subdirectory(ui)
|
||||
|
||||
@@ -24,8 +24,6 @@ set(OLIVE_SOURCES
|
||||
audio/outputdeviceproxy.cpp
|
||||
audio/outputmanager.h
|
||||
audio/outputmanager.cpp
|
||||
audio/sampleformat.h
|
||||
audio/sampleformat.cpp
|
||||
audio/tempoprocessor.h
|
||||
audio/tempoprocessor.cpp
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -124,36 +124,8 @@ void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
format.setChannelCount(output_params_.channel_count());
|
||||
format.setCodec("audio/pcm");
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
|
||||
switch (output_params_.format()) {
|
||||
case SampleFormat::SAMPLE_FMT_U8:
|
||||
format.setSampleSize(8);
|
||||
format.setSampleType(QAudioFormat::UnSignedInt);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_S16:
|
||||
format.setSampleSize(16);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_S32:
|
||||
format.setSampleSize(32);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_S64:
|
||||
format.setSampleSize(64);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_FLT:
|
||||
format.setSampleSize(32);
|
||||
format.setSampleType(QAudioFormat::Float);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_DBL:
|
||||
format.setSampleSize(64);
|
||||
format.setSampleType(QAudioFormat::Float);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_COUNT:
|
||||
case SampleFormat::SAMPLE_FMT_INVALID:
|
||||
abort();
|
||||
}
|
||||
format.setSampleSize(output_params_.bits_per_sample());
|
||||
format.setSampleType(AudioParams::GetQtSampleType(output_params_.format()));
|
||||
|
||||
if (info.isFormatSupported(format)) {
|
||||
QMetaObject::invokeMethod(output_manager_,
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 "sampleformat.h"
|
||||
|
||||
#include "core.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
const SampleFormat::Format SampleFormat::kInternalFormat = SAMPLE_FMT_FLT;
|
||||
|
||||
QString SampleFormat::GetSampleFormatName(const SampleFormat::Format &f)
|
||||
{
|
||||
switch (f) {
|
||||
case SAMPLE_FMT_U8:
|
||||
return tr("Unsigned 8-bit");
|
||||
case SAMPLE_FMT_S16:
|
||||
return tr("Signed 16-bit");
|
||||
case SAMPLE_FMT_S32:
|
||||
return tr("Signed 32-bit");
|
||||
case SAMPLE_FMT_S64:
|
||||
return tr("Signed 64-bit");
|
||||
case SAMPLE_FMT_FLT:
|
||||
return tr("32-bit Float");
|
||||
case SAMPLE_FMT_DBL:
|
||||
return tr("64-bit Float");
|
||||
case SAMPLE_FMT_COUNT:
|
||||
case SAMPLE_FMT_INVALID:
|
||||
break;
|
||||
}
|
||||
|
||||
return tr("Invalid");
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -28,7 +28,7 @@ extern "C" {
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "codec/ffmpeg/ffmpegcommon.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -75,7 +75,7 @@ bool TempoProcessor::Open(const AudioParams ¶ms, const double& speed)
|
||||
1,
|
||||
params_.sample_rate(),
|
||||
params_.sample_rate(),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(params_.format()),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(params_.format()),
|
||||
params.channel_layout());
|
||||
|
||||
// Create buffer and buffersink
|
||||
@@ -171,7 +171,7 @@ void TempoProcessor::Push(const char *data, int length)
|
||||
|
||||
// Allocate a buffer for the number of samples we got
|
||||
src_frame->sample_rate = params_.sample_rate();
|
||||
src_frame->format = FFmpegCommon::GetFFmpegSampleFormat(params_.format());
|
||||
src_frame->format = FFmpegUtils::GetFFmpegSampleFormat(params_.format());
|
||||
src_frame->channel_layout = params_.channel_layout();
|
||||
src_frame->nb_samples = params_.bytes_to_samples(length);
|
||||
src_frame->pts = timestamp_;
|
||||
|
||||
+169
-59
@@ -24,11 +24,11 @@
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "codec/ffmpeg/ffmpegcommon.h"
|
||||
#include "codec/ffmpeg/ffmpegdecoder.h"
|
||||
#include "codec/oiio/oiiodecoder.h"
|
||||
#include "codec/waveinput.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
#include "common/filefunctions.h"
|
||||
#include "common/timecodefunctions.h"
|
||||
#ifdef USE_OTIO
|
||||
@@ -39,55 +39,160 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
QMutex Decoder::currently_conforming_mutex_;
|
||||
QWaitCondition Decoder::currently_conforming_wait_cond_;
|
||||
QVector<Decoder::CurrentlyConforming> Decoder::currently_conforming_;
|
||||
|
||||
Decoder::Decoder() :
|
||||
open_(false),
|
||||
stream_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Decoder::Decoder(Stream *fs) :
|
||||
open_(false),
|
||||
stream_(fs)
|
||||
bool Decoder::Open(StreamPtr fs)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
if (stream_) {
|
||||
// Decoder is already open. Return TRUE if the stream is the stream we have, or FALSE if not.
|
||||
if (stream_ == fs) {
|
||||
return true;
|
||||
} else {
|
||||
qWarning() << "Tried to open a decoder that was already open with another stream";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Stream was not open, try opening it now
|
||||
if (fs == nullptr) {
|
||||
// Cannot open null stream
|
||||
qCritical() << "Decoder attempted to open null stream";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fs->footage()->decoder() != id()) {
|
||||
qCritical() << "Tried to open footage in incorrect decoder";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set stream
|
||||
stream_ = fs;
|
||||
|
||||
// Try open internal
|
||||
if (OpenInternal()) {
|
||||
return true;
|
||||
} else {
|
||||
// Unset stream
|
||||
CloseInternal();
|
||||
stream_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StreamPtr Decoder::stream() const
|
||||
FramePtr Decoder::RetrieveVideo(const rational &timecode, const int ÷r)
|
||||
{
|
||||
return stream_;
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
if (!stream_) {
|
||||
qCritical() << "Can't retrieve video on a closed decoder";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!SupportsVideo()) {
|
||||
qCritical() << "Decoder doesn't support video";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stream_->type() != Stream::kVideo) {
|
||||
qCritical() << "Tried to retrieve video from a non-video stream";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return RetrieveVideoInternal(timecode, divider);
|
||||
}
|
||||
|
||||
void Decoder::set_stream(StreamPtr fs)
|
||||
SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams ¶ms, const QAtomicInt *cancelled)
|
||||
{
|
||||
Close();
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
stream_ = fs;
|
||||
if (!stream_) {
|
||||
qCritical() << "Can't retrieve audio on a closed decoder";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!SupportsAudio()) {
|
||||
qCritical() << "Decoder doesn't support audio";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stream_->type() != Stream::kAudio) {
|
||||
qCritical() << "Tried to retrieve audio from a non-audio stream";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Determine if we already have a conformed version
|
||||
QString conform_filename = GetConformedFilename(params);
|
||||
CurrentlyConforming want_conform = {stream_, params};
|
||||
|
||||
currently_conforming_mutex_.lock();
|
||||
|
||||
// Wait for conform to complete
|
||||
while (currently_conforming_.contains(want_conform)) {
|
||||
currently_conforming_wait_cond_.wait(¤tly_conforming_mutex_);
|
||||
}
|
||||
|
||||
// See if we got the conform
|
||||
SampleBufferPtr buffer = RetrieveAudioFromConform(conform_filename, range);
|
||||
|
||||
if (!buffer) {
|
||||
// We'll need to conform this ourselves
|
||||
currently_conforming_.append(want_conform);
|
||||
currently_conforming_mutex_.unlock();
|
||||
|
||||
// We conform to a different filename until it's done to make it clear even across sessions
|
||||
// whether this conform is ready or not
|
||||
QString working_fn = conform_filename;
|
||||
working_fn.append(QStringLiteral(".working"));
|
||||
|
||||
if (ConformAudioInternal(working_fn, params, cancelled)) {
|
||||
// Move file to standard conform name, making it clear this conform is ready for use
|
||||
QFile::remove(conform_filename);
|
||||
QFile::rename(working_fn, conform_filename);
|
||||
|
||||
// Return audio as planned
|
||||
buffer = RetrieveAudioFromConform(conform_filename, range);
|
||||
} else {
|
||||
// Failed
|
||||
qCritical() << "Failed to conform audio";
|
||||
}
|
||||
|
||||
currently_conforming_mutex_.lock();
|
||||
currently_conforming_.removeOne(want_conform);
|
||||
currently_conforming_wait_cond_.wakeAll();
|
||||
}
|
||||
|
||||
currently_conforming_mutex_.unlock();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/, const int &/*divider*/)
|
||||
void Decoder::Close()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
SampleBufferPtr Decoder::RetrieveAudio(const rational &/*timecode*/, const rational &/*length*/, const AudioParams &/*params*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Decoder::SupportsVideo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::SupportsAudio()
|
||||
{
|
||||
return false;
|
||||
if (stream_) {
|
||||
CloseInternal();
|
||||
stream_ = nullptr;
|
||||
} else {
|
||||
qWarning() << "Tried to close a decoder that wasn't open";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DECODER STATIC PUBLIC MEMBERS
|
||||
*/
|
||||
|
||||
QVector<DecoderPtr> ReceiveListOfAllDecoders() {
|
||||
QVector<DecoderPtr> ReceiveListOfAllDecoders()
|
||||
{
|
||||
QVector<DecoderPtr> decoders;
|
||||
|
||||
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
|
||||
@@ -98,7 +203,7 @@ QVector<DecoderPtr> ReceiveListOfAllDecoders() {
|
||||
return decoders;
|
||||
}
|
||||
|
||||
FootagePtr Decoder::ProbeMedia(Project* project, const QString &filename, const QAtomicInt* cancelled)
|
||||
FootagePtr Decoder::Probe(Project* project, const QString &filename, const QAtomicInt* cancelled)
|
||||
{
|
||||
// Check for a valid filename
|
||||
if (filename.isEmpty()) {
|
||||
@@ -184,37 +289,6 @@ QString Decoder::GetIndexFilename()
|
||||
return QDir(stream_->footage()->project()->cache_path()).filePath(FileFunctions::GetUniqueFileIdentifier(stream()->footage()->filename()).append(QString::number(stream()->index())));
|
||||
}
|
||||
|
||||
bool Decoder::ConformAudio(const QAtomicInt *, const AudioParams& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::HasConformedVersion(const AudioParams ¶ms)
|
||||
{
|
||||
if (stream()->type() != Stream::kAudio) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(stream());
|
||||
|
||||
if (audio_stream->has_conformed_version(params)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get indexed WAV file
|
||||
WaveInput input(GetIndexFilename());
|
||||
|
||||
bool index_already_matches = false;
|
||||
|
||||
if (input.open()) {
|
||||
index_already_matches = (input.params() == params);
|
||||
|
||||
input.close();
|
||||
}
|
||||
|
||||
return index_already_matches;
|
||||
}
|
||||
|
||||
void Decoder::SignalProcessingProgress(const int64_t &ts)
|
||||
{
|
||||
if (stream()->duration() != AV_NOPTS_VALUE && stream()->duration() != 0) {
|
||||
@@ -267,4 +341,40 @@ int64_t Decoder::GetImageSequenceIndex(const QString &filename)
|
||||
return number_only.toLongLong();
|
||||
}
|
||||
|
||||
FramePtr Decoder::RetrieveVideoInternal(const rational &timecode, const int ÷r)
|
||||
{
|
||||
Q_UNUSED(timecode)
|
||||
Q_UNUSED(divider)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Decoder::ConformAudioInternal(const QString& filename, const AudioParams ¶ms, const QAtomicInt* cancelled)
|
||||
{
|
||||
Q_UNUSED(filename)
|
||||
Q_UNUSED(cancelled)
|
||||
Q_UNUSED(params)
|
||||
return false;
|
||||
}
|
||||
|
||||
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range)
|
||||
{
|
||||
WaveInput input(conform_filename);
|
||||
|
||||
if (input.open()) {
|
||||
const AudioParams& input_params = input.params();
|
||||
|
||||
// Read bytes from wav
|
||||
QByteArray packed_data = input.read(input_params.time_to_bytes(range.in()),
|
||||
input_params.time_to_bytes(range.length()));
|
||||
input.close();
|
||||
|
||||
// Create sample buffer
|
||||
SampleBufferPtr sample_buffer = SampleBuffer::CreateFromPackedData(input_params, packed_data);
|
||||
|
||||
return sample_buffer;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
+111
-125
@@ -27,6 +27,7 @@ extern "C" {
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QWaitCondition>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "codec/frame.h"
|
||||
@@ -68,117 +69,46 @@ public:
|
||||
|
||||
Decoder();
|
||||
|
||||
Decoder(Stream* fs);
|
||||
|
||||
DISABLE_COPY_MOVE(Decoder)
|
||||
|
||||
/**
|
||||
* @brief Unique decoder ID
|
||||
*/
|
||||
virtual QString id() = 0;
|
||||
|
||||
StreamPtr stream() const;
|
||||
void set_stream(StreamPtr fs);
|
||||
virtual bool SupportsVideo(){return false;}
|
||||
virtual bool SupportsAudio(){return false;}
|
||||
|
||||
/**
|
||||
* @brief Probe a footage file and dump metadata about it
|
||||
* @brief Open stream for decoding
|
||||
*
|
||||
* When a Footage file is imported, we'll need to know whether Olive is equipped with a decoder for utilizing it
|
||||
* and metadata should be retrieved about it if so. For this purpose, the Footage object is passed through all
|
||||
* Probe() functions of available deocders until one returns TRUE. A FALSE return means the Decoder was unable to
|
||||
* parse this file and the next should be tried.
|
||||
* This function is thread safe.
|
||||
*
|
||||
* Probe() differs from Open() since it focuses on a file as a whole rather than one particular stream. Probe()
|
||||
* should be able to be run directly without calling Open() or Close() and should free its memory before returning.
|
||||
*
|
||||
* Probe() will never be called on an object that is also used for decoding. In other words, it will never be called
|
||||
* alongside Open() or Close() externally, so Probe() can use variables that would otherwise be used for decoding
|
||||
* without conflict.
|
||||
*
|
||||
* @param f
|
||||
*
|
||||
* A Footage object to probe. The Footage object will have a valid filename and will be empty prior to being sent
|
||||
* to this function (i.e. Footage::Clear() will not have to be called).
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* TRUE if the Decoder was able to decode this file. FALSE if not. This function should have filled the Footage
|
||||
* object with metadata if it returns TRUE. Otherwise, the Footage object should be untouched.
|
||||
* Returns TRUE if stream could be opened successfully. Also returns TRUE if the decoder is
|
||||
* already open and the stream == the stream provided. Returns FALSE if the stream couldn't
|
||||
* be opened OR if already open and the stream is NOT the same.
|
||||
*/
|
||||
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const = 0;
|
||||
bool Open(StreamPtr fs);
|
||||
|
||||
/**
|
||||
* @brief Open media/allocate memory
|
||||
* @brief Retrieves a video frame from footage
|
||||
*
|
||||
* Any file handles or memory allocation that needs to be done before this instance of a Decoder can return data
|
||||
* should be done here.
|
||||
* This function will always return a valid frame unless a fatal error occurs (in such case,
|
||||
* nullptr will return). If the timecode is before the start of the footage, this function should
|
||||
* return the first frame. Likewise, if it is after the timecode, this function should return the
|
||||
* last frame.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* TRUE if successful and ready to return data, FALSE if failed to open and unable to retrieve data. If the function
|
||||
* fails, any memory allocated should be free'd before returning FALSE, possibly by calling Close().
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
virtual bool Open() = 0;
|
||||
FramePtr RetrieveVideo(const rational& timecode, const int& divider);
|
||||
|
||||
/**
|
||||
* @brief Retrieve video frame
|
||||
* @brief Retrieve audio data from footage
|
||||
*
|
||||
* The main function for retrieving video data from the Decoder. This function should always provide complete frame
|
||||
* data (i.e. no partial frames) at the timecode provided. The Decoder should perform any steps required to retrieve
|
||||
* a complete frame separate from the rest of the program, using any form of caching/indexing to keep this as
|
||||
* performant as possible.
|
||||
* This function will always return a sample buffer unless a fatal error occurs (in such case,
|
||||
* nullptr will return). The SampleBuffer should always have enough audio for the range provided.
|
||||
*
|
||||
* It's acceptable for this function to check whether the Decoder is open, and call Open() if not. If Open() returns
|
||||
* false, this function should return nullptr.
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* The timecode (a rational in seconds) to retrieve the frame at. If there is not a frame at this precise location
|
||||
* this should be corrected internally to the closest fit for the timecode.
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A FramePtr of valid data at this timecode or nullptr if there was nothing to retrieve at the provided timecode or
|
||||
* the media could not be opened.
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
virtual FramePtr RetrieveVideo(const rational& timecode, const int& divider);
|
||||
|
||||
/**
|
||||
* @brief Retrieve video frame
|
||||
*
|
||||
* The main function for retrieving audio data from the Decoder. This function should always provide complete frame
|
||||
* data (i.e. no missing samples) at the timecode and length requested. The Decoder should perform any steps
|
||||
* required to retrieve a complete frame separate from the rest of the program, using any form of caching/indexing
|
||||
* to keep this as performant as possible.
|
||||
*
|
||||
* It's acceptable for this function to check whether the Decoder is open, and call Open() if not. If Open() returns
|
||||
* false, this function should return nullptr.
|
||||
*
|
||||
* @param timecode
|
||||
*
|
||||
* The starting timecode (a rational in seconds) to retrieve the data at.
|
||||
*
|
||||
* @param length
|
||||
*
|
||||
* The total length of audio data to retrieve (a rational in seconds).
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* A FramePtr of valid data at this timecode of the requested length or nullptr if there was nothing to retrieve at
|
||||
* the provided timecode or the media could not be opened.
|
||||
*/
|
||||
virtual SampleBufferPtr RetrieveAudio(const rational& timecode, const rational& length, const AudioParams& params);
|
||||
|
||||
virtual bool SupportsVideo();
|
||||
virtual bool SupportsAudio();
|
||||
|
||||
/**
|
||||
* @brief Close media/deallocate memory
|
||||
*
|
||||
* Any file handles or memory allocations opened in Open() should be cleaned up here.
|
||||
*
|
||||
* As the main memory freeing function, it's good practice to call this in Open() if there's an error that prevents
|
||||
* correct function before Open() returns. As such, Close() should be prepared for not all memory/file handles to
|
||||
* have been opened successfully.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
SampleBufferPtr RetrieveAudio(const TimeRange& range, const AudioParams& params, const QAtomicInt *cancelled);
|
||||
|
||||
/**
|
||||
* @brief Try to probe a Footage file by passing it through all available Decoders
|
||||
@@ -199,7 +129,27 @@ public:
|
||||
*
|
||||
* TRUE if a Decoder was successfully able to parse and probe this file. FALSE if not.
|
||||
*/
|
||||
static FootagePtr ProbeMedia(Project *project, const QString& filename, const QAtomicInt *cancelled);
|
||||
static FootagePtr Probe(Project *project, const QString& filename, const QAtomicInt *cancelled);
|
||||
|
||||
/**
|
||||
* @brief Generate a Footage object from a file
|
||||
*
|
||||
* If this decoder is able to parse this file, it will return a valid FootagePtr. Otherwise, it
|
||||
* will return nullptr.
|
||||
*
|
||||
* For sub-classes, this function should be effectively static. We can't do virtual static
|
||||
* functions in C++, but it should hold and access no state during its run.
|
||||
*
|
||||
* This function is re-entrant.
|
||||
*/
|
||||
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Closes media/deallocates memory
|
||||
*
|
||||
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||
*/
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* @brief Create a Decoder instance using a Decoder ID
|
||||
@@ -210,42 +160,45 @@ public:
|
||||
*/
|
||||
static DecoderPtr CreateFromID(const QString& id);
|
||||
|
||||
/**
|
||||
* @brief AUDIO ONLY: Produces a complete PCM extraction of the audio stream
|
||||
*
|
||||
* Internally, our render engine only deals with PCM since it provides the least headaches and
|
||||
* modern computers have the processing power to do it.
|
||||
*
|
||||
* Resamples and converts the currently open audio to match the params. If the audio doesn't need
|
||||
* conforming (e.g. audio params already match or a conformed match already exists), this function
|
||||
* will return immediately. Otherwise it will block the calling thread until the conform is
|
||||
* complete. This function should therefore only be called from a background render thread.
|
||||
*
|
||||
* All audio decoders must override this. It's not pure since video decoders don't need to use
|
||||
* this, but default behavior will abort since it should never be called.
|
||||
*/
|
||||
virtual bool ConformAudio(const QAtomicInt* cancelled, const AudioParams ¶ms);
|
||||
|
||||
/**
|
||||
* @brief AUDIO ONLY: Returns whether a transcode of this audio matching the specified params
|
||||
* already exists
|
||||
*/
|
||||
bool HasConformedVersion(const AudioParams& params);
|
||||
|
||||
static QString TransformImageSequenceFileName(const QString& filename, const int64_t& number);
|
||||
|
||||
static int GetImageSequenceDigitCount(const QString& filename);
|
||||
|
||||
static int64_t GetImageSequenceIndex(const QString& filename);
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
|
||||
* available
|
||||
*/
|
||||
void IndexProgress(double);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Internal open function
|
||||
*
|
||||
* Sub-classes must override this function. Function will already be mutexed, so there is no need
|
||||
* to worry about thread safety. Also many other sanity checks will be done before this, so
|
||||
* sub-classes only need to worry about their own opening functions. It is guaranteed that the
|
||||
* decoder is not open yet and that the footage stream was from that sub-classes probe function.
|
||||
*
|
||||
* Return TRUE if everything opened successfully and the decoder is ready to work. Otherwise,
|
||||
* return FALSE. If this function returns false, Decoder will call CloseInternal to clean any
|
||||
* memory allocated during OpenInternal.
|
||||
*/
|
||||
virtual bool OpenInternal() = 0;
|
||||
|
||||
/**
|
||||
* @brief Internal close function
|
||||
*
|
||||
* Sub-classes must override this function. Function should be able to safely clear all allocated
|
||||
* memory. It may be called even if Open() didn't complete or RetrieveVideo() was never called.
|
||||
*/
|
||||
virtual void CloseInternal() = 0;
|
||||
|
||||
/**
|
||||
* @brief Internal frame retrieval function
|
||||
*
|
||||
* Sub-classes must override this function IF they support video. Function is already mutexed
|
||||
* so sub-classes don't need to worry about thread safety.
|
||||
*/
|
||||
virtual FramePtr RetrieveVideoInternal(const rational& timecode, const int& divider);
|
||||
|
||||
virtual bool ConformAudioInternal(const QString& filename, const AudioParams ¶ms, const QAtomicInt* cancelled);
|
||||
|
||||
void SignalProcessingProgress(const int64_t& ts);
|
||||
|
||||
/**
|
||||
@@ -255,11 +208,44 @@ protected:
|
||||
|
||||
QString GetIndexFilename();
|
||||
|
||||
bool open_;
|
||||
struct CurrentlyConforming {
|
||||
StreamPtr stream;
|
||||
AudioParams params;
|
||||
|
||||
bool operator==(const CurrentlyConforming& rhs) const
|
||||
{
|
||||
return this->stream == rhs.stream && this->params == rhs.params;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Return currently open stream
|
||||
*
|
||||
* This function is NOT thread safe and should therefore only be called by thread safe functions.
|
||||
*/
|
||||
StreamPtr stream() const
|
||||
{
|
||||
return stream_;
|
||||
}
|
||||
|
||||
static QMutex currently_conforming_mutex_;
|
||||
static QWaitCondition currently_conforming_wait_cond_;
|
||||
static QVector<CurrentlyConforming> currently_conforming_;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief While indexing, this signal will provide progress as a percentage (0-100 inclusive) if
|
||||
* available
|
||||
*/
|
||||
void IndexProgress(double);
|
||||
|
||||
private:
|
||||
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range);
|
||||
|
||||
StreamPtr stream_;
|
||||
|
||||
QMutex mutex_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -232,7 +232,7 @@ void EncodingParams::Save(QXmlStreamWriter *writer) const
|
||||
Encoder* Encoder::CreateFromID(const QString &id, const EncodingParams& params)
|
||||
{
|
||||
Q_UNUSED(id)
|
||||
|
||||
|
||||
return new FFmpegEncoder(params);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,11 @@ public:
|
||||
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual VideoParams::Format GetDesiredPixelFormat() const
|
||||
{
|
||||
return VideoParams::kFormatInvalid;
|
||||
}
|
||||
|
||||
private:
|
||||
EncodingParams params_;
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
codec/ffmpeg/avframeptr.h
|
||||
codec/ffmpeg/ffmpegcommon.h
|
||||
codec/ffmpeg/ffmpegcommon.cpp
|
||||
codec/ffmpeg/ffmpegdecoder.h
|
||||
codec/ffmpeg/ffmpegdecoder.cpp
|
||||
codec/ffmpeg/ffmpegencoder.h
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 "ffmpegcommon.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AVPixelFormat FFmpegCommon::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt)
|
||||
{
|
||||
AVPixelFormat possible_pix_fmts[] = {
|
||||
AV_PIX_FMT_RGB24,
|
||||
AV_PIX_FMT_RGBA,
|
||||
AV_PIX_FMT_RGB48,
|
||||
AV_PIX_FMT_RGBA64,
|
||||
AV_PIX_FMT_NONE
|
||||
};
|
||||
|
||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
|
||||
pix_fmt,
|
||||
1,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
SampleFormat::Format FFmpegCommon::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return SampleFormat::SAMPLE_FMT_U8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return SampleFormat::SAMPLE_FMT_S16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return SampleFormat::SAMPLE_FMT_S32;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return SampleFormat::SAMPLE_FMT_S64;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return SampleFormat::SAMPLE_FMT_FLT;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return SampleFormat::SAMPLE_FMT_DBL;
|
||||
case AV_SAMPLE_FMT_U8P :
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
}
|
||||
|
||||
return SampleFormat::SAMPLE_FMT_INVALID;
|
||||
}
|
||||
|
||||
AVSampleFormat FFmpegCommon::GetFFmpegSampleFormat(const SampleFormat::Format &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case SampleFormat::SAMPLE_FMT_U8:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case SampleFormat::SAMPLE_FMT_S16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case SampleFormat::SAMPLE_FMT_S32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case SampleFormat::SAMPLE_FMT_S64:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case SampleFormat::SAMPLE_FMT_FLT:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case SampleFormat::SAMPLE_FMT_DBL:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case SampleFormat::SAMPLE_FMT_INVALID:
|
||||
case SampleFormat::SAMPLE_FMT_COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return AV_SAMPLE_FMT_NONE;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegCommon::GetFFmpegPixelFormat(const PixelFormat::Format &pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::PIX_FMT_RGBA8:
|
||||
return AV_PIX_FMT_RGBA;
|
||||
case PixelFormat::PIX_FMT_RGBA16U:
|
||||
return AV_PIX_FMT_RGBA64;
|
||||
case PixelFormat::PIX_FMT_RGB8:
|
||||
return AV_PIX_FMT_RGB24;
|
||||
case PixelFormat::PIX_FMT_RGB16U:
|
||||
return AV_PIX_FMT_RGB48;
|
||||
case PixelFormat::PIX_FMT_RGBA16F:
|
||||
case PixelFormat::PIX_FMT_RGBA32F:
|
||||
case PixelFormat::PIX_FMT_RGB16F:
|
||||
case PixelFormat::PIX_FMT_RGB32F:
|
||||
case PixelFormat::PIX_FMT_INVALID:
|
||||
case PixelFormat::PIX_FMT_COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
PixelFormat::Format FFmpegCommon::GetCompatiblePixelFormat(const PixelFormat::Format &pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case PixelFormat::PIX_FMT_RGB8:
|
||||
return PixelFormat::PIX_FMT_RGB8;
|
||||
case PixelFormat::PIX_FMT_RGBA8:
|
||||
return PixelFormat::PIX_FMT_RGBA8;
|
||||
case PixelFormat::PIX_FMT_RGB16U:
|
||||
case PixelFormat::PIX_FMT_RGB16F:
|
||||
case PixelFormat::PIX_FMT_RGB32F:
|
||||
return PixelFormat::PIX_FMT_RGB16U;
|
||||
case PixelFormat::PIX_FMT_RGBA16U:
|
||||
case PixelFormat::PIX_FMT_RGBA16F:
|
||||
case PixelFormat::PIX_FMT_RGBA32F:
|
||||
return PixelFormat::PIX_FMT_RGBA16U;
|
||||
case PixelFormat::PIX_FMT_INVALID:
|
||||
case PixelFormat::PIX_FMT_COUNT:
|
||||
break;
|
||||
}
|
||||
|
||||
return PixelFormat::PIX_FMT_INVALID;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
+339
-775
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,6 @@ extern "C" {
|
||||
#include <QVector>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "audio/sampleformat.h"
|
||||
#include "avframeptr.h"
|
||||
#include "codec/decoder.h"
|
||||
#include "codec/waveoutput.h"
|
||||
@@ -41,99 +40,6 @@ extern "C" {
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class FFmpegDecoderInstance : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FFmpegDecoderInstance(const char* filename, int stream_index);
|
||||
virtual ~FFmpegDecoderInstance();
|
||||
|
||||
DISABLE_COPY_MOVE(FFmpegDecoderInstance)
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
void SetFramePool(FFmpegFramePool* frame_pool);
|
||||
|
||||
int64_t RangeStart() const;
|
||||
int64_t RangeEnd() const;
|
||||
bool CacheContainsTime(const int64_t& t) const;
|
||||
bool CacheWillContainTime(const int64_t& t) const;
|
||||
bool CacheCouldContainTime(const int64_t& t) const;
|
||||
bool CacheIsEmpty() const;
|
||||
FFmpegFramePool::ElementPtr GetFrameFromCache(const int64_t& t) const;
|
||||
|
||||
void RemoveFramesBefore(const qint64& t);
|
||||
int TruncateCacheRangeToTime(const qint64& t);
|
||||
int TruncateCacheRangeToFrames(int nb_frames);
|
||||
void RemoveFirstFrame();
|
||||
|
||||
AVFormatContext* fmt_ctx() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
|
||||
AVStream* stream() const
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
|
||||
void ClearFrameCache();
|
||||
|
||||
FFmpegFramePool::ElementPtr RetrieveFrame(const int64_t &target_ts, int divider, bool cache_is_locked);
|
||||
|
||||
/**
|
||||
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* An FFmpeg error code, or >= 0 on success
|
||||
*/
|
||||
int GetFrame(AVPacket* pkt, AVFrame* frame);
|
||||
|
||||
QMutex* cache_lock();
|
||||
QWaitCondition* cache_wait_cond();
|
||||
|
||||
bool IsWorking();
|
||||
void SetWorking(bool working);
|
||||
|
||||
private:
|
||||
void ClearResources();
|
||||
|
||||
void Seek(int64_t timestamp);
|
||||
|
||||
void InitScaler(int divider);
|
||||
void FreeScaler();
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
AVDictionary* opts_;
|
||||
|
||||
SwsContext* scale_ctx_;
|
||||
int scale_divider_;
|
||||
|
||||
int64_t second_ts_;
|
||||
|
||||
QWaitCondition cache_wait_cond_;
|
||||
QMutex cache_lock_;
|
||||
QList<FFmpegFramePool::ElementPtr> cached_frames_;
|
||||
FFmpegFramePool* frame_pool_;
|
||||
|
||||
int64_t cache_target_time_;
|
||||
|
||||
bool is_working_;
|
||||
QMutex is_working_mutex_;
|
||||
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
QTimer* clear_timer_;
|
||||
static const int kMaxFrameLife;
|
||||
|
||||
private slots:
|
||||
void ClearTimerEvent();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||
*/
|
||||
@@ -147,40 +53,62 @@ public:
|
||||
// Destructor
|
||||
virtual ~FFmpegDecoder() override;
|
||||
|
||||
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const override;
|
||||
|
||||
virtual bool Open() override;
|
||||
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
|
||||
virtual SampleBufferPtr RetrieveAudio(const rational &timecode, const rational &length, const AudioParams& params) override;
|
||||
virtual void Close() override;
|
||||
|
||||
virtual QString id() override;
|
||||
|
||||
virtual bool SupportsVideo() override;
|
||||
virtual bool SupportsAudio() override;
|
||||
virtual bool SupportsVideo() override{return true;}
|
||||
virtual bool SupportsAudio() override{return true;}
|
||||
|
||||
virtual bool ConformAudio(const QAtomicInt* cancelled, const AudioParams& p) override;
|
||||
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const override;
|
||||
|
||||
struct FFmpegFramePoolKey {
|
||||
int width;
|
||||
int height;
|
||||
AVPixelFormat format;
|
||||
|
||||
bool operator==(const FFmpegFramePoolKey& k) const
|
||||
{
|
||||
return width == k.width && height == k.height && format == k.format;
|
||||
}
|
||||
};
|
||||
protected:
|
||||
virtual bool OpenInternal() override;
|
||||
virtual FramePtr RetrieveVideoInternal(const rational &timecode, const int& divider) override;
|
||||
virtual bool ConformAudioInternal(const QString& filename, const AudioParams ¶ms, const QAtomicInt* cancelled) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handle an error
|
||||
*
|
||||
* Immediately closes the Decoder (freeing memory resources) and sends the string provided to the warning stream.
|
||||
* As this function closes the Decoder, no further Decoder functions should be performed after this is called
|
||||
* (unless the Decoder is opened again first).
|
||||
*/
|
||||
void Error(const QString& s);
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
Instance();
|
||||
|
||||
~Instance()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
bool Open(const char* filename, int stream_index);
|
||||
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* @brief Uses the FFmpeg API to retrieve a packet (stored in pkt_) and decode it (stored in frame_)
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* An FFmpeg error code, or >= 0 on success
|
||||
*/
|
||||
int GetFrame(AVPacket* pkt, AVFrame* frame);
|
||||
|
||||
void Seek(int64_t timestamp);
|
||||
|
||||
AVFormatContext* fmt_ctx() const
|
||||
{
|
||||
return fmt_ctx_;
|
||||
}
|
||||
|
||||
AVStream* avstream() const
|
||||
{
|
||||
return avstream_;
|
||||
}
|
||||
|
||||
private:
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
AVDictionary* opts_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Handle an FFmpeg error code
|
||||
@@ -190,42 +118,50 @@ private:
|
||||
*
|
||||
* @param error_code
|
||||
*/
|
||||
void FFmpegError(int error_code);
|
||||
|
||||
void ClearResources();
|
||||
static QString FFmpegError(int error_code);
|
||||
|
||||
void InitScaler(int divider);
|
||||
void FreeScaler();
|
||||
|
||||
FramePtr RetrieveStillImage(const rational& timecode, const int& divider);
|
||||
|
||||
static PixelFormat::Format GetNativePixelFormat(AVPixelFormat pix_fmt);
|
||||
static VideoParams::Format GetNativePixelFormat(AVPixelFormat pix_fmt);
|
||||
static int GetNativeChannelCount(AVPixelFormat pix_fmt);
|
||||
|
||||
static uint64_t ValidateChannelLayout(AVStream *stream);
|
||||
|
||||
static bool StreamUsesMultipleInstances(StreamPtr stream);
|
||||
void FFmpegBufferToNativeBuffer(uint8_t** input_data, int* input_linesize, uint8_t **output_buffer, int *output_linesize);
|
||||
|
||||
FramePtr BuffersToNativeFrame(int divider, int width, int height, const rational &ts, uint8_t **input_data, int* input_linesize);
|
||||
FFmpegFramePool::ElementPtr GetFrameFromCache(const int64_t& t) const;
|
||||
|
||||
void ClearFrameCache();
|
||||
|
||||
FFmpegFramePool::ElementPtr RetrieveFrame(const int64_t &target_ts, int divider);
|
||||
|
||||
void RemoveFirstFrame();
|
||||
|
||||
SwsContext* scale_ctx_;
|
||||
int scale_divider_;
|
||||
AVPixelFormat src_pix_fmt_;
|
||||
AVPixelFormat ideal_pix_fmt_;
|
||||
PixelFormat::Format native_pix_fmt_;
|
||||
VideoParams::Format native_pix_fmt_;
|
||||
int native_channel_count_;
|
||||
|
||||
struct FFmpegFramePoolValue {
|
||||
FFmpegFramePool* pool = nullptr;
|
||||
int handles = 0;
|
||||
};
|
||||
FFmpegFramePool pool_;
|
||||
|
||||
static QHash< Stream*, QList<FFmpegDecoderInstance*> > instance_map_;
|
||||
static QHash< FFmpegFramePoolKey, FFmpegFramePoolValue > frame_pool_map_;
|
||||
static QMutex instance_map_lock_;
|
||||
int64_t second_ts_;
|
||||
|
||||
QList<FFmpegFramePool::ElementPtr> cached_frames_;
|
||||
|
||||
bool is_working_;
|
||||
QMutex is_working_mutex_;
|
||||
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
Instance instance_;
|
||||
|
||||
};
|
||||
|
||||
uint qHash(const FFmpegDecoder::FFmpegFramePoolKey& r);
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // FFMPEGDECODER_H
|
||||
|
||||
@@ -26,8 +26,7 @@ extern "C" {
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "ffmpegcommon.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "common/ffmpegutils.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -36,7 +35,8 @@ FFmpegEncoder::FFmpegEncoder(const EncodingParams ¶ms) :
|
||||
fmt_ctx_(nullptr),
|
||||
video_stream_(nullptr),
|
||||
video_codec_ctx_(nullptr),
|
||||
video_scale_ctx_(nullptr),
|
||||
video_alpha_scale_ctx_(nullptr),
|
||||
video_noalpha_scale_ctx_(nullptr),
|
||||
audio_stream_(nullptr),
|
||||
audio_codec_ctx_(nullptr),
|
||||
audio_resample_ctx_(nullptr),
|
||||
@@ -72,29 +72,49 @@ bool FFmpegEncoder::Open()
|
||||
}
|
||||
|
||||
// This is the format we will expect frames received in Write() to be in
|
||||
PixelFormat::Format native_pixel_fmt = params().video_params().format();
|
||||
VideoParams::Format native_pixel_fmt = params().video_params().format();
|
||||
|
||||
// This is the format we will need to convert the frame to for swscale to understand it
|
||||
video_conversion_fmt_ = FFmpegCommon::GetCompatiblePixelFormat(native_pixel_fmt);
|
||||
video_conversion_fmt_ = FFmpegUtils::GetCompatiblePixelFormat(native_pixel_fmt);
|
||||
|
||||
// This is the equivalent pixel format above as an AVPixelFormat that swscale can understand
|
||||
AVPixelFormat src_pix_fmt = FFmpegCommon::GetFFmpegPixelFormat(video_conversion_fmt_);
|
||||
AVPixelFormat src_alpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_,
|
||||
VideoParams::kRGBAChannelCount);
|
||||
|
||||
AVPixelFormat src_noalpha_pix_fmt = FFmpegUtils::GetFFmpegPixelFormat(video_conversion_fmt_,
|
||||
VideoParams::kRGBChannelCount);
|
||||
|
||||
if (src_alpha_pix_fmt == AV_PIX_FMT_NONE || src_noalpha_pix_fmt == AV_PIX_FMT_NONE) {
|
||||
Error(QStringLiteral("Failed to find suitable pixel format for this buffer"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is the pixel format the encoder wants to encode to
|
||||
AVPixelFormat encoder_pix_fmt = video_codec_ctx_->pix_fmt;
|
||||
|
||||
// Set up a scaling context - if the native pixel format is not equal to the encoder's, we'll need to convert it
|
||||
// before encoding. Even if we don't, this may be useful for converting between linesizes, etc.
|
||||
video_scale_ctx_ = sws_getContext(params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
src_pix_fmt,
|
||||
params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
encoder_pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
video_alpha_scale_ctx_ = sws_getContext(params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
src_alpha_pix_fmt,
|
||||
params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
encoder_pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
video_noalpha_scale_ctx_ = sws_getContext(params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
src_noalpha_pix_fmt,
|
||||
params().video_params().width(),
|
||||
params().video_params().height(),
|
||||
encoder_pix_fmt,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// Initialize an audio stream if it's enabled
|
||||
@@ -157,19 +177,22 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
|
||||
|
||||
// We may need to convert this frame to a frame that swscale will understand
|
||||
if (frame->format() != video_conversion_fmt_) {
|
||||
frame = PixelFormat::ConvertPixelFormat(frame, video_conversion_fmt_);
|
||||
frame = frame->convert(video_conversion_fmt_);
|
||||
}
|
||||
|
||||
// Use swscale context to convert formats/linesizes
|
||||
input_data = frame->const_data();
|
||||
input_linesize = frame->linesize_bytes();
|
||||
error_code = sws_scale(video_scale_ctx_,
|
||||
|
||||
error_code = sws_scale((frame->channel_count() == VideoParams::kRGBAChannelCount) ? video_alpha_scale_ctx_ : video_noalpha_scale_ctx_,
|
||||
reinterpret_cast<const uint8_t**>(&input_data),
|
||||
&input_linesize,
|
||||
0,
|
||||
frame->height(),
|
||||
encoded_frame->data,
|
||||
encoded_frame->linesize);
|
||||
|
||||
|
||||
if (error_code < 0) {
|
||||
FFmpegError("Failed to scale frame", error_code);
|
||||
goto fail;
|
||||
@@ -208,7 +231,7 @@ void FFmpegEncoder::WriteAudio(AudioParams pcm_info, QIODevice* file)
|
||||
audio_codec_ctx_->sample_fmt,
|
||||
audio_codec_ctx_->sample_rate,
|
||||
static_cast<int64_t>(pcm_info.channel_layout()),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(pcm_info.format()),
|
||||
FFmpegUtils::GetFFmpegSampleFormat(pcm_info.format()),
|
||||
pcm_info.sample_rate(),
|
||||
0,
|
||||
nullptr);
|
||||
@@ -300,9 +323,14 @@ void FFmpegEncoder::Close()
|
||||
open_ = false;
|
||||
}
|
||||
|
||||
if (video_scale_ctx_) {
|
||||
sws_freeContext(video_scale_ctx_);
|
||||
video_scale_ctx_ = nullptr;
|
||||
if (video_alpha_scale_ctx_) {
|
||||
sws_freeContext(video_alpha_scale_ctx_);
|
||||
video_alpha_scale_ctx_ = nullptr;
|
||||
}
|
||||
|
||||
if (video_noalpha_scale_ctx_) {
|
||||
sws_freeContext(video_noalpha_scale_ctx_);
|
||||
video_noalpha_scale_ctx_ = nullptr;
|
||||
}
|
||||
|
||||
if (video_codec_ctx_) {
|
||||
|
||||
@@ -47,6 +47,11 @@ public:
|
||||
|
||||
virtual void Close() override;
|
||||
|
||||
virtual VideoParams::Format GetDesiredPixelFormat() const override
|
||||
{
|
||||
return video_conversion_fmt_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Handle an error
|
||||
@@ -80,8 +85,9 @@ private:
|
||||
|
||||
AVStream* video_stream_;
|
||||
AVCodecContext* video_codec_ctx_;
|
||||
SwsContext* video_scale_ctx_;
|
||||
PixelFormat::Format video_conversion_fmt_;
|
||||
SwsContext* video_alpha_scale_ctx_;
|
||||
SwsContext* video_noalpha_scale_ctx_;
|
||||
VideoParams::Format video_conversion_fmt_;
|
||||
|
||||
AVStream* audio_stream_;
|
||||
AVCodecContext* audio_codec_ctx_;
|
||||
|
||||
@@ -20,9 +20,7 @@
|
||||
|
||||
#include "ffmpegframepool.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/imgutils.h>
|
||||
}
|
||||
#include "codec/frame.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -30,32 +28,24 @@ FFmpegFramePool::FFmpegFramePool(int element_count) :
|
||||
MemoryPool(element_count),
|
||||
width_(0),
|
||||
height_(0),
|
||||
format_(AV_PIX_FMT_NONE)
|
||||
format_(VideoParams::kFormatInvalid),
|
||||
channel_count_(0)
|
||||
{
|
||||
}
|
||||
|
||||
void FFmpegFramePool::SetParameters(int width, int height, AVPixelFormat format)
|
||||
void FFmpegFramePool::SetParameters(int width, int height, VideoParams::Format format, int channel_count)
|
||||
{
|
||||
Clear();
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
format_ = format;
|
||||
channel_count_ = channel_count;
|
||||
}
|
||||
|
||||
size_t FFmpegFramePool::GetElementSize()
|
||||
{
|
||||
int buf_sz = av_image_get_buffer_size(static_cast<AVPixelFormat>(format_),
|
||||
width_,
|
||||
height_,
|
||||
1);
|
||||
|
||||
if (buf_sz < 0) {
|
||||
qDebug() << "Failed to find buffer size:" << buf_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return buf_sz;
|
||||
return Frame::generate_linesize_bytes(width_, format_, channel_count_) * height_;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#define FFMPEGFRAMEPOOL_H
|
||||
|
||||
#include "common/memorypool.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -32,7 +31,7 @@ class FFmpegFramePool : public MemoryPool<uint8_t>
|
||||
public:
|
||||
FFmpegFramePool(int element_count);
|
||||
|
||||
void SetParameters(int width, int height, AVPixelFormat format);
|
||||
void SetParameters(int width, int height, VideoParams::Format format, int channel_count);
|
||||
|
||||
const int& width() const
|
||||
{
|
||||
@@ -52,7 +51,9 @@ private:
|
||||
|
||||
int height_;
|
||||
|
||||
AVPixelFormat format_;
|
||||
VideoParams::Format format_;
|
||||
|
||||
int channel_count_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+43
-65
@@ -20,10 +20,13 @@
|
||||
|
||||
#include "frame.h"
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <QDebug>
|
||||
#include <QtGlobal>
|
||||
#include <QtMath>
|
||||
|
||||
#include "common/oiioutils.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
Frame::Frame() :
|
||||
@@ -45,33 +48,14 @@ void Frame::set_video_params(const VideoParams ¶ms)
|
||||
{
|
||||
params_ = params;
|
||||
|
||||
// Align linesize to 32
|
||||
linesize_ = qCeil(static_cast<double>(width()) / 32.0) * 32;
|
||||
linesize_ = generate_linesize_bytes(width(), params_.format(), params_.channel_count());
|
||||
linesize_pixels_ = linesize_ / params_.GetBytesPerPixel();
|
||||
}
|
||||
|
||||
int Frame::linesize_pixels() const
|
||||
int Frame::generate_linesize_bytes(int width, VideoParams::Format format, int channel_count)
|
||||
{
|
||||
return linesize_;
|
||||
}
|
||||
|
||||
int Frame::linesize_bytes() const
|
||||
{
|
||||
return linesize_pixels() * PixelFormat::BytesPerPixel(params_.format());
|
||||
}
|
||||
|
||||
const int &Frame::width() const
|
||||
{
|
||||
return params_.effective_width();
|
||||
}
|
||||
|
||||
const int &Frame::height() const
|
||||
{
|
||||
return params_.effective_height();
|
||||
}
|
||||
|
||||
const PixelFormat::Format &Frame::format() const
|
||||
{
|
||||
return params_.format();
|
||||
// Align to 32 bytes (not sure if this is necessary?)
|
||||
return VideoParams::GetBytesPerPixel(format, channel_count) * ((width + 31) & ~31);
|
||||
}
|
||||
|
||||
Color Frame::get_pixel(int x, int y) const
|
||||
@@ -80,11 +64,9 @@ Color Frame::get_pixel(int x, int y) const
|
||||
return Color();
|
||||
}
|
||||
|
||||
int pixel_index = y * linesize_pixels() + x;
|
||||
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
|
||||
|
||||
int byte_offset = PixelFormat::GetBufferSize(video_params().format(), pixel_index, 1);
|
||||
|
||||
return Color(data_.data() + byte_offset, video_params().format());
|
||||
return Color(data_.data() + byte_offset, video_params().format(), video_params().channel_count());
|
||||
}
|
||||
|
||||
bool Frame::contains_pixel(int x, int y) const
|
||||
@@ -98,57 +80,53 @@ void Frame::set_pixel(int x, int y, const Color &c)
|
||||
return;
|
||||
}
|
||||
|
||||
int pixel_index = y * linesize_pixels() + x;
|
||||
int byte_offset = y * linesize_bytes() + x * video_params().GetBytesPerPixel();
|
||||
|
||||
int byte_offset = PixelFormat::GetBufferSize(video_params().format(), pixel_index, 1);
|
||||
|
||||
c.toData(data_.data() + byte_offset, video_params().format());
|
||||
c.toData(data_.data() + byte_offset, video_params().format(), video_params().channel_count());
|
||||
}
|
||||
|
||||
const rational &Frame::timestamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
void Frame::set_timestamp(const rational ×tamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
char *Frame::data()
|
||||
{
|
||||
return data_.data();
|
||||
}
|
||||
|
||||
const char *Frame::const_data() const
|
||||
{
|
||||
return data_.constData();
|
||||
}
|
||||
|
||||
void Frame::allocate()
|
||||
bool Frame::allocate()
|
||||
{
|
||||
// Assume this frame is intended to be a video frame
|
||||
if (!params_.is_valid()) {
|
||||
qWarning() << "Tried to allocate a frame with invalid parameters";
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
data_.resize(PixelFormat::GetBufferSize(params_.format(), linesize_, params_.height()));
|
||||
data_.resize(VideoParams::GetBufferSize(linesize_, height(), params_.format(), params_.channel_count()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frame::is_allocated() const
|
||||
FramePtr Frame::convert(VideoParams::Format format) const
|
||||
{
|
||||
return !data_.isEmpty();
|
||||
}
|
||||
// Create new params with destination format
|
||||
VideoParams params = params_;
|
||||
params.set_format(format);
|
||||
|
||||
void Frame::destroy()
|
||||
{
|
||||
data_.clear();
|
||||
}
|
||||
// Create new frame
|
||||
FramePtr converted = Frame::Create();
|
||||
converted->set_video_params(params);
|
||||
converted->set_timestamp(timestamp_);
|
||||
converted->allocate();
|
||||
|
||||
int Frame::allocated_size() const
|
||||
{
|
||||
return data_.size();
|
||||
// Do the conversion through OIIO for convenience
|
||||
OIIO::ImageBuf src(OIIO::ImageSpec(width(), height(),
|
||||
channel_count(),
|
||||
OIIOUtils::GetOIIOBaseTypeFromFormat(this->format())));
|
||||
|
||||
OIIOUtils::FrameToBuffer(this, &src);
|
||||
|
||||
OIIO::ImageBuf dst(OIIO::ImageSpec(converted->width(), converted->height(),
|
||||
channel_count(),
|
||||
OIIOUtils::GetOIIOBaseTypeFromFormat(format)));
|
||||
|
||||
if (dst.copy_pixels(src)) {
|
||||
OIIOUtils::BufferToFrame(&dst, converted.get());
|
||||
return converted;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
+65
-14
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "common/rational.h"
|
||||
#include "render/color.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -47,11 +46,37 @@ public:
|
||||
const VideoParams& video_params() const;
|
||||
void set_video_params(const VideoParams& params);
|
||||
|
||||
int linesize_pixels() const;
|
||||
int linesize_bytes() const;
|
||||
const int& width() const;
|
||||
const int& height() const;
|
||||
const PixelFormat::Format& format() const;
|
||||
static int generate_linesize_bytes(int width, VideoParams::Format format, int channel_count);
|
||||
|
||||
int linesize_pixels() const
|
||||
{
|
||||
return linesize_pixels_;
|
||||
}
|
||||
|
||||
int linesize_bytes() const
|
||||
{
|
||||
return linesize_;
|
||||
}
|
||||
|
||||
int width() const
|
||||
{
|
||||
return params_.effective_width();
|
||||
}
|
||||
|
||||
int height() const
|
||||
{
|
||||
return params_.effective_height();
|
||||
}
|
||||
|
||||
VideoParams::Format format() const
|
||||
{
|
||||
return params_.format();
|
||||
}
|
||||
|
||||
int channel_count() const
|
||||
{
|
||||
return params_.channel_count();
|
||||
}
|
||||
|
||||
Color get_pixel(int x, int y) const;
|
||||
bool contains_pixel(int x, int y) const;
|
||||
@@ -62,18 +87,31 @@ public:
|
||||
*
|
||||
* This timestamp is always a rational that will equate to the time in seconds.
|
||||
*/
|
||||
const rational& timestamp() const;
|
||||
void set_timestamp(const rational& timestamp);
|
||||
const rational& timestamp() const
|
||||
{
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
void set_timestamp(const rational& timestamp)
|
||||
{
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the data buffer of this frame
|
||||
*/
|
||||
char* data();
|
||||
char* data()
|
||||
{
|
||||
return data_.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the const data buffer of this frame
|
||||
*/
|
||||
const char* const_data() const;
|
||||
const char* const_data() const
|
||||
{
|
||||
return data_.constData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocate memory buffer to store data based on parameters
|
||||
@@ -82,24 +120,35 @@ public:
|
||||
*
|
||||
* If a memory buffer has been previously allocated without destroying, this function will destroy it.
|
||||
*/
|
||||
void allocate();
|
||||
bool allocate();
|
||||
|
||||
/**
|
||||
* @brief Return whether the frame is allocated or not
|
||||
*/
|
||||
bool is_allocated() const;
|
||||
bool is_allocated() const
|
||||
{
|
||||
return !data_.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroy a memory buffer allocated with allocate()
|
||||
*/
|
||||
void destroy();
|
||||
void destroy()
|
||||
{
|
||||
data_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the size of the array returned in data() in bytes
|
||||
*
|
||||
* Returns 0 if nothing is allocated.
|
||||
*/
|
||||
int allocated_size() const;
|
||||
int allocated_size() const
|
||||
{
|
||||
return data_.size();
|
||||
}
|
||||
|
||||
FramePtr convert(VideoParams::Format format) const;
|
||||
|
||||
private:
|
||||
VideoParams params_;
|
||||
@@ -110,6 +159,8 @@ private:
|
||||
|
||||
int linesize_;
|
||||
|
||||
int linesize_pixels_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
codec/oiio/oiiodecoder.h
|
||||
codec/oiio/oiiodecoder.cpp
|
||||
codec/oiio/oiiodecoder.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
+56
-130
@@ -27,6 +27,7 @@
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "common/define.h"
|
||||
#include "common/oiioutils.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
|
||||
@@ -40,6 +41,11 @@ OIIODecoder::OIIODecoder() :
|
||||
{
|
||||
}
|
||||
|
||||
OIIODecoder::~OIIODecoder()
|
||||
{
|
||||
CloseInternal();
|
||||
}
|
||||
|
||||
QString OIIODecoder::id()
|
||||
{
|
||||
return QStringLiteral("oiio");
|
||||
@@ -47,6 +53,10 @@ QString OIIODecoder::id()
|
||||
|
||||
FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const
|
||||
{
|
||||
Q_UNUSED(cancelled)
|
||||
|
||||
// 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 nullptr;
|
||||
}
|
||||
@@ -59,8 +69,9 @@ FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancell
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 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")) {
|
||||
// If this is FFmpeg via OIIO, fall-through to our native FFmpeg decoder
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -70,8 +81,9 @@ FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancell
|
||||
|
||||
image_stream->set_width(in->spec().width);
|
||||
image_stream->set_height(in->spec().height);
|
||||
image_stream->set_format(GetFormatFromOIIOBasetype(in->spec()));
|
||||
image_stream->set_pixel_aspect_ratio(GetPixelAspectRatioFromOIIO(in->spec()));
|
||||
image_stream->set_format(OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(in->spec().format.basetype)));
|
||||
image_stream->set_channel_count(in->spec().nchannels);
|
||||
image_stream->set_pixel_aspect_ratio(OIIOUtils::GetPixelAspectRatioFromOIIO(in->spec()));
|
||||
image_stream->set_video_type(VideoStream::kVideoTypeStill);
|
||||
|
||||
// Images will always have just one stream
|
||||
@@ -95,45 +107,40 @@ FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancell
|
||||
return footage;
|
||||
}
|
||||
|
||||
bool OIIODecoder::Open()
|
||||
bool OIIODecoder::OpenInternal()
|
||||
{
|
||||
Q_ASSERT(stream());
|
||||
// If we can open the filename provided, assume everything is working (even if this is an image
|
||||
// sequence with potentially missing frame)
|
||||
if (OpenImageHandler(stream()->footage()->filename())) {
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||
|
||||
if (stream()->type() != Stream::kVideo) {
|
||||
// Guard against non-video types
|
||||
return false;
|
||||
if (video_stream->video_type() == VideoStream::kVideoTypeStill) {
|
||||
last_sequence_index_ = 0;
|
||||
} else {
|
||||
last_sequence_index_ = GetImageSequenceIndex(stream()->footage()->filename());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||
|
||||
if (video_stream->video_type() == VideoStream::kVideoTypeVideo) {
|
||||
// This decoder only handles kVideoTypeImageSequence and kVideoTypeStill
|
||||
return false;
|
||||
}
|
||||
|
||||
if (video_stream->video_type() == VideoStream::kVideoTypeStill
|
||||
&& !OpenImageHandler(stream()->footage()->filename())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
open_ = true;
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider)
|
||||
FramePtr OIIODecoder::RetrieveVideoInternal(const rational &timecode, const int& divider)
|
||||
{
|
||||
if (!open_) {
|
||||
qWarning() << "Tried to retrieve video on a decoder that's still closed";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||
|
||||
if (video_stream->video_type() == VideoStream::kVideoTypeImageSequence) {
|
||||
int64_t ts = video_stream->get_time_in_timebase_units(timecode);
|
||||
int64_t sequence_index;
|
||||
|
||||
if (!OpenImageHandler(TransformImageSequenceFileName(stream()->footage()->filename(), ts))) {
|
||||
if (video_stream->video_type() == VideoStream::kVideoTypeStill) {
|
||||
sequence_index = 0;
|
||||
} else {
|
||||
sequence_index = video_stream->get_time_in_timebase_units(timecode);
|
||||
}
|
||||
|
||||
if (last_sequence_index_ != sequence_index) {
|
||||
CloseImageHandle();
|
||||
|
||||
if (!OpenImageHandler(TransformImageSequenceFileName(stream()->footage()->filename(), sequence_index))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -143,14 +150,15 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider
|
||||
frame->set_video_params(VideoParams(buffer_->spec().width,
|
||||
buffer_->spec().height,
|
||||
pix_fmt_,
|
||||
GetPixelAspectRatioFromOIIO(buffer_->spec()),
|
||||
channel_count_,
|
||||
OIIOUtils::GetPixelAspectRatioFromOIIO(buffer_->spec()),
|
||||
VideoParams::kInterlaceNone, // FIXME: Does OIIO deinterlace for us?
|
||||
divider));
|
||||
frame->allocate();
|
||||
|
||||
if (divider == 1) {
|
||||
|
||||
BufferToFrame(buffer_, frame);
|
||||
OIIOUtils::BufferToFrame(buffer_, frame.get());
|
||||
|
||||
} else {
|
||||
|
||||
@@ -161,106 +169,18 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider
|
||||
qWarning() << "OIIO resize failed";
|
||||
}
|
||||
|
||||
BufferToFrame(&dst, frame);
|
||||
OIIOUtils::BufferToFrame(&dst, frame.get());
|
||||
|
||||
}
|
||||
|
||||
if (video_stream->video_type() == VideoStream::kVideoTypeImageSequence) {
|
||||
CloseImageHandle();
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void OIIODecoder::Close()
|
||||
void OIIODecoder::CloseInternal()
|
||||
{
|
||||
CloseImageHandle();
|
||||
}
|
||||
|
||||
bool OIIODecoder::SupportsVideo()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void OIIODecoder::FrameToBuffer(FramePtr frame, OIIO::ImageBuf *buf)
|
||||
{
|
||||
#if OIIO_VERSION < 20112
|
||||
//
|
||||
// Workaround for OIIO bug that ignores destination stride in versions OLDER than 2.1.12
|
||||
//
|
||||
// See more: https://github.com/OpenImageIO/oiio/pull/2487
|
||||
//
|
||||
int width_in_bytes = frame->width() * PixelFormat::BytesPerPixel(frame->format());
|
||||
|
||||
for (int i=0;i<buf->spec().height;i++) {
|
||||
memcpy(
|
||||
#if OIIO_VERSION < 10903
|
||||
reinterpret_cast<char*>(buf->localpixels()) + i * width_in_bytes,
|
||||
#else
|
||||
reinterpret_cast<char*>(buf->localpixels()) + i * buf->scanline_stride(),
|
||||
#endif
|
||||
frame->data() + i * frame->linesize_bytes(),
|
||||
width_in_bytes);
|
||||
}
|
||||
#else
|
||||
buf->set_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
#endif
|
||||
}
|
||||
|
||||
void OIIODecoder::BufferToFrame(OIIO::ImageBuf *buf, FramePtr frame)
|
||||
{
|
||||
#if OIIO_VERSION < 20112
|
||||
//
|
||||
// Workaround for OIIO bug that ignores destination stride in versions OLDER than 2.1.12
|
||||
//
|
||||
// See more: https://github.com/OpenImageIO/oiio/pull/2487
|
||||
//
|
||||
int width_in_bytes = frame->width() * PixelFormat::BytesPerPixel(frame->format());
|
||||
|
||||
for (int i=0;i<buf->spec().height;i++) {
|
||||
memcpy(frame->data() + i * frame->linesize_bytes(),
|
||||
#if OIIO_VERSION < 10903
|
||||
reinterpret_cast<const char*>(buf->localpixels()) + i * width_in_bytes,
|
||||
#else
|
||||
reinterpret_cast<const char*>(buf->localpixels()) + i * buf->scanline_stride(),
|
||||
#endif
|
||||
width_in_bytes);
|
||||
}
|
||||
#else
|
||||
buf->get_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
#endif
|
||||
}
|
||||
|
||||
PixelFormat::Format OIIODecoder::GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec)
|
||||
{
|
||||
bool has_alpha = (spec.nchannels == kRGBAChannels);
|
||||
|
||||
if (spec.format == OIIO::TypeDesc::UINT8) {
|
||||
return has_alpha ? PixelFormat::PIX_FMT_RGBA8 : PixelFormat::PIX_FMT_RGB8;
|
||||
} else if (spec.format == OIIO::TypeDesc::UINT16) {
|
||||
return has_alpha ? PixelFormat::PIX_FMT_RGBA16U : PixelFormat::PIX_FMT_RGB16U;
|
||||
} else if (spec.format == OIIO::TypeDesc::HALF) {
|
||||
return has_alpha ? PixelFormat::PIX_FMT_RGBA16F : PixelFormat::PIX_FMT_RGB16F;
|
||||
} else if (spec.format == OIIO::TypeDesc::FLOAT) {
|
||||
return has_alpha ? PixelFormat::PIX_FMT_RGBA32F : PixelFormat::PIX_FMT_RGB32F;
|
||||
} else {
|
||||
return PixelFormat::PIX_FMT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
rational OIIODecoder::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
|
||||
{
|
||||
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1));
|
||||
}
|
||||
|
||||
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
|
||||
{
|
||||
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
||||
@@ -297,17 +217,23 @@ bool OIIODecoder::OpenImageHandler(const QString &fn)
|
||||
// Check if we can work with this pixel format
|
||||
const OIIO::ImageSpec& spec = image_->spec();
|
||||
|
||||
is_rgba_ = (spec.nchannels == kRGBAChannels);
|
||||
// Store channel count
|
||||
channel_count_ = spec.nchannels;
|
||||
|
||||
pix_fmt_ = GetFormatFromOIIOBasetype(spec);
|
||||
// We use RGBA frames because that tends to be the native format of GPUs
|
||||
pix_fmt_ = OIIOUtils::GetFormatFromOIIOBasetype(static_cast<OIIO::TypeDesc::BASETYPE>(spec.format.basetype));
|
||||
|
||||
if (pix_fmt_ == PixelFormat::PIX_FMT_INVALID) {
|
||||
if (pix_fmt_ == VideoParams::kFormatInvalid) {
|
||||
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: Many OIIO pixel formats are not handled here
|
||||
OIIO::TypeDesc type = PixelFormat::GetOIIOTypeDesc(pix_fmt_);
|
||||
OIIO::TypeDesc::BASETYPE type = OIIOUtils::GetOIIOBaseTypeFromFormat(pix_fmt_);
|
||||
|
||||
if (type == OIIO::TypeDesc::UNKNOWN) {
|
||||
qCritical() << "Failed to determine appropriate OIIO basetype from native format";
|
||||
return false;
|
||||
}
|
||||
|
||||
#if OIIO_VERSION < 20100
|
||||
buffer_ = new OIIO::ImageBuf(OIIO::ImageSpec(spec.width, spec.height, spec.nchannels, type));
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
|
||||
#include "codec/decoder.h"
|
||||
#include "render/pixelformat.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -35,23 +34,18 @@ class OIIODecoder : public Decoder
|
||||
public:
|
||||
OIIODecoder();
|
||||
|
||||
virtual ~OIIODecoder() override;
|
||||
|
||||
virtual QString id() override;
|
||||
|
||||
virtual bool SupportsVideo() override{return true;}
|
||||
|
||||
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const override;
|
||||
|
||||
virtual bool Open() override;
|
||||
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
|
||||
virtual void Close() override;
|
||||
|
||||
virtual bool SupportsVideo() override;
|
||||
|
||||
static void FrameToBuffer(FramePtr frame, OIIO::ImageBuf* buf);
|
||||
|
||||
static void BufferToFrame(OIIO::ImageBuf* buf, FramePtr frame);
|
||||
|
||||
static PixelFormat::Format GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec);
|
||||
|
||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
|
||||
protected:
|
||||
virtual bool OpenInternal() override;
|
||||
virtual FramePtr RetrieveVideoInternal(const rational &timecode, const int& divider) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
private:
|
||||
#if OIIO_VERSION < 10903
|
||||
@@ -66,9 +60,11 @@ private:
|
||||
|
||||
void CloseImageHandle();
|
||||
|
||||
PixelFormat::Format pix_fmt_;
|
||||
int64_t last_sequence_index_;
|
||||
|
||||
bool is_rgba_;
|
||||
VideoParams::Format pix_fmt_;
|
||||
|
||||
int channel_count_;
|
||||
|
||||
OIIO::ImageBuf* buffer_;
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ SampleBufferPtr SampleBuffer::Create()
|
||||
return std::make_shared<SampleBuffer>();
|
||||
}
|
||||
|
||||
SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, const rational &length)
|
||||
{
|
||||
return CreateAllocated(audio_params, audio_params.time_to_samples(length));
|
||||
}
|
||||
|
||||
SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, int samples_per_channel)
|
||||
{
|
||||
SampleBufferPtr buffer = Create();
|
||||
|
||||
@@ -46,6 +46,7 @@ public:
|
||||
virtual ~SampleBuffer();
|
||||
|
||||
static SampleBufferPtr Create();
|
||||
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, const rational& length);
|
||||
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, int samples_per_channel);
|
||||
static SampleBufferPtr CreateFromPackedData(const AudioParams& audio_params, const QByteArray& bytes);
|
||||
|
||||
|
||||
@@ -108,27 +108,27 @@ bool WaveInput::open()
|
||||
uint16_t bits_per_sample;
|
||||
data_stream >> bits_per_sample;
|
||||
|
||||
SampleFormat::Format format;
|
||||
AudioParams::Format format;
|
||||
|
||||
switch (bits_per_sample) {
|
||||
case 8:
|
||||
format = SampleFormat::SAMPLE_FMT_U8;
|
||||
format = AudioParams::kFormatUnsigned8;
|
||||
break;
|
||||
case 16:
|
||||
format = SampleFormat::SAMPLE_FMT_S16;
|
||||
format = AudioParams::kFormatSigned16;
|
||||
break;
|
||||
case 32:
|
||||
if (data_is_float) {
|
||||
format = SampleFormat::SAMPLE_FMT_FLT;
|
||||
format = AudioParams::kFormatFloat32;
|
||||
} else {
|
||||
format = SampleFormat::SAMPLE_FMT_S32;
|
||||
format = AudioParams::kFormatSigned32;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
if (data_is_float) {
|
||||
format = SampleFormat::SAMPLE_FMT_DBL;
|
||||
format = AudioParams::kFormatFloat64;
|
||||
} else {
|
||||
format = SampleFormat::SAMPLE_FMT_S64;
|
||||
format = AudioParams::kFormatSigned64;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "waveoutput.h"
|
||||
|
||||
#include "render/audioparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
const int16_t kWAVIntegerFormat = 1;
|
||||
@@ -60,18 +62,18 @@ bool WaveOutput::open()
|
||||
|
||||
// Type of format
|
||||
switch (params_.format()) {
|
||||
case SampleFormat::SAMPLE_FMT_U8:
|
||||
case SampleFormat::SAMPLE_FMT_S16:
|
||||
case SampleFormat::SAMPLE_FMT_S32:
|
||||
case SampleFormat::SAMPLE_FMT_S64:
|
||||
case AudioParams::kFormatUnsigned8:
|
||||
case AudioParams::kFormatSigned16:
|
||||
case AudioParams::kFormatSigned32:
|
||||
case AudioParams::kFormatSigned64:
|
||||
write_int<int16_t>(&file_, kWAVIntegerFormat);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_FLT:
|
||||
case SampleFormat::SAMPLE_FMT_DBL:
|
||||
case AudioParams::kFormatFloat32:
|
||||
case AudioParams::kFormatFloat64:
|
||||
write_int<int16_t>(&file_, kWAVFloatFormat);
|
||||
break;
|
||||
case SampleFormat::SAMPLE_FMT_INVALID:
|
||||
case SampleFormat::SAMPLE_FMT_COUNT:
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
qWarning() << "Invalid sample format for WAVE audio";
|
||||
file_.close();
|
||||
return false;
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
|
||||
#include "audio/sampleformat.h"
|
||||
#include "render/audioparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
+20
-13
@@ -16,42 +16,49 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
common/bezier.h
|
||||
common/bezier.cpp
|
||||
common/bezier.h
|
||||
common/cancelableobject.h
|
||||
common/channellayout.h
|
||||
common/clamp.h
|
||||
common/commandlineparser.h
|
||||
common/commandlineparser.cpp
|
||||
common/crashpadinterface.h
|
||||
common/commandlineparser.h
|
||||
common/crashpadinterface.cpp
|
||||
common/crashpadinterface.h
|
||||
common/crashpadutils.h
|
||||
common/debug.h
|
||||
common/debug.cpp
|
||||
common/debug.h
|
||||
common/define.h
|
||||
common/filefunctions.h
|
||||
common/ffmpegutils.cpp
|
||||
common/ffmpegutils.h
|
||||
common/filefunctions.cpp
|
||||
common/flipmodifiers.h
|
||||
common/filefunctions.h
|
||||
common/flipmodifiers.cpp
|
||||
common/flipmodifiers.h
|
||||
common/functiontimer.h
|
||||
common/lerp.h
|
||||
common/memorypool.h
|
||||
common/memorypool.cpp
|
||||
common/qtutils.h
|
||||
common/memorypool.h
|
||||
common/ocioutils.cpp
|
||||
common/ocioutils.h
|
||||
common/oiioutils.cpp
|
||||
common/oiioutils.h
|
||||
common/qtutils.cpp
|
||||
common/qtutils.h
|
||||
common/range.h
|
||||
common/ratiodialog.h
|
||||
common/ratiodialog.cpp
|
||||
common/ratiodialog.h
|
||||
common/rational.h
|
||||
common/rational.cpp
|
||||
common/threadedobject.h
|
||||
common/threadsafemap.h
|
||||
common/threadedobject.cpp
|
||||
common/timecodefunctions.h
|
||||
common/threadedobject.h
|
||||
common/timecodefunctions.cpp
|
||||
common/timerange.h
|
||||
common/timecodefunctions.h
|
||||
common/timerange.cpp
|
||||
common/timerange.h
|
||||
common/tohex.h
|
||||
common/xmlutils.h
|
||||
common/xmlutils.cpp
|
||||
common/xmlutils.h
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -34,14 +34,20 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void Cancel() {
|
||||
void Cancel()
|
||||
{
|
||||
cancelled_ = true;
|
||||
CancelEvent();
|
||||
}
|
||||
|
||||
const QAtomicInt& IsCancelled() const {
|
||||
const QAtomicInt& IsCancelled() const
|
||||
{
|
||||
return cancelled_;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void CancelEvent(){}
|
||||
|
||||
private:
|
||||
QAtomicInt cancelled_;
|
||||
|
||||
|
||||
@@ -29,10 +29,6 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
const int kHSVChannels = 3;
|
||||
const int kRGBChannels = 3;
|
||||
const int kRGBAChannels = 4;
|
||||
|
||||
/// The minimum size an icon in ProjectExplorer can be
|
||||
const int kProjectIconSizeMinimum = 16;
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 "common/ffmpegutils.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt)
|
||||
{
|
||||
AVPixelFormat possible_pix_fmts[] = {
|
||||
AV_PIX_FMT_RGB24,
|
||||
AV_PIX_FMT_RGBA,
|
||||
AV_PIX_FMT_RGB48,
|
||||
AV_PIX_FMT_RGBA64,
|
||||
AV_PIX_FMT_NONE
|
||||
};
|
||||
|
||||
return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts,
|
||||
pix_fmt,
|
||||
1,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
AudioParams::Format FFmpegUtils::GetNativeSampleFormat(const AVSampleFormat &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return AudioParams::kFormatUnsigned8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return AudioParams::kFormatSigned16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return AudioParams::kFormatSigned32;
|
||||
case AV_SAMPLE_FMT_S64:
|
||||
return AudioParams::kFormatSigned64;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return AudioParams::kFormatFloat32;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return AudioParams::kFormatFloat64;
|
||||
case AV_SAMPLE_FMT_U8P :
|
||||
case AV_SAMPLE_FMT_S16P:
|
||||
case AV_SAMPLE_FMT_S32P:
|
||||
case AV_SAMPLE_FMT_S64P:
|
||||
case AV_SAMPLE_FMT_FLTP:
|
||||
case AV_SAMPLE_FMT_DBLP:
|
||||
case AV_SAMPLE_FMT_NONE:
|
||||
case AV_SAMPLE_FMT_NB:
|
||||
break;
|
||||
}
|
||||
|
||||
return AudioParams::kFormatInvalid;
|
||||
}
|
||||
|
||||
AVSampleFormat FFmpegUtils::GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt)
|
||||
{
|
||||
switch (smp_fmt) {
|
||||
case AudioParams::kFormatUnsigned8:
|
||||
return AV_SAMPLE_FMT_U8;
|
||||
case AudioParams::kFormatSigned16:
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
case AudioParams::kFormatSigned32:
|
||||
return AV_SAMPLE_FMT_S32;
|
||||
case AudioParams::kFormatSigned64:
|
||||
return AV_SAMPLE_FMT_S64;
|
||||
case AudioParams::kFormatFloat32:
|
||||
return AV_SAMPLE_FMT_FLT;
|
||||
case AudioParams::kFormatFloat64:
|
||||
return AV_SAMPLE_FMT_DBL;
|
||||
case AudioParams::kFormatInvalid:
|
||||
case AudioParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return AV_SAMPLE_FMT_NONE;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const VideoParams::Format &pix_fmt, int channel_layout)
|
||||
{
|
||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case VideoParams::kFormatUnsigned8:
|
||||
return AV_PIX_FMT_RGB24;
|
||||
case VideoParams::kFormatUnsigned16:
|
||||
return AV_PIX_FMT_RGB48;
|
||||
case VideoParams::kFormatFloat16:
|
||||
case VideoParams::kFormatFloat32:
|
||||
case VideoParams::kFormatInvalid:
|
||||
case VideoParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
} else if (channel_layout == VideoParams::kRGBAChannelCount) {
|
||||
switch (pix_fmt) {
|
||||
case VideoParams::kFormatUnsigned8:
|
||||
return AV_PIX_FMT_RGBA;
|
||||
case VideoParams::kFormatUnsigned16:
|
||||
return AV_PIX_FMT_RGBA64;
|
||||
case VideoParams::kFormatFloat16:
|
||||
case VideoParams::kFormatFloat32:
|
||||
case VideoParams::kFormatInvalid:
|
||||
case VideoParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return AV_PIX_FMT_NONE;
|
||||
}
|
||||
|
||||
VideoParams::Format FFmpegUtils::GetCompatiblePixelFormat(const VideoParams::Format &pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case VideoParams::kFormatUnsigned8:
|
||||
return VideoParams::kFormatUnsigned8;
|
||||
case VideoParams::kFormatUnsigned16:
|
||||
case VideoParams::kFormatFloat16:
|
||||
case VideoParams::kFormatFloat32:
|
||||
return VideoParams::kFormatUnsigned16;
|
||||
case VideoParams::kFormatInvalid:
|
||||
case VideoParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return VideoParams::kFormatInvalid;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -25,12 +25,12 @@ extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
|
||||
#include "audio/sampleformat.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class FFmpegCommon {
|
||||
class FFmpegUtils {
|
||||
public:
|
||||
/**
|
||||
* @brief Returns an AVPixelFormat that can be used to convert a frame to a data type Olive supports with minimal data loss
|
||||
@@ -40,22 +40,22 @@ public:
|
||||
/**
|
||||
* @brief Returns a native pixel format that can be used to convert from a native frame to an AVFrame with minimal data loss
|
||||
*/
|
||||
static PixelFormat::Format GetCompatiblePixelFormat(const PixelFormat::Format& pix_fmt);
|
||||
static VideoParams::Format GetCompatiblePixelFormat(const VideoParams::Format& pix_fmt);
|
||||
|
||||
/**
|
||||
* @brief Returns an FFmpeg pixel format for a given native pixel format
|
||||
*/
|
||||
static AVPixelFormat GetFFmpegPixelFormat(const PixelFormat::Format& pix_fmt);
|
||||
static AVPixelFormat GetFFmpegPixelFormat(const VideoParams::Format& pix_fmt, int channel_layout);
|
||||
|
||||
/**
|
||||
* @brief Returns a native sample format type for a given AVSampleFormat
|
||||
*/
|
||||
static SampleFormat::Format GetNativeSampleFormat(const AVSampleFormat& smp_fmt);
|
||||
static AudioParams::Format GetNativeSampleFormat(const AVSampleFormat& smp_fmt);
|
||||
|
||||
/**
|
||||
* @brief Returns an FFmpeg sample format type for a given native type
|
||||
*/
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const SampleFormat::Format &smp_fmt);
|
||||
static AVSampleFormat GetFFmpegSampleFormat(const AudioParams::Format &smp_fmt);
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -75,7 +75,7 @@ QString FileFunctions::GetTempFilePath()
|
||||
{
|
||||
QString temp_path = QDir(QDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
||||
.filePath(QCoreApplication::organizationName()))
|
||||
.filePath(QCoreApplication::applicationName());
|
||||
.filePath(QCoreApplication::applicationName());
|
||||
|
||||
// Ensure it exists
|
||||
QDir(temp_path).mkpath(".");
|
||||
@@ -187,4 +187,57 @@ QString FileFunctions::EnsureFilenameExtension(QString fn, const QString &extens
|
||||
return fn;
|
||||
}
|
||||
|
||||
QString FileFunctions::ReadFileAsString(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
}
|
||||
|
||||
QString FileFunctions::GetSafeTemporaryFilename(const QString &original)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
QFileInfo original_info(original);
|
||||
QString basename = original_info.baseName();
|
||||
QString complete_suffix = original_info.completeSuffix();
|
||||
|
||||
// If we have a complete suffix, make sure there's a period in it
|
||||
if (!complete_suffix.isEmpty()) {
|
||||
complete_suffix.prepend('.');
|
||||
}
|
||||
|
||||
QString temp_abs_path;
|
||||
do {
|
||||
temp_abs_path = original_info.dir().filePath(
|
||||
QStringLiteral("%1.tmp%2%3").arg(basename,
|
||||
QString::number(counter),
|
||||
complete_suffix));
|
||||
counter++;
|
||||
} while (QFileInfo::exists(temp_abs_path));
|
||||
|
||||
return temp_abs_path;
|
||||
}
|
||||
|
||||
bool FileFunctions::RenameFileAllowOverwrite(const QString &from, const QString &to)
|
||||
{
|
||||
if (QFileInfo::exists(to) && !QFile::remove(to)) {
|
||||
qCritical() << "Couldn't remove existing file" << to << "for overwrite";
|
||||
return false;
|
||||
}
|
||||
|
||||
// By this point, we can assume `to` either never existed or has now been deleted
|
||||
if (!QFile::rename(from, to)) {
|
||||
qCritical() << "Failed to rename file" << from << "to" << to;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -65,6 +65,25 @@ public:
|
||||
*/
|
||||
static QString EnsureFilenameExtension(QString fn, const QString& extension);
|
||||
|
||||
static QString ReadFileAsString(const QString& filename);
|
||||
|
||||
/**
|
||||
* @brief Returns a temporary filename that can be used while writing rather than the original
|
||||
*
|
||||
* If overwriting a file, it's safest to write to a new file first and then only replace it at
|
||||
* the end so that if the program crashes or the user cancels the save half way through, the
|
||||
* original file is still intact.
|
||||
*
|
||||
* This function returns a slight variant of the filename provided that's guaranteed to not exist
|
||||
* and therefore won't overwrite anything important.
|
||||
*/
|
||||
static QString GetSafeTemporaryFilename(const QString& original);
|
||||
|
||||
/**
|
||||
* @brief Renames a file from `from` to `to`, deleting `to` if such a file already exists first
|
||||
*/
|
||||
static bool RenameFileAllowOverwrite(const QString& from, const QString& to);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,41 +18,30 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef SAMPLEFORMAT_H
|
||||
#define SAMPLEFORMAT_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "common/define.h"
|
||||
#include "render/rendermodes.h"
|
||||
#include "ocioutils.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class SampleFormat : public QObject
|
||||
OCIO::BitDepth OCIOUtils::GetOCIOBitDepthFromPixelFormat(VideoParams::Format format)
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SampleFormat() = default;
|
||||
switch (format) {
|
||||
case VideoParams::kFormatUnsigned8:
|
||||
return OCIO::BIT_DEPTH_UINT8;
|
||||
case VideoParams::kFormatUnsigned16:
|
||||
return OCIO::BIT_DEPTH_UINT16;
|
||||
break;
|
||||
case VideoParams::kFormatFloat16:
|
||||
return OCIO::BIT_DEPTH_F16;
|
||||
break;
|
||||
case VideoParams::kFormatFloat32:
|
||||
return OCIO::BIT_DEPTH_F32;
|
||||
break;
|
||||
case VideoParams::kFormatInvalid:
|
||||
case VideoParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
enum Format {
|
||||
SAMPLE_FMT_INVALID = -1,
|
||||
|
||||
SAMPLE_FMT_U8,
|
||||
SAMPLE_FMT_S16,
|
||||
SAMPLE_FMT_S32,
|
||||
SAMPLE_FMT_S64,
|
||||
SAMPLE_FMT_FLT,
|
||||
SAMPLE_FMT_DBL,
|
||||
|
||||
SAMPLE_FMT_COUNT
|
||||
};
|
||||
|
||||
static const Format kInternalFormat;
|
||||
|
||||
static QString GetSampleFormatName(const Format& f);
|
||||
|
||||
};
|
||||
return OCIO::BIT_DEPTH_UNKNOWN;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // SAMPLEFORMAT_H
|
||||
@@ -18,26 +18,22 @@
|
||||
|
||||
***/
|
||||
|
||||
#include "openglbackend.h"
|
||||
#ifndef OCIOUTILS_H
|
||||
#define OCIOUTILS_H
|
||||
|
||||
#include "openglworker.h"
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OpenColorIO_v2_0dev;
|
||||
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
OpenGLBackend::OpenGLBackend(QObject* parent) :
|
||||
RenderBackend(parent)
|
||||
class OCIOUtils
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
OpenGLBackend::~OpenGLBackend()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
RenderWorker *OpenGLBackend::CreateNewWorker()
|
||||
{
|
||||
return new OpenGLWorker(this);
|
||||
}
|
||||
public:
|
||||
static OCIO::BitDepth GetOCIOBitDepthFromPixelFormat(VideoParams::Format format);
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // OCIOUTILS_H
|
||||
@@ -0,0 +1,120 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 "oiioutils.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
void OIIOUtils::FrameToBuffer(const Frame* frame, OIIO::ImageBuf *buf)
|
||||
{
|
||||
#if OIIO_VERSION < 20112
|
||||
//
|
||||
// Workaround for OIIO bug that ignores destination stride in versions OLDER than 2.1.12
|
||||
//
|
||||
// See more: https://github.com/OpenImageIO/oiio/pull/2487
|
||||
//
|
||||
int width_in_bytes = frame->width() * PixelFormat::BytesPerPixel(frame->format());
|
||||
|
||||
for (int i=0;i<buf->spec().height;i++) {
|
||||
memcpy(
|
||||
#if OIIO_VERSION < 10903
|
||||
reinterpret_cast<char*>(buf->localpixels()) + i * width_in_bytes,
|
||||
#else
|
||||
reinterpret_cast<char*>(buf->localpixels()) + i * buf->scanline_stride(),
|
||||
#endif
|
||||
frame->data() + i * frame->linesize_bytes(),
|
||||
width_in_bytes);
|
||||
}
|
||||
#else
|
||||
buf->set_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->const_data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
#endif
|
||||
}
|
||||
|
||||
void OIIOUtils::BufferToFrame(OIIO::ImageBuf *buf, Frame* frame)
|
||||
{
|
||||
#if OIIO_VERSION < 20112
|
||||
//
|
||||
// Workaround for OIIO bug that ignores destination stride in versions OLDER than 2.1.12
|
||||
//
|
||||
// See more: https://github.com/OpenImageIO/oiio/pull/2487
|
||||
//
|
||||
int width_in_bytes = frame->width() * PixelFormat::BytesPerPixel(frame->format());
|
||||
|
||||
for (int i=0;i<buf->spec().height;i++) {
|
||||
memcpy(frame->data() + i * frame->linesize_bytes(),
|
||||
#if OIIO_VERSION < 10903
|
||||
reinterpret_cast<const char*>(buf->localpixels()) + i * width_in_bytes,
|
||||
#else
|
||||
reinterpret_cast<const char*>(buf->localpixels()) + i * buf->scanline_stride(),
|
||||
#endif
|
||||
width_in_bytes);
|
||||
}
|
||||
#else
|
||||
buf->get_pixels(OIIO::ROI(),
|
||||
buf->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
#endif
|
||||
}
|
||||
|
||||
rational OIIOUtils::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
|
||||
{
|
||||
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1));
|
||||
}
|
||||
|
||||
VideoParams::Format OIIOUtils::GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type)
|
||||
{
|
||||
switch (type) {
|
||||
case OIIO::TypeDesc::UNKNOWN:
|
||||
case OIIO::TypeDesc::NONE:
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::INT8:
|
||||
case OIIO::TypeDesc::INT16:
|
||||
case OIIO::TypeDesc::INT32:
|
||||
case OIIO::TypeDesc::UINT32:
|
||||
case OIIO::TypeDesc::INT64:
|
||||
case OIIO::TypeDesc::UINT64:
|
||||
case OIIO::TypeDesc::STRING:
|
||||
case OIIO::TypeDesc::PTR:
|
||||
case OIIO::TypeDesc::LASTBASE:
|
||||
case OIIO::TypeDesc::DOUBLE:
|
||||
qDebug() << "Tried to use unknown OIIO base type";
|
||||
break;
|
||||
|
||||
case OIIO::TypeDesc::UINT8:
|
||||
return VideoParams::kFormatUnsigned8;
|
||||
case OIIO::TypeDesc::UINT16:
|
||||
return VideoParams::kFormatUnsigned16;
|
||||
case OIIO::TypeDesc::HALF:
|
||||
return VideoParams::kFormatFloat16;
|
||||
case OIIO::TypeDesc::FLOAT:
|
||||
return VideoParams::kFormatFloat32;
|
||||
}
|
||||
|
||||
return VideoParams::kFormatInvalid;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,65 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 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 OIIOUTILS_H
|
||||
#define OIIOUTILS_H
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/typedesc.h>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class OIIOUtils {
|
||||
public:
|
||||
static OIIO::TypeDesc::BASETYPE GetOIIOBaseTypeFromFormat(VideoParams::Format format)
|
||||
{
|
||||
switch (format) {
|
||||
case VideoParams::kFormatUnsigned8:
|
||||
return OIIO::TypeDesc::UINT8;
|
||||
case VideoParams::kFormatUnsigned16:
|
||||
return OIIO::TypeDesc::UINT16;
|
||||
case VideoParams::kFormatFloat16:
|
||||
return OIIO::TypeDesc::HALF;
|
||||
case VideoParams::kFormatFloat32:
|
||||
return OIIO::TypeDesc::FLOAT;
|
||||
case VideoParams::kFormatInvalid:
|
||||
case VideoParams::kFormatCount:
|
||||
break;
|
||||
}
|
||||
|
||||
return OIIO::TypeDesc::UNKNOWN;
|
||||
}
|
||||
|
||||
static void FrameToBuffer(const Frame *frame, OIIO::ImageBuf* buf);
|
||||
|
||||
static void BufferToFrame(OIIO::ImageBuf* buf, Frame* frame);
|
||||
|
||||
static VideoParams::Format GetFormatFromOIIOBasetype(OIIO::TypeDesc::BASETYPE type);
|
||||
|
||||
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // OIIOUTILS_H
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef THREADSAFEMAP_H
|
||||
#define THREADSAFEMAP_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
|
||||
template <typename K, typename V>
|
||||
class ThreadSafeMap
|
||||
{
|
||||
public:
|
||||
ThreadSafeMap() = default;
|
||||
|
||||
void insert(K key, V value)
|
||||
{
|
||||
mutex_.lock();
|
||||
map_.insert(key, value);
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex mutex_;
|
||||
|
||||
QMap<K, V> map_;
|
||||
|
||||
};
|
||||
|
||||
#endif // THREADSAFEMAP_H
|
||||
+73
-25
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "timerange.h"
|
||||
|
||||
#include <QtMath>
|
||||
#include <utility>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -77,11 +78,11 @@ bool TimeRange::operator!=(const TimeRange &r) const
|
||||
|
||||
bool TimeRange::OverlapsWith(const TimeRange &a, bool in_inclusive, bool out_inclusive) const
|
||||
{
|
||||
bool overlaps_in = (in_inclusive) ? (a.out() < in()) : (a.out() <= in());
|
||||
bool doesnt_overlap_in = (in_inclusive) ? (a.out() < in()) : (a.out() <= in());
|
||||
|
||||
bool overlaps_out = (out_inclusive) ? (a.in() > out()) : (a.in() >= out());
|
||||
bool doesnt_overlap_out = (out_inclusive) ? (a.in() > out()) : (a.in() >= out());
|
||||
|
||||
return !(overlaps_in || overlaps_out);
|
||||
return !doesnt_overlap_in && !doesnt_overlap_out;
|
||||
}
|
||||
|
||||
TimeRange TimeRange::Combined(const TimeRange &a) const
|
||||
@@ -148,6 +149,21 @@ const TimeRange &TimeRange::operator-=(const rational &rhs)
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::list<TimeRange> TimeRange::Split(const int &chunk_size) const
|
||||
{
|
||||
std::list<TimeRange> split_ranges;
|
||||
|
||||
int start_time = qFloor(this->in().toDouble() / static_cast<double>(chunk_size)) * chunk_size;
|
||||
int end_time = qCeil(this->out().toDouble() / static_cast<double>(chunk_size)) * chunk_size;
|
||||
|
||||
for (int i=start_time; i<end_time; i+=chunk_size) {
|
||||
split_ranges.push_back(TimeRange(qMax(this->in(), rational(i)),
|
||||
qMin(this->out(), rational(i + chunk_size))));
|
||||
}
|
||||
|
||||
return split_ranges;
|
||||
}
|
||||
|
||||
void TimeRange::normalize()
|
||||
{
|
||||
// If `out` is earlier than `in`, swap them
|
||||
@@ -160,43 +176,45 @@ void TimeRange::normalize()
|
||||
length_ = out_ - in_;
|
||||
}
|
||||
|
||||
void TimeRangeList::InsertTimeRange(TimeRange range_to_add)
|
||||
void TimeRangeList::insert(TimeRange range_to_add)
|
||||
{
|
||||
// See if list contains this range
|
||||
if (ContainsTimeRange(range_to_add)) {
|
||||
if (contains(range_to_add)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Does not contain range, so we'll almost certainly be adding it in some way
|
||||
for (int i=0;i<size();i++) {
|
||||
const TimeRange& compare = at(i);
|
||||
const TimeRange& compare = array_.at(i);
|
||||
|
||||
if (compare.OverlapsWith(range_to_add)) {
|
||||
range_to_add = TimeRange::Combine(range_to_add, compare);
|
||||
removeAt(i);
|
||||
array_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
append(range_to_add);
|
||||
array_.append(range_to_add);
|
||||
}
|
||||
|
||||
void TimeRangeList::RemoveTimeRange(const TimeRange &remove)
|
||||
void TimeRangeList::remove(const TimeRange &remove)
|
||||
{
|
||||
int sz = this->size();
|
||||
|
||||
for (int i=0;i<sz;i++) {
|
||||
TimeRange& compare = (*this)[i];
|
||||
TimeRange& compare = array_[i];
|
||||
|
||||
if (remove.Contains(compare)) {
|
||||
// This element is entirely encompassed in this range, remove it
|
||||
this->removeAt(i);
|
||||
array_.removeAt(i);
|
||||
i--;
|
||||
sz--;
|
||||
} else if (compare.Contains(remove, false, false)) {
|
||||
// The remove range is within this element, only choice is to split the element into two
|
||||
this->append(TimeRange(remove.out(), compare.out()));
|
||||
TimeRange new_range(remove.out(), compare.out());
|
||||
compare.set_out(remove.in());
|
||||
insert(new_range);
|
||||
break;
|
||||
} else if (compare.in() < remove.in() && compare.out() > remove.in()) {
|
||||
// This element's out point overlaps the range's in, we'll trim it
|
||||
compare.set_out(remove.in());
|
||||
@@ -207,10 +225,10 @@ void TimeRangeList::RemoveTimeRange(const TimeRange &remove)
|
||||
}
|
||||
}
|
||||
|
||||
bool TimeRangeList::ContainsTimeRange(const TimeRange &range, bool in_inclusive, bool out_inclusive) const
|
||||
bool TimeRangeList::contains(const TimeRange &range, bool in_inclusive, bool out_inclusive) const
|
||||
{
|
||||
for (int i=0;i<size();i++) {
|
||||
if (at(i).Contains(range, in_inclusive, out_inclusive)) {
|
||||
if (array_.at(i).Contains(range, in_inclusive, out_inclusive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -218,12 +236,45 @@ bool TimeRangeList::ContainsTimeRange(const TimeRange &range, bool in_inclusive,
|
||||
return false;
|
||||
}
|
||||
|
||||
void TimeRangeList::shift(const rational &diff)
|
||||
{
|
||||
for (int i=0; i<array_.size(); i++) {
|
||||
array_[i] += diff;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeRangeList::trim_in(const rational &diff)
|
||||
{
|
||||
// Re-do list since we want to handle overlaps
|
||||
TimeRangeList temp = *this;
|
||||
|
||||
clear();
|
||||
|
||||
foreach (TimeRange r, temp) {
|
||||
r.set_in(r.in() + diff);
|
||||
insert(r);
|
||||
}
|
||||
}
|
||||
|
||||
void TimeRangeList::trim_out(const rational &diff)
|
||||
{
|
||||
// Re-do list since we want to handle overlaps
|
||||
TimeRangeList temp = *this;
|
||||
|
||||
clear();
|
||||
|
||||
foreach (TimeRange r, temp) {
|
||||
r.set_out(r.out() + diff);
|
||||
insert(r);
|
||||
}
|
||||
}
|
||||
|
||||
TimeRangeList TimeRangeList::Intersects(const TimeRange &range) const
|
||||
{
|
||||
TimeRangeList intersect_list;
|
||||
|
||||
for (int i=0;i<size();i++) {
|
||||
const TimeRange& compare = at(i);
|
||||
const TimeRange& compare = array_.at(i);
|
||||
|
||||
if (compare.out() <= range.in() || compare.in() >= range.out()) {
|
||||
// No intersect
|
||||
@@ -233,22 +284,13 @@ TimeRangeList TimeRangeList::Intersects(const TimeRange &range) const
|
||||
TimeRange cropped(qMax(range.in(), compare.in()),
|
||||
qMin(range.out(), compare.out()));
|
||||
|
||||
intersect_list.append(cropped);
|
||||
intersect_list.insert(cropped);
|
||||
}
|
||||
}
|
||||
|
||||
return intersect_list;
|
||||
}
|
||||
|
||||
void TimeRangeList::PrintTimeList()
|
||||
{
|
||||
qDebug() << "TimeRangeList now contains:";
|
||||
|
||||
for (int i=0;i<size();i++) {
|
||||
qDebug() << " " << at(i);
|
||||
}
|
||||
}
|
||||
|
||||
uint qHash(const TimeRange &r, uint seed)
|
||||
{
|
||||
return qHash(r.in(), seed) ^ qHash(r.out(), seed);
|
||||
@@ -261,3 +303,9 @@ QDebug operator<<(QDebug debug, const OLIVE_NAMESPACE::TimeRange &r)
|
||||
debug.nospace() << r.in().toDouble() << " - " << r.out().toDouble();
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const olive::TimeRangeList &r)
|
||||
{
|
||||
debug << r.internal_array();
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
+57
-6
@@ -56,6 +56,8 @@ public:
|
||||
const TimeRange& operator+=(const rational &rhs);
|
||||
const TimeRange& operator-=(const rational &rhs);
|
||||
|
||||
std::list<TimeRange> Split(const int &chunk_size) const;
|
||||
|
||||
private:
|
||||
void normalize();
|
||||
|
||||
@@ -65,25 +67,73 @@ private:
|
||||
|
||||
};
|
||||
|
||||
class TimeRangeList : public QList<TimeRange> {
|
||||
class TimeRangeList {
|
||||
public:
|
||||
TimeRangeList() = default;
|
||||
|
||||
TimeRangeList(std::initializer_list<TimeRange> r) :
|
||||
QList<TimeRange>(r)
|
||||
array_(r)
|
||||
{
|
||||
}
|
||||
|
||||
void InsertTimeRange(TimeRange range_to_add);
|
||||
void insert(TimeRange range_to_add);
|
||||
|
||||
void RemoveTimeRange(const TimeRange& remove);
|
||||
void remove(const TimeRange& remove);
|
||||
|
||||
bool ContainsTimeRange(const TimeRange& range, bool in_inclusive = true, bool out_inclusive = true) const;
|
||||
bool contains(const TimeRange& range, bool in_inclusive = true, bool out_inclusive = true) const;
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return array_.isEmpty();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
array_.clear();
|
||||
}
|
||||
|
||||
int size() const
|
||||
{
|
||||
return array_.size();
|
||||
}
|
||||
|
||||
void shift(const rational& diff);
|
||||
|
||||
void trim_in(const rational& diff);
|
||||
|
||||
void trim_out(const rational& diff);
|
||||
|
||||
TimeRangeList Intersects(const TimeRange& range) const;
|
||||
|
||||
using const_iterator = QVector<TimeRange>::const_iterator;
|
||||
|
||||
const_iterator begin() const
|
||||
{
|
||||
return array_.constBegin();
|
||||
}
|
||||
|
||||
const_iterator end() const
|
||||
{
|
||||
return array_.constEnd();
|
||||
}
|
||||
|
||||
const TimeRange& first() const
|
||||
{
|
||||
return array_.first();
|
||||
}
|
||||
|
||||
const TimeRange& last() const
|
||||
{
|
||||
return array_.last();
|
||||
}
|
||||
|
||||
const QVector<TimeRange>& internal_array() const
|
||||
{
|
||||
return array_;
|
||||
}
|
||||
|
||||
private:
|
||||
void PrintTimeList();
|
||||
QVector<TimeRange> array_;
|
||||
|
||||
};
|
||||
|
||||
@@ -92,6 +142,7 @@ uint qHash(const TimeRange& r, uint seed);
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
QDebug operator<<(QDebug debug, const OLIVE_NAMESPACE::TimeRange& r);
|
||||
QDebug operator<<(QDebug debug, const OLIVE_NAMESPACE::TimeRangeList& r);
|
||||
|
||||
Q_DECLARE_METATYPE(OLIVE_NAMESPACE::TimeRange)
|
||||
|
||||
|
||||
+15
-9
@@ -89,6 +89,7 @@ void Config::SetDefaults()
|
||||
SetEntryInternal(QStringLiteral("RectifiedWaveforms"), NodeParam::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("DropWithoutSequenceBehavior"), NodeParam::kInt, ImportTool::kDWSAsk);
|
||||
SetEntryInternal(QStringLiteral("Loop"), NodeParam::kBoolean, false);
|
||||
SetEntryInternal(QStringLiteral("SplitClipsCopyNodes"), NodeParam::kBoolean, true);
|
||||
|
||||
SetEntryInternal(QStringLiteral("AutoCacheInterval"), NodeParam::kInt, 250);
|
||||
|
||||
@@ -116,13 +117,10 @@ void Config::SetDefaults()
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceInterlacing"), NodeParam::kInt, VideoParams::kInterlaceNone);
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceAudioFrequency"), NodeParam::kInt, 48000);
|
||||
SetEntryInternal(QStringLiteral("DefaultSequenceAudioLayout"), NodeParam::kInt, QVariant::fromValue(static_cast<int64_t>(AV_CH_LAYOUT_STEREO)));
|
||||
SetEntryInternal(QStringLiteral("DefaultSequencePreviewFormat"), NodeParam::kInt, PixelFormat::PIX_FMT_RGBA16F);
|
||||
|
||||
// Online/offline settings
|
||||
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeParam::kInt, PixelFormat::PIX_FMT_RGBA32F);
|
||||
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeParam::kInt, PixelFormat::PIX_FMT_RGBA16F);
|
||||
SetEntryInternal(QStringLiteral("OnlineOCIOMethod"), NodeParam::kInt, ColorManager::kOCIOAccurate);
|
||||
SetEntryInternal(QStringLiteral("OfflineOCIOMethod"), NodeParam::kInt, ColorManager::kOCIOFast);
|
||||
SetEntryInternal(QStringLiteral("OnlinePixelFormat"), NodeParam::kInt, VideoParams::kFormatFloat32);
|
||||
SetEntryInternal(QStringLiteral("OfflinePixelFormat"), NodeParam::kInt, VideoParams::kFormatFloat16);
|
||||
}
|
||||
|
||||
void Config::Load()
|
||||
@@ -206,7 +204,10 @@ void Config::Load()
|
||||
|
||||
void Config::Save()
|
||||
{
|
||||
QFile config_file(GetConfigFilePath());
|
||||
QString real_filename = GetConfigFilePath();
|
||||
QString temp_filename = FileFunctions::GetSafeTemporaryFilename(real_filename);
|
||||
|
||||
QFile config_file(temp_filename);
|
||||
|
||||
if (!config_file.open(QFile::WriteOnly)) {
|
||||
QMessageBox::critical(Core::instance()->main_window(),
|
||||
@@ -233,10 +234,10 @@ void Config::Save()
|
||||
|
||||
QString value = NodeInput::ValueToString(iterator.value().type, iterator.value().data, false);
|
||||
|
||||
writer.writeTextElement(iterator.key(), value);
|
||||
|
||||
if (iterator.value().type == NodeParam::kNone) {
|
||||
qWarning() << "Config key" << iterator.key() << "had null type";
|
||||
qWarning() << "Config key" << iterator.key() << "had null type and was discarded";
|
||||
} else {
|
||||
writer.writeTextElement(iterator.key(), value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +246,11 @@ void Config::Save()
|
||||
writer.writeEndDocument();
|
||||
|
||||
config_file.close();
|
||||
|
||||
if (!FileFunctions::RenameFileAllowOverwrite(temp_filename, real_filename)) {
|
||||
qWarning() << QStringLiteral("Failed to overwrite \"%1\". Config has been saved as \"%2\" instead.")
|
||||
.arg(real_filename, temp_filename);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant Config::operator[](const QString &key) const
|
||||
|
||||
+17
-19
@@ -49,11 +49,9 @@
|
||||
#include "panel/panelmanager.h"
|
||||
#include "panel/project/project.h"
|
||||
#include "panel/viewer/viewer.h"
|
||||
#include "render/backend/opengl/opengltexturecache.h"
|
||||
#include "render/colormanager.h"
|
||||
#include "render/diskmanager.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/shaderinfo.h"
|
||||
#include "render/rendermanager.h"
|
||||
#ifdef USE_OTIO
|
||||
#include "task/project/loadotio/loadotio.h"
|
||||
#include "task/project/saveotio/saveotio.h"
|
||||
@@ -95,8 +93,6 @@ Core *Core::instance()
|
||||
void Core::DeclareTypesForQt()
|
||||
{
|
||||
qRegisterMetaType<rational>();
|
||||
qRegisterMetaType<OpenGLTexturePtr>();
|
||||
qRegisterMetaType<OpenGLTextureCache::ReferencePtr>();
|
||||
qRegisterMetaType<NodeValue>();
|
||||
qRegisterMetaType<NodeValueTable>();
|
||||
qRegisterMetaType<NodeValueDatabase>();
|
||||
@@ -135,8 +131,8 @@ void Core::Start()
|
||||
// Initialize task manager
|
||||
TaskManager::CreateInstance();
|
||||
|
||||
// Initialize OpenGL service
|
||||
OpenGLProxy::CreateInstance();
|
||||
// Initialize RenderManager
|
||||
RenderManager::CreateInstance();
|
||||
|
||||
//
|
||||
// Start application
|
||||
@@ -172,15 +168,13 @@ void Core::Stop()
|
||||
if (recent_projects_file.open(QFile::WriteOnly | QFile::Text)) {
|
||||
QTextStream ts(&recent_projects_file);
|
||||
|
||||
foreach (const QString& s, recent_projects_) {
|
||||
ts << s << "\n";
|
||||
}
|
||||
ts << recent_projects_.join('\n');
|
||||
|
||||
recent_projects_file.close();
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLProxy::DestroyInstance();
|
||||
RenderManager::DestroyInstance();
|
||||
|
||||
MenuShared::DestroyInstance();
|
||||
|
||||
@@ -192,11 +186,10 @@ void Core::Stop()
|
||||
|
||||
DiskManager::DestroyInstance();
|
||||
|
||||
PixelFormat::DestroyInstance();
|
||||
|
||||
NodeFactory::Destroy();
|
||||
|
||||
delete main_window_;
|
||||
main_window_ = nullptr;
|
||||
}
|
||||
|
||||
MainWindow *Core::main_window()
|
||||
@@ -259,6 +252,7 @@ void Core::SetSelectedTransitionObject(const QString &obj)
|
||||
void Core::ClearOpenRecentList()
|
||||
{
|
||||
recent_projects_.clear();
|
||||
emit OpenRecentListChanged();
|
||||
}
|
||||
|
||||
void Core::CreateNewProject()
|
||||
@@ -648,9 +642,6 @@ void Core::StartGUI(bool full_screen)
|
||||
// Initialize disk service
|
||||
DiskManager::CreateInstance();
|
||||
|
||||
// Initialize pixel service
|
||||
PixelFormat::CreateInstance();
|
||||
|
||||
// Connect the PanelFocusManager to the application's focus change signal
|
||||
connect(qApp,
|
||||
&QApplication::focusChanged,
|
||||
@@ -686,12 +677,15 @@ void Core::StartGUI(bool full_screen)
|
||||
if (recent_projects_file.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream ts(&recent_projects_file);
|
||||
|
||||
while (!ts.atEnd()) {
|
||||
recent_projects_.append(ts.readLine());
|
||||
QString s;
|
||||
while (!(s = ts.readLine()).isEmpty()) {
|
||||
recent_projects_.append(s);
|
||||
}
|
||||
|
||||
recent_projects_file.close();
|
||||
}
|
||||
|
||||
emit OpenRecentListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -961,6 +955,8 @@ void Core::PushRecentlyOpenedProject(const QString& s)
|
||||
} else {
|
||||
recent_projects_.prepend(s);
|
||||
}
|
||||
|
||||
emit OpenRecentListChanged();
|
||||
}
|
||||
|
||||
void Core::OpenProjectInternal(const QString &filename)
|
||||
@@ -1038,7 +1034,7 @@ void Core::SetPreferenceForRenderMode(RenderMode::Mode mode, const QString &pref
|
||||
Config::Current()[GetRenderModePreferencePrefix(mode, preference)] = value;
|
||||
}
|
||||
|
||||
void Core::LabelNodes(const QList<Node *> &nodes) const
|
||||
void Core::LabelNodes(const QVector<Node *> &nodes) const
|
||||
{
|
||||
if (nodes.isEmpty()) {
|
||||
return;
|
||||
@@ -1097,6 +1093,8 @@ void Core::OpenProjectFromRecentList(int index)
|
||||
tr("The project \"%1\" doesn't exist. Would you like to remove this file from the recent list?").arg(open_fn),
|
||||
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
||||
recent_projects_.removeAt(index);
|
||||
|
||||
emit OpenRecentListChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-1
@@ -233,7 +233,7 @@ public:
|
||||
/**
|
||||
* @brief Show a dialog to the user to rename a set of nodes
|
||||
*/
|
||||
void LabelNodes(const QList<Node*>& nodes) const;
|
||||
void LabelNodes(const QVector<Node *> &nodes) const;
|
||||
|
||||
/**
|
||||
* @brief Create a new sequence named appropriately for the active project
|
||||
@@ -415,6 +415,11 @@ signals:
|
||||
*/
|
||||
void TimecodeDisplayChanged(Timecode::Display d);
|
||||
|
||||
/**
|
||||
* @brief Signal emitted when a change is made to the open recent list
|
||||
*/
|
||||
void OpenRecentListChanged();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Get the file filter than can be used with QFileDialog to open and save compatible projects
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
#include "dialog/task/task.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "project/project.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "ui/icons/icons.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -179,6 +178,7 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) :
|
||||
video_tab_->height_slider()->SetDefaultValue(viewer_node_->video_params().height());
|
||||
video_tab_->frame_rate_combobox()->SetFrameRate(viewer_node_->video_params().time_base().flipped());
|
||||
video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(viewer_node_->video_params().pixel_aspect_ratio());
|
||||
video_tab_->pixel_format_field()->SetPixelFormat(static_cast<VideoParams::Format>(Config::Current()["OnlinePixelFormat"].toInt()));
|
||||
video_tab_->interlaced_combobox()->SetInterlaceMode(viewer_node_->video_params().interlacing());
|
||||
audio_tab_->sample_rate_combobox()->SetSampleRate(viewer_node_->audio_params().sample_rate());
|
||||
audio_tab_->channel_layout_combobox()->SetChannelLayout(viewer_node_->audio_params().channel_layout());
|
||||
@@ -285,25 +285,36 @@ void ExportDialog::StartExport()
|
||||
}
|
||||
|
||||
// Validate video resolution
|
||||
if (video_enabled_->isChecked()) {
|
||||
if (video_tab_->width_slider()->GetValue() % 2 != 0
|
||||
|| video_tab_->height_slider()->GetValue() % 2 != 0) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Invalid parameters"));
|
||||
b.setText(tr("Width and height must be multiples of 2."));
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
if (video_enabled_->isChecked()
|
||||
&& video_tab_->GetSelectedCodec() == ExportCodec::kCodecH264
|
||||
&& (video_tab_->width_slider()->GetValue()%2 != 0 || video_tab_->height_slider()->GetValue()%2 != 0)) {
|
||||
QMessageBox b(this);
|
||||
b.setIcon(QMessageBox::Critical);
|
||||
b.setWindowModality(Qt::WindowModal);
|
||||
b.setWindowTitle(tr("Invalid Parameters"));
|
||||
b.setText(tr("Width and height must be multiples of 2."));
|
||||
b.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
ExportTask* task = new ExportTask(viewer_node_, color_manager_, GenerateParams());
|
||||
TaskDialog* td = new TaskDialog(task, tr("Export"), this);
|
||||
connect(td, &TaskDialog::TaskSucceeded, this, &QDialog::accept);
|
||||
connect(td, &TaskDialog::TaskSucceeded, this, &ExportDialog::ExportFinished);
|
||||
td->open();
|
||||
}
|
||||
|
||||
void ExportDialog::ExportFinished()
|
||||
{
|
||||
TaskDialog* td = static_cast<TaskDialog*>(sender());
|
||||
|
||||
if (td->GetTask()->IsCancelled()) {
|
||||
// If this task was cancelled, we stay open so the user can potentially queue another export
|
||||
} else {
|
||||
// Accept this dialog and close
|
||||
this->accept();
|
||||
}
|
||||
}
|
||||
|
||||
void ExportDialog::closeEvent(QCloseEvent *e)
|
||||
{
|
||||
preview_viewer_->ConnectViewerNode(nullptr);
|
||||
@@ -373,7 +384,7 @@ void ExportDialog::ResolutionChanged()
|
||||
new_width *= video_aspect_ratio_;
|
||||
|
||||
// Align to even number and set
|
||||
video_tab_->width_slider()->SetValue(AlignEvenNumber(new_width));
|
||||
video_tab_->width_slider()->SetValue(new_width);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -384,7 +395,7 @@ void ExportDialog::ResolutionChanged()
|
||||
new_height /= video_aspect_ratio_;
|
||||
|
||||
// Align to even number and set
|
||||
video_tab_->height_slider()->SetValue(AlignEvenNumber(new_height));
|
||||
video_tab_->height_slider()->SetValue(new_height);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -414,24 +425,20 @@ void ExportDialog::SetDefaultFilename()
|
||||
filename_edit_->setText(file_location);
|
||||
}
|
||||
|
||||
int ExportDialog::AlignEvenNumber(double d)
|
||||
{
|
||||
return qCeil(d * 0.5) * 2;
|
||||
}
|
||||
|
||||
ExportParams ExportDialog::GenerateParams() const
|
||||
{
|
||||
VideoParams video_render_params(static_cast<int>(video_tab_->width_slider()->GetValue()),
|
||||
static_cast<int>(video_tab_->height_slider()->GetValue()),
|
||||
video_tab_->frame_rate_combobox()->GetFrameRate().flipped(),
|
||||
PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOnline),
|
||||
video_tab_->pixel_format_field()->GetPixelFormat(),
|
||||
VideoParams::kInternalChannelCount,
|
||||
video_tab_->pixel_aspect_combobox()->GetPixelAspectRatio(),
|
||||
video_tab_->interlaced_combobox()->GetInterlaceMode(),
|
||||
1);
|
||||
|
||||
AudioParams audio_render_params(audio_tab_->sample_rate_combobox()->currentData().toInt(),
|
||||
audio_tab_->channel_layout_combobox()->GetChannelLayout(),
|
||||
SampleFormat::kInternalFormat);
|
||||
AudioParams::kInternalFormat);
|
||||
|
||||
ExportParams params;
|
||||
params.SetFilename(filename_edit_->text());
|
||||
|
||||
@@ -49,8 +49,6 @@ private:
|
||||
void LoadPresets();
|
||||
void SetDefaultFilename();
|
||||
|
||||
static int AlignEvenNumber(double d);
|
||||
|
||||
ExportParams GenerateParams() const;
|
||||
|
||||
ViewerOutput* viewer_node_;
|
||||
@@ -85,6 +83,8 @@ private slots:
|
||||
|
||||
void StartExport();
|
||||
|
||||
void ExportFinished();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -115,6 +115,13 @@ QWidget* ExportVideoTab::SetupResolutionSection()
|
||||
interlaced_combobox_ = new InterlacedComboBox();
|
||||
layout->addWidget(interlaced_combobox_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||
|
||||
pixel_format_field_ = new PixelFormatComboBox(true);
|
||||
layout->addWidget(pixel_format_field_, row, 1);
|
||||
|
||||
return resolution_group;
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,11 @@ public:
|
||||
return pixel_aspect_combobox_;
|
||||
}
|
||||
|
||||
PixelFormatComboBox* pixel_format_field() const
|
||||
{
|
||||
return pixel_format_field_;
|
||||
}
|
||||
|
||||
const int& threads() const
|
||||
{
|
||||
return threads_;
|
||||
@@ -149,6 +154,7 @@ private:
|
||||
|
||||
InterlacedComboBox* interlaced_combobox_;
|
||||
PixelAspectRatioComboBox* pixel_aspect_combobox_;
|
||||
PixelFormatComboBox* pixel_format_field_;
|
||||
|
||||
int threads_;
|
||||
|
||||
|
||||
@@ -25,9 +25,8 @@
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "common/ocioutils.h"
|
||||
#include "core.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/project.h"
|
||||
@@ -78,11 +77,13 @@ VideoStreamProperties::VideoStreamProperties(VideoStreamPtr stream) :
|
||||
|
||||
video_layout->addWidget(video_color_space_, row, 1);
|
||||
|
||||
row++;
|
||||
if (stream->channel_count() == VideoParams::kRGBAChannelCount) {
|
||||
row++;
|
||||
|
||||
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
|
||||
video_premultiply_alpha_->setChecked(stream_->premultiplied_alpha());
|
||||
video_layout->addWidget(video_premultiply_alpha_, row, 0, 1, 2);
|
||||
video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha"));
|
||||
video_premultiply_alpha_->setChecked(stream_->premultiplied_alpha());
|
||||
video_layout->addWidget(video_premultiply_alpha_, row, 0, 1, 2);
|
||||
}
|
||||
|
||||
row++;
|
||||
|
||||
|
||||
@@ -99,6 +99,11 @@ PreferencesBehaviorTab::PreferencesBehaviorTab()
|
||||
AddItem(tr("Auto-Scale By Default"),
|
||||
QStringLiteral("AutoscaleByDefault"),
|
||||
node_group);
|
||||
AddItem(tr("Splitting Clips Copies Dependencies"),
|
||||
QStringLiteral("SplitClipsCopyNodes"),
|
||||
tr("Multiple clips can share the same nodes. Disable this to automatically share node "
|
||||
"dependencies among clips when copying or splitting them."),
|
||||
node_group);
|
||||
}
|
||||
|
||||
void PreferencesBehaviorTab::Accept()
|
||||
|
||||
@@ -27,10 +27,9 @@
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <OpenColorIO/OpenColorIO.h>
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "common/ocioutils.h"
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "render/colormanager.h"
|
||||
|
||||
@@ -107,13 +107,14 @@ void SequenceDialog::accept()
|
||||
parameter_tab_->GetSelectedVideoHeight(),
|
||||
parameter_tab_->GetSelectedVideoFrameRate().flipped(),
|
||||
parameter_tab_->GetSelectedPreviewFormat(),
|
||||
VideoParams::kInternalChannelCount,
|
||||
parameter_tab_->GetSelectedVideoPixelAspect(),
|
||||
parameter_tab_->GetSelectedVideoInterlacingMode(),
|
||||
parameter_tab_->GetSelectedPreviewResolution());
|
||||
|
||||
AudioParams audio_params = AudioParams(parameter_tab_->GetSelectedAudioSampleRate(),
|
||||
parameter_tab_->GetSelectedAudioChannelLayout(),
|
||||
SampleFormat::kInternalFormat);
|
||||
AudioParams::kInternalFormat);
|
||||
|
||||
if (make_undoable_) {
|
||||
|
||||
|
||||
@@ -74,8 +74,8 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg
|
||||
preview_resolution_label_ = new QLabel();
|
||||
preview_layout->addWidget(preview_resolution_label_, row, 2);
|
||||
row++;
|
||||
preview_layout->addWidget(new QLabel(tr("Format:")), row, 0);
|
||||
preview_format_field_ = new PixelFormatComboBox(true, true);
|
||||
preview_layout->addWidget(new QLabel(tr("Quality:")), row, 0);
|
||||
preview_format_field_ = new PixelFormatComboBox(true);
|
||||
preview_layout->addWidget(preview_format_field_, row, 1, 1, 2);
|
||||
layout->addWidget(preview_group);
|
||||
|
||||
@@ -133,7 +133,8 @@ void SequenceDialogParameterTab::UpdatePreviewResolutionLabel()
|
||||
{
|
||||
VideoParams test_param(video_width_field_->GetValue(),
|
||||
video_height_field_->GetValue(),
|
||||
PixelFormat::PIX_FMT_INVALID,
|
||||
VideoParams::kFormatInvalid,
|
||||
VideoParams::kInternalChannelCount,
|
||||
rational(1),
|
||||
VideoParams::kInterlaceNone,
|
||||
preview_resolution_field_->currentData().toInt());
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
return preview_resolution_field_->GetDivider();
|
||||
}
|
||||
|
||||
PixelFormat::Format GetSelectedPreviewFormat() const
|
||||
VideoParams::Format GetSelectedPreviewFormat() const
|
||||
{
|
||||
return preview_format_field_->GetPixelFormat();
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
#include "config/config.h"
|
||||
#include "node/input.h"
|
||||
#include "render/videoparams.h"
|
||||
#include "ui/icons/icons.h"
|
||||
@@ -41,8 +42,6 @@ const int kDataIsPreset = Qt::UserRole;
|
||||
const int kDataPresetIsCustomRole = Qt::UserRole + 1;
|
||||
const int kDataPresetDataRole = Qt::UserRole + 2;
|
||||
|
||||
const PixelFormat::Format kDefaultPreviewFormat = PixelFormat::PIX_FMT_RGBA16F;
|
||||
|
||||
SequenceDialogPresetTab::SequenceDialogPresetTab(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
PresetManager<SequencePreset>(this, QStringLiteral("sequencepresets"))
|
||||
@@ -100,6 +99,7 @@ QTreeWidgetItem* SequenceDialogPresetTab::CreateFolder(const QString &name)
|
||||
|
||||
QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &name, int width, int height, int divider)
|
||||
{
|
||||
VideoParams::Format default_format = static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt());
|
||||
QTreeWidgetItem* parent = CreateFolder(name);
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 23.976 FPS").arg(name),
|
||||
width,
|
||||
@@ -110,7 +110,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 25 FPS").arg(name),
|
||||
width,
|
||||
height,
|
||||
@@ -120,7 +120,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 29.97 FPS").arg(name),
|
||||
width,
|
||||
height,
|
||||
@@ -130,7 +130,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 50 FPS").arg(name),
|
||||
width,
|
||||
height,
|
||||
@@ -140,7 +140,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 59.94 FPS").arg(name),
|
||||
width,
|
||||
height,
|
||||
@@ -150,12 +150,13 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateHDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
return parent;
|
||||
}
|
||||
|
||||
QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &name, int width, int height, const rational& frame_rate, const rational &standard_par, const rational &wide_par, int divider)
|
||||
{
|
||||
VideoParams::Format default_format = static_cast<VideoParams::Format>(Config::Current()["OfflinePixelFormat"].toInt());
|
||||
QTreeWidgetItem* parent = CreateFolder(name);
|
||||
preset_tree_->addTopLevelItem(parent);
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 Standard").arg(name),
|
||||
@@ -167,7 +168,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
AddStandardItem(parent, SequencePreset::Create(tr("%1 Widescreen").arg(name),
|
||||
width,
|
||||
height,
|
||||
@@ -177,7 +178,7 @@ QTreeWidgetItem *SequenceDialogPresetTab::CreateSDPresetFolder(const QString &na
|
||||
48000,
|
||||
AV_CH_LAYOUT_STEREO,
|
||||
divider,
|
||||
kDefaultPreviewFormat));
|
||||
default_format));
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "common/rational.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "dialog/sequence/presetmanager.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -44,7 +43,7 @@ public:
|
||||
int sample_rate,
|
||||
uint64_t channel_layout,
|
||||
int preview_divider,
|
||||
PixelFormat::Format preview_format) :
|
||||
VideoParams::Format preview_format) :
|
||||
width_(width),
|
||||
height_(height),
|
||||
frame_rate_(frame_rate),
|
||||
@@ -67,7 +66,7 @@ public:
|
||||
int sample_rate,
|
||||
uint64_t channel_layout,
|
||||
int preview_divider,
|
||||
PixelFormat::Format preview_format)
|
||||
VideoParams::Format preview_format)
|
||||
{
|
||||
return std::make_shared<SequencePreset>(name, width, height, frame_rate, pixel_aspect,
|
||||
interlacing, sample_rate, channel_layout,
|
||||
@@ -96,7 +95,7 @@ public:
|
||||
} else if (reader->name() == QStringLiteral("divider")) {
|
||||
preview_divider_ = reader->readElementText().toInt();
|
||||
} else if (reader->name() == QStringLiteral("format")) {
|
||||
preview_format_ = static_cast<PixelFormat::Format>(reader->readElementText().toInt());
|
||||
preview_format_ = static_cast<VideoParams::Format>(reader->readElementText().toInt());
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
@@ -157,7 +156,7 @@ public:
|
||||
return preview_divider_;
|
||||
}
|
||||
|
||||
PixelFormat::Format preview_format() const
|
||||
VideoParams::Format preview_format() const
|
||||
{
|
||||
return preview_format_;
|
||||
}
|
||||
@@ -171,7 +170,7 @@ private:
|
||||
int sample_rate_;
|
||||
uint64_t channel_layout_;
|
||||
int preview_divider_;
|
||||
PixelFormat::Format preview_format_;
|
||||
VideoParams::Format preview_format_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ QString PanNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.pan");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> PanNode::Category() const
|
||||
QVector<Node::CategoryID> PanNode::Category() const
|
||||
{
|
||||
return {kCategoryChannels};
|
||||
}
|
||||
@@ -69,7 +69,7 @@ NodeValueTable PanNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (job.HasSamples()) {
|
||||
float pan_volume = job.GetValue(panning_input_).data().toFloat();
|
||||
float pan_volume = job.GetValue(panning_input_).data.toFloat();
|
||||
if (panning_input_->is_static()) {
|
||||
if (!qIsNull(pan_volume) && job.samples()->audio_params().channel_count() == 2) {
|
||||
if (pan_volume > 0) {
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
||||
|
||||
@@ -49,7 +49,7 @@ QString VolumeNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.volume");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> VolumeNode::Category() const
|
||||
QVector<Node::CategoryID> VolumeNode::Category() const
|
||||
{
|
||||
return {kCategoryFilter};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual NodeValueTable Value(NodeValueDatabase &value) const override;
|
||||
|
||||
@@ -58,7 +58,7 @@ Block::Block() :
|
||||
set_length_and_media_out(1);
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> Block::Category() const
|
||||
QVector<Node::CategoryID> Block::Category() const
|
||||
{
|
||||
return {kCategoryTimeline};
|
||||
}
|
||||
@@ -241,9 +241,9 @@ void Block::SaveInternal(QXmlStreamWriter *writer) const
|
||||
}
|
||||
}
|
||||
|
||||
QList<NodeInput *> Block::GetInputsToHash() const
|
||||
QVector<NodeInput *> Block::GetInputsToHash() const
|
||||
{
|
||||
QList<NodeInput*> inputs = Node::GetInputsToHash();
|
||||
QVector<NodeInput*> inputs = Node::GetInputsToHash();
|
||||
|
||||
// Ignore these inputs
|
||||
inputs.removeOne(media_in_input_);
|
||||
|
||||
@@ -43,7 +43,7 @@ public:
|
||||
|
||||
virtual Type type() const = 0;
|
||||
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
|
||||
const rational& in() const;
|
||||
const rational& out() const;
|
||||
@@ -110,7 +110,7 @@ protected:
|
||||
|
||||
virtual void SaveInternal(QXmlStreamWriter* writer) const override;
|
||||
|
||||
virtual QList<NodeInput*> GetInputsToHash() const override;
|
||||
virtual QVector<NodeInput*> GetInputsToHash() const override;
|
||||
|
||||
virtual void LengthChangedEvent(const rational& old_length,
|
||||
const rational& new_length,
|
||||
|
||||
@@ -42,7 +42,7 @@ QString CrossDissolveTransition::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.crossdissolve");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> CrossDissolveTransition::Category() const
|
||||
QVector<Node::CategoryID> CrossDissolveTransition::Category() const
|
||||
{
|
||||
return {kCategoryTransition};
|
||||
}
|
||||
@@ -56,7 +56,7 @@ ShaderCode CrossDissolveTransition::GetShaderCode(const QString &shader_id) cons
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(Node::ReadFileAsString(":/shaders/crossdissolve.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/crossdissolve.frag"), QString());
|
||||
}
|
||||
|
||||
void CrossDissolveTransition::SampleJobEvent(SampleBufferPtr from_samples, SampleBufferPtr to_samples, SampleBufferPtr out_samples, double time_in) const
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
//virtual void Retranslate() override;
|
||||
|
||||
@@ -43,7 +43,7 @@ QString DipToColorTransition::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.diptocolor");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> DipToColorTransition::Category() const
|
||||
QVector<Node::CategoryID> DipToColorTransition::Category() const
|
||||
{
|
||||
return {kCategoryTransition};
|
||||
}
|
||||
@@ -57,7 +57,7 @@ ShaderCode DipToColorTransition::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(Node::ReadFileAsString(":/shaders/diptoblack.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/diptoblack.frag"), QString());
|
||||
}
|
||||
|
||||
void DipToColorTransition::ShaderJobEvent(NodeValueDatabase &value, ShaderJob &job) const
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual ShaderCode GetShaderCode(const QString& shader_id) const override;
|
||||
|
||||
@@ -161,15 +161,15 @@ void TransitionBlock::InsertTransitionTimes(AcceleratedJob *job, const double &t
|
||||
{
|
||||
// Provides total transition progress from 0.0 (start) - 1.0 (end)
|
||||
job->InsertValue(QStringLiteral("ove_tprog_all"),
|
||||
NodeValue(NodeParam::kFloat, GetTotalProgress(time), this));
|
||||
ShaderValue(GetTotalProgress(time), NodeParam::kFloat));
|
||||
|
||||
// Provides progress of out section from 1.0 (start) - 0.0 (end)
|
||||
job->InsertValue(QStringLiteral("ove_tprog_out"),
|
||||
NodeValue(NodeParam::kFloat, GetOutProgress(time), this));
|
||||
ShaderValue(GetOutProgress(time), NodeParam::kFloat));
|
||||
|
||||
// Provides progress of in section from 0.0 (start) - 1.0 (end)
|
||||
job->InsertValue(QStringLiteral("ove_tprog_in"),
|
||||
NodeValue(NodeParam::kFloat, GetInProgress(time), this));
|
||||
ShaderValue(GetInProgress(time), NodeParam::kFloat));
|
||||
}
|
||||
|
||||
void TransitionBlock::BlockConnected(NodeEdgePtr edge)
|
||||
|
||||
@@ -59,7 +59,7 @@ QString BlurFilterNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.blur");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> BlurFilterNode::Category() const
|
||||
QVector<Node::CategoryID> BlurFilterNode::Category() const
|
||||
{
|
||||
return {kCategoryFilter};
|
||||
}
|
||||
@@ -83,7 +83,7 @@ void BlurFilterNode::Retranslate()
|
||||
ShaderCode BlurFilterNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
return ShaderCode(ReadFileAsString(":/shaders/blur.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/blur.frag"), QString());
|
||||
}
|
||||
|
||||
NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
||||
@@ -100,19 +100,19 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
// If there's no texture, no need to run an operation
|
||||
if (!job.GetValue(texture_input_).data().isNull()) {
|
||||
if (!job.GetValue(texture_input_).data.isNull()) {
|
||||
|
||||
// Check if radius > 0, and both "horiz" and/or "vert" are enabled
|
||||
if ((job.GetValue(horiz_input_).data().toBool() || job.GetValue(vert_input_).data().toBool())
|
||||
&& job.GetValue(radius_input_).data().toDouble() > 0.0) {
|
||||
if ((job.GetValue(horiz_input_).data.toBool() || job.GetValue(vert_input_).data.toBool())
|
||||
&& job.GetValue(radius_input_).data.toDouble() > 0.0) {
|
||||
|
||||
// Set iteration count to 2 if we're blurring both horizontally and vertically
|
||||
if (job.GetValue(horiz_input_).data().toBool() && job.GetValue(vert_input_).data().toBool()) {
|
||||
if (job.GetValue(horiz_input_).data.toBool() && job.GetValue(vert_input_).data.toBool()) {
|
||||
job.SetIterations(2, texture_input_);
|
||||
}
|
||||
|
||||
// If we're not repeating pixels, expect an alpha channel to appear
|
||||
if (!job.GetValue(repeat_edge_pixels_input_).data().toBool()) {
|
||||
if (!job.GetValue(repeat_edge_pixels_input_).data.toBool()) {
|
||||
job.SetAlphaChannelRequired(true);
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ NodeValueTable BlurFilterNode::Value(NodeValueDatabase &value) const
|
||||
|
||||
} else {
|
||||
// If we're not performing the blur job, just push the texture
|
||||
table.Push(job.GetValue(texture_input_));
|
||||
table.Push(job.GetValue(texture_input_), this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -63,7 +63,7 @@ QString StrokeFilterNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.stroke");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> StrokeFilterNode::Category() const
|
||||
QVector<Node::CategoryID> StrokeFilterNode::Category() const
|
||||
{
|
||||
return {kCategoryFilter};
|
||||
}
|
||||
@@ -94,12 +94,12 @@ NodeValueTable StrokeFilterNode::Value(NodeValueDatabase &value) const
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (!job.GetValue(tex_input_).data().isNull()) {
|
||||
if (job.GetValue(radius_input_).data().toDouble() > 0.0
|
||||
&& job.GetValue(opacity_input_).data().toDouble() > 0.0) {
|
||||
if (!job.GetValue(tex_input_).data.isNull()) {
|
||||
if (job.GetValue(radius_input_).data.toDouble() > 0.0
|
||||
&& job.GetValue(opacity_input_).data.toDouble() > 0.0) {
|
||||
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
|
||||
} else {
|
||||
table.Push(job.GetValue(tex_input_));
|
||||
table.Push(job.GetValue(tex_input_), this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ ShaderCode StrokeFilterNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(ReadFileAsString(":/shaders/stroke.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/stroke.frag"), QString());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -72,7 +72,7 @@ QString MatrixGenerator::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.transform");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> MatrixGenerator::Category() const
|
||||
QVector<Node::CategoryID> MatrixGenerator::Category() const
|
||||
{
|
||||
return {kCategoryGenerator, kCategoryMath};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
virtual QString Name() const override;
|
||||
virtual QString ShortName() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -69,7 +69,7 @@ QString PolygonGenerator::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.polygon");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> PolygonGenerator::Category() const
|
||||
QVector<Node::CategoryID> PolygonGenerator::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
@@ -89,7 +89,7 @@ ShaderCode PolygonGenerator::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(Node::ReadFileAsString(":/shaders/polygon.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/polygon.frag"), QString());
|
||||
}
|
||||
|
||||
NodeValueTable PolygonGenerator::Value(NodeValueDatabase &value) const
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -48,7 +48,7 @@ QString SolidGenerator::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.solidgenerator");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> SolidGenerator::Category() const
|
||||
QVector<Node::CategoryID> SolidGenerator::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
@@ -77,7 +77,7 @@ ShaderCode SolidGenerator::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(ReadFileAsString(":/shaders/solid.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/solid.frag"), QString());
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -72,7 +72,7 @@ QString TextGenerator::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.textgenerator");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> TextGenerator::Category() const
|
||||
QVector<Node::CategoryID> TextGenerator::Category() const
|
||||
{
|
||||
return {kCategoryGenerator};
|
||||
}
|
||||
@@ -104,7 +104,7 @@ NodeValueTable TextGenerator::Value(NodeValueDatabase &value) const
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (!job.GetValue(text_input_).data().toString().isEmpty()) {
|
||||
if (!job.GetValue(text_input_).data.toString().isEmpty()) {
|
||||
table.Push(NodeParam::kGenerateJob, QVariant::fromValue(job), this);
|
||||
}
|
||||
|
||||
@@ -124,14 +124,14 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
|
||||
// Set default font
|
||||
QFont default_font;
|
||||
default_font.setFamily(job.GetValue(font_input_).data().toString());
|
||||
default_font.setPointSizeF(job.GetValue(font_size_input_).data().toFloat());
|
||||
default_font.setFamily(job.GetValue(font_input_).data.toString());
|
||||
default_font.setPointSizeF(job.GetValue(font_size_input_).data.toFloat());
|
||||
text_doc.setDefaultFont(default_font);
|
||||
|
||||
// Center by default
|
||||
text_doc.setDefaultTextOption(QTextOption(Qt::AlignCenter));
|
||||
|
||||
text_doc.setHtml(job.GetValue(text_input_).data().toString());
|
||||
text_doc.setHtml(job.GetValue(text_input_).data.toString());
|
||||
|
||||
// Align to 80% width because that's considered the "title safe" area
|
||||
int tenth_of_width = frame->video_params().width() / 10;
|
||||
@@ -144,7 +144,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
// Push 10% inwards to compensate for title safe area
|
||||
p.translate(tenth_of_width, 0);
|
||||
|
||||
TextVerticalAlign valign = static_cast<TextVerticalAlign>(job.GetValue(valign_input_).data().toInt());
|
||||
TextVerticalAlign valign = static_cast<TextVerticalAlign>(job.GetValue(valign_input_).data.toInt());
|
||||
int doc_height = text_doc.size().height();
|
||||
|
||||
switch (valign) {
|
||||
@@ -165,7 +165,7 @@ void TextGenerator::GenerateFrame(FramePtr frame, const GenerateJob& job) const
|
||||
text_doc.drawContents(&p);
|
||||
|
||||
// Transplant alpha channel to frame
|
||||
Color rgb = job.GetValue(color_input_).data().value<Color>();
|
||||
Color rgb = job.GetValue(color_input_).data.value<Color>();
|
||||
for (int x=0; x<frame->width(); x++) {
|
||||
for (int y=0; y<frame->height(); y++) {
|
||||
uchar src_alpha = img.bits()[img.bytesPerLine() * y + x];
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
+6
-6
@@ -423,7 +423,7 @@ QVariant NodeInput::StringToValue(const DataType& data_type, const QString &stri
|
||||
}
|
||||
}
|
||||
|
||||
void NodeInput::GetDependencies(QList<Node *> &list, bool traverse, bool exclusive_only) const
|
||||
void NodeInput::GetDependencies(QVector<Node *> &list, bool traverse, bool exclusive_only) const
|
||||
{
|
||||
if (is_connected()
|
||||
&& (get_connected_output()->edges().size() == 1 || !exclusive_only)) {
|
||||
@@ -433,7 +433,7 @@ void NodeInput::GetDependencies(QList<Node *> &list, bool traverse, bool exclusi
|
||||
list.append(connected);
|
||||
|
||||
if (traverse) {
|
||||
QList<NodeInput*> connected_inputs = connected->GetInputsIncludingArrays();
|
||||
QVector<NodeInput*> connected_inputs = connected->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, connected_inputs) {
|
||||
i->GetDependencies(list, traverse, exclusive_only);
|
||||
@@ -461,21 +461,21 @@ QVariant NodeInput::GetDefaultValueForTrack(int track) const
|
||||
return default_value_.at(track);
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetDependencies(bool traverse, bool exclusive_only) const
|
||||
QVector<Node *> NodeInput::GetDependencies(bool traverse, bool exclusive_only) const
|
||||
{
|
||||
QList<Node *> list;
|
||||
QVector<Node *> list;
|
||||
|
||||
GetDependencies(list, traverse, exclusive_only);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetExclusiveDependencies() const
|
||||
QVector<Node *> NodeInput::GetExclusiveDependencies() const
|
||||
{
|
||||
return GetDependencies(true, true);
|
||||
}
|
||||
|
||||
QList<Node *> NodeInput::GetImmediateDependencies() const
|
||||
QVector<Node *> NodeInput::GetImmediateDependencies() const
|
||||
{
|
||||
return GetDependencies(false, false);
|
||||
}
|
||||
|
||||
+4
-4
@@ -282,17 +282,17 @@ public:
|
||||
|
||||
static QVariant StringToValue(const DataType &data_type, const QString &string, bool value_is_a_key_track);
|
||||
|
||||
void GetDependencies(QList<Node*>& list, bool traverse, bool exclusive_only) const;
|
||||
void GetDependencies(QVector<Node *> &list, bool traverse, bool exclusive_only) const;
|
||||
|
||||
QVariant GetDefaultValue() const;
|
||||
|
||||
QVariant GetDefaultValueForTrack(int track) const;
|
||||
|
||||
QList<Node*> GetDependencies(bool traverse = true, bool exclusive_only = false) const;
|
||||
QVector<Node*> GetDependencies(bool traverse = true, bool exclusive_only = false) const;
|
||||
|
||||
QList<Node*> GetExclusiveDependencies() const;
|
||||
QVector<Node*> GetExclusiveDependencies() const;
|
||||
|
||||
QList<Node*> GetImmediateDependencies() const;
|
||||
QVector<Node*> GetImmediateDependencies() const;
|
||||
|
||||
signals:
|
||||
void ValueChanged(const OLIVE_NAMESPACE::TimeRange& range);
|
||||
|
||||
@@ -35,7 +35,7 @@ MediaInput::MediaInput() :
|
||||
AddInput(footage_input_);
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> MediaInput::Category() const
|
||||
QVector<Node::CategoryID> MediaInput::Category() const
|
||||
{
|
||||
return {kCategoryInput};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
|
||||
virtual Stream::Type type() const = 0;
|
||||
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
|
||||
StreamPtr stream();
|
||||
void SetStream(StreamPtr s);
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include "codec/ffmpeg/ffmpegdecoder.h"
|
||||
#include "core.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "render/pixelformat.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -38,7 +37,7 @@ Node *VideoInput::copy() const
|
||||
|
||||
Stream::Type VideoInput::type() const
|
||||
{
|
||||
return Stream::kVideo;
|
||||
return Stream::kVideo;
|
||||
}
|
||||
|
||||
QString VideoInput::Name() const
|
||||
|
||||
@@ -41,7 +41,7 @@ QString TimeInput::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.time");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> TimeInput::Category() const
|
||||
QVector<Node::CategoryID> TimeInput::Category() const
|
||||
{
|
||||
return {kCategoryInput};
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual NodeValueTable Value(NodeValueDatabase& value) const override;
|
||||
|
||||
@@ -55,7 +55,7 @@ QString MathNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.math");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> MathNode::Category() const
|
||||
QVector<Node::CategoryID> MathNode::Category() const
|
||||
{
|
||||
return {kCategoryMath};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -48,7 +48,7 @@ ShaderCode MathNodeBase::GetShaderCodeInternal(const QString &shader_id, NodeInp
|
||||
// No-op frag shader (can we return QString() instead?)
|
||||
operation = QStringLiteral("texture(%1, ove_texcoord)").arg(tex_in->id());
|
||||
|
||||
vert = ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id());
|
||||
vert = FileFunctions::ReadFileAsString(":/shaders/matrix.vert").arg(mat_in->id(), tex_in->id());
|
||||
|
||||
} else {
|
||||
switch (op) {
|
||||
@@ -329,7 +329,7 @@ NodeValueTable MathNodeBase::ValueInternal(NodeValueDatabase &value, Operation o
|
||||
float number = RetrieveNumber(number_val);
|
||||
|
||||
SampleJob job(val_a.type() == NodeParam::kSamples ? val_a : val_b);
|
||||
job.InsertValue(number_param, NodeValue(NodeParam::kFloat, number, this));
|
||||
job.InsertValue(number_param, ShaderValue(number, NodeParam::kFloat));
|
||||
|
||||
if (job.HasSamples()) {
|
||||
if (number_param->is_static()) {
|
||||
|
||||
@@ -46,7 +46,7 @@ QString MergeNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.merge");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> MergeNode::Category() const
|
||||
QVector<Node::CategoryID> MergeNode::Category() const
|
||||
{
|
||||
return {kCategoryMath};
|
||||
}
|
||||
@@ -66,7 +66,7 @@ ShaderCode MergeNode::GetShaderCode(const QString &shader_id) const
|
||||
{
|
||||
Q_UNUSED(shader_id)
|
||||
|
||||
return ShaderCode(ReadFileAsString(":/shaders/alphaover.frag"), QString());
|
||||
return ShaderCode(FileFunctions::ReadFileAsString(":/shaders/alphaover.frag"), QString());
|
||||
}
|
||||
|
||||
NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
|
||||
@@ -75,17 +75,18 @@ NodeValueTable MergeNode::Value(NodeValueDatabase &value) const
|
||||
job.InsertValue(base_in_, value);
|
||||
job.InsertValue(blend_in_, value);
|
||||
|
||||
// FIXME: Check if "blend" is RGB-only, in which case it's a no-op
|
||||
|
||||
NodeValueTable table = value.Merge();
|
||||
|
||||
if (!job.GetValue(base_in_).data().isNull() || !job.GetValue(blend_in_).data().isNull()) {
|
||||
if (job.GetValue(base_in_).data().isNull()) {
|
||||
// We only have a blend texture, no need to alpha over
|
||||
table.Push(job.GetValue(blend_in_));
|
||||
} else if (job.GetValue(blend_in_).data().isNull()) {
|
||||
TexturePtr base_tex = job.GetValue(base_in_).data.value<TexturePtr>();
|
||||
TexturePtr blend_tex = job.GetValue(blend_in_).data.value<TexturePtr>();
|
||||
|
||||
if (base_tex || blend_tex) {
|
||||
if (!base_tex || (blend_tex && blend_tex->channel_count() < VideoParams::kRGBAChannelCount)) {
|
||||
// We only have a blend texture or the blend texture is RGB only, no need to alpha over
|
||||
table.Push(job.GetValue(blend_in_), this);
|
||||
} else if (!blend_tex) {
|
||||
// We only have a base texture, no need to alpha over
|
||||
table.Push(job.GetValue(base_in_));
|
||||
table.Push(job.GetValue(base_in_), this);
|
||||
} else {
|
||||
// We have both textures, push the job
|
||||
table.Push(NodeParam::kShaderJob, QVariant::fromValue(job), this);
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
@@ -48,7 +48,7 @@ QString TrigonometryNode::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.trigonometry");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> TrigonometryNode::Category() const
|
||||
QVector<Node::CategoryID> TrigonometryNode::Category() const
|
||||
{
|
||||
return {kCategoryMath};
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
+87
-38
@@ -29,6 +29,7 @@
|
||||
#include "project/project.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "project/item/footage/videostream.h"
|
||||
#include "widget/nodeview/nodeviewundo.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -239,6 +240,66 @@ TimeRange Node::OutputTimeAdjustment(NodeInput *, const TimeRange &input_time) c
|
||||
return input_time;
|
||||
}
|
||||
|
||||
QVector<Node *> Node::CopyDependencyGraph(const QVector<Node *> &nodes, QUndoCommand* command)
|
||||
{
|
||||
int nb_nodes = nodes.size();
|
||||
|
||||
QVector<Node*> copies(nb_nodes);
|
||||
|
||||
for (int i=0; i<nb_nodes; i++) {
|
||||
// Create another of the same node
|
||||
Node* c = nodes.at(i)->copy();;
|
||||
|
||||
// Copy the values, but NOT the connections, since we'll be connecting to our own clones later
|
||||
Node::CopyInputs(nodes.at(i), c, false);
|
||||
|
||||
// Add to graph
|
||||
NodeGraph* graph = static_cast<NodeGraph*>(nodes.at(i)->parent());
|
||||
if (command) {
|
||||
new NodeAddCommand(graph, c, command);
|
||||
} else {
|
||||
graph->AddNode(c);
|
||||
}
|
||||
|
||||
// Store in array at the same index as source
|
||||
copies[i] = c;
|
||||
}
|
||||
|
||||
CopyDependencyGraph(nodes, copies, command);
|
||||
|
||||
return copies;
|
||||
}
|
||||
|
||||
void Node::CopyDependencyGraph(const QVector<Node *> &src, const QVector<Node *> &dst, QUndoCommand *command)
|
||||
{
|
||||
int nb_nodes = src.size();
|
||||
|
||||
for (int i=0; i<nb_nodes; i++) {
|
||||
// Find any interconnections
|
||||
QVector<NodeInput*> inputs = src.at(i)->GetInputsIncludingArrays();
|
||||
|
||||
for (int j=0; j<nb_nodes; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (NodeInput* input, inputs) {
|
||||
if (input->get_connected_node() == src.at(j)) {
|
||||
// Found a connection
|
||||
NodeOutput* copy_output = dst.at(j)->GetOutputWithID(input->get_connected_output()->id());
|
||||
NodeInput* copy_input = dst.at(i)->GetInputWithID(input->id());
|
||||
|
||||
if (command) {
|
||||
new NodeEdgeAddCommand(copy_output, copy_input, command);
|
||||
} else {
|
||||
NodeParam::ConnectEdge(copy_output, copy_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Node::SendInvalidateCache(const TimeRange &range, NodeInput *source)
|
||||
{
|
||||
// Loop through all parameters (there should be no children that are not NodeParams)
|
||||
@@ -265,24 +326,12 @@ void Node::SaveInternal(QXmlStreamWriter *) const
|
||||
{
|
||||
}
|
||||
|
||||
QList<NodeInput *> Node::GetInputsToHash() const
|
||||
QVector<NodeInput *> Node::GetInputsToHash() const
|
||||
{
|
||||
return GetInputsIncludingArrays();
|
||||
}
|
||||
|
||||
QString Node::ReadFileAsString(const QString &filename)
|
||||
{
|
||||
QFile f(filename);
|
||||
QString file_data;
|
||||
if (f.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream text_stream(&f);
|
||||
file_data = text_stream.readAll();
|
||||
f.close();
|
||||
}
|
||||
return file_data;
|
||||
}
|
||||
|
||||
void GetInputsIncludingArraysInternal(NodeInputArray* array, QList<NodeInput *>& list)
|
||||
void GetInputsIncludingArraysInternal(NodeInputArray* array, QVector<NodeInput *>& list)
|
||||
{
|
||||
foreach (NodeInput* input, array->sub_params()) {
|
||||
list.append(input);
|
||||
@@ -293,9 +342,9 @@ void GetInputsIncludingArraysInternal(NodeInputArray* array, QList<NodeInput *>&
|
||||
}
|
||||
}
|
||||
|
||||
QList<NodeInput *> Node::GetInputsIncludingArrays() const
|
||||
QVector<NodeInput *> Node::GetInputsIncludingArrays() const
|
||||
{
|
||||
QList<NodeInput *> inputs;
|
||||
QVector<NodeInput *> inputs;
|
||||
|
||||
foreach (NodeParam* param, params_) {
|
||||
if (param->type() == NodeParam::kInput) {
|
||||
@@ -312,7 +361,7 @@ QList<NodeInput *> Node::GetInputsIncludingArrays() const
|
||||
return inputs;
|
||||
}
|
||||
|
||||
QList<NodeOutput *> Node::GetOutputs() const
|
||||
QVector<NodeOutput *> Node::GetOutputs() const
|
||||
{
|
||||
// The current design only uses one output per node. This function returns a list just in case that changes.
|
||||
return {output_};
|
||||
@@ -359,7 +408,7 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const
|
||||
// Add this Node's ID
|
||||
hash.addData(id().toUtf8());
|
||||
|
||||
QList<NodeInput*> inputs = GetInputsToHash();
|
||||
QVector<NodeInput*> inputs = GetInputsToHash();
|
||||
|
||||
foreach (NodeInput* input, inputs) {
|
||||
// For each input, try to hash its value
|
||||
@@ -428,8 +477,8 @@ void Node::CopyInputs(Node *source, Node *destination, bool include_connections)
|
||||
{
|
||||
Q_ASSERT(source->id() == destination->id());
|
||||
|
||||
const QList<NodeParam*>& src_param = source->params_;
|
||||
const QList<NodeParam*>& dst_param = destination->params_;
|
||||
const QVector<NodeParam*>& src_param = source->params_;
|
||||
const QVector<NodeParam*>& dst_param = destination->params_;
|
||||
|
||||
for (int i=0;i<src_param.size();i++) {
|
||||
NodeParam* p = src_param.at(i);
|
||||
@@ -472,7 +521,7 @@ bool Node::IsMedia() const
|
||||
return false;
|
||||
}
|
||||
|
||||
const QList<NodeParam *>& Node::parameters() const
|
||||
const QVector<NodeParam *>& Node::parameters() const
|
||||
{
|
||||
return params_;
|
||||
}
|
||||
@@ -490,9 +539,9 @@ int Node::IndexOfParameter(NodeParam *param) const
|
||||
* TRUE to recursively traverse each node for a complete dependency graph. FALSE to return only the immediate
|
||||
* dependencies.
|
||||
*/
|
||||
QList<Node*> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const {
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QList<Node*> list;
|
||||
QVector<Node *> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) const {
|
||||
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QVector<Node*> list;
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
i->GetDependencies(list, traverse, exclusive_only);
|
||||
@@ -501,17 +550,17 @@ QList<Node*> Node::GetDependenciesInternal(bool traverse, bool exclusive_only) c
|
||||
return list;
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetDependencies() const
|
||||
QVector<Node *> Node::GetDependencies() const
|
||||
{
|
||||
return GetDependenciesInternal(true, false);
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetExclusiveDependencies() const
|
||||
QVector<Node *> Node::GetExclusiveDependencies() const
|
||||
{
|
||||
return GetDependenciesInternal(true, true);
|
||||
}
|
||||
|
||||
QList<Node *> Node::GetImmediateDependencies() const
|
||||
QVector<Node *> Node::GetImmediateDependencies() const
|
||||
{
|
||||
return GetDependenciesInternal(false, false);
|
||||
}
|
||||
@@ -535,7 +584,7 @@ void Node::GenerateFrame(FramePtr frame, const GenerateJob &job) const
|
||||
|
||||
NodeInput *Node::GetInputWithID(const QString &id) const
|
||||
{
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* i, inputs) {
|
||||
if (i->id() == id) {
|
||||
@@ -560,7 +609,7 @@ NodeOutput *Node::GetOutputWithID(const QString &id) const
|
||||
|
||||
bool Node::OutputsTo(Node *n, bool recursively) const
|
||||
{
|
||||
QList<NodeOutput*> outputs = GetOutputs();
|
||||
QVector<NodeOutput*> outputs = GetOutputs();
|
||||
|
||||
foreach (NodeOutput* output, outputs) {
|
||||
foreach (NodeEdgePtr edge, output->edges()) {
|
||||
@@ -579,7 +628,7 @@ bool Node::OutputsTo(Node *n, bool recursively) const
|
||||
|
||||
bool Node::OutputsTo(const QString &id, bool recursively) const
|
||||
{
|
||||
QList<NodeOutput*> outputs = GetOutputs();
|
||||
QVector<NodeOutput*> outputs = GetOutputs();
|
||||
|
||||
foreach (NodeOutput* output, outputs) {
|
||||
foreach (NodeEdgePtr edge, output->edges()) {
|
||||
@@ -598,7 +647,7 @@ bool Node::OutputsTo(const QString &id, bool recursively) const
|
||||
|
||||
bool Node::OutputsTo(NodeInput *input, bool recursively, bool include_arrays) const
|
||||
{
|
||||
QList<NodeOutput*> outputs = GetOutputs();
|
||||
QVector<NodeOutput*> outputs = GetOutputs();
|
||||
|
||||
foreach (NodeOutput* output, outputs) {
|
||||
foreach (NodeEdgePtr edge, output->edges()) {
|
||||
@@ -620,7 +669,7 @@ bool Node::OutputsTo(NodeInput *input, bool recursively, bool include_arrays) co
|
||||
|
||||
bool Node::InputsFrom(Node *n, bool recursively) const
|
||||
{
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* input, inputs) {
|
||||
foreach (NodeEdgePtr edge, input->edges()) {
|
||||
@@ -639,7 +688,7 @@ bool Node::InputsFrom(Node *n, bool recursively) const
|
||||
|
||||
bool Node::InputsFrom(const QString &id, bool recursively) const
|
||||
{
|
||||
QList<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
QVector<NodeInput*> inputs = GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* input, inputs) {
|
||||
foreach (NodeEdgePtr edge, input->edges()) {
|
||||
@@ -661,7 +710,7 @@ int Node::GetRoutesTo(Node *n) const
|
||||
bool outputs_directly = false;
|
||||
int routes = 0;
|
||||
|
||||
QList<NodeOutput*> outputs = GetOutputs();
|
||||
QVector<NodeOutput*> outputs = GetOutputs();
|
||||
|
||||
foreach (NodeOutput* o, outputs) {
|
||||
foreach (NodeEdgePtr edge, o->edges()) {
|
||||
@@ -740,13 +789,13 @@ QString Node::GetCategoryName(const CategoryID &c)
|
||||
return tr("Uncategorized");
|
||||
}
|
||||
|
||||
QList<TimeRange> Node::TransformTimeTo(const TimeRange &time, Node *target, NodeParam::Type direction)
|
||||
QVector<TimeRange> Node::TransformTimeTo(const TimeRange &time, Node *target, NodeParam::Type direction)
|
||||
{
|
||||
QList<TimeRange> paths_found;
|
||||
QVector<TimeRange> paths_found;
|
||||
|
||||
if (direction == NodeParam::kInput) {
|
||||
// Get list of all inputs
|
||||
QList<NodeInput *> inputs = GetInputsIncludingArrays();
|
||||
QVector<NodeInput *> inputs = GetInputsIncludingArrays();
|
||||
|
||||
// If this input is connected, traverse it to see if we stumble across the specified `node`
|
||||
foreach (NodeInput* input, inputs) {
|
||||
@@ -767,7 +816,7 @@ QList<TimeRange> Node::TransformTimeTo(const TimeRange &time, Node *target, Node
|
||||
}
|
||||
} else {
|
||||
// Get list of all outputs
|
||||
QList<NodeOutput*> outputs = GetOutputs();
|
||||
QVector<NodeOutput*> outputs = GetOutputs();
|
||||
|
||||
// If this input is connected, traverse it to see if we stumble across the specified `node`
|
||||
foreach (NodeOutput* output, outputs) {
|
||||
|
||||
+32
-25
@@ -36,7 +36,10 @@
|
||||
#include "node/output.h"
|
||||
#include "node/value.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/shaderinfo.h"
|
||||
#include "render/job/generatejob.h"
|
||||
#include "render/job/samplejob.h"
|
||||
#include "render/job/shaderjob.h"
|
||||
#include "render/shadercode.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -129,7 +132,7 @@ public:
|
||||
* interpreted as an empty string category. This value should be run through a translator as its largely user
|
||||
* oriented.
|
||||
*/
|
||||
virtual QList<CategoryID> Category() const = 0;
|
||||
virtual QVector<CategoryID> Category() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Return a description of this node's purpose (optional for subclassing, but recommended)
|
||||
@@ -147,7 +150,7 @@ public:
|
||||
/**
|
||||
* @brief Return a list of NodeParams
|
||||
*/
|
||||
const QList<NodeParam*>& parameters() const;
|
||||
const QVector<NodeParam*>& parameters() const;
|
||||
|
||||
/**
|
||||
* @brief Return the index of a parameter
|
||||
@@ -158,7 +161,7 @@ public:
|
||||
/**
|
||||
* @brief Return a list of all Nodes that this Node's inputs are connected to (does not include this Node)
|
||||
*/
|
||||
QList<Node*> GetDependencies() const;
|
||||
QVector<Node *> GetDependencies() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a list of Nodes that this Node is dependent on, provided no other Nodes are dependent on them
|
||||
@@ -166,12 +169,12 @@ public:
|
||||
*
|
||||
* Similar to GetDependencies(), but excludes any Nodes that are used outside the dependency graph of this Node.
|
||||
*/
|
||||
QList<Node*> GetExclusiveDependencies() const;
|
||||
QVector<Node *> GetExclusiveDependencies() const;
|
||||
|
||||
/**
|
||||
* @brief Retrieve immediate dependencies (only nodes that are directly connected to the inputs of this one)
|
||||
*/
|
||||
QList<Node*> GetImmediateDependencies() const;
|
||||
QVector<Node *> GetImmediateDependencies() const;
|
||||
|
||||
/**
|
||||
* @brief Generate hardware accelerated code for this Node
|
||||
@@ -274,19 +277,19 @@ public:
|
||||
/**
|
||||
* @brief Transforms time from this node through the connections it takes to get to the specified node
|
||||
*/
|
||||
QList<TimeRange> TransformTimeTo(const TimeRange& time, Node* target, NodeParam::Type direction);
|
||||
QVector<TimeRange> TransformTimeTo(const TimeRange& time, Node* target, NodeParam::Type direction);
|
||||
|
||||
/**
|
||||
* @brief Find nodes of a certain type that this Node takes inputs from
|
||||
*/
|
||||
template<class T>
|
||||
QList<T*> FindInputNodes() const;
|
||||
QVector<T*> FindInputNodes() const;
|
||||
|
||||
template<class T>
|
||||
/**
|
||||
* @brief Find a node of a certain type that this Node outputs to
|
||||
*/
|
||||
QList<T *> FindOutputNode();
|
||||
QVector<T *> FindOutputNode();
|
||||
|
||||
/**
|
||||
* @brief Convert a pointer to a value that can be sent between NodeParams
|
||||
@@ -343,6 +346,12 @@ public:
|
||||
*/
|
||||
static void CopyInputs(Node* source, Node* destination, bool include_connections = true);
|
||||
|
||||
/**
|
||||
* @brief Clones a set of nodes and connects the new ones the way the old ones were
|
||||
*/
|
||||
static QVector<Node*> CopyDependencyGraph(const QVector<Node*>& nodes, QUndoCommand *command);
|
||||
static void CopyDependencyGraph(const QVector<Node*>& src, const QVector<Node*>& dst, QUndoCommand *command);
|
||||
|
||||
/**
|
||||
* @brief Return whether this Node can be deleted or not
|
||||
*/
|
||||
@@ -404,11 +413,9 @@ public:
|
||||
|
||||
void SetPosition(const QPointF& pos);
|
||||
|
||||
static QString ReadFileAsString(const QString& filename);
|
||||
QVector<NodeInput*> GetInputsIncludingArrays() const;
|
||||
|
||||
QList<NodeInput*> GetInputsIncludingArrays() const;
|
||||
|
||||
QList<NodeOutput*> GetOutputs() const;
|
||||
QVector<NodeOutput*> GetOutputs() const;
|
||||
|
||||
virtual bool HasGizmos() const;
|
||||
|
||||
@@ -434,7 +441,7 @@ protected:
|
||||
|
||||
virtual void SaveInternal(QXmlStreamWriter* writer) const;
|
||||
|
||||
virtual QList<NodeInput*> GetInputsToHash() const;
|
||||
virtual QVector<NodeInput*> GetInputsToHash() const;
|
||||
|
||||
protected slots:
|
||||
void InputChanged(const OLIVE_NAMESPACE::TimeRange &range);
|
||||
@@ -487,14 +494,14 @@ private:
|
||||
void DisconnectInput(NodeInput* input);
|
||||
|
||||
template<class T>
|
||||
static void FindInputNodeInternal(const Node* n, QList<T *>& list);
|
||||
static void FindInputNodeInternal(const Node* n, QVector<T *>& list);
|
||||
|
||||
template<class T>
|
||||
static void FindOutputNodeInternal(const Node* n, QList<T *>& list);
|
||||
static void FindOutputNodeInternal(const Node* n, QVector<T *>& list);
|
||||
|
||||
QList<Node *> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
|
||||
QVector<Node *> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
|
||||
|
||||
QList<NodeParam *> params_;
|
||||
QVector<NodeParam *> params_;
|
||||
|
||||
/**
|
||||
* @brief Internal variable for whether this Node can be deleted or not
|
||||
@@ -519,9 +526,9 @@ private:
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void Node::FindInputNodeInternal(const Node* n, QList<T *>& list)
|
||||
void Node::FindInputNodeInternal(const Node* n, QVector<T *> &list)
|
||||
{
|
||||
QList<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
QVector<NodeInput*> inputs = n->GetInputsIncludingArrays();
|
||||
|
||||
foreach (NodeInput* input, inputs) {
|
||||
if (input->is_connected()) {
|
||||
@@ -538,9 +545,9 @@ void Node::FindInputNodeInternal(const Node* n, QList<T *>& list)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
QList<T *> Node::FindInputNodes() const
|
||||
QVector<T *> Node::FindInputNodes() const
|
||||
{
|
||||
QList<T *> list;
|
||||
QVector<T *> list;
|
||||
|
||||
FindInputNodeInternal<T>(this, list);
|
||||
|
||||
@@ -554,7 +561,7 @@ T* Node::ValueToPtr(const QVariant &ptr)
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Node::FindOutputNodeInternal(const Node* n, QList<T *>& list) {
|
||||
void Node::FindOutputNodeInternal(const Node* n, QVector<T *>& list) {
|
||||
foreach (NodeEdgePtr edge, n->output()->edges()) {
|
||||
Node* connected = edge->input()->parentNode();
|
||||
T* cast_test = dynamic_cast<T*>(connected);
|
||||
@@ -568,9 +575,9 @@ void Node::FindOutputNodeInternal(const Node* n, QList<T *>& list) {
|
||||
}
|
||||
|
||||
template<class T>
|
||||
QList<T *> Node::FindOutputNode()
|
||||
QVector<T *> Node::FindOutputNode()
|
||||
{
|
||||
QList<T *> list;
|
||||
QVector<T *> list;
|
||||
|
||||
FindOutputNodeInternal<T>(this, list);
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ QString TrackOutput::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.track");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> TrackOutput::Category() const
|
||||
QVector<Node::CategoryID> TrackOutput::Category() const
|
||||
{
|
||||
return {kCategoryTimeline};
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
const double& GetTrackHeight() const;
|
||||
@@ -217,11 +217,6 @@ public:
|
||||
return waveform_;
|
||||
}
|
||||
|
||||
QMutex* waveform_lock()
|
||||
{
|
||||
return &waveform_lock_;
|
||||
}
|
||||
|
||||
static const double kTrackHeightDefault;
|
||||
static const double kTrackHeightMinimum;
|
||||
static const double kTrackHeightInterval;
|
||||
@@ -297,7 +292,6 @@ private:
|
||||
bool locked_;
|
||||
|
||||
AudioVisualWaveform waveform_;
|
||||
QMutex waveform_lock_;
|
||||
|
||||
private slots:
|
||||
void BlockConnected(NodeEdgePtr edge);
|
||||
|
||||
@@ -82,7 +82,7 @@ QString ViewerOutput::id() const
|
||||
return QStringLiteral("org.olivevideoeditor.Olive.vieweroutput");
|
||||
}
|
||||
|
||||
QList<Node::CategoryID> ViewerOutput::Category() const
|
||||
QVector<Node::CategoryID> ViewerOutput::Category() const
|
||||
{
|
||||
return {kCategoryOutput};
|
||||
}
|
||||
@@ -102,7 +102,6 @@ void ViewerOutput::ShiftAudioCache(const rational &from, const rational &to)
|
||||
audio_playback_cache_.Shift(from, to);
|
||||
|
||||
foreach (TrackOutput* track, track_lists_.at(Timeline::kTrackTypeAudio)->GetTracks()) {
|
||||
QMutexLocker locker(track->waveform_lock());
|
||||
track->waveform().Shift(from, to);
|
||||
}
|
||||
}
|
||||
@@ -164,6 +163,8 @@ void ViewerOutput::set_video_params(const VideoParams &video)
|
||||
}
|
||||
|
||||
emit VideoParamsChanged();
|
||||
|
||||
video_frame_cache_.InvalidateAll();
|
||||
}
|
||||
|
||||
void ViewerOutput::set_audio_params(const AudioParams &audio)
|
||||
@@ -171,6 +172,9 @@ void ViewerOutput::set_audio_params(const AudioParams &audio)
|
||||
audio_params_ = audio;
|
||||
|
||||
emit AudioParamsChanged();
|
||||
|
||||
// This will automatically InvalidateAll
|
||||
audio_playback_cache_.SetParameters(audio_params());
|
||||
}
|
||||
|
||||
rational ViewerOutput::GetLength()
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
|
||||
virtual QString Name() const override;
|
||||
virtual QString id() const override;
|
||||
virtual QList<CategoryID> Category() const override;
|
||||
virtual QVector<CategoryID> Category() const override;
|
||||
virtual QString Description() const override;
|
||||
|
||||
void ShiftVideoCache(const rational& from, const rational& to);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user