Merge branch 'master' into pr/1770

This commit is contained in:
itsmattkc
2021-10-13 16:02:36 -07:00
17 changed files with 340 additions and 110 deletions
+2
View File
@@ -31,6 +31,8 @@ set(OLIVE_SOURCES
codec/exportformat.h
codec/frame.cpp
codec/frame.h
codec/planarfiledevice.cpp
codec/planarfiledevice.h
codec/samplebuffer.cpp
codec/samplebuffer.h
PARENT_SCOPE
+47 -20
View File
@@ -14,9 +14,9 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
QMutexLocker locker(&mutex_);
// Return existing conform if exists
QString filename = GetConformedFilename(cache_path, stream, params);
if (QFileInfo::exists(filename)) {
return {kConformExists, filename, nullptr};
QVector<QString> filenames = GetConformedFilename(cache_path, stream, params);
if (AllConformsExist(filenames)) {
return {kConformExists, filenames, nullptr};
}
ConformTask *conforming_task = nullptr;
@@ -34,36 +34,56 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
// 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 = filename;
working_fn.append(QStringLiteral(".working"));
QVector<QString> working_filenames = filenames;
for (int i=0; i<working_filenames.size(); i++) {
working_filenames[i].append(QStringLiteral(".working"));
}
conforming_task = new ConformTask(decoder_id, stream, params, working_fn);
conforming_task = new ConformTask(decoder_id, stream, params, working_filenames);
connect(conforming_task, &ConformTask::Finished, this, &ConformManager::ConformTaskFinished);
conforming_task->moveToThread(TaskManager::instance()->thread());
QMetaObject::invokeMethod(TaskManager::instance(), "AddTask", Qt::QueuedConnection, Q_ARG(Task *, conforming_task));
conforming_.append({stream, params, conforming_task, working_fn, filename});
conforming_.append({stream, params, conforming_task, working_filenames, filenames});
}
if (wait) {
do {
conform_done_condition_.wait(&mutex_);
} while (!QFileInfo::exists(filename));
return {kConformExists, filename, nullptr};
} while (!AllConformsExist(filenames));
return {kConformExists, filenames, nullptr};
}
return {kConformGenerating, QString(), conforming_task};
return {kConformGenerating, QVector<QString>(), conforming_task};
}
QString ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params)
QVector<QString> ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params)
{
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(stream.stream()),
QString::number(params.sample_rate()),
QString::number(params.format()),
QString::number(params.channel_layout()));
QVector<QString> filenames(params.channel_count());
return QDir(cache_path).filePath(index_fn);
for (int i=0; i<filenames.size(); i++) {
QString index_fn = QStringLiteral("%1-%2.%3.%4.%5.%6.pcm").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(stream.stream()),
QString::number(params.sample_rate()),
QString::number(params.format()),
QString::number(params.channel_layout()),
QString::number(i));
filenames[i] = QDir(cache_path).filePath(index_fn);
}
return filenames;
}
bool ConformManager::AllConformsExist(const QVector<QString> &filenames)
{
foreach (const QString &fn, filenames) {
if (!QFileInfo::exists(fn)) {
return false;
}
}
return true;
}
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
@@ -84,15 +104,22 @@ void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
if (succeeded) {
// Move file to standard conform name, making it clear this conform is ready for use
QFile::remove(data.finished_filename);
QFile::rename(data.working_filename, data.finished_filename);
for (int i=0; i<data.finished_filename.size(); i++) {
const QString &finished = data.finished_filename.at(i);
const QString &working = data.working_filename.at(i);
QFile::remove(finished);
QFile::rename(working, finished);
}
conform_done_condition_.wakeAll();
locker.unlock();
emit ConformReady();
} else {
// Failed, just delete the working filename if exists
QFile::remove(data.working_filename);
for (int i=0; i<data.working_filename.size(); i++) {
QFile::remove(data.working_filename.at(i));
}
}
}
+6 -4
View File
@@ -38,7 +38,7 @@ public:
struct Conform {
ConformState state;
QString filename;
QVector<QString> filenames;
ConformTask *task;
};
@@ -65,8 +65,8 @@ private:
Decoder::CodecStream stream;
AudioParams params;
ConformTask *task;
QString working_filename;
QString finished_filename;
QVector<QString> working_filename;
QVector<QString> finished_filename;
};
QVector<ConformData> conforming_;
@@ -74,7 +74,9 @@ private:
/**
* @brief Get the destination filename of an audio stream conformed to a set of parameters
*/
static QString GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params);
static QVector<QString> GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params);
static bool AllConformsExist(const QVector<QString> &filenames);
private slots:
void ConformTaskFinished(Task *task, bool succeeded);
+20 -21
View File
@@ -24,6 +24,7 @@
#include <QDebug>
#include "codec/ffmpeg/ffmpegdecoder.h"
#include "codec/planarfiledevice.h"
#include "codec/oiio/oiiodecoder.h"
#include "common/ffmpegutils.h"
#include "common/filefunctions.h"
@@ -131,7 +132,7 @@ Decoder::RetrieveAudioData Decoder::RetrieveAudio(const TimeRange &range, const
}
// See if we got the conform
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode, params);
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filenames, range, loop_mode, params);
return {kOK, out_buffer, nullptr};
}
@@ -156,9 +157,9 @@ void Decoder::Close()
}
}
bool Decoder::ConformAudio(const QString &output_filename, const AudioParams &params, const QAtomicInt *cancelled)
bool Decoder::ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, const QAtomicInt *cancelled)
{
return ConformAudioInternal(output_filename, params, cancelled);
return ConformAudioInternal(output_filenames, params, cancelled);
}
/*
@@ -260,25 +261,26 @@ FramePtr Decoder::RetrieveVideoInternal(const rational &timecode, const Retrieve
return nullptr;
}
bool Decoder::ConformAudioInternal(const QString& filename, const AudioParams &params, const QAtomicInt* cancelled)
bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, const QAtomicInt* cancelled)
{
Q_UNUSED(filename)
Q_UNUSED(filenames)
Q_UNUSED(cancelled)
Q_UNUSED(params)
return false;
}
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
{
QFile input(conform_filename);
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
SampleBufferPtr sample_buffer = SampleBuffer::CreateAllocated(input_params, range.length());
if (input.open(QFile::ReadOnly)) {
QByteArray packed_data(input_params.time_to_bytes(range.length()), Qt::Uninitialized);
qint64 read_index = input_params.time_to_bytes(range.in());
qint64 read_index = input_params.time_to_bytes(range.in()) / input_params.channel_count();
qint64 write_index = 0;
while (write_index < packed_data.size()) {
const qint64 buffer_length_in_bytes = sample_buffer->sample_count() * input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) {
if (loop_mode == Footage::kLoopModeLoop) {
while (read_index >= input.size()) {
read_index -= input.size();
@@ -293,16 +295,16 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
if (read_index < 0) {
// Reading before 0, write silence here until audio data would actually start
write_count = qMin(-read_index, qint64(packed_data.size()));
memset(packed_data.data() + write_index, 0, write_count);
write_count = qMin(-read_index, buffer_length_in_bytes);
sample_buffer->silence_bytes(write_index, write_index + write_count);
} else if (read_index >= input.size()) {
// Reading after data length, write silence until the end of the buffer
write_count = packed_data.size() - write_index;
memset(packed_data.data() + write_index, 0, write_count);
write_count = buffer_length_in_bytes - write_index;
sample_buffer->silence_bytes(write_index, write_index + write_count);
} else {
write_count = qMin(input.size() - read_index, packed_data.size() - write_index);
write_count = qMin(input.size() - read_index, buffer_length_in_bytes - write_index);
input.seek(read_index);
input.read(packed_data.data() + write_index, write_count);
input.read(reinterpret_cast<char**>(sample_buffer->to_raw_ptrs()), write_count, write_index);
}
read_index += write_count;
@@ -311,9 +313,6 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
input.close();
// Create sample buffer
SampleBufferPtr sample_buffer = SampleBuffer::CreateFromPackedData(input_params, packed_data);
return sample_buffer;
}
+3 -3
View File
@@ -236,7 +236,7 @@ public:
/**
* @brief Conform audio stream
*/
bool ConformAudio(const QString &output_filename, const AudioParams &params, const QAtomicInt *cancelled = nullptr);
bool ConformAudio(const QVector<QString> &output_filenames, const AudioParams &params, const QAtomicInt *cancelled = nullptr);
/**
* @brief Create a Decoder instance using a Decoder ID
@@ -286,7 +286,7 @@ protected:
*/
virtual FramePtr RetrieveVideoInternal(const rational& timecode, const RetrieveVideoParams& divider, const QAtomicInt *cancelled);
virtual bool ConformAudioInternal(const QString& filename, const AudioParams &params, const QAtomicInt* cancelled);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, const QAtomicInt* cancelled);
void SignalProcessingProgress(int64_t ts, int64_t duration);
@@ -312,7 +312,7 @@ signals:
private:
void UpdateLastAccessed();
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams &params);
SampleBufferPtr RetrieveAudioFromConform(const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams &params);
CodecStream stream_;
+26 -15
View File
@@ -38,6 +38,7 @@ extern "C" {
#include <QThread>
#include <QtConcurrent/QtConcurrent>
#include "codec/planarfiledevice.h"
#include "common/define.h"
#include "common/ffmpegutils.h"
#include "common/filefunctions.h"
@@ -437,7 +438,7 @@ QString FFmpegDecoder::FFmpegError(int error_code)
return QStringLiteral("%1 %2").arg(QString::number(error_code), err);
}
bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioParams &params, const QAtomicInt *cancelled)
bool FFmpegDecoder::ConformAudioInternal(const QVector<QString> &filenames, const AudioParams &params, const QAtomicInt *cancelled)
{
// Iterate through each audio frame and extract the PCM data
@@ -454,7 +455,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
// Create resampling context
SwrContext* resampler = swr_alloc_set_opts(nullptr,
params.channel_layout(),
FFmpegUtils::GetFFmpegSampleFormat(params.format()),
FFmpegUtils::GetFFmpegSampleFormat(params.format(), true),
params.sample_rate(),
channel_layout,
static_cast<AVSampleFormat>(instance_.avstream()->codecpar->format),
@@ -464,8 +465,6 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
swr_init(resampler);
QFile wave_out(filename);
AVPacket* pkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
int ret;
@@ -481,7 +480,12 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
}
}
if (wave_out.open(QFile::WriteOnly)) {
PlanarFileDevice wave_out;
if (wave_out.open(filenames, QFile::WriteOnly)) {
int nb_channels = params.channel_count();
SampleBuffer data;
data.set_audio_params(params);
while (true) {
// Check if we have a `cancelled` ptr and its value
if (cancelled && *cancelled) {
@@ -505,15 +509,30 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
// Allocate buffers
int nb_samples = swr_get_out_samples(resampler, frame->nb_samples);
char* data = new char[params.samples_to_bytes(nb_samples)];
int nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
data.set_sample_count(nb_bytes_per_channel);
data.allocate();
// Resample audio to our destination parameters
nb_samples = swr_convert(resampler,
reinterpret_cast<uint8_t**>(&data),
reinterpret_cast<uint8_t**>(data.to_raw_ptrs()),
nb_samples,
const_cast<const uint8_t**>(frame->data),
frame->nb_samples);
// If no error, write to files
if (nb_samples > 0) {
// Update byte count for the number of samples we actually received
nb_bytes_per_channel = params.samples_to_bytes(nb_samples) / nb_channels;
// Write to files
wave_out.write(const_cast<const char**>(reinterpret_cast<char**>(data.to_raw_ptrs())), nb_bytes_per_channel);
}
// Free buffer
data.destroy();
// Handle error now after freeing
if (nb_samples < 0) {
char err_str[512];
av_strerror(nb_samples, err_str, 512);
@@ -521,14 +540,6 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
break;
}
// Write packed WAV data to the disk cache
wave_out.write(data, params.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;
}
SignalProcessingProgress(frame->pts, duration);
}
+1 -1
View File
@@ -64,7 +64,7 @@ public:
protected:
virtual bool OpenInternal() override;
virtual FramePtr RetrieveVideoInternal(const rational &timecode, const RetrieveVideoParams& params, const QAtomicInt *cancelled) override;
virtual bool ConformAudioInternal(const QString& filename, const AudioParams &params, const QAtomicInt* cancelled) override;
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, const QAtomicInt* cancelled) override;
virtual void CloseInternal() override;
private:
+119
View File
@@ -0,0 +1,119 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 "planarfiledevice.h"
namespace olive {
PlanarFileDevice::PlanarFileDevice(QObject *parent) :
QObject(parent)
{
}
PlanarFileDevice::~PlanarFileDevice()
{
close();
}
bool PlanarFileDevice::open(const QVector<QString> &filenames, QIODevice::OpenMode mode)
{
if (isOpen()) {
// Already open
return false;
}
files_.resize(filenames.size());
files_.fill(nullptr);
for (int i=0; i<files_.size(); i++) {
files_[i] = new QFile(filenames.at(i));
if (!files_[i]->open(mode)) {
close();
return false;
}
}
return true;
}
qint64 PlanarFileDevice::read(char **data, qint64 bytes_per_channel, qint64 offset)
{
qint64 ret = -1;
if (isOpen()) {
for (int i=0; i<files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->read(data[i] + offset, bytes_per_channel);
}
}
return ret;
}
qint64 PlanarFileDevice::write(const char **data, qint64 bytes_per_channel, qint64 offset)
{
qint64 ret = -1;
if (isOpen()) {
for (int i=0; i<files_.size(); i++) {
// Kind of clunky but should be largely fine
ret = files_[i]->write(data[i] + offset, bytes_per_channel);
}
}
return ret;
}
qint64 PlanarFileDevice::size() const
{
if (isOpen()) {
return files_.first()->size();
} else {
return 0;
}
}
bool PlanarFileDevice::seek(qint64 pos)
{
bool ret = true;
for (int i=0; i<files_.size(); i++) {
ret = files_[i]->seek(pos) & ret;
}
return ret;
}
void PlanarFileDevice::close()
{
for (int i=0; i<files_.size(); i++) {
QFile *f = files_.at(i);
if (f) {
if (f->isOpen()) {
f->close();
}
delete f;
}
}
files_.clear();
}
}
+64
View File
@@ -0,0 +1,64 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2021 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 PLANARFILEDEVICE_H
#define PLANARFILEDEVICE_H
#include <QFile>
#include <QObject>
#include "codec/samplebuffer.h"
#include "common/define.h"
namespace olive {
class PlanarFileDevice : public QObject
{
Q_OBJECT
public:
PlanarFileDevice(QObject *parent = nullptr);
virtual ~PlanarFileDevice() override;
bool isOpen() const
{
return !files_.isEmpty();
}
bool open(const QVector<QString> &filenames, QIODevice::OpenMode mode);
qint64 read(char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 write(const char **data, qint64 bytes_per_channel, qint64 offset = 0);
qint64 size() const;
bool seek(qint64 pos);
void close();
private:
QVector<QFile*> files_;
};
}
#endif // PLANARFILEDEVICE_H
+18 -25
View File
@@ -48,30 +48,6 @@ SampleBufferPtr SampleBuffer::CreateAllocated(const AudioParams &audio_params, i
return buffer;
}
SampleBufferPtr SampleBuffer::CreateFromPackedData(const AudioParams &audio_params, const QByteArray &bytes)
{
if (!audio_params.is_valid()) {
qWarning() << "Tried to create from packed data with invalid parameters";
return nullptr;
}
int samples_per_channel = audio_params.bytes_to_samples(bytes.size());
SampleBufferPtr buffer = CreateAllocated(audio_params, samples_per_channel);
int total_samples = samples_per_channel * audio_params.channel_count();
const float* packed_data = reinterpret_cast<const float*>(bytes.constData());
for (int i=0;i<total_samples;i++) {
int channel = i % audio_params.channel_count();
int index = i / audio_params.channel_count();
buffer->data_[channel][index] = packed_data[i];
}
return buffer;
}
const AudioParams &SampleBuffer::audio_params() const
{
return audio_params_;
@@ -128,11 +104,14 @@ void SampleBuffer::allocate()
for (int i=0; i<audio_params_.channel_count(); i++) {
data_[i].resize(sample_count_per_channel_);
}
update_raw();
}
void SampleBuffer::destroy()
{
data_.clear();
raw_ptrs_.clear();
}
void SampleBuffer::reverse()
@@ -178,6 +157,7 @@ void SampleBuffer::speed(double speed)
}
data_ = output_data;
update_raw();
}
void SampleBuffer::transform_volume(float f)
@@ -214,6 +194,11 @@ void SampleBuffer::silence()
}
void SampleBuffer::silence(int start_sample, int end_sample)
{
silence_bytes(start_sample * sizeof(float), end_sample * sizeof(float));
}
void SampleBuffer::silence_bytes(int start_byte, int end_byte)
{
if (!is_allocated()) {
qWarning() << "Tried to fill an unallocated sample buffer";
@@ -221,7 +206,7 @@ void SampleBuffer::silence(int start_sample, int end_sample)
}
for (int i=0;i<audio_params().channel_count();i++) {
memset(data_[i].data(), start_sample * sizeof(float), (end_sample-start_sample) * sizeof(float));
memset(reinterpret_cast<char*>(data_[i].data()) + start_byte, 0, end_byte - start_byte);
}
}
@@ -235,4 +220,12 @@ void SampleBuffer::set(int channel, const float *data, int sample_offset, int sa
memcpy(&data_[channel].data()[sample_offset], data, sizeof(float) * sample_length);
}
void SampleBuffer::update_raw()
{
raw_ptrs_.resize(data_.size());
for (int i=0; i<raw_ptrs_.size(); i++) {
raw_ptrs_[i] = data_[i].data();
}
}
}
+9 -1
View File
@@ -46,7 +46,6 @@ public:
static SampleBufferPtr Create();
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, const rational& length);
static SampleBufferPtr CreateAllocated(const AudioParams& audio_params, int samples_per_channel);
static SampleBufferPtr CreateFromPackedData(const AudioParams& audio_params, const QByteArray& bytes);
DISABLE_COPY_MOVE(SampleBuffer)
@@ -66,6 +65,11 @@ public:
return data_.at(channel).constData();
}
float **to_raw_ptrs()
{
return raw_ptrs_.data();
}
bool is_allocated() const;
void allocate();
void destroy();
@@ -79,6 +83,7 @@ public:
void silence();
void silence(int start_sample, int end_sample);
void silence_bytes(int start_byte, int end_byte);
void set(int channel, const float* data, int sample_offset, int sample_length);
void set(int channel, const float* data, int sample_length)
@@ -87,11 +92,14 @@ public:
}
private:
void update_raw();
AudioParams audio_params_;
int sample_count_per_channel_;
QVector< QVector<float> > data_;
QVector<float*> raw_ptrs_;
};
+10 -1
View File
@@ -41,6 +41,7 @@ const bool PreviewAutoCacher::kRealTimeWaveformsEnabled = true;
PreviewAutoCacher::PreviewAutoCacher() :
viewer_node_(nullptr),
use_custom_range_(false),
pause_audio_(false),
single_frame_render_(nullptr)
{
// Set defaults
@@ -532,6 +533,14 @@ void PreviewAutoCacher::CancelAudioTasks(bool and_wait_for_them_to_finish)
CancelTasks(audio_tasks_, and_wait_for_them_to_finish);
}
void PreviewAutoCacher::SetAudioPaused(bool e)
{
pause_audio_ = e;
if (!e) {
TryRender();
}
}
void PreviewAutoCacher::NodeAdded(Node *node)
{
graph_update_queue_.append({QueuedJob::kNodeAdded, node, NodeInput(), nullptr});
@@ -672,7 +681,7 @@ void PreviewAutoCacher::TryRender()
}
// Handle audio tasks
while (!audio_iterator_.isEmpty() && audio_tasks_.size() < max_tasks) {
while (!audio_iterator_.isEmpty() && audio_tasks_.size() < max_tasks && !pause_audio_) {
// Copy first range in list
TimeRange r = audio_iterator_.first();
+4
View File
@@ -99,6 +99,8 @@ public:
return queued_frame_iterator_.IsCustomRange() && queued_frame_iterator_.HasNext();
}
void SetAudioPaused(bool e);
signals:
void StopCacheProxyTasks();
@@ -182,6 +184,8 @@ private:
TimeRangeList invalidated_video_;
TimeRangeList invalidated_audio_;
bool pause_audio_;
RenderTicketPtr single_frame_render_;
QList<QFutureWatcher< QVector<HashData> >*> hash_tasks_;
+3 -3
View File
@@ -22,11 +22,11 @@
namespace olive {
ConformTask::ConformTask(const QString &decoder_id, const Decoder::CodecStream &stream, const AudioParams& params, const QString &output_filename) :
ConformTask::ConformTask(const QString &decoder_id, const Decoder::CodecStream &stream, const AudioParams& params, const QVector<QString> &output_filenames) :
decoder_id_(decoder_id),
stream_(stream),
params_(params),
output_filename_(output_filename)
output_filenames_(output_filenames)
{
SetTitle(tr("Conforming Audio %1:%2").arg(stream.filename(), QString::number(stream.stream())));
}
@@ -42,7 +42,7 @@ bool ConformTask::Run()
connect(decoder.get(), &Decoder::IndexProgress, this, &ConformTask::ProgressChanged);
bool ret = decoder->ConformAudio(output_filename_, params_, &IsCancelled());
bool ret = decoder->ConformAudio(output_filenames_, params_, &IsCancelled());
decoder->Close();
+2 -2
View File
@@ -32,7 +32,7 @@ class ConformTask : public Task
{
Q_OBJECT
public:
ConformTask(const QString &decoder_id, const Decoder::CodecStream &stream, const AudioParams& params, const QString &output_filename);
ConformTask(const QString &decoder_id, const Decoder::CodecStream &stream, const AudioParams& params, const QVector<QString> &output_filenames);
protected:
virtual bool Run() override;
@@ -44,7 +44,7 @@ private:
AudioParams params_;
QString output_filename_;
QVector<QString> output_filenames_;
};
-14
View File
@@ -40,20 +40,6 @@ AudioWaveformView::AudioWaveformView(QWidget *parent) :
setBackgroundRole(QPalette::Base);
}
AudioVisualWaveform GenerateWaveform(QIODevice* device, AudioParams params, TimeRange range)
{
device->open(QFile::ReadOnly);
device->seek(params.time_to_bytes(range.in()));
SampleBufferPtr samples = SampleBuffer::CreateFromPackedData(params, device->read(params.time_to_bytes(range.length())));
AudioVisualWaveform waveform;
waveform.set_channel_count(params.channel_count());
waveform.OverwriteSamples(samples, params.sample_rate());
device->close();
delete device;
return waveform;
}
void AudioWaveformView::SetViewer(AudioPlaybackCache *playback)
{
if (playback_) {
+6
View File
@@ -634,6 +634,8 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
viewer->PauseInternal();
viewer->ClearVideoAutoCacherQueue();
}
viewer->auto_cacher_.SetAudioPaused(true);
}
// If the playhead is beyond the end, restart at 0
@@ -722,6 +724,10 @@ void ViewerWidget::PauseInternal()
tempo_processor_.Close();
}
foreach (ViewerWidget* viewer, instances_) {
viewer->auto_cacher_.SetAudioPaused(false);
}
UpdateTextureFromNode();
}