codec system rework
Codecs are now much cleaner and thread safe by design.
This commit is contained in:
+168
-58
@@ -39,55 +39,160 @@
|
|||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
QMutex Decoder::currently_conforming_mutex_;
|
||||||
|
QWaitCondition Decoder::currently_conforming_wait_cond_;
|
||||||
|
QVector<Decoder::CurrentlyConforming> Decoder::currently_conforming_;
|
||||||
|
|
||||||
Decoder::Decoder() :
|
Decoder::Decoder() :
|
||||||
open_(false),
|
|
||||||
stream_(nullptr)
|
stream_(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Decoder::Decoder(Stream *fs) :
|
bool Decoder::Open(StreamPtr fs)
|
||||||
open_(false),
|
|
||||||
stream_(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*/)
|
if (stream_) {
|
||||||
{
|
CloseInternal();
|
||||||
return nullptr;
|
stream_ = nullptr;
|
||||||
}
|
} else {
|
||||||
|
qWarning() << "Tried to close a decoder that wasn't open";
|
||||||
bool Decoder::SupportsVideo()
|
}
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Decoder::SupportsAudio()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DECODER STATIC PUBLIC MEMBERS
|
* DECODER STATIC PUBLIC MEMBERS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
QVector<DecoderPtr> ReceiveListOfAllDecoders() {
|
QVector<DecoderPtr> ReceiveListOfAllDecoders()
|
||||||
|
{
|
||||||
QVector<DecoderPtr> decoders;
|
QVector<DecoderPtr> decoders;
|
||||||
|
|
||||||
// The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last,
|
// 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;
|
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
|
// Check for a valid filename
|
||||||
if (filename.isEmpty()) {
|
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())));
|
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)
|
void Decoder::SignalProcessingProgress(const int64_t &ts)
|
||||||
{
|
{
|
||||||
if (stream()->duration() != AV_NOPTS_VALUE && stream()->duration() != 0) {
|
if (stream()->duration() != AV_NOPTS_VALUE && stream()->duration() != 0) {
|
||||||
@@ -267,4 +341,40 @@ int64_t Decoder::GetImageSequenceIndex(const QString &filename)
|
|||||||
return number_only.toLongLong();
|
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
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
+111
-125
@@ -27,6 +27,7 @@ extern "C" {
|
|||||||
|
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QWaitCondition>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "codec/frame.h"
|
#include "codec/frame.h"
|
||||||
@@ -68,117 +69,46 @@ public:
|
|||||||
|
|
||||||
Decoder();
|
Decoder();
|
||||||
|
|
||||||
Decoder(Stream* fs);
|
/**
|
||||||
|
* @brief Unique decoder ID
|
||||||
DISABLE_COPY_MOVE(Decoder)
|
*/
|
||||||
|
|
||||||
virtual QString id() = 0;
|
virtual QString id() = 0;
|
||||||
|
|
||||||
StreamPtr stream() const;
|
virtual bool SupportsVideo(){return false;}
|
||||||
void set_stream(StreamPtr fs);
|
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
|
* This function is thread safe.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* Probe() differs from Open() since it focuses on a file as a whole rather than one particular stream. Probe()
|
* Returns TRUE if stream could be opened successfully. Also returns TRUE if the decoder is
|
||||||
* should be able to be run directly without calling Open() or Close() and should free its memory before returning.
|
* 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.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
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
|
* This function will always return a valid frame unless a fatal error occurs (in such case,
|
||||||
* should be done here.
|
* 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
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||||
*
|
|
||||||
* 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().
|
|
||||||
*/
|
*/
|
||||||
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
|
* This function will always return a sample buffer unless a fatal error occurs (in such case,
|
||||||
* data (i.e. no partial frames) at the timecode provided. The Decoder should perform any steps required to retrieve
|
* nullptr will return). The SampleBuffer should always have enough audio for the range provided.
|
||||||
* 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
|
* This function is thread safe and can only run while the decoder is open. \see Open()
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
virtual FramePtr RetrieveVideo(const rational& timecode, const int& divider);
|
SampleBufferPtr RetrieveAudio(const TimeRange& range, const AudioParams& params, const QAtomicInt *cancelled);
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Try to probe a Footage file by passing it through all available Decoders
|
* @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.
|
* 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
|
* @brief Create a Decoder instance using a Decoder ID
|
||||||
@@ -210,42 +160,45 @@ public:
|
|||||||
*/
|
*/
|
||||||
static DecoderPtr CreateFromID(const QString& id);
|
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 QString TransformImageSequenceFileName(const QString& filename, const int64_t& number);
|
||||||
|
|
||||||
static int GetImageSequenceDigitCount(const QString& filename);
|
static int GetImageSequenceDigitCount(const QString& filename);
|
||||||
|
|
||||||
static int64_t GetImageSequenceIndex(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:
|
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);
|
void SignalProcessingProgress(const int64_t& ts);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,11 +208,44 @@ protected:
|
|||||||
|
|
||||||
QString GetIndexFilename();
|
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:
|
private:
|
||||||
|
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range);
|
||||||
|
|
||||||
StreamPtr stream_;
|
StreamPtr stream_;
|
||||||
|
|
||||||
|
QMutex mutex_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ OLIVE_NAMESPACE_ENTER
|
|||||||
AVPixelFormat FFmpegCommon::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt)
|
AVPixelFormat FFmpegCommon::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt)
|
||||||
{
|
{
|
||||||
AVPixelFormat possible_pix_fmts[] = {
|
AVPixelFormat possible_pix_fmts[] = {
|
||||||
AV_PIX_FMT_RGB24,
|
|
||||||
AV_PIX_FMT_RGBA,
|
AV_PIX_FMT_RGBA,
|
||||||
AV_PIX_FMT_RGB48,
|
|
||||||
AV_PIX_FMT_RGBA64,
|
AV_PIX_FMT_RGBA64,
|
||||||
AV_PIX_FMT_NONE
|
AV_PIX_FMT_NONE
|
||||||
};
|
};
|
||||||
@@ -97,14 +95,8 @@ AVPixelFormat FFmpegCommon::GetFFmpegPixelFormat(const PixelFormat::Format &pix_
|
|||||||
return AV_PIX_FMT_RGBA;
|
return AV_PIX_FMT_RGBA;
|
||||||
case PixelFormat::PIX_FMT_RGBA16U:
|
case PixelFormat::PIX_FMT_RGBA16U:
|
||||||
return AV_PIX_FMT_RGBA64;
|
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_RGBA16F:
|
||||||
case PixelFormat::PIX_FMT_RGBA32F:
|
case PixelFormat::PIX_FMT_RGBA32F:
|
||||||
case PixelFormat::PIX_FMT_RGB16F:
|
|
||||||
case PixelFormat::PIX_FMT_RGB32F:
|
|
||||||
case PixelFormat::PIX_FMT_INVALID:
|
case PixelFormat::PIX_FMT_INVALID:
|
||||||
case PixelFormat::PIX_FMT_COUNT:
|
case PixelFormat::PIX_FMT_COUNT:
|
||||||
break;
|
break;
|
||||||
@@ -116,14 +108,8 @@ AVPixelFormat FFmpegCommon::GetFFmpegPixelFormat(const PixelFormat::Format &pix_
|
|||||||
PixelFormat::Format FFmpegCommon::GetCompatiblePixelFormat(const PixelFormat::Format &pix_fmt)
|
PixelFormat::Format FFmpegCommon::GetCompatiblePixelFormat(const PixelFormat::Format &pix_fmt)
|
||||||
{
|
{
|
||||||
switch (pix_fmt) {
|
switch (pix_fmt) {
|
||||||
case PixelFormat::PIX_FMT_RGB8:
|
|
||||||
return PixelFormat::PIX_FMT_RGB8;
|
|
||||||
case PixelFormat::PIX_FMT_RGBA8:
|
case PixelFormat::PIX_FMT_RGBA8:
|
||||||
return 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_RGBA16U:
|
||||||
case PixelFormat::PIX_FMT_RGBA16F:
|
case PixelFormat::PIX_FMT_RGBA16F:
|
||||||
case PixelFormat::PIX_FMT_RGBA32F:
|
case PixelFormat::PIX_FMT_RGBA32F:
|
||||||
|
|||||||
+305
-764
File diff suppressed because it is too large
Load Diff
@@ -41,99 +41,6 @@ extern "C" {
|
|||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
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
|
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||||
*/
|
*/
|
||||||
@@ -147,40 +54,62 @@ public:
|
|||||||
// Destructor
|
// Destructor
|
||||||
virtual ~FFmpegDecoder() override;
|
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 QString id() override;
|
||||||
|
|
||||||
virtual bool SupportsVideo() override;
|
virtual bool SupportsVideo() override{return true;}
|
||||||
virtual bool SupportsAudio() override;
|
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 {
|
protected:
|
||||||
int width;
|
virtual bool OpenInternal() override;
|
||||||
int height;
|
virtual FramePtr RetrieveVideoInternal(const rational &timecode, const int& divider) override;
|
||||||
AVPixelFormat format;
|
virtual bool ConformAudioInternal(const QString& filename, const AudioParams ¶ms, const QAtomicInt* cancelled) override;
|
||||||
|
virtual void CloseInternal() override;
|
||||||
bool operator==(const FFmpegFramePoolKey& k) const
|
|
||||||
{
|
|
||||||
return width == k.width && height == k.height && format == k.format;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
class Instance
|
||||||
* @brief Handle an error
|
{
|
||||||
*
|
public:
|
||||||
* Immediately closes the Decoder (freeing memory resources) and sends the string provided to the warning stream.
|
Instance();
|
||||||
* As this function closes the Decoder, no further Decoder functions should be performed after this is called
|
|
||||||
* (unless the Decoder is opened again first).
|
~Instance()
|
||||||
*/
|
{
|
||||||
void Error(const QString& s);
|
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
|
* @brief Handle an FFmpeg error code
|
||||||
@@ -190,9 +119,7 @@ private:
|
|||||||
*
|
*
|
||||||
* @param error_code
|
* @param error_code
|
||||||
*/
|
*/
|
||||||
void FFmpegError(int error_code);
|
static QString FFmpegError(int error_code);
|
||||||
|
|
||||||
void ClearResources();
|
|
||||||
|
|
||||||
void InitScaler(int divider);
|
void InitScaler(int divider);
|
||||||
void FreeScaler();
|
void FreeScaler();
|
||||||
@@ -203,29 +130,37 @@ private:
|
|||||||
|
|
||||||
static uint64_t ValidateChannelLayout(AVStream *stream);
|
static uint64_t ValidateChannelLayout(AVStream *stream);
|
||||||
|
|
||||||
static bool StreamUsesMultipleInstances(StreamPtr stream);
|
void FFmpegFrameToNativeBuffer(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_;
|
SwsContext* scale_ctx_;
|
||||||
int scale_divider_;
|
int scale_divider_;
|
||||||
AVPixelFormat src_pix_fmt_;
|
|
||||||
AVPixelFormat ideal_pix_fmt_;
|
AVPixelFormat ideal_pix_fmt_;
|
||||||
PixelFormat::Format native_pix_fmt_;
|
PixelFormat::Format native_pix_fmt_;
|
||||||
|
|
||||||
struct FFmpegFramePoolValue {
|
FFmpegFramePool pool_;
|
||||||
FFmpegFramePool* pool = nullptr;
|
|
||||||
int handles = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
static QHash< Stream*, QList<FFmpegDecoderInstance*> > instance_map_;
|
int64_t second_ts_;
|
||||||
static QHash< FFmpegFramePoolKey, FFmpegFramePoolValue > frame_pool_map_;
|
|
||||||
static QMutex instance_map_lock_;
|
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
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|
||||||
#endif // FFMPEGDECODER_H
|
#endif // FFMPEGDECODER_H
|
||||||
|
|||||||
@@ -20,9 +20,7 @@
|
|||||||
|
|
||||||
#include "ffmpegframepool.h"
|
#include "ffmpegframepool.h"
|
||||||
|
|
||||||
extern "C" {
|
#include "codec/frame.h"
|
||||||
#include <libavutil/imgutils.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
@@ -30,11 +28,11 @@ FFmpegFramePool::FFmpegFramePool(int element_count) :
|
|||||||
MemoryPool(element_count),
|
MemoryPool(element_count),
|
||||||
width_(0),
|
width_(0),
|
||||||
height_(0),
|
height_(0),
|
||||||
format_(AV_PIX_FMT_NONE)
|
format_(PixelFormat::PIX_FMT_INVALID)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFmpegFramePool::SetParameters(int width, int height, AVPixelFormat format)
|
void FFmpegFramePool::SetParameters(int width, int height, PixelFormat::Format format)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
@@ -45,17 +43,7 @@ void FFmpegFramePool::SetParameters(int width, int height, AVPixelFormat format)
|
|||||||
|
|
||||||
size_t FFmpegFramePool::GetElementSize()
|
size_t FFmpegFramePool::GetElementSize()
|
||||||
{
|
{
|
||||||
int buf_sz = av_image_get_buffer_size(static_cast<AVPixelFormat>(format_),
|
return Frame::generate_linesize_bytes(width_, format_) * height_;
|
||||||
width_,
|
|
||||||
height_,
|
|
||||||
1);
|
|
||||||
|
|
||||||
if (buf_sz < 0) {
|
|
||||||
qDebug() << "Failed to find buffer size:" << buf_sz;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf_sz;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class FFmpegFramePool : public MemoryPool<uint8_t>
|
|||||||
public:
|
public:
|
||||||
FFmpegFramePool(int element_count);
|
FFmpegFramePool(int element_count);
|
||||||
|
|
||||||
void SetParameters(int width, int height, AVPixelFormat format);
|
void SetParameters(int width, int height, PixelFormat::Format format);
|
||||||
|
|
||||||
const int& width() const
|
const int& width() const
|
||||||
{
|
{
|
||||||
@@ -52,7 +52,7 @@ private:
|
|||||||
|
|
||||||
int height_;
|
int height_;
|
||||||
|
|
||||||
AVPixelFormat format_;
|
PixelFormat::Format format_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+7
-65
@@ -45,33 +45,14 @@ void Frame::set_video_params(const VideoParams ¶ms)
|
|||||||
{
|
{
|
||||||
params_ = params;
|
params_ = params;
|
||||||
|
|
||||||
// Align linesize to 32
|
linesize_ = generate_linesize_bytes(params_.width(), params_.format());
|
||||||
linesize_ = qCeil(static_cast<double>(width()) / 32.0) * 32;
|
linesize_pixels_ = linesize_ / PixelFormat::BytesPerPixel(params_.format());
|
||||||
}
|
}
|
||||||
|
|
||||||
int Frame::linesize_pixels() const
|
int Frame::generate_linesize_bytes(int width, PixelFormat::Format format)
|
||||||
{
|
{
|
||||||
return linesize_;
|
// Align to 32 bytes (not sure if this is necessary?)
|
||||||
}
|
return ((PixelFormat::BytesPerPixel(format) * width) + 31) & ~31;
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Color Frame::get_pixel(int x, int y) const
|
Color Frame::get_pixel(int x, int y) const
|
||||||
@@ -80,9 +61,7 @@ Color Frame::get_pixel(int x, int y) const
|
|||||||
return Color();
|
return Color();
|
||||||
}
|
}
|
||||||
|
|
||||||
int pixel_index = y * linesize_pixels() + x;
|
int byte_offset = y * linesize_bytes() + x * PixelFormat::BytesPerPixel(video_params().format());
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
@@ -98,33 +77,11 @@ void Frame::set_pixel(int x, int y, const Color &c)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int pixel_index = y * linesize_pixels() + x;
|
int byte_offset = y * linesize_bytes() + x * PixelFormat::BytesPerPixel(video_params().format());
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
void Frame::allocate()
|
||||||
{
|
{
|
||||||
// Assume this frame is intended to be a video frame
|
// Assume this frame is intended to be a video frame
|
||||||
@@ -136,19 +93,4 @@ void Frame::allocate()
|
|||||||
data_.resize(PixelFormat::GetBufferSize(params_.format(), linesize_, params_.height()));
|
data_.resize(PixelFormat::GetBufferSize(params_.format(), linesize_, params_.height()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Frame::is_allocated() const
|
|
||||||
{
|
|
||||||
return !data_.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Frame::destroy()
|
|
||||||
{
|
|
||||||
data_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
int Frame::allocated_size() const
|
|
||||||
{
|
|
||||||
return data_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
+57
-12
@@ -47,11 +47,32 @@ public:
|
|||||||
const VideoParams& video_params() const;
|
const VideoParams& video_params() const;
|
||||||
void set_video_params(const VideoParams& params);
|
void set_video_params(const VideoParams& params);
|
||||||
|
|
||||||
int linesize_pixels() const;
|
static int generate_linesize_bytes(int width, PixelFormat::Format format);
|
||||||
int linesize_bytes() const;
|
|
||||||
const int& width() const;
|
int linesize_pixels() const
|
||||||
const int& height() const;
|
{
|
||||||
const PixelFormat::Format& format() const;
|
return linesize_pixels_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int linesize_bytes() const
|
||||||
|
{
|
||||||
|
return linesize_;
|
||||||
|
}
|
||||||
|
|
||||||
|
int width() const
|
||||||
|
{
|
||||||
|
return params_.effective_width();
|
||||||
|
}
|
||||||
|
|
||||||
|
int height() const
|
||||||
|
{
|
||||||
|
return params_.effective_height();
|
||||||
|
}
|
||||||
|
|
||||||
|
PixelFormat::Format format() const
|
||||||
|
{
|
||||||
|
return params_.format();
|
||||||
|
}
|
||||||
|
|
||||||
Color get_pixel(int x, int y) const;
|
Color get_pixel(int x, int y) const;
|
||||||
bool contains_pixel(int x, int y) const;
|
bool contains_pixel(int x, int y) const;
|
||||||
@@ -62,18 +83,31 @@ public:
|
|||||||
*
|
*
|
||||||
* This timestamp is always a rational that will equate to the time in seconds.
|
* This timestamp is always a rational that will equate to the time in seconds.
|
||||||
*/
|
*/
|
||||||
const rational& timestamp() const;
|
const rational& timestamp() const
|
||||||
void set_timestamp(const rational& timestamp);
|
{
|
||||||
|
return timestamp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_timestamp(const rational& timestamp)
|
||||||
|
{
|
||||||
|
timestamp_ = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the data buffer of this frame
|
* @brief Get the data buffer of this frame
|
||||||
*/
|
*/
|
||||||
char* data();
|
char* data()
|
||||||
|
{
|
||||||
|
return data_.data();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the const data buffer of this frame
|
* @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
|
* @brief Allocate memory buffer to store data based on parameters
|
||||||
@@ -87,19 +121,28 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Return whether the frame is allocated or not
|
* @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()
|
* @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
|
* @brief Returns the size of the array returned in data() in bytes
|
||||||
*
|
*
|
||||||
* Returns 0 if nothing is allocated.
|
* Returns 0 if nothing is allocated.
|
||||||
*/
|
*/
|
||||||
int allocated_size() const;
|
int allocated_size() const
|
||||||
|
{
|
||||||
|
return data_.size();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VideoParams params_;
|
VideoParams params_;
|
||||||
@@ -110,6 +153,8 @@ private:
|
|||||||
|
|
||||||
int linesize_;
|
int linesize_;
|
||||||
|
|
||||||
|
int linesize_pixels_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
OLIVE_NAMESPACE_EXIT
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
set(OLIVE_SOURCES
|
set(OLIVE_SOURCES
|
||||||
${OLIVE_SOURCES}
|
${OLIVE_SOURCES}
|
||||||
codec/oiio/oiiodecoder.h
|
codec/oiio/oiiocommon.cpp
|
||||||
|
codec/oiio/oiiocommon.h
|
||||||
codec/oiio/oiiodecoder.cpp
|
codec/oiio/oiiodecoder.cpp
|
||||||
|
codec/oiio/oiiodecoder.h
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 "oiiocommon.h"
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
void OIIOCommon::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 OIIOCommon::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 OIIOCommon::GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec)
|
||||||
|
{
|
||||||
|
if (spec.format == OIIO::TypeDesc::UINT8) {
|
||||||
|
return PixelFormat::PIX_FMT_RGBA8;
|
||||||
|
} else if (spec.format == OIIO::TypeDesc::UINT16) {
|
||||||
|
return PixelFormat::PIX_FMT_RGBA16U;
|
||||||
|
} else if (spec.format == OIIO::TypeDesc::HALF) {
|
||||||
|
return PixelFormat::PIX_FMT_RGBA16F;
|
||||||
|
} else if (spec.format == OIIO::TypeDesc::FLOAT) {
|
||||||
|
return PixelFormat::PIX_FMT_RGBA32F;
|
||||||
|
} else {
|
||||||
|
return PixelFormat::PIX_FMT_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rational OIIOCommon::GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec &spec)
|
||||||
|
{
|
||||||
|
return rational::fromDouble(spec.get_float_attribute("PixelAspectRatio", 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_EXIT
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/***
|
||||||
|
|
||||||
|
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 OIIOCOMMON_H
|
||||||
|
#define OIIOCOMMON_H
|
||||||
|
|
||||||
|
#include <OpenImageIO/imageio.h>
|
||||||
|
#include <OpenImageIO/imagebuf.h>
|
||||||
|
|
||||||
|
#include "codec/frame.h"
|
||||||
|
#include "render/pixelformat.h"
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
|
class OIIOCommon
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
OLIVE_NAMESPACE_EXIT
|
||||||
|
|
||||||
|
#endif // OIIOCOMMON_H
|
||||||
+44
-126
@@ -29,6 +29,7 @@
|
|||||||
#include "common/define.h"
|
#include "common/define.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
#include "oiiocommon.h"
|
||||||
|
|
||||||
OLIVE_NAMESPACE_ENTER
|
OLIVE_NAMESPACE_ENTER
|
||||||
|
|
||||||
@@ -40,6 +41,11 @@ OIIODecoder::OIIODecoder() :
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OIIODecoder::~OIIODecoder()
|
||||||
|
{
|
||||||
|
CloseInternal();
|
||||||
|
}
|
||||||
|
|
||||||
QString OIIODecoder::id()
|
QString OIIODecoder::id()
|
||||||
{
|
{
|
||||||
return QStringLiteral("oiio");
|
return QStringLiteral("oiio");
|
||||||
@@ -47,6 +53,10 @@ QString OIIODecoder::id()
|
|||||||
|
|
||||||
FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancelled) const
|
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)) {
|
if (!FileTypeIsSupported(filename)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -59,8 +69,9 @@ FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancell
|
|||||||
return nullptr;
|
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 (!strcmp(in->format_name(), "FFmpeg movie")) {
|
||||||
// If this is FFmpeg via OIIO, fall-through to our native FFmpeg decoder
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +81,8 @@ FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancell
|
|||||||
|
|
||||||
image_stream->set_width(in->spec().width);
|
image_stream->set_width(in->spec().width);
|
||||||
image_stream->set_height(in->spec().height);
|
image_stream->set_height(in->spec().height);
|
||||||
image_stream->set_format(GetFormatFromOIIOBasetype(in->spec()));
|
image_stream->set_format(OIIOCommon::GetFormatFromOIIOBasetype(in->spec()));
|
||||||
image_stream->set_pixel_aspect_ratio(GetPixelAspectRatioFromOIIO(in->spec()));
|
image_stream->set_pixel_aspect_ratio(OIIOCommon::GetPixelAspectRatioFromOIIO(in->spec()));
|
||||||
image_stream->set_video_type(VideoStream::kVideoTypeStill);
|
image_stream->set_video_type(VideoStream::kVideoTypeStill);
|
||||||
|
|
||||||
// Images will always have just one stream
|
// Images will always have just one stream
|
||||||
@@ -95,45 +106,40 @@ FootagePtr OIIODecoder::Probe(const QString& filename, const QAtomicInt* cancell
|
|||||||
return footage;
|
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) {
|
if (video_stream->video_type() == VideoStream::kVideoTypeStill) {
|
||||||
// Guard against non-video types
|
last_sequence_index_ = 0;
|
||||||
return false;
|
} else {
|
||||||
|
last_sequence_index_ = GetImageSequenceIndex(stream()->footage()->filename());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||||
|
|
||||||
if (video_stream->video_type() == VideoStream::kVideoTypeImageSequence) {
|
int64_t sequence_index;
|
||||||
int64_t ts = video_stream->get_time_in_timebase_units(timecode);
|
|
||||||
|
|
||||||
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,14 +149,14 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider
|
|||||||
frame->set_video_params(VideoParams(buffer_->spec().width,
|
frame->set_video_params(VideoParams(buffer_->spec().width,
|
||||||
buffer_->spec().height,
|
buffer_->spec().height,
|
||||||
pix_fmt_,
|
pix_fmt_,
|
||||||
GetPixelAspectRatioFromOIIO(buffer_->spec()),
|
OIIOCommon::GetPixelAspectRatioFromOIIO(buffer_->spec()),
|
||||||
VideoParams::kInterlaceNone, // FIXME: Does OIIO deinterlace for us?
|
VideoParams::kInterlaceNone, // FIXME: Does OIIO deinterlace for us?
|
||||||
divider));
|
divider));
|
||||||
frame->allocate();
|
frame->allocate();
|
||||||
|
|
||||||
if (divider == 1) {
|
if (divider == 1) {
|
||||||
|
|
||||||
BufferToFrame(buffer_, frame);
|
OIIOCommon::BufferToFrame(buffer_, frame);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -161,106 +167,18 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider
|
|||||||
qWarning() << "OIIO resize failed";
|
qWarning() << "OIIO resize failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferToFrame(&dst, frame);
|
OIIOCommon::BufferToFrame(&dst, frame);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video_stream->video_type() == VideoStream::kVideoTypeImageSequence) {
|
|
||||||
CloseImageHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OIIODecoder::Close()
|
void OIIODecoder::CloseInternal()
|
||||||
{
|
{
|
||||||
CloseImageHandle();
|
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)
|
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
|
||||||
{
|
{
|
||||||
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
|
||||||
@@ -299,7 +217,7 @@ bool OIIODecoder::OpenImageHandler(const QString &fn)
|
|||||||
|
|
||||||
is_rgba_ = (spec.nchannels == kRGBAChannels);
|
is_rgba_ = (spec.nchannels == kRGBAChannels);
|
||||||
|
|
||||||
pix_fmt_ = GetFormatFromOIIOBasetype(spec);
|
pix_fmt_ = OIIOCommon::GetFormatFromOIIOBasetype(spec);
|
||||||
|
|
||||||
if (pix_fmt_ == PixelFormat::PIX_FMT_INVALID) {
|
if (pix_fmt_ == PixelFormat::PIX_FMT_INVALID) {
|
||||||
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
|
qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format";
|
||||||
|
|||||||
@@ -35,23 +35,18 @@ class OIIODecoder : public Decoder
|
|||||||
public:
|
public:
|
||||||
OIIODecoder();
|
OIIODecoder();
|
||||||
|
|
||||||
|
virtual ~OIIODecoder() override;
|
||||||
|
|
||||||
virtual QString id() override;
|
virtual QString id() override;
|
||||||
|
|
||||||
|
virtual bool SupportsVideo() override{return true;}
|
||||||
|
|
||||||
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const override;
|
virtual FootagePtr Probe(const QString& filename, const QAtomicInt* cancelled) const override;
|
||||||
|
|
||||||
virtual bool Open() override;
|
protected:
|
||||||
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
|
virtual bool OpenInternal() override;
|
||||||
virtual void Close() override;
|
virtual FramePtr RetrieveVideoInternal(const rational &timecode, const int& divider) override;
|
||||||
|
virtual void CloseInternal() 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);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if OIIO_VERSION < 10903
|
#if OIIO_VERSION < 10903
|
||||||
@@ -66,6 +61,8 @@ private:
|
|||||||
|
|
||||||
void CloseImageHandle();
|
void CloseImageHandle();
|
||||||
|
|
||||||
|
int64_t last_sequence_index_;
|
||||||
|
|
||||||
PixelFormat::Format pix_fmt_;
|
PixelFormat::Format pix_fmt_;
|
||||||
|
|
||||||
bool is_rgba_;
|
bool is_rgba_;
|
||||||
|
|||||||
@@ -66,38 +66,6 @@ void AudioStream::set_sample_rate(const int &sample_rate)
|
|||||||
sample_rate_ = sample_rate;
|
sample_rate_ = sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AudioStream::try_start_conforming(const AudioParams ¶ms)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(proxy_access_lock());
|
|
||||||
|
|
||||||
if (!currently_conforming_.contains(params)
|
|
||||||
&& !conformed_.contains(params)) {
|
|
||||||
currently_conforming_.append(params);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioStream::has_conformed_version(const AudioParams ¶ms)
|
|
||||||
{
|
|
||||||
QMutexLocker locker(proxy_access_lock());
|
|
||||||
|
|
||||||
return conformed_.contains(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioStream::append_conformed_version(const AudioParams ¶ms)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
QMutexLocker locker(proxy_access_lock());
|
|
||||||
|
|
||||||
currently_conforming_.removeOne(params);
|
|
||||||
conformed_.append(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit ConformAppended(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon AudioStream::icon() const
|
QIcon AudioStream::icon() const
|
||||||
{
|
{
|
||||||
return icon::Audio;
|
return icon::Audio;
|
||||||
|
|||||||
@@ -49,10 +49,6 @@ public:
|
|||||||
const int& sample_rate() const;
|
const int& sample_rate() const;
|
||||||
void set_sample_rate(const int& sample_rate);
|
void set_sample_rate(const int& sample_rate);
|
||||||
|
|
||||||
bool try_start_conforming(const AudioParams& params);
|
|
||||||
bool has_conformed_version(const AudioParams& params);
|
|
||||||
void append_conformed_version(const AudioParams& params);
|
|
||||||
|
|
||||||
virtual QIcon icon() const override;
|
virtual QIcon icon() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -60,18 +56,11 @@ protected:
|
|||||||
|
|
||||||
virtual void SaveCustomParameters(QXmlStreamWriter* writer) const override;
|
virtual void SaveCustomParameters(QXmlStreamWriter* writer) const override;
|
||||||
|
|
||||||
signals:
|
|
||||||
void ConformAppended(OLIVE_NAMESPACE::AudioParams params);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int channels_;
|
int channels_;
|
||||||
uint64_t layout_;
|
uint64_t layout_;
|
||||||
int sample_rate_;
|
int sample_rate_;
|
||||||
|
|
||||||
QList<AudioParams> conformed_;
|
|
||||||
|
|
||||||
QList<AudioParams> currently_conforming_;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using AudioStreamPtr = std::shared_ptr<AudioStream>;
|
using AudioStreamPtr = std::shared_ptr<AudioStream>;
|
||||||
|
|||||||
@@ -308,7 +308,7 @@ bool Footage::CompareFootageToItsFilename(FootagePtr footage)
|
|||||||
} else {
|
} else {
|
||||||
// Footage may have changed and we'll have to re-probe it. It also may not have, in which
|
// Footage may have changed and we'll have to re-probe it. It also may not have, in which
|
||||||
// case nothing needs to change.
|
// case nothing needs to change.
|
||||||
ItemPtr item = Decoder::ProbeMedia(footage->project(), footage->filename(), nullptr);
|
ItemPtr item = Decoder::Probe(footage->project(), footage->filename(), nullptr);
|
||||||
|
|
||||||
if (item && item->type() == footage->type()) {
|
if (item && item->type() == footage->type()) {
|
||||||
// Item is the same type, that's a good sign. Let's look for any differences.
|
// Item is the same type, that's a good sign. Let's look for any differences.
|
||||||
|
|||||||
Reference in New Issue
Block a user