Merge branch 'decoder-ffpool'
This commit is contained in:
@@ -32,7 +32,6 @@ extern "C" {
|
||||
#include "codec/frame.h"
|
||||
#include "codec/samplebuffer.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "common/constructors.h"
|
||||
#include "common/rational.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <QString>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "common/constructors.h"
|
||||
#include "common/timerange.h"
|
||||
#include "render/audioparams.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
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
|
||||
codec/ffmpeg/ffmpegencoder.cpp
|
||||
codec/ffmpeg/ffmpegframecache.h
|
||||
codec/ffmpeg/ffmpegframecache.cpp
|
||||
codec/ffmpeg/ffmpegframepool.h
|
||||
codec/ffmpeg/ffmpegframepool.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
@@ -18,30 +18,43 @@
|
||||
|
||||
***/
|
||||
|
||||
#ifndef CONSTRUCTORS_H
|
||||
#define CONSTRUCTORS_H
|
||||
#ifndef AVFRAMEPTR_H
|
||||
#define AVFRAMEPTR_H
|
||||
|
||||
extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <memory>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
/**
|
||||
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
|
||||
* use our own functions for portability.
|
||||
*/
|
||||
class AVFrameWrapper {
|
||||
public:
|
||||
AVFrameWrapper() {
|
||||
frame_ = av_frame_alloc();
|
||||
}
|
||||
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete;\
|
||||
Class &operator=(const Class &) = delete;
|
||||
virtual ~AVFrameWrapper() {
|
||||
av_frame_free(&frame_);
|
||||
}
|
||||
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
DISABLE_COPY_MOVE(AVFrameWrapper)
|
||||
|
||||
#define DISABLE_COPY_MOVE(Class) \
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
inline AVFrame* frame() const {
|
||||
return frame_;
|
||||
}
|
||||
|
||||
private:
|
||||
AVFrame* frame_;
|
||||
|
||||
};
|
||||
|
||||
using AVFramePtr = std::shared_ptr<AVFrameWrapper>;
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // CONSTRUCTORS_H
|
||||
#endif // AVFRAMEPTR_H
|
||||
+605
-348
File diff suppressed because it is too large
Load Diff
@@ -30,15 +30,84 @@ extern "C" {
|
||||
#include <QAtomicInt>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include "audio/sampleformat.h"
|
||||
#include "avframeptr.h"
|
||||
#include "codec/decoder.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "ffmpegframecache.h"
|
||||
#include "ffmpegframepool.h"
|
||||
#include "project/item/footage/videostream.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class FFmpegDecoderInstance {
|
||||
public:
|
||||
FFmpegDecoderInstance(const char* filename, int stream_index);
|
||||
virtual ~FFmpegDecoderInstance();
|
||||
|
||||
DISABLE_COPY_MOVE(FFmpegDecoderInstance)
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
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);
|
||||
|
||||
rational sample_aspect_ratio() const;
|
||||
AVStream* stream() const;
|
||||
|
||||
void ClearFrameCache();
|
||||
|
||||
FFmpegFramePool::ElementPtr RetrieveFrame(const int64_t &target_ts, 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() const;
|
||||
void SetWorking(bool working);
|
||||
|
||||
private:
|
||||
void ClearResources();
|
||||
|
||||
void Seek(int64_t timestamp);
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
AVDictionary* opts_;
|
||||
|
||||
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_;
|
||||
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder
|
||||
*/
|
||||
@@ -87,50 +156,34 @@ private:
|
||||
*/
|
||||
void FFmpegError(int error_code);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
virtual QString GetIndexFilename() override;
|
||||
|
||||
void UnconditionalAudioIndex(const QAtomicInt* cancelled);
|
||||
|
||||
void Seek(int64_t timestamp);
|
||||
|
||||
void CacheFrameToDisk(AVFrame* f);
|
||||
|
||||
void ClearFrameCache();
|
||||
|
||||
void ClearResources();
|
||||
|
||||
void SetupScaler(const int& divider);
|
||||
void InitScaler(int divider);
|
||||
void FreeScaler();
|
||||
|
||||
AVFormatContext* fmt_ctx_;
|
||||
AVCodecContext* codec_ctx_;
|
||||
AVStream* avstream_;
|
||||
|
||||
AVPixelFormat ideal_pix_fmt_;
|
||||
PixelFormat::Format native_pix_fmt_;
|
||||
|
||||
SwsContext* scale_ctx_;
|
||||
int scale_divider_;
|
||||
AVPixelFormat src_pix_fmt_;
|
||||
AVPixelFormat ideal_pix_fmt_;
|
||||
PixelFormat::Format native_pix_fmt_;
|
||||
|
||||
FFmpegFrameCache::Client cached_frames_;
|
||||
bool cache_at_zero_;
|
||||
bool cache_at_eof_;
|
||||
|
||||
int64_t second_ts_;
|
||||
|
||||
AVDictionary* opts_;
|
||||
rational time_base_;
|
||||
rational aspect_ratio_;
|
||||
int64_t start_time_;
|
||||
|
||||
QTimer clear_timer_;
|
||||
|
||||
FFmpegDecoderInstance* our_instance_;
|
||||
|
||||
static QHash< Stream*, QList<FFmpegDecoderInstance*> > instances_;
|
||||
static QMutex instance_lock_;
|
||||
|
||||
static const int kMaxFrameLife;
|
||||
|
||||
private slots:
|
||||
void ClearTimerEvent();
|
||||
|
||||
|
||||
@@ -1,123 +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 "ffmpegframecache.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
QMutex FFmpegFrameCache::pool_lock_;
|
||||
QList<Frame*> FFmpegFrameCache::frame_pool_;
|
||||
|
||||
Frame *FFmpegFrameCache::Client::append(const VideoRenderingParams& params)
|
||||
{
|
||||
Frame* f = FFmpegFrameCache::Get(params);
|
||||
|
||||
frames_.append({f, QDateTime::currentMSecsSinceEpoch()});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::Client::clear()
|
||||
{
|
||||
foreach (const CachedFrame& cf, frames_) {
|
||||
FFmpegFrameCache::Release(cf.frame);
|
||||
}
|
||||
frames_.clear();
|
||||
}
|
||||
|
||||
bool FFmpegFrameCache::Client::isEmpty() const
|
||||
{
|
||||
return frames_.isEmpty();
|
||||
}
|
||||
|
||||
Frame *FFmpegFrameCache::Client::first() const
|
||||
{
|
||||
return frames_.first().frame;
|
||||
}
|
||||
|
||||
Frame *FFmpegFrameCache::Client::at(int i) const
|
||||
{
|
||||
return frames_.at(i).frame;
|
||||
}
|
||||
|
||||
Frame *FFmpegFrameCache::Client::last() const
|
||||
{
|
||||
return frames_.last().frame;
|
||||
}
|
||||
|
||||
int FFmpegFrameCache::Client::size() const
|
||||
{
|
||||
return frames_.size();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::Client::accessedFirst()
|
||||
{
|
||||
frames_.first().accessed = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::Client::accessedLast()
|
||||
{
|
||||
frames_.last().accessed = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::Client::accessed(int i)
|
||||
{
|
||||
frames_[i].accessed = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::Client::remove_old_frames(qint64 older_than)
|
||||
{
|
||||
while (!frames_.isEmpty() && frames_.first().accessed < older_than) {
|
||||
FFmpegFrameCache::Release(frames_.takeFirst().frame);
|
||||
}
|
||||
}
|
||||
|
||||
Frame* FFmpegFrameCache::Get(const VideoRenderingParams ¶ms)
|
||||
{
|
||||
QMutexLocker locker(&pool_lock_);
|
||||
|
||||
// See if we have a frame matching this description in the pool
|
||||
for (int i=0;i<frame_pool_.size();i++) {
|
||||
if (frame_pool_.at(i)->width() == params.width()
|
||||
&& frame_pool_.at(i)->height() == params.height()
|
||||
&& frame_pool_.at(i)->format() == params.format()) {
|
||||
return frame_pool_.takeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we'll need to create one
|
||||
Frame* f = new Frame();
|
||||
f->set_video_params(params);
|
||||
f->allocate();
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void FFmpegFrameCache::Release(Frame *f)
|
||||
{
|
||||
QMutexLocker locker(&pool_lock_);
|
||||
|
||||
frame_pool_.append(f);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -1,80 +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/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef FFMPEGFRAMECACHE_H
|
||||
#define FFMPEGFRAMECACHE_H
|
||||
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class FFmpegFrameCache
|
||||
{
|
||||
public:
|
||||
FFmpegFrameCache() = default;
|
||||
|
||||
static Frame* Get(const VideoRenderingParams& params);
|
||||
|
||||
static void Release(Frame* f);
|
||||
|
||||
class Client
|
||||
{
|
||||
public:
|
||||
Client() = default;
|
||||
|
||||
Frame* append(const VideoRenderingParams ¶ms);
|
||||
void clear();
|
||||
|
||||
bool isEmpty() const;
|
||||
Frame* first() const;
|
||||
Frame* at(int i) const;
|
||||
Frame* last() const;
|
||||
int size() const;
|
||||
|
||||
void accessedFirst();
|
||||
void accessedLast();
|
||||
void accessed(int i);
|
||||
|
||||
void remove_old_frames(qint64 older_than);
|
||||
|
||||
private:
|
||||
struct CachedFrame {
|
||||
Frame* frame;
|
||||
qint64 accessed;
|
||||
};
|
||||
|
||||
QList<CachedFrame> frames_;
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
static QMutex pool_lock_;
|
||||
|
||||
static QList<Frame*> frame_pool_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // FFMPEGFRAMECACHE_H
|
||||
@@ -0,0 +1,95 @@
|
||||
/***
|
||||
|
||||
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 "ffmpegframepool.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/imgutils.h>
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
FFmpegFramePool::FFmpegFramePool() :
|
||||
width_(0),
|
||||
height_(0),
|
||||
format_(AV_PIX_FMT_NONE)
|
||||
{
|
||||
}
|
||||
|
||||
FFmpegFramePool::ElementPtr FFmpegFramePool::Get(AVFrame *copy)
|
||||
{
|
||||
ElementPtr ele = MemoryPool::Get();
|
||||
|
||||
if (ele) {
|
||||
av_image_copy_to_buffer(ele->data(),
|
||||
GetElementSize(),
|
||||
copy->data,
|
||||
copy->linesize,
|
||||
format_,
|
||||
width_,
|
||||
height_,
|
||||
1);
|
||||
}
|
||||
|
||||
return ele;
|
||||
}
|
||||
|
||||
void FFmpegFramePool::SetParams(int width, int height, AVPixelFormat format)
|
||||
{
|
||||
int old_nb_elements;
|
||||
|
||||
if (IsAllocated()) {
|
||||
old_nb_elements = GetElementCount();
|
||||
|
||||
Destroy();
|
||||
} else {
|
||||
old_nb_elements = 0;
|
||||
}
|
||||
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
format_ = format;
|
||||
|
||||
if (old_nb_elements) {
|
||||
// Re-allocate automatically
|
||||
Allocate(old_nb_elements);
|
||||
}
|
||||
}
|
||||
|
||||
size_t FFmpegFramePool::GetElementSize()
|
||||
{
|
||||
if (width_ == 0 || height_ == 0 || format_ == AV_PIX_FMT_NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -0,0 +1,53 @@
|
||||
/***
|
||||
|
||||
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 FFMPEGFRAMEPOOL_H
|
||||
#define FFMPEGFRAMEPOOL_H
|
||||
|
||||
#include "common/memorypool.h"
|
||||
#include "render/pixelformat.h"
|
||||
#include "render/videoparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class FFmpegFramePool : public MemoryPool<uint8_t>
|
||||
{
|
||||
public:
|
||||
FFmpegFramePool();
|
||||
|
||||
ElementPtr Get(AVFrame* copy);
|
||||
|
||||
void SetParams(int width, int height, AVPixelFormat format);
|
||||
|
||||
protected:
|
||||
virtual size_t GetElementSize() override;
|
||||
|
||||
private:
|
||||
int width_;
|
||||
|
||||
int height_;
|
||||
|
||||
AVPixelFormat format_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // FFMPEGFRAMEPOOL_H
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "render/audioparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <QFile>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "render/audioparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "audio/sampleformat.h"
|
||||
#include "common/constructors.h"
|
||||
#include "render/audioparams.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -21,7 +21,6 @@ set(OLIVE_SOURCES
|
||||
common/cancelableobject.h
|
||||
common/channellayout.h
|
||||
common/clamp.h
|
||||
common/constructors.h
|
||||
common/crashhandler.h
|
||||
common/crashhandler.cpp
|
||||
common/debug.h
|
||||
@@ -33,6 +32,7 @@ set(OLIVE_SOURCES
|
||||
common/flipmodifiers.cpp
|
||||
common/functiontimer.h
|
||||
common/lerp.h
|
||||
common/memorypool.h
|
||||
common/qtutils.h
|
||||
common/qtutils.cpp
|
||||
common/range.h
|
||||
|
||||
@@ -49,4 +49,21 @@ OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#define OLIVE_NS_ARG(x, y) QArgument<OLIVE_NAMESPACE::x>(MACRO_VAL_AS_STR(OLIVE_NAMESPACE) "::" #x, y)
|
||||
|
||||
/**
|
||||
* Copy/move deleters. Similar to Q_DISABLE_COPY_MOVE, et al. but those functions are not present in Qt < 5.13 so we
|
||||
* use our own functions for portability.
|
||||
*/
|
||||
|
||||
#define DISABLE_COPY(Class) \
|
||||
Class(const Class &) = delete;\
|
||||
Class &operator=(const Class &) = delete;
|
||||
|
||||
#define DISABLE_MOVE(Class) \
|
||||
Class(Class &&) = delete; \
|
||||
Class &operator=(Class &&) = delete;
|
||||
|
||||
#define DISABLE_COPY_MOVE(Class) \
|
||||
DISABLE_COPY(Class) \
|
||||
DISABLE_MOVE(Class)
|
||||
|
||||
#endif // OLIVECOMMONDEFINE_H
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/***
|
||||
|
||||
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 MEMORYPOOL_H
|
||||
#define MEMORYPOOL_H
|
||||
|
||||
#include <memory>
|
||||
#include <QDateTime>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "common/define.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
template <typename T>
|
||||
class MemoryPool
|
||||
{
|
||||
public:
|
||||
MemoryPool() {
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
~MemoryPool() {
|
||||
delete [] data_;
|
||||
}
|
||||
|
||||
DISABLE_COPY_MOVE(MemoryPool)
|
||||
|
||||
bool Allocate(int nb_elements) {
|
||||
delete [] data_;
|
||||
|
||||
size_t ele_sz = GetElementSize();
|
||||
|
||||
if (!ele_sz) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((data_ = new char[ele_sz * nb_elements])) {
|
||||
available_.resize(nb_elements);
|
||||
available_.fill(true);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
available_.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Destroy() {
|
||||
delete [] data_;
|
||||
data_ = nullptr;
|
||||
|
||||
available_.clear();
|
||||
}
|
||||
|
||||
inline bool IsAllocated() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline int GetElementCount() const {
|
||||
return available_.size();
|
||||
}
|
||||
|
||||
class Element {
|
||||
public:
|
||||
Element(MemoryPool* parent, T* data) {
|
||||
parent_ = parent;
|
||||
data_ = data;
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
~Element() {
|
||||
parent_->Release(this);
|
||||
}
|
||||
|
||||
inline T* data() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
inline const int64_t& timestamp() const {
|
||||
return timestamp_;
|
||||
}
|
||||
|
||||
inline void set_timestamp(const int64_t& timestamp) {
|
||||
timestamp_ = timestamp;
|
||||
}
|
||||
|
||||
inline void access() {
|
||||
accessed_ = QDateTime::currentMSecsSinceEpoch();
|
||||
}
|
||||
|
||||
inline const int64_t& last_accessed() const {
|
||||
return accessed_;
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryPool* parent_;
|
||||
|
||||
T* data_;
|
||||
|
||||
int64_t timestamp_;
|
||||
|
||||
int64_t accessed_;
|
||||
|
||||
};
|
||||
|
||||
using ElementPtr = std::shared_ptr<Element>;
|
||||
|
||||
ElementPtr Get() {
|
||||
for (int i=0;i<available_.size();i++) {
|
||||
if (available_.at(i)) {
|
||||
// This buffer is available
|
||||
available_.replace(i, false);
|
||||
|
||||
return std::make_shared<Element>(this, reinterpret_cast<T*>(data_ + i * GetElementSize()));
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Allocate a new "arena"
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Release(Element* e) {
|
||||
quintptr diff = reinterpret_cast<quintptr>(e->data()) - reinterpret_cast<quintptr>(data_);
|
||||
|
||||
int index = diff / GetElementSize();
|
||||
|
||||
available_.replace(index, true);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual size_t GetElementSize() {
|
||||
return sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
char* data_;
|
||||
|
||||
QVector<bool> available_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // MEMORYPOOL_H
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <QList>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "common/rational.h"
|
||||
#include "project/item/item.h"
|
||||
#include "project/item/footage/audiostream.h"
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <QString>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "common/threadedobject.h"
|
||||
#include "common/xmlutils.h"
|
||||
#include "node/param.h"
|
||||
|
||||
@@ -46,7 +46,7 @@ bool AudioBackend::InitInternal()
|
||||
// Initiate one thread per CPU core
|
||||
for (int i=0;i<threads().size();i++) {
|
||||
// Create one processor object for each thread
|
||||
AudioWorker* processor = new AudioWorker(decoder_cache(), ©_map_);
|
||||
AudioWorker* processor = new AudioWorker(©_map_);
|
||||
processor->SetParameters(params());
|
||||
processors_.append(processor);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AudioWorker::AudioWorker(DecoderCache* decoder_cache, QHash<Node *, Node *> *copy_map, QObject *parent) :
|
||||
AudioRenderWorker(decoder_cache, copy_map, parent)
|
||||
AudioWorker::AudioWorker(QHash<Node *, Node *> *copy_map, QObject *parent) :
|
||||
AudioRenderWorker(copy_map, parent)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
class AudioWorker : public AudioRenderWorker
|
||||
{
|
||||
public:
|
||||
AudioWorker(DecoderCache* decoder_cache, QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
|
||||
AudioWorker(QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
|
||||
|
||||
protected:
|
||||
virtual void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table) override;
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AudioRenderWorker::AudioRenderWorker(DecoderCache* decoder_cache, QHash<Node *, Node *> *copy_map, QObject *parent) :
|
||||
RenderWorker(decoder_cache, parent),
|
||||
AudioRenderWorker::AudioRenderWorker(QHash<Node *, Node *> *copy_map, QObject *parent) :
|
||||
RenderWorker(parent),
|
||||
copy_map_(copy_map)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class AudioRenderWorker : public RenderWorker
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioRenderWorker(DecoderCache* decoder_cache, QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
|
||||
AudioRenderWorker(QHash<Node*, Node*>* copy_map, QObject* parent = nullptr);
|
||||
|
||||
void SetParameters(const AudioRenderingParams& audio_params);
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ bool OpenGLBackend::InitInternal()
|
||||
// Initiate one thread per CPU core
|
||||
for (int i=0;i<threads().size();i++) {
|
||||
// Create one processor object for each thread
|
||||
OpenGLWorker* processor = new OpenGLWorker(frame_cache(), decoder_cache());
|
||||
OpenGLWorker* processor = new OpenGLWorker(frame_cache());
|
||||
processor->SetParameters(params());
|
||||
processors_.append(processor);
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <QOpenGLContext>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "opengltexture.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -67,7 +67,7 @@ bool OpenGLProxy::Init()
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table)
|
||||
void OpenGLProxy::FrameToValue(FramePtr frame, StreamPtr stream, const TimeRange &range, NodeValueTable* table)
|
||||
{
|
||||
// Ensure stream is video or image type
|
||||
if (stream->type() != Stream::kVideo && stream->type() != Stream::kImage) {
|
||||
@@ -105,13 +105,6 @@ void OpenGLProxy::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeR
|
||||
|
||||
ColorManager::OCIOMethod ocio_method = ColorManager::GetOCIOMethodForMode(video_params_.mode());
|
||||
|
||||
FramePtr frame = decoder->RetrieveVideo(range.in(), video_params_.divider());
|
||||
|
||||
if (!frame) {
|
||||
// Nothing to be done
|
||||
return;
|
||||
}
|
||||
|
||||
// OCIO's CPU conversion is more accurate, so for online we render on CPU but offline we render GPU
|
||||
if (ocio_method == ColorManager::kOCIOAccurate) {
|
||||
bool has_alpha = PixelFormat::FormatHasAlphaChannel(frame->format());
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
void Close();
|
||||
|
||||
void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
|
||||
void FrameToValue(FramePtr frame, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
|
||||
|
||||
void RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <QOpenGLFunctions>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "common/constructors.h"
|
||||
#include "render/pixelformat.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -31,14 +31,18 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
OpenGLWorker::OpenGLWorker(VideoRenderFrameCache *frame_cache, DecoderCache* decoder_cache, QObject *parent) :
|
||||
VideoRenderWorker(frame_cache, decoder_cache, parent)
|
||||
OpenGLWorker::OpenGLWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
VideoRenderWorker(frame_cache, parent)
|
||||
{
|
||||
}
|
||||
|
||||
void OpenGLWorker::FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable *table)
|
||||
{
|
||||
emit RequestFrameToValue(decoder, stream, range, table);
|
||||
FramePtr frame = decoder->RetrieveVideo(range.in(), video_params().divider());
|
||||
|
||||
if (frame) {
|
||||
emit RequestFrameToValue(frame, stream, range, table);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLWorker::RunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable *output_params)
|
||||
|
||||
@@ -34,11 +34,11 @@ OLIVE_NAMESPACE_ENTER
|
||||
class OpenGLWorker : public VideoRenderWorker {
|
||||
Q_OBJECT
|
||||
public:
|
||||
OpenGLWorker(VideoRenderFrameCache* frame_cache, DecoderCache *decoder_cache,
|
||||
OpenGLWorker(VideoRenderFrameCache* frame_cache,
|
||||
QObject* parent = nullptr);
|
||||
|
||||
signals:
|
||||
void RequestFrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
|
||||
void RequestFrameToValue(FramePtr frame, StreamPtr stream, const TimeRange &range, NodeValueTable* table);
|
||||
|
||||
void RequestRunNodeAccelerated(const Node *node, const TimeRange &range, const NodeValueDatabase &input_params, NodeValueTable* output_params);
|
||||
|
||||
|
||||
@@ -105,8 +105,6 @@ void RenderBackend::Close()
|
||||
|
||||
threads_.clear();
|
||||
processors_.clear();
|
||||
|
||||
decoder_cache_.Clear();
|
||||
}
|
||||
|
||||
const QString &RenderBackend::GetError() const
|
||||
@@ -404,11 +402,6 @@ void RenderBackend::SetWorkerBusyState(RenderWorker *worker, bool busy)
|
||||
processor_busy_state_.replace(processors_.indexOf(worker), busy);
|
||||
}
|
||||
|
||||
DecoderCache *RenderBackend::decoder_cache()
|
||||
{
|
||||
return &decoder_cache_;
|
||||
}
|
||||
|
||||
bool RenderBackend::AllProcessorsAreAvailable() const
|
||||
{
|
||||
foreach (bool busy, processor_busy_state_) {
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <QLinkedList>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "dialog/rendercancel/rendercancel.h"
|
||||
#include "decodercache.h"
|
||||
#include "node/graph.h"
|
||||
@@ -118,14 +117,10 @@ protected:
|
||||
bool WorkerIsBusy(RenderWorker* worker) const;
|
||||
void SetWorkerBusyState(RenderWorker* worker, bool busy);
|
||||
|
||||
DecoderCache* decoder_cache();
|
||||
|
||||
TimeRangeList cache_queue_;
|
||||
|
||||
QVector<RenderWorker*> processors_;
|
||||
|
||||
DecoderCache decoder_cache_;
|
||||
|
||||
bool compiled_;
|
||||
|
||||
QHash<TimeRange, qint64> render_job_info_;
|
||||
|
||||
@@ -26,10 +26,9 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
RenderWorker::RenderWorker(DecoderCache *decoder_cache, QObject *parent) :
|
||||
RenderWorker::RenderWorker(QObject *parent) :
|
||||
QObject(parent),
|
||||
started_(false),
|
||||
decoder_cache_(decoder_cache)
|
||||
started_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -50,6 +49,8 @@ void RenderWorker::Close()
|
||||
{
|
||||
CloseInternal();
|
||||
|
||||
decoder_cache_.Clear();
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
@@ -82,11 +83,9 @@ StreamPtr RenderWorker::ResolveStreamFromInput(NodeInput *input)
|
||||
|
||||
DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
|
||||
{
|
||||
QMutexLocker locker(decoder_cache_->lock());
|
||||
|
||||
// Access a map of Node inputs and decoder instances and retrieve a frame!
|
||||
|
||||
DecoderPtr decoder = decoder_cache_->Get(stream.get());
|
||||
DecoderPtr decoder = decoder_cache_.Get(stream.get());
|
||||
|
||||
if (!decoder && stream) {
|
||||
// Create a new Decoder here
|
||||
@@ -94,7 +93,7 @@ DecoderPtr RenderWorker::ResolveDecoderFromInput(StreamPtr stream)
|
||||
decoder->set_stream(stream);
|
||||
|
||||
if (decoder->Open()) {
|
||||
decoder_cache_->Add(stream.get(), decoder);
|
||||
decoder_cache_.Add(stream.get(), decoder);
|
||||
} else {
|
||||
decoder = nullptr;
|
||||
qWarning() << "Failed to open decoder for" << stream->footage()->filename() << "::" << stream->index();
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "decodercache.h"
|
||||
#include "node/node.h"
|
||||
#include "node/output/track/track.h"
|
||||
@@ -35,7 +34,7 @@ class RenderWorker : public QObject, public NodeTraverser
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RenderWorker(DecoderCache* decoder_cache, QObject* parent = nullptr);
|
||||
RenderWorker(QObject* parent = nullptr);
|
||||
|
||||
bool Init();
|
||||
|
||||
@@ -77,7 +76,7 @@ protected:
|
||||
private:
|
||||
bool started_;
|
||||
|
||||
DecoderCache* decoder_cache_;
|
||||
DecoderCache decoder_cache_;
|
||||
|
||||
NodeDependency path_;
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) :
|
||||
RenderBackend(parent),
|
||||
operating_mode_(VideoRenderWorker::kHashRenderCache),
|
||||
only_signal_last_frame_requested_(true),
|
||||
limit_caching_(true)
|
||||
limit_caching_(true),
|
||||
pop_toggle_(false)
|
||||
{
|
||||
connect(DiskManager::instance(), &DiskManager::DeletedFrame, this, &VideoRenderBackend::FrameRemovedFromDiskCache);
|
||||
}
|
||||
@@ -164,7 +165,7 @@ VideoRenderFrameCache *VideoRenderBackend::frame_cache()
|
||||
|
||||
QString VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||
{
|
||||
last_time_requested_ = time;
|
||||
UpdateLastRequestedTime(time);
|
||||
|
||||
if (viewer_node() == nullptr) {
|
||||
// Nothing is connected - nothing to show or render
|
||||
@@ -181,8 +182,6 @@ QString VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Requeue();
|
||||
|
||||
// Find frame in map
|
||||
QByteArray frame_hash = frame_cache_.TimeToHash(time);
|
||||
|
||||
@@ -195,6 +194,13 @@ QString VideoRenderBackend::GetCachedFrame(const rational &time)
|
||||
return QString();
|
||||
}
|
||||
|
||||
void VideoRenderBackend::UpdateLastRequestedTime(const rational &time)
|
||||
{
|
||||
last_time_requested_ = time;
|
||||
|
||||
Requeue();
|
||||
}
|
||||
|
||||
NodeInput *VideoRenderBackend::GetDependentInput()
|
||||
{
|
||||
return viewer_node()->texture_input();
|
||||
@@ -208,44 +214,35 @@ bool VideoRenderBackend::CanRender()
|
||||
TimeRange VideoRenderBackend::PopNextFrameFromQueue()
|
||||
{
|
||||
// Try to find the frame that's closest to the last time requested (the playhead)
|
||||
rational earliest_allowed_time = (pop_toggle_) ? 0 : last_time_requested_;
|
||||
pop_toggle_ = !pop_toggle_;
|
||||
|
||||
// Set up playhead frame range to see if the queue contains this frame precisely
|
||||
TimeRange test_range(last_time_requested_, last_time_requested_ + params_.time_base());
|
||||
TimeRange test_range(earliest_allowed_time, earliest_allowed_time + params_.time_base());
|
||||
|
||||
// Use this variable to find the closest frame in the range
|
||||
rational closest_time = -1;
|
||||
rational closest_time = RATIONAL_MAX;
|
||||
|
||||
foreach (const TimeRange& range_here, cache_queue_) {
|
||||
if (range_here.OverlapsWith(test_range, false, false)) {
|
||||
closest_time = -1;
|
||||
closest_time = RATIONAL_MAX;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int j=0;j<2;j++) {
|
||||
rational compare;
|
||||
if (range_here.in() >= earliest_allowed_time) {
|
||||
rational frame_here = Timecode::snap_time_to_timebase(range_here.in(), params_.time_base());
|
||||
|
||||
if (j == 0) {
|
||||
compare = Timecode::snap_time_to_timebase(range_here.in(), params_.time_base());
|
||||
if (compare > range_here.in()) {
|
||||
compare -= params_.time_base();
|
||||
}
|
||||
} else {
|
||||
compare = Timecode::snap_time_to_timebase(range_here.out(), params_.time_base());
|
||||
if (compare >= range_here.out()) {
|
||||
compare -= params_.time_base();
|
||||
}
|
||||
if (frame_here > range_here.in()) {
|
||||
frame_here = qMax(rational(), frame_here - params_.time_base());
|
||||
}
|
||||
|
||||
if (closest_time < 0
|
||||
|| qAbs(compare - last_time_requested_) < qAbs(closest_time - last_time_requested_)) {
|
||||
closest_time = compare;
|
||||
}
|
||||
closest_time = qMin(closest_time, frame_here);
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange frame_range;
|
||||
|
||||
if (closest_time == -1) {
|
||||
if (closest_time == RATIONAL_MAX) {
|
||||
frame_range = test_range;
|
||||
} else {
|
||||
frame_range = TimeRange(closest_time, closest_time + params_.time_base());
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
|
||||
QString GetCachedFrame(const rational& time);
|
||||
|
||||
void UpdateLastRequestedTime(const rational& time);
|
||||
|
||||
VideoRenderFrameCache* frame_cache();
|
||||
|
||||
const VideoRenderingParams& params() const;
|
||||
@@ -129,6 +131,8 @@ private:
|
||||
|
||||
bool limit_caching_;
|
||||
|
||||
bool pop_toggle_;
|
||||
|
||||
private slots:
|
||||
void ThreadCompletedDownload(NodeDependency dep, qint64 job_time, QByteArray hash, bool texture_existed);
|
||||
void ThreadSkippedFrame(NodeDependency dep, qint64 job_time, QByteArray hash);
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, DecoderCache* decoder_cache, QObject *parent) :
|
||||
RenderWorker(decoder_cache, parent),
|
||||
VideoRenderWorker::VideoRenderWorker(VideoRenderFrameCache *frame_cache, QObject *parent) :
|
||||
RenderWorker(parent),
|
||||
frame_cache_(frame_cache),
|
||||
operating_mode_(kHashRenderCache)
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
kHashRenderCache = 0x7
|
||||
};
|
||||
|
||||
VideoRenderWorker(VideoRenderFrameCache* frame_cache, DecoderCache *decoder_cache, QObject* parent = nullptr);
|
||||
VideoRenderWorker(VideoRenderFrameCache* frame_cache, QObject* parent = nullptr);
|
||||
|
||||
void SetParameters(const VideoRenderingParams& video_params);
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
namespace OCIO = OCIO_NAMESPACE::v1;
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "common/constructors.h"
|
||||
#include "render/color.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -42,9 +42,7 @@ void ConformTask::Action()
|
||||
|
||||
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
|
||||
|
||||
decoder->Open();
|
||||
decoder->Conform(params_, &IsCancelled());
|
||||
decoder->Close();
|
||||
|
||||
emit Succeeded();
|
||||
}
|
||||
|
||||
@@ -42,9 +42,7 @@ void IndexTask::Action()
|
||||
|
||||
connect(decoder.get(), &Decoder::IndexProgress, this, &IndexTask::ProgressChanged);
|
||||
|
||||
decoder->Open();
|
||||
decoder->Index(&IsCancelled());
|
||||
decoder->Close();
|
||||
|
||||
emit Succeeded();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <QVector>
|
||||
#include <QUndoCommand>
|
||||
|
||||
#include "common/constructors.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -320,6 +320,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
{
|
||||
if (!GetConnectedNode() || time >= GetConnectedNode()->Length()) {
|
||||
main_gl_widget()->SetImage(QString());
|
||||
video_renderer_->UpdateLastRequestedTime(time);
|
||||
} else {
|
||||
QString frame_fn = video_renderer_->GetCachedFrame(time);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user