renderer/decoder: simplified audio PCM transcode
Turned the two-step PCM transcode into one step and simplified/removed much of the unnecessary infrastructure that supported it. This makes the code cleaner and generally improves the code paths.
This commit is contained in:
+6
-16
@@ -29,8 +29,6 @@
|
||||
#include "codec/oiio/oiiodecoder.h"
|
||||
#include "codec/waveinput.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "render/backend/indexmanager.h"
|
||||
#include "task/index/index.h"
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -134,16 +132,6 @@ bool Decoder::ProbeMedia(Footage *f, const QAtomicInt* cancelled)
|
||||
|
||||
// FIXME: Cache the results so we don't have to probe if this media is added a second time
|
||||
|
||||
// Start an index task
|
||||
foreach (StreamPtr stream, f->streams()) {
|
||||
if (stream->type() == Stream::kAudio) {
|
||||
QMetaObject::invokeMethod(IndexManager::instance(),
|
||||
"StartIndexingStream",
|
||||
Qt::QueuedConnection,
|
||||
OLIVE_NS_ARG(StreamPtr, stream));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -173,7 +161,7 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Decoder::Conform(const AudioRenderingParams ¶ms, const QAtomicInt* cancelled)
|
||||
/*void Decoder::Conform(const AudioRenderingParams ¶ms, const QAtomicInt* cancelled)
|
||||
{
|
||||
if (stream()->type() != Stream::kAudio) {
|
||||
// Nothing to be done
|
||||
@@ -265,7 +253,7 @@ void Decoder::Conform(const AudioRenderingParams ¶ms, const QAtomicInt* canc
|
||||
} else {
|
||||
qWarning() << "Failed to conform file:" << stream()->footage()->filename();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void Decoder::ConformInternal(SwrContext* resampler, WaveOutput* output, const char* in_data, int in_sample_count)
|
||||
{
|
||||
@@ -319,12 +307,14 @@ QString Decoder::GetConformedFilename(const AudioRenderingParams ¶ms)
|
||||
return index_fn;
|
||||
}
|
||||
|
||||
void Decoder::ProxyVideo(const QAtomicInt *, int )
|
||||
bool Decoder::ProxyVideo(const QAtomicInt *, int )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Decoder::ProxyAudio(const QAtomicInt *)
|
||||
bool Decoder::ConformAudio(const QAtomicInt *, const AudioRenderingParams& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::HasConformedVersion(const AudioRenderingParams ¶ms)
|
||||
|
||||
+10
-20
@@ -117,11 +117,6 @@ public:
|
||||
*/
|
||||
virtual bool Open() = 0;
|
||||
|
||||
/**
|
||||
* @brief Determine whether the Decoder is able to retrieve data
|
||||
*/
|
||||
virtual RetrieveState GetRetrieveState(const rational& time) = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieve video frame
|
||||
*
|
||||
@@ -216,7 +211,15 @@ public:
|
||||
static DecoderPtr CreateFromID(const QString& id);
|
||||
|
||||
/**
|
||||
* @brief AUDIO ONLY: Conform an audio stream to match certain parameters
|
||||
* @brief VIDEO ONLY: Produce a compressed EXR proxy with the specified divider
|
||||
*/
|
||||
virtual bool ProxyVideo(const QAtomicInt* cancelled, int divider);
|
||||
|
||||
/**
|
||||
* @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
|
||||
@@ -226,20 +229,7 @@ public:
|
||||
* 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.
|
||||
*/
|
||||
void Conform(const AudioRenderingParams& params, const QAtomicInt* cancelled);
|
||||
|
||||
/**
|
||||
* @brief VIDEO ONLY: Produce a compressed EXR proxy with the specified divider
|
||||
*/
|
||||
virtual void ProxyVideo(const QAtomicInt* cancelled, int divider);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
virtual void ProxyAudio(const QAtomicInt* cancelled);
|
||||
virtual bool ConformAudio(const QAtomicInt* cancelled, const AudioRenderingParams ¶ms);
|
||||
|
||||
/**
|
||||
* @brief AUDIO ONLY: Returns whether a transcode of this audio matching the specified params
|
||||
|
||||
+172
-183
@@ -108,23 +108,9 @@ bool FFmpegDecoder::Open()
|
||||
|
||||
// Determine which Olive native pixel format we retrieved
|
||||
// Note that FFmpeg doesn't support float formats
|
||||
switch (ideal_pix_fmt_) {
|
||||
case AV_PIX_FMT_RGB24:
|
||||
native_pix_fmt_ = PixelFormat::PIX_FMT_RGB8;
|
||||
break;
|
||||
case AV_PIX_FMT_RGBA:
|
||||
native_pix_fmt_ = PixelFormat::PIX_FMT_RGBA8;
|
||||
break;
|
||||
case AV_PIX_FMT_RGB48:
|
||||
native_pix_fmt_ = PixelFormat::PIX_FMT_RGB16U;
|
||||
break;
|
||||
case AV_PIX_FMT_RGBA64:
|
||||
native_pix_fmt_ = PixelFormat::PIX_FMT_RGBA16U;
|
||||
break;
|
||||
default:
|
||||
// We should never get here, but just in case...
|
||||
qFatal("Invalid output format");
|
||||
}
|
||||
native_pix_fmt_ = GetNativePixelFormat(ideal_pix_fmt_);
|
||||
|
||||
Q_ASSERT(native_pix_fmt_ != PixelFormat::PIX_FMT_INVALID);
|
||||
|
||||
aspect_ratio_ = our_instance->sample_aspect_ratio();
|
||||
}
|
||||
@@ -146,29 +132,6 @@ bool FFmpegDecoder::Open()
|
||||
return true;
|
||||
}
|
||||
|
||||
Decoder::RetrieveState FFmpegDecoder::GetRetrieveState(const rational& time)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
if (!open_) {
|
||||
return kFailedToOpen;
|
||||
}
|
||||
|
||||
if (stream()->type() == Stream::kVideo) {
|
||||
|
||||
// Do nothing
|
||||
|
||||
} else if (stream()->type() == Stream::kAudio) {
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(stream());
|
||||
|
||||
if (time > audio_stream->index_length() && !audio_stream->index_done()) {
|
||||
return kIndexUnavailable;
|
||||
}
|
||||
}
|
||||
|
||||
return kReady;
|
||||
}
|
||||
|
||||
FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int ÷r)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
@@ -623,173 +586,174 @@ void FFmpegDecoder::Error(const QString &s)
|
||||
ClearResources();
|
||||
}
|
||||
|
||||
void FFmpegDecoder::ProxyVideo(const QAtomicInt *cancelled, int divider)
|
||||
bool FFmpegDecoder::ProxyVideo(const QAtomicInt *cancelled, int divider)
|
||||
{
|
||||
// Iterate through each video frame transcode each frame to compressed EXR
|
||||
QMutexLocker locker(stream()->index_process_lock());
|
||||
return false;
|
||||
|
||||
QByteArray fn_bytes = stream()->footage()->filename().toUtf8();
|
||||
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
|
||||
|
||||
FFmpegDecoderInstance index_instance(fn_bytes.constData(), stream()->index());
|
||||
QString frame_index_file = GetIndexFilename().append('d').append(QString::number(divider));
|
||||
|
||||
if (QFileInfo::exists(frame_index_file)) {
|
||||
|
||||
}
|
||||
// A proxy of this type already exists so we can do nothing
|
||||
video_stream->append_proxy(divider);
|
||||
|
||||
void FFmpegDecoder::ProxyAudio(const QAtomicInt *cancelled)
|
||||
{
|
||||
// Iterate through each audio frame and extract the PCM data
|
||||
QMutexLocker locker(stream()->index_process_lock());
|
||||
|
||||
if (QFileInfo::exists(GetIndexFilename())) {
|
||||
WaveInput input(GetIndexFilename());
|
||||
if (input.open()) {
|
||||
std::static_pointer_cast<AudioStream>(stream())->set_index_done(true);
|
||||
std::static_pointer_cast<AudioStream>(stream())->set_index_length(input.params().bytes_to_time(input.data_length()));
|
||||
|
||||
input.close();
|
||||
}
|
||||
} else {
|
||||
QByteArray fn_bytes = stream()->footage()->filename().toUtf8();
|
||||
|
||||
FFmpegDecoderInstance index_instance(fn_bytes.constData(), stream()->index());
|
||||
// Iterate each frame and transcode it to EXR
|
||||
FFmpegDecoderInstance instance(stream()->footage()->filename().toUtf8(), stream()->index());
|
||||
|
||||
uint64_t channel_layout = index_instance.stream()->codecpar->channel_layout;
|
||||
if (!channel_layout) {
|
||||
if (!index_instance.stream()->codecpar->channels) {
|
||||
// No channel data - we can't do anything with this
|
||||
return;
|
||||
}
|
||||
|
||||
channel_layout = static_cast<uint64_t>(av_get_default_channel_layout(index_instance.stream()->codecpar->channels));
|
||||
}
|
||||
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(stream());
|
||||
|
||||
// This should be unnecessary, but just in case...
|
||||
audio_stream->clear_index();
|
||||
|
||||
SwrContext* resampler = nullptr;
|
||||
AVSampleFormat src_sample_fmt = static_cast<AVSampleFormat>(index_instance.stream()->codecpar->format);
|
||||
AVSampleFormat dst_sample_fmt;
|
||||
|
||||
// We don't use planar types internally, so if this is a planar format convert it now
|
||||
if (av_sample_fmt_is_planar(src_sample_fmt)) {
|
||||
dst_sample_fmt = av_get_packed_sample_fmt(src_sample_fmt);
|
||||
|
||||
resampler = swr_alloc_set_opts(nullptr,
|
||||
static_cast<int64_t>(index_instance.stream()->codecpar->channel_layout),
|
||||
dst_sample_fmt,
|
||||
index_instance.stream()->codecpar->sample_rate,
|
||||
static_cast<int64_t>(index_instance.stream()->codecpar->channel_layout),
|
||||
src_sample_fmt,
|
||||
index_instance.stream()->codecpar->sample_rate,
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
swr_init(resampler);
|
||||
} else {
|
||||
dst_sample_fmt = src_sample_fmt;
|
||||
}
|
||||
|
||||
AudioRenderingParams wave_params(index_instance.stream()->codecpar->sample_rate,
|
||||
channel_layout,
|
||||
FFmpegCommon::GetNativeSampleFormat(dst_sample_fmt));
|
||||
WaveOutput wave_out(GetIndexFilename(), wave_params);
|
||||
int ret;
|
||||
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
int ret;
|
||||
|
||||
if (wave_out.open()) {
|
||||
bool success = false;
|
||||
while (true) {
|
||||
ret = instance.GetFrame(pkt, frame);
|
||||
|
||||
while (true) {
|
||||
// Check if we have a `cancelled` ptr and its value
|
||||
if (cancelled && *cancelled) {
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (ret == AVERROR_EOF) {
|
||||
|
||||
ret = index_instance.GetFrame(pkt, frame);
|
||||
|
||||
if (ret < 0) {
|
||||
|
||||
if (ret == AVERROR_EOF) {
|
||||
success = true;
|
||||
} else {
|
||||
char err_str[50];
|
||||
av_strerror(ret, err_str, 50);
|
||||
qWarning() << "Failed to index:" << ret << err_str;
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
char* data;
|
||||
int nb_samples;
|
||||
|
||||
if (resampler) {
|
||||
|
||||
nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
|
||||
|
||||
data = new char[wave_params.samples_to_bytes(nb_samples)];
|
||||
|
||||
// We must need to resample this (mainly just convert from planar to packed if necessary)
|
||||
nb_samples = swr_convert(resampler,
|
||||
reinterpret_cast<uint8_t**>(&data),
|
||||
nb_samples,
|
||||
const_cast<const uint8_t**>(frame->data),
|
||||
frame->nb_samples);
|
||||
|
||||
if (nb_samples < 0) {
|
||||
char err_str[50];
|
||||
av_strerror(nb_samples, err_str, 50);
|
||||
qWarning() << "libswresample failed with error:" << nb_samples << err_str;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// No resampling required, we can write directly from the frame buffer
|
||||
data = reinterpret_cast<char*>(frame->data[0]);
|
||||
nb_samples = frame->nb_samples;
|
||||
|
||||
}
|
||||
|
||||
// Write packed WAV data to the disk cache
|
||||
wave_out.write(data, wave_params.samples_to_bytes(nb_samples));
|
||||
|
||||
audio_stream->set_index_length(wave_params.bytes_to_time(wave_out.data_length()));
|
||||
|
||||
// If we allocated an output for the resampler, delete it here
|
||||
if (data != reinterpret_cast<char*>(frame->data[0])) {
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
SignalIndexProgress(frame->pts);
|
||||
}
|
||||
}
|
||||
|
||||
wave_out.close();
|
||||
|
||||
if (success) {
|
||||
audio_stream->set_index_done(true);
|
||||
} else {
|
||||
// Audio index didn't complete, delete it
|
||||
QFile(GetIndexFilename()).remove();
|
||||
audio_stream->clear_index();
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to open WAVE output for indexing";
|
||||
}
|
||||
|
||||
if (resampler != nullptr) {
|
||||
swr_free(&resampler);
|
||||
}
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::ConformAudio(const QAtomicInt *cancelled, const AudioRenderingParams &p)
|
||||
{
|
||||
// Iterate through each audio frame and extract the PCM data
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(stream());
|
||||
|
||||
// Check if we already have a conform of this type
|
||||
QString conformed_fn = GetConformedFilename(p);
|
||||
|
||||
if (QFileInfo::exists(conformed_fn)) {
|
||||
|
||||
// If we have one, and we can open it correctly, we can use it as-is
|
||||
WaveInput input(conformed_fn);
|
||||
if (input.open()) {
|
||||
audio_stream->append_conformed_version(p);
|
||||
|
||||
input.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Conform doesn't exist, we'll have to produce one
|
||||
FFmpegDecoderInstance index_instance(stream()->footage()->filename().toUtf8(),
|
||||
stream()->index());
|
||||
|
||||
// Handle NULL channel layout
|
||||
uint64_t channel_layout = ValidateChannelLayout(index_instance.stream());
|
||||
if (!channel_layout) {
|
||||
qCritical() << "Failed to determine channel layout of audio file, could not conform";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create resampling context
|
||||
SwrContext* resampler = swr_alloc_set_opts(nullptr,
|
||||
p.channel_layout(),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(p.format()),
|
||||
p.sample_rate(),
|
||||
static_cast<int64_t>(index_instance.stream()->codecpar->channel_layout),
|
||||
static_cast<AVSampleFormat>(index_instance.stream()->codecpar->format),
|
||||
index_instance.stream()->codecpar->sample_rate,
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
swr_init(resampler);
|
||||
|
||||
WaveOutput wave_out(conformed_fn, p);
|
||||
|
||||
AVPacket* pkt = av_packet_alloc();
|
||||
AVFrame* frame = av_frame_alloc();
|
||||
int ret;
|
||||
|
||||
bool success = false;
|
||||
|
||||
if (wave_out.open()) {
|
||||
while (true) {
|
||||
// Check if we have a `cancelled` ptr and its value
|
||||
if (cancelled && *cancelled) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = index_instance.GetFrame(pkt, frame);
|
||||
|
||||
if (ret < 0) {
|
||||
|
||||
if (ret == AVERROR_EOF) {
|
||||
success = true;
|
||||
} else {
|
||||
char err_str[50];
|
||||
av_strerror(ret, err_str, 50);
|
||||
qWarning() << "Failed to index:" << ret << err_str;
|
||||
}
|
||||
break;
|
||||
|
||||
} else {
|
||||
// Allocate buffers
|
||||
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
|
||||
char* data = new char[p.samples_to_bytes(nb_samples)];
|
||||
|
||||
// Resample audio to our destination parameters
|
||||
nb_samples = swr_convert(resampler,
|
||||
reinterpret_cast<uint8_t**>(&data),
|
||||
nb_samples,
|
||||
const_cast<const uint8_t**>(frame->data),
|
||||
frame->nb_samples);
|
||||
|
||||
if (nb_samples < 0) {
|
||||
char err_str[50];
|
||||
av_strerror(nb_samples, err_str, 50);
|
||||
qWarning() << "libswresample failed with error:" << nb_samples << err_str;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write packed WAV data to the disk cache
|
||||
wave_out.write(data, p.samples_to_bytes(nb_samples));
|
||||
|
||||
// If we allocated an output for the resampler, delete it here
|
||||
if (data != reinterpret_cast<char*>(frame->data[0])) {
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
SignalIndexProgress(frame->pts);
|
||||
}
|
||||
}
|
||||
|
||||
wave_out.close();
|
||||
|
||||
if (success) {
|
||||
|
||||
// If our conform succeeded, add it
|
||||
audio_stream->append_conformed_version(p);
|
||||
|
||||
} else {
|
||||
|
||||
// Audio index didn't complete, delete it
|
||||
QFile(conformed_fn).remove();
|
||||
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to open WAVE output for indexing";
|
||||
}
|
||||
|
||||
swr_free(&resampler);
|
||||
|
||||
av_frame_free(&frame);
|
||||
av_packet_free(&pkt);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
QString FFmpegDecoder::GetIndexFilename()
|
||||
{
|
||||
return FileFunctions::GetMediaIndexFilename(FileFunctions::GetUniqueFileIdentifier(stream()->footage()->filename()))
|
||||
@@ -801,6 +765,31 @@ int FFmpegDecoder::GetScaledDimension(int dim, int divider)
|
||||
return dim / divider;
|
||||
}
|
||||
|
||||
PixelFormat::Format FFmpegDecoder::GetNativePixelFormat(AVPixelFormat pix_fmt)
|
||||
{
|
||||
switch (pix_fmt) {
|
||||
case AV_PIX_FMT_RGB24:
|
||||
return PixelFormat::PIX_FMT_RGB8;
|
||||
case AV_PIX_FMT_RGBA:
|
||||
return PixelFormat::PIX_FMT_RGBA8;
|
||||
case AV_PIX_FMT_RGB48:
|
||||
return PixelFormat::PIX_FMT_RGB16U;
|
||||
case AV_PIX_FMT_RGBA64:
|
||||
return PixelFormat::PIX_FMT_RGBA16U;
|
||||
default:
|
||||
return PixelFormat::PIX_FMT_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t FFmpegDecoder::ValidateChannelLayout(AVStream* stream)
|
||||
{
|
||||
if (stream->codecpar->channel_layout) {
|
||||
return stream->codecpar->channel_layout;
|
||||
}
|
||||
|
||||
return av_get_default_channel_layout(stream->codecpar->channels);
|
||||
}
|
||||
|
||||
int FFmpegDecoderInstance::GetFrame(AVPacket *pkt, AVFrame *frame)
|
||||
{
|
||||
bool eof = false;
|
||||
|
||||
@@ -134,7 +134,6 @@ public:
|
||||
virtual bool Probe(Footage *f, const QAtomicInt *cancelled) override;
|
||||
|
||||
virtual bool Open() override;
|
||||
virtual RetrieveState GetRetrieveState(const rational &time) override;
|
||||
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
|
||||
virtual SampleBufferPtr RetrieveAudio(const rational &timecode, const rational &length, const AudioRenderingParams& params) override;
|
||||
virtual void Close() override;
|
||||
@@ -144,8 +143,8 @@ public:
|
||||
virtual bool SupportsVideo() override;
|
||||
virtual bool SupportsAudio() override;
|
||||
|
||||
virtual void ProxyVideo(const QAtomicInt* cancelled, int divider) override;
|
||||
virtual void ProxyAudio(const QAtomicInt* cancelled) override;
|
||||
virtual bool ProxyVideo(const QAtomicInt* cancelled, int divider) override;
|
||||
virtual bool ConformAudio(const QAtomicInt* cancelled, const AudioRenderingParams& p) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
@@ -176,6 +175,10 @@ private:
|
||||
|
||||
static int GetScaledDimension(int dim, int divider);
|
||||
|
||||
static PixelFormat::Format GetNativePixelFormat(AVPixelFormat pix_fmt);
|
||||
|
||||
static uint64_t ValidateChannelLayout(AVStream *stream);
|
||||
|
||||
SwsContext* scale_ctx_;
|
||||
int scale_divider_;
|
||||
AVPixelFormat src_pix_fmt_;
|
||||
|
||||
@@ -154,17 +154,6 @@ bool OIIODecoder::Open()
|
||||
return true;
|
||||
}
|
||||
|
||||
Decoder::RetrieveState OIIODecoder::GetRetrieveState(const rational &time)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
if (!open_) {
|
||||
return kFailedToOpen;
|
||||
}
|
||||
|
||||
return kReady;
|
||||
}
|
||||
|
||||
FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider)
|
||||
{
|
||||
QMutexLocker locker(&mutex_);
|
||||
|
||||
@@ -40,7 +40,6 @@ public:
|
||||
virtual bool Probe(Footage *f, const QAtomicInt* cancelled) override;
|
||||
|
||||
virtual bool Open() override;
|
||||
virtual RetrieveState GetRetrieveState(const rational &time) override;
|
||||
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
|
||||
virtual void Close() override;
|
||||
|
||||
|
||||
+3
-9
@@ -48,7 +48,6 @@
|
||||
#include "project/projectimportmanager.h"
|
||||
#include "project/projectloadmanager.h"
|
||||
#include "project/projectsavemanager.h"
|
||||
#include "render/backend/indexmanager.h"
|
||||
#include "render/backend/opengl/opengltexturecache.h"
|
||||
#include "render/colormanager.h"
|
||||
#include "render/diskmanager.h"
|
||||
@@ -119,15 +118,9 @@ bool Core::Start()
|
||||
// Set up node factory/library
|
||||
NodeFactory::Initialize();
|
||||
|
||||
// Set up the index manager for renderers
|
||||
IndexManager::CreateInstance();
|
||||
|
||||
// Set up color manager's default config
|
||||
ColorManager::SetUpDefaultConfig();
|
||||
|
||||
// Initialize disk service
|
||||
DiskManager::CreateInstance();
|
||||
|
||||
// Initialize task manager
|
||||
TaskManager::CreateInstance();
|
||||
|
||||
@@ -226,8 +219,6 @@ void Core::Stop()
|
||||
|
||||
NodeFactory::Destroy();
|
||||
|
||||
IndexManager::DestroyInstance();
|
||||
|
||||
delete main_window_;
|
||||
}
|
||||
|
||||
@@ -565,6 +556,9 @@ void Core::StartGUI(bool full_screen)
|
||||
// Initialize audio service
|
||||
AudioManager::CreateInstance();
|
||||
|
||||
// Initialize disk service
|
||||
DiskManager::CreateInstance();
|
||||
|
||||
// Initialize pixel service
|
||||
PixelFormat::CreateInstance();
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
AudioStream::AudioStream() :
|
||||
index_done_(false)
|
||||
AudioStream::AudioStream()
|
||||
{
|
||||
set_type(kAudio);
|
||||
}
|
||||
@@ -65,68 +64,32 @@ void AudioStream::set_sample_rate(const int &sample_rate)
|
||||
sample_rate_ = sample_rate;
|
||||
}
|
||||
|
||||
const rational &AudioStream::index_length()
|
||||
bool AudioStream::try_start_conforming(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
return index_length_;
|
||||
}
|
||||
|
||||
void AudioStream::set_index_length(const rational &index_length)
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
|
||||
index_length_ = index_length;
|
||||
}
|
||||
|
||||
emit IndexChanged();
|
||||
}
|
||||
|
||||
const bool &AudioStream::index_done()
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
|
||||
return index_done_;
|
||||
}
|
||||
|
||||
void AudioStream::set_index_done(const bool& index_done)
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
|
||||
index_done_ = index_done;
|
||||
}
|
||||
|
||||
emit IndexChanged();
|
||||
}
|
||||
|
||||
void AudioStream::clear_index()
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
|
||||
index_done_ = false;
|
||||
index_length_ = 0;
|
||||
}
|
||||
|
||||
bool AudioStream::has_conformed_version(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
|
||||
foreach (const AudioRenderingParams& p, conformed_) {
|
||||
if (p == params) {
|
||||
return true;
|
||||
}
|
||||
if (!currently_conforming_.contains(params)
|
||||
&& !conformed_.contains(params)) {
|
||||
currently_conforming_.append(params);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AudioStream::has_conformed_version(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
return conformed_.contains(params);
|
||||
}
|
||||
|
||||
void AudioStream::append_conformed_version(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
currently_conforming_.removeOne(params);
|
||||
conformed_.append(params);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,29 +49,21 @@ public:
|
||||
const int& sample_rate() const;
|
||||
void set_sample_rate(const int& sample_rate);
|
||||
|
||||
const rational& index_length();
|
||||
void set_index_length(const rational& index_length);
|
||||
|
||||
const bool& index_done();
|
||||
void set_index_done(const bool &index_done);
|
||||
|
||||
void clear_index();
|
||||
|
||||
bool try_start_conforming(const AudioRenderingParams& params);
|
||||
bool has_conformed_version(const AudioRenderingParams& params);
|
||||
void append_conformed_version(const AudioRenderingParams& params);
|
||||
|
||||
signals:
|
||||
void ConformAppended(const AudioRenderingParams& params);
|
||||
void ConformAppended(OLIVE_NAMESPACE::AudioRenderingParams params);
|
||||
|
||||
private:
|
||||
int channels_;
|
||||
uint64_t layout_;
|
||||
int sample_rate_;
|
||||
|
||||
rational index_length_;
|
||||
bool index_done_;
|
||||
QList<AudioRenderingParams> conformed_;
|
||||
|
||||
QVector<AudioRenderingParams> conformed_;
|
||||
QList<AudioRenderingParams> currently_conforming_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -139,14 +139,9 @@ QIcon Stream::IconFromType(const Stream::Type &type)
|
||||
return QIcon();
|
||||
}
|
||||
|
||||
QMutex* Stream::index_process_lock()
|
||||
QMutex *Stream::proxy_access_lock()
|
||||
{
|
||||
return &index_process_lock_;
|
||||
}
|
||||
|
||||
QMutex *Stream::index_access_lock()
|
||||
{
|
||||
return &index_access_lock_;
|
||||
return &proxy_access_lock_;
|
||||
}
|
||||
|
||||
void Stream::FootageSetEvent(Footage*)
|
||||
|
||||
@@ -92,8 +92,7 @@ public:
|
||||
|
||||
static QIcon IconFromType(const Type& type);
|
||||
|
||||
QMutex* index_process_lock();
|
||||
QMutex* index_access_lock();
|
||||
QMutex* proxy_access_lock();
|
||||
|
||||
protected:
|
||||
virtual void FootageSetEvent(Footage*);
|
||||
@@ -120,9 +119,7 @@ private:
|
||||
|
||||
bool enabled_;
|
||||
|
||||
QMutex index_process_lock_;
|
||||
|
||||
QMutex index_access_lock_;
|
||||
QMutex proxy_access_lock_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -75,14 +75,14 @@ void VideoStream::set_image_sequence(bool e)
|
||||
|
||||
bool VideoStream::has_proxy(const int ÷r)
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
return proxies_.contains(divider);
|
||||
}
|
||||
|
||||
void VideoStream::append_proxy(const int ÷r)
|
||||
{
|
||||
QMutexLocker locker(index_access_lock());
|
||||
QMutexLocker locker(proxy_access_lock());
|
||||
|
||||
proxies_.append(divider);
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ set(OLIVE_SOURCES
|
||||
render/backend/audiorenderbackend.cpp
|
||||
render/backend/audiorenderworker.h
|
||||
render/backend/audiorenderworker.cpp
|
||||
render/backend/indexmanager.h
|
||||
render/backend/indexmanager.cpp
|
||||
|
||||
render/backend/videorenderbackend.h
|
||||
render/backend/videorenderbackend.cpp
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
|
||||
#include "audiorenderworker.h"
|
||||
#include "common/filefunctions.h"
|
||||
#include "render/backend/indexmanager.h"
|
||||
#include "task/conform/conform.h"
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
@@ -33,7 +34,6 @@ AudioRenderBackend::AudioRenderBackend(QObject *parent) :
|
||||
RenderBackend(parent),
|
||||
ic_from_conform_(false)
|
||||
{
|
||||
connect(IndexManager::instance(), &IndexManager::StreamConformAppended, this, &AudioRenderBackend::ConformUpdated);
|
||||
connect(this, &AudioRenderBackend::QueueComplete, this, &AudioRenderBackend::FilterQueueCompleteSignal);
|
||||
}
|
||||
|
||||
@@ -154,6 +154,30 @@ void AudioRenderBackend::InvalidateCacheInternal(const rational &start_range, co
|
||||
RenderBackend::InvalidateCacheInternal(start_range, end_range);
|
||||
}
|
||||
|
||||
void AudioRenderBackend::ListenForConformSignal(AudioStreamPtr s)
|
||||
{
|
||||
foreach (const ConformWaitInfo& info, conform_wait_info_) {
|
||||
if (info.stream == s) {
|
||||
// We've probably already connected to this one
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connect(s.get(), &AudioStream::ConformAppended, this, &AudioRenderBackend::ConformUpdated);
|
||||
}
|
||||
|
||||
void AudioRenderBackend::StopListeningForConformSignal(AudioStream* s)
|
||||
{
|
||||
foreach (const ConformWaitInfo& info, conform_wait_info_) {
|
||||
if (info.stream.get() == s) {
|
||||
// There are still conforms we're waiting for, don't disconnect
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
disconnect(s, &AudioStream::ConformAppended, this, &AudioRenderBackend::ConformUpdated);
|
||||
}
|
||||
|
||||
void AudioRenderBackend::ConformUnavailable(StreamPtr stream, TimeRange range, rational stream_time, AudioRenderingParams params)
|
||||
{
|
||||
ConformWaitInfo info = {stream, params, range, stream_time};
|
||||
@@ -164,26 +188,38 @@ void AudioRenderBackend::ConformUnavailable(StreamPtr stream, TimeRange range, r
|
||||
|
||||
AudioStreamPtr audio_stream = std::static_pointer_cast<AudioStream>(stream);
|
||||
|
||||
if (IndexManager::instance()->IsConforming(audio_stream, params)) {
|
||||
if (audio_stream->try_start_conforming(params)) {
|
||||
|
||||
// Start indexing process
|
||||
ListenForConformSignal(audio_stream);
|
||||
|
||||
conform_wait_info_.append(info);
|
||||
|
||||
ConformTask* conform_task = new ConformTask(audio_stream, params);
|
||||
|
||||
TaskManager::instance()->AddTask(conform_task);
|
||||
|
||||
} else if (audio_stream->has_conformed_version(params)) {
|
||||
|
||||
// Index JUST finished, requeue this time
|
||||
// Conform JUST finished, requeue this time
|
||||
ic_from_conform_ = true;
|
||||
InvalidateCache(range, nullptr);
|
||||
ic_from_conform_ = false;
|
||||
|
||||
} else {
|
||||
|
||||
// Start indexing process
|
||||
// A conform task is already running, so we'll just wait for it
|
||||
ListenForConformSignal(audio_stream);
|
||||
|
||||
conform_wait_info_.append(info);
|
||||
IndexManager::instance()->StartConformingStream(audio_stream, params);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams params)
|
||||
void AudioRenderBackend::ConformUpdated(AudioRenderingParams params)
|
||||
{
|
||||
AudioStream *stream = static_cast<AudioStream*>(sender());
|
||||
|
||||
for (int i=0;i<conform_wait_info_.size();i++) {
|
||||
const ConformWaitInfo& info = conform_wait_info_.at(i);
|
||||
|
||||
@@ -204,6 +240,8 @@ void AudioRenderBackend::ConformUpdated(Stream *stream, AudioRenderingParams par
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
StopListeningForConformSignal(stream);
|
||||
}
|
||||
|
||||
void AudioRenderBackend::TruncateCache(const rational &r)
|
||||
|
||||
@@ -79,6 +79,10 @@ private:
|
||||
bool operator==(const ConformWaitInfo& rhs) const;
|
||||
};
|
||||
|
||||
void ListenForConformSignal(AudioStreamPtr s);
|
||||
|
||||
void StopListeningForConformSignal(AudioStream *s);
|
||||
|
||||
QList<ConformWaitInfo> conform_wait_info_;
|
||||
|
||||
AudioRenderingParams params_;
|
||||
@@ -88,7 +92,7 @@ private:
|
||||
private slots:
|
||||
void ConformUnavailable(StreamPtr stream, TimeRange range, rational stream_time, AudioRenderingParams params);
|
||||
|
||||
void ConformUpdated(Stream *stream, AudioRenderingParams params);
|
||||
void ConformUpdated(OLIVE_NAMESPACE::AudioRenderingParams params);
|
||||
|
||||
void TruncateCache(const rational& r);
|
||||
|
||||
|
||||
@@ -1,119 +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 "indexmanager.h"
|
||||
|
||||
#include "task/taskmanager.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
IndexManager* IndexManager::instance_ = nullptr;
|
||||
|
||||
void IndexManager::CreateInstance()
|
||||
{
|
||||
instance_ = new IndexManager();
|
||||
}
|
||||
|
||||
IndexManager *IndexManager::instance()
|
||||
{
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void IndexManager::DestroyInstance()
|
||||
{
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
void IndexManager::StartIndexingStream(StreamPtr stream)
|
||||
{
|
||||
if (IsIndexing(stream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IndexTask* index_task = new IndexTask(stream);
|
||||
indexing_.append({stream, index_task});
|
||||
|
||||
connect(stream.get(), &Stream::IndexChanged, this, &IndexManager::StreamIndexUpdatedEvent, Qt::QueuedConnection);
|
||||
connect(index_task, &IndexTask::Succeeded, this, &IndexManager::IndexTaskFinished, Qt::QueuedConnection);
|
||||
|
||||
TaskManager::instance()->AddTask(index_task);
|
||||
}
|
||||
|
||||
void IndexManager::StartConformingStream(AudioStreamPtr stream, AudioRenderingParams params)
|
||||
{
|
||||
if (IsConforming(stream, params)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConformTask* conform_task = new ConformTask(stream, params);
|
||||
conforming_.append({stream, params, conform_task});
|
||||
|
||||
connect(stream.get(), &AudioStream::ConformAppended, this, &IndexManager::StreamConformAppendedEvent, Qt::QueuedConnection);
|
||||
connect(conform_task, &ConformTask::Succeeded, this, &IndexManager::IndexTaskFinished, Qt::QueuedConnection);
|
||||
|
||||
TaskManager::instance()->AddTask(conform_task);
|
||||
}
|
||||
|
||||
bool IndexManager::IsIndexing(StreamPtr stream) const
|
||||
{
|
||||
foreach (const IndexPair& stp, indexing_) {
|
||||
if (stp.stream == stream) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IndexManager::IsConforming(AudioStreamPtr stream, const AudioRenderingParams ¶ms) const
|
||||
{
|
||||
foreach (const ConformPair& cfp, conforming_) {
|
||||
if (cfp.stream == stream && cfp.params == params) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IndexManager::IndexTaskFinished()
|
||||
{
|
||||
for (int i=0;i<indexing_.size();i++) {
|
||||
const IndexPair& stp = indexing_.at(i);
|
||||
|
||||
if (stp.task == sender()) {
|
||||
indexing_.removeAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IndexManager::StreamIndexUpdatedEvent()
|
||||
{
|
||||
emit StreamIndexUpdated(static_cast<Stream*>(sender()));
|
||||
}
|
||||
|
||||
void IndexManager::StreamConformAppendedEvent(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
emit StreamConformAppended(static_cast<Stream*>(sender()), params);
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -1,82 +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 INDEXMANAGER_H
|
||||
#define INDEXMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "project/item/footage/stream.h"
|
||||
#include "task/conform/conform.h"
|
||||
#include "task/index/index.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class IndexManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
IndexManager() = default;
|
||||
|
||||
static void CreateInstance();
|
||||
static IndexManager* instance();
|
||||
static void DestroyInstance();
|
||||
|
||||
bool IsIndexing(StreamPtr stream) const;
|
||||
bool IsConforming(AudioStreamPtr stream, const AudioRenderingParams& params) const;
|
||||
|
||||
public slots:
|
||||
void StartIndexingStream(OLIVE_NAMESPACE::StreamPtr stream);
|
||||
void StartConformingStream(OLIVE_NAMESPACE::AudioStreamPtr stream, OLIVE_NAMESPACE::AudioRenderingParams params);
|
||||
|
||||
signals:
|
||||
void StreamIndexUpdated(Stream* stream);
|
||||
void StreamConformAppended(Stream* stream, OLIVE_NAMESPACE::AudioRenderingParams params);
|
||||
|
||||
private:
|
||||
static IndexManager* instance_;
|
||||
|
||||
struct IndexPair {
|
||||
StreamPtr stream;
|
||||
IndexTask* task;
|
||||
};
|
||||
|
||||
struct ConformPair {
|
||||
StreamPtr stream;
|
||||
AudioRenderingParams params;
|
||||
ConformTask* task;
|
||||
};
|
||||
|
||||
QList<IndexPair> indexing_;
|
||||
|
||||
QList<ConformPair> conforming_;
|
||||
|
||||
private slots:
|
||||
void IndexTaskFinished();
|
||||
|
||||
void StreamIndexUpdatedEvent();
|
||||
|
||||
void StreamConformAppendedEvent(const AudioRenderingParams& params);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // INDEXMANAGER_H
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <QThread>
|
||||
|
||||
#include "core.h"
|
||||
#include "render/backend/indexmanager.h"
|
||||
#include "window/mainwindow/mainwindow.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
@@ -37,8 +36,6 @@ RenderBackend::RenderBackend(QObject *parent) :
|
||||
{
|
||||
// FIXME: Don't create in CLI mode
|
||||
cancel_dialog_ = new RenderCancelDialog(Core::instance()->main_window());
|
||||
|
||||
connect(IndexManager::instance(), &IndexManager::StreamIndexUpdated, this, &RenderBackend::IndexUpdated);
|
||||
}
|
||||
|
||||
bool RenderBackend::Init()
|
||||
@@ -473,7 +470,6 @@ void RenderBackend::InitWorkers()
|
||||
|
||||
// Connect cancel dialog to it
|
||||
connect(processor, &RenderWorker::CompletedCache, cancel_dialog_, &RenderCancelDialog::WorkerDone, Qt::QueuedConnection);
|
||||
connect(processor, &RenderWorker::FootageUnavailable, this, &RenderBackend::FootageUnavailable, Qt::QueuedConnection);
|
||||
|
||||
// Finally, we can move it to its own thread
|
||||
processor->moveToThread(thread);
|
||||
@@ -486,83 +482,6 @@ void RenderBackend::InitWorkers()
|
||||
processor_busy_state_.fill(false);
|
||||
}
|
||||
|
||||
void RenderBackend::FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange &range, const rational &stream_time)
|
||||
{
|
||||
if (state == Decoder::kFailedToOpen){
|
||||
|
||||
qWarning() << "For range" << range.in() << "-" << range.out() << stream->footage()->filename() << "stream" << stream->index() << "failed to open";
|
||||
|
||||
} else if (state == Decoder::kIndexUnavailable) {
|
||||
|
||||
FootageWaitInfo info = {stream, range, stream_time};
|
||||
|
||||
if (footage_wait_info_.contains(info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Waiting for" << stream.get() << "time" << stream_time.toDouble() << "for frame" << range.in();
|
||||
|
||||
if (IndexManager::instance()->IsIndexing(stream)) {
|
||||
|
||||
footage_wait_info_.append(info);
|
||||
|
||||
} else if ((stream->type() == Stream::kVideo && std::static_pointer_cast<VideoStream>(stream)->is_frame_index_ready())
|
||||
|| (stream->type() == Stream::kAudio && std::static_pointer_cast<AudioStream>(stream)->index_done())) {
|
||||
|
||||
// Index JUST finished, requeue this time
|
||||
InvalidateCache(range, nullptr);
|
||||
|
||||
} else {
|
||||
|
||||
// Start indexing process
|
||||
footage_wait_info_.append(info);
|
||||
IndexManager::instance()->StartIndexingStream(stream);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void RenderBackend::IndexUpdated(Stream* stream)
|
||||
{
|
||||
for (int i=0;i<footage_wait_info_.size();i++) {
|
||||
const FootageWaitInfo& info = footage_wait_info_.at(i);
|
||||
|
||||
if (info.stream.get() == stream) {
|
||||
bool footage_ready = false;
|
||||
|
||||
// See if this stream now has an index for the requested time
|
||||
if (stream->type() == Stream::kVideo) {
|
||||
|
||||
VideoStream* video_stream = static_cast<VideoStream*>(stream);
|
||||
|
||||
if (video_stream->get_closest_timestamp_in_frame_index(info.stream_time) >= 0) {
|
||||
// This index now has this frame, we can re-render it
|
||||
qDebug() << "Re-ICing video" << info.affected_range.in().toDouble() << "to" << info.affected_range.out().toDouble();
|
||||
footage_ready = true;
|
||||
}
|
||||
|
||||
} else if (stream->type() == Stream::kAudio) {
|
||||
|
||||
AudioStream* audio_stream = static_cast<AudioStream*>(stream);
|
||||
|
||||
if (audio_stream->index_length() >= info.stream_time) {
|
||||
// The index now has this audio, we can re-render it
|
||||
qDebug() << "Re-ICing audio" << info.affected_range.in().toDouble() << "to" << info.affected_range.out().toDouble();
|
||||
footage_ready = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (footage_ready) {
|
||||
InvalidateCache(info.affected_range, nullptr);
|
||||
footage_wait_info_.removeAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderBackend::FootageWaitInfo::operator==(const RenderBackend::FootageWaitInfo &rhs) const
|
||||
{
|
||||
return rhs.stream == stream
|
||||
|
||||
@@ -167,11 +167,6 @@ private:
|
||||
|
||||
QList<FootageWaitInfo> footage_wait_info_;
|
||||
|
||||
private slots:
|
||||
void FootageUnavailable(StreamPtr stream, Decoder::RetrieveState state, const TimeRange& path, const rational& stream_time);
|
||||
|
||||
void IndexUpdated(Stream *stream);
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -108,11 +108,6 @@ bool RenderWorker::IsStarted()
|
||||
return started_;
|
||||
}
|
||||
|
||||
void RenderWorker::ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time)
|
||||
{
|
||||
emit FootageUnavailable(stream, state, path_.range(), stream_time);
|
||||
}
|
||||
|
||||
void RenderWorker::InputProcessingEvent(NodeInput* input, const TimeRange& input_time, NodeValueTable *table)
|
||||
{
|
||||
// Exception for Footage types where we actually retrieve some Footage data from a decoder
|
||||
@@ -124,13 +119,8 @@ void RenderWorker::InputProcessingEvent(NodeInput* input, const TimeRange& input
|
||||
|
||||
if (decoder) {
|
||||
|
||||
Decoder::RetrieveState state = decoder->GetRetrieveState(input_time.out());
|
||||
FrameToValue(decoder, stream, input_time, table);
|
||||
|
||||
if (state == Decoder::kReady) {
|
||||
FrameToValue(decoder, stream, input_time, table);
|
||||
} else {
|
||||
ReportUnavailableFootage(stream, state, input_time.out());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +48,6 @@ public slots:
|
||||
signals:
|
||||
void CompletedCache(OLIVE_NAMESPACE::NodeDependency dep, OLIVE_NAMESPACE::NodeValueTable data, qint64 job_time);
|
||||
|
||||
void FootageUnavailable(OLIVE_NAMESPACE::StreamPtr stream, OLIVE_NAMESPACE::Decoder::RetrieveState state, const OLIVE_NAMESPACE::TimeRange& range, const OLIVE_NAMESPACE::rational& stream_time);
|
||||
|
||||
protected:
|
||||
virtual bool InitInternal() = 0;
|
||||
|
||||
@@ -61,8 +59,6 @@ protected:
|
||||
|
||||
virtual void FrameToValue(DecoderPtr decoder, StreamPtr stream, const TimeRange &range, NodeValueTable* table) = 0;
|
||||
|
||||
virtual void ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time);
|
||||
|
||||
virtual void InputProcessingEvent(NodeInput *input, const TimeRange &input_time, NodeValueTable* table) override;
|
||||
|
||||
virtual void ProcessNodeEvent(const Node *node, const TimeRange &range, NodeValueDatabase &input_params, NodeValueTable &output_params) override;
|
||||
|
||||
@@ -295,14 +295,6 @@ NodeValueTable VideoRenderWorker::RenderBlock(const TrackOutput *track, const Ti
|
||||
return table;
|
||||
}
|
||||
|
||||
void VideoRenderWorker::ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational &stream_time)
|
||||
{
|
||||
emit FootageUnavailable(stream,
|
||||
state,
|
||||
TimeRange(CurrentPath().in(), CurrentPath().in() + video_params().time_base()),
|
||||
stream_time);
|
||||
}
|
||||
|
||||
ColorProcessorCache *VideoRenderWorker::color_cache()
|
||||
{
|
||||
return &color_cache_;
|
||||
|
||||
@@ -100,8 +100,6 @@ protected:
|
||||
|
||||
virtual NodeValueTable RenderBlock(const TrackOutput *track, const TimeRange& range) override;
|
||||
|
||||
virtual void ReportUnavailableFootage(StreamPtr stream, Decoder::RetrieveState state, const rational& stream_time) override;
|
||||
|
||||
ColorProcessorCache* color_cache();
|
||||
|
||||
private:
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(conform)
|
||||
add_subdirectory(index)
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
|
||||
@@ -42,7 +42,7 @@ void ConformTask::Action()
|
||||
|
||||
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
|
||||
|
||||
decoder->Conform(params_, &IsCancelled());
|
||||
decoder->ConformAudio(&IsCancelled(), params_);
|
||||
|
||||
emit Succeeded();
|
||||
}
|
||||
|
||||
@@ -1,22 +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/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
task/index/index.h
|
||||
task/index/index.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
@@ -1,51 +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 "index.h"
|
||||
|
||||
#include "codec/decoder.h"
|
||||
#include "codec/ffmpeg/ffmpegdecoder.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
IndexTask::IndexTask(StreamPtr stream) :
|
||||
stream_(stream)
|
||||
{
|
||||
SetTitle(tr("Indexing %1:%2").arg(stream_->footage()->filename(), QString::number(stream_->index())));
|
||||
}
|
||||
|
||||
void IndexTask::Action()
|
||||
{
|
||||
if (stream_->footage()->decoder().isEmpty()) {
|
||||
emit Failed(QStringLiteral("Stream has no decoder"));
|
||||
} else {
|
||||
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
|
||||
|
||||
decoder->set_stream(stream_);
|
||||
|
||||
connect(decoder.get(), &Decoder::IndexProgress, this, &IndexTask::ProgressChanged);
|
||||
|
||||
decoder->Index(&IsCancelled());
|
||||
|
||||
emit Succeeded();
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
@@ -1,44 +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 INDEXTASK_H
|
||||
#define INDEXTASK_H
|
||||
|
||||
#include "project/item/footage/footage.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class IndexTask : public Task
|
||||
{
|
||||
public:
|
||||
IndexTask(StreamPtr stream);
|
||||
|
||||
protected:
|
||||
virtual void Action() override;
|
||||
|
||||
private:
|
||||
StreamPtr stream_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // INDEXTASK_H
|
||||
Reference in New Issue
Block a user