This commit is contained in:
itsmattkc
2021-08-09 01:22:55 -07:00
12 changed files with 50 additions and 612 deletions
-4
View File
@@ -33,9 +33,5 @@ set(OLIVE_SOURCES
codec/frame.h
codec/samplebuffer.cpp
codec/samplebuffer.h
codec/waveinput.cpp
codec/waveinput.h
codec/waveoutput.cpp
codec/waveoutput.h
PARENT_SCOPE
)
+6 -12
View File
@@ -57,19 +57,13 @@ ConformManager::Conform ConformManager::GetConformState(const QString &decoder_i
QString ConformManager::GetConformedFilename(const QString &cache_path, const Decoder::CodecStream &stream, const AudioParams &params)
{
QString index_fn = QStringLiteral("%1.%2:%3").arg(FileFunctions::GetUniqueFileIdentifier(stream.filename()),
QString::number(stream.stream()));
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()));
index_fn = QDir(cache_path).filePath(index_fn);
index_fn.append('.');
index_fn.append(QString::number(params.sample_rate()));
index_fn.append('.');
index_fn.append(QString::number(params.format()));
index_fn.append('.');
index_fn.append(QString::number(params.channel_layout()));
return index_fn;
return QDir(cache_path).filePath(index_fn);
}
void ConformManager::ConformTaskFinished(Task *task, bool succeeded)
+11 -14
View File
@@ -25,8 +25,6 @@
#include "codec/ffmpeg/ffmpegdecoder.h"
#include "codec/oiio/oiiodecoder.h"
#include "codec/waveinput.h"
#include "codec/waveoutput.h"
#include "common/ffmpegutils.h"
#include "common/filefunctions.h"
#include "common/timecodefunctions.h"
@@ -129,7 +127,7 @@ Decoder::RetrieveAudioData Decoder::RetrieveAudio(const TimeRange &range, const
}
// See if we got the conform
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode);
SampleBufferPtr out_buffer = RetrieveAudioFromConform(conform.filename, range, loop_mode, params);
return {kOK, out_buffer, nullptr};
}
@@ -265,13 +263,11 @@ bool Decoder::ConformAudioInternal(const QString& filename, const AudioParams &p
return false;
}
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode)
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
{
WaveInput input(conform_filename);
if (input.open()) {
const AudioParams& input_params = input.params();
QFile input(conform_filename);
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());
@@ -279,12 +275,12 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
while (write_index < packed_data.size()) {
if (loop_mode == Footage::kLoopModeLoop) {
while (read_index >= input.data_length()) {
read_index -= input.data_length();
while (read_index >= input.size()) {
read_index -= input.size();
}
while (read_index < 0) {
read_index += input.data_length();
read_index += input.size();
}
}
@@ -294,13 +290,14 @@ SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filenam
// 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);
} else if (read_index >= input.data_length()) {
} 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);
} else {
write_count = qMin(input.data_length() - read_index, packed_data.size() - write_index);
input.read(read_index, packed_data.data() + write_index, write_count);
write_count = qMin(input.size() - read_index, packed_data.size() - write_index);
input.seek(read_index);
input.read(packed_data.data() + write_index, write_count);
}
read_index += write_count;
+1 -2
View File
@@ -33,7 +33,6 @@ extern "C" {
#include "codec/frame.h"
#include "codec/samplebuffer.h"
#include "codec/waveoutput.h"
#include "common/rational.h"
#include "node/project/footage/footage.h"
#include "node/project/footage/footagedescription.h"
@@ -313,7 +312,7 @@ signals:
private:
void UpdateLastAccessed();
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode);
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams &params);
CodecStream stream_;
+2 -3
View File
@@ -38,7 +38,6 @@ extern "C" {
#include <QThread>
#include <QtConcurrent/QtConcurrent>
#include "codec/waveinput.h"
#include "common/define.h"
#include "common/ffmpegutils.h"
#include "common/filefunctions.h"
@@ -465,7 +464,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
swr_init(resampler);
WaveOutput wave_out(filename, params);
QFile wave_out(filename);
AVPacket* pkt = av_packet_alloc();
AVFrame* frame = av_frame_alloc();
@@ -473,7 +472,7 @@ bool FFmpegDecoder::ConformAudioInternal(const QString &filename, const AudioPar
bool success = false;
if (wave_out.open()) {
if (wave_out.open(QFile::WriteOnly)) {
while (true) {
// Check if we have a `cancelled` ptr and its value
if (cancelled && *cancelled) {
-1
View File
@@ -37,7 +37,6 @@ extern "C" {
#include <QWaitCondition>
#include "codec/decoder.h"
#include "codec/waveoutput.h"
#include "ffmpegframepool.h"
namespace olive {
-244
View File
@@ -1,244 +0,0 @@
/***
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 "waveinput.h"
extern "C" {
#include <libavcodec/avcodec.h>
}
#include <QDataStream>
#include <QtMath>
namespace olive {
WaveInput::WaveInput(const QString &f) :
file_(f)
{
}
WaveInput::~WaveInput()
{
close();
}
bool WaveInput::open()
{
if (!file_.open(QFile::ReadOnly)) {
return false;
}
if (file_.read(4) != "RIFF") {
close();
qCritical() << "No RIFF found";
return false;
}
// Skip filesize bytes
file_.seek(file_.pos() + 4);
if (file_.read(4) != "WAVE") {
close();
qCritical() << "No WAVE found";
return false;
}
// Find fmt_ section
if (!find_str(&file_, "fmt ")) {
close();
qCritical() << "No fmt found";
return false;
}
// Skip fmt_ section size
file_.seek(file_.pos()+4);
// Create data stream for reading bytes into types
QDataStream data_stream(&file_);
data_stream.setByteOrder(QDataStream::LittleEndian);
// Read data type
uint16_t data_type;
data_stream >> data_type;
bool data_is_float;
switch (data_type) {
case 1: // PCM Integer
data_is_float = false;
break;
case 3:
data_is_float = true;
break;
default:
// If it's neither float nor int, we can't work with this file
close();
qCritical() << "Invalid WAV type" << data_type;
return false;
}
// Read number of channels
uint16_t channel_count;
data_stream >> channel_count;
uint64_t channel_layout = static_cast<uint64_t>(av_get_default_channel_layout(channel_count));
int32_t sample_rate;
data_stream >> sample_rate;
// Skip bytes per second value and bytes per sample value
file_.seek(file_.pos() + 6);
uint16_t bits_per_sample;
data_stream >> bits_per_sample;
AudioParams::Format format;
switch (bits_per_sample) {
case 8:
format = AudioParams::kFormatUnsigned8;
break;
case 16:
format = AudioParams::kFormatSigned16;
break;
case 32:
if (data_is_float) {
format = AudioParams::kFormatFloat32;
} else {
format = AudioParams::kFormatSigned32;
}
break;
case 64:
if (data_is_float) {
format = AudioParams::kFormatFloat64;
} else {
format = AudioParams::kFormatSigned64;
}
break;
default:
// We don't know this format...
close();
qCritical() << "Invalid format found" << bits_per_sample;
return false;
}
// We're good to go!
params_ = AudioParams(sample_rate, channel_layout, format);
if (!find_str(&file_, "data")) {
close();
qCritical() << "No data tag found";
return false;
}
data_stream >> data_size_;
data_position_ = file_.pos();
return true;
}
bool WaveInput::is_open() const
{
return file_.isOpen();
}
QByteArray WaveInput::read(qint64 length)
{
if (!is_open()) {
return QByteArray();
}
return file_.read(qMin(calculate_max_read(), length));
}
QByteArray WaveInput::read(qint64 offset, qint64 length)
{
if (!is_open()) {
return QByteArray();
}
seek(offset);
return file_.read(qMin(calculate_max_read(), length));
}
qint64 WaveInput::read(qint64 offset, char *buffer, qint64 length)
{
if (!is_open()) {
return 0;
}
Q_ASSERT(length > 0);
seek(offset);
return file_.read(buffer, qMin(calculate_max_read(), length));
}
bool WaveInput::seek(qint64 pos)
{
return file_.seek(data_position_ + qMin(pos, qint64(data_size_)));
}
bool WaveInput::at_end() const
{
return file_.pos() == (data_position_ + data_size_);
}
const AudioParams &WaveInput::params() const
{
return params_;
}
void WaveInput::close()
{
if (file_.isOpen()) {
file_.close();
}
}
const quint32 &WaveInput::data_length() const
{
return data_size_;
}
int WaveInput::sample_count() const
{
return params_.bytes_to_samples(data_size_);
}
bool WaveInput::find_str(QFile *f, const char *str)
{
qint64 pos = f->pos();
while (f->read(4) != str) {
if (f->atEnd()) {
return false;
}
pos++;
f->seek(pos);
}
return true;
}
qint64 WaveInput::calculate_max_read() const
{
return data_size_ - (file_.pos() - data_position_ );
}
}
-75
View File
@@ -1,75 +0,0 @@
/***
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 WAVEINPUT_H
#define WAVEINPUT_H
#include <QFile>
#include "render/audioparams.h"
namespace olive {
class WaveInput
{
public:
WaveInput(const QString& f);
~WaveInput();
DISABLE_COPY_MOVE(WaveInput)
bool open();
bool is_open() const;
QByteArray read(qint64 length);
QByteArray read(qint64 offset, qint64 length);
qint64 read(qint64 offset, char *buffer, qint64 length);
bool seek(qint64 pos);
bool at_end() const;
const AudioParams& params() const;
void close();
const quint32& data_length() const;
int sample_count() const;
private:
bool find_str(QFile* f, const char* str);
qint64 calculate_max_read() const;
AudioParams params_;
QFile file_;
qint64 data_position_;
quint32 data_size_;
};
}
#endif // WAVEINPUT_H
-180
View File
@@ -1,180 +0,0 @@
/***
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 "waveoutput.h"
#include "render/audioparams.h"
namespace olive {
const int16_t kWAVIntegerFormat = 1;
const int16_t kWAVFloatFormat = 3;
WaveOutput::WaveOutput(const QString &f,
const AudioParams& params) :
file_(f),
params_(params)
{
Q_ASSERT(params_.is_valid());
}
WaveOutput::~WaveOutput()
{
close();
}
bool WaveOutput::open()
{
data_length_ = 0;
if (file_.open(QFile::WriteOnly)) {
// RIFF header
file_.write("RIFF");
// Total file size minus RIFF and this integer (minus 8 bytes, filled in later)
write_int<int32_t>(&file_, 0);
// File type header
file_.write("WAVE");
// Begin format descriptor chunk
file_.write("fmt ");
// Format chunk size
write_int<int32_t>(&file_, 16);
// Type of format
switch (params_.format()) {
case AudioParams::kFormatUnsigned8:
case AudioParams::kFormatSigned16:
case AudioParams::kFormatSigned32:
case AudioParams::kFormatSigned64:
write_int<int16_t>(&file_, kWAVIntegerFormat);
break;
case AudioParams::kFormatFloat32:
case AudioParams::kFormatFloat64:
write_int<int16_t>(&file_, kWAVFloatFormat);
break;
case AudioParams::kFormatInvalid:
case AudioParams::kFormatCount:
qWarning() << "Invalid sample format for WAVE audio";
file_.close();
return false;
}
// Number of channels
write_int<int16_t>(&file_, static_cast<int16_t>(params_.channel_count()));
// Sample rate
write_int<int32_t>(&file_, params_.sample_rate());
// Bytes per second
write_int<int32_t>(&file_, params_.samples_to_bytes(params_.sample_rate()));
// Bytes per sample
write_int<int16_t>(&file_, static_cast<int16_t>(params_.samples_to_bytes(1)));
// Bits per sample per channel
write_int<int16_t>(&file_, static_cast<int16_t>(params_.bits_per_sample()));
// Data chunk header
file_.write("data");
// Size of data chunk (filled in later)
write_int<int32_t>(&file_, 0);
return true;
}
return false;
}
void WaveOutput::write(const QByteArray &bytes)
{
if (file_.isOpen()) {
file_.write(bytes);
data_length_ += bytes.size();
}
}
void WaveOutput::write(const char *bytes, int length)
{
if (file_.isOpen()) {
file_.write(bytes, length);
data_length_ += length;
}
}
void WaveOutput::close()
{
if (file_.isOpen()) {
// Write file sizes
file_.seek(4);
write_int<int32_t>(&file_, data_length_ + 36);
file_.seek(40);
write_int<int32_t>(&file_, data_length_);
file_.close();
}
}
const int& WaveOutput::data_length() const
{
return data_length_;
}
const AudioParams &WaveOutput::params() const
{
return params_;
}
void WaveOutput::switch_endianness(QByteArray& array)
{
int half_sz = array.size()/2;
for (int i=0;i<half_sz;i++) {
int oppose_index = array.size() - i - 1;
char temp = array[i];
array[i] = array[oppose_index];
array[oppose_index] = temp;
}
}
template<typename T>
void WaveOutput::write_int(QFile *file, T integer)
{
QByteArray bytes;
bytes.resize(sizeof(T));
memcpy(bytes.data(), &integer, static_cast<size_t>(bytes.size()));
// WAV expects little-endian, so if the integer is big endian we need to switch
if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
switch_endianness(bytes);
}
file->write(bytes);
}
}
-68
View File
@@ -1,68 +0,0 @@
/***
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 WAVEAUDIO_H
#define WAVEAUDIO_H
#include <QByteArray>
#include <QFile>
#include "render/audioparams.h"
namespace olive {
class WaveOutput
{
public:
WaveOutput(const QString& f,
const AudioParams& params);
~WaveOutput();
DISABLE_COPY_MOVE(WaveOutput)
bool open();
void write(const QByteArray& bytes);
void write(const char* bytes, int length);
void close();
const int& data_length() const;
const AudioParams& params() const;
private:
template<typename T>
void write_int(QFile* file, T integer);
void switch_endianness(QByteArray &array);
QFile file_;
AudioParams params_;
int data_length_;
};
}
#endif // WAVEAUDIO_H
+23 -9
View File
@@ -26,6 +26,7 @@
#include <QUuid>
#include "common/filefunctions.h"
#include "node/output/viewer/viewer.h"
namespace olive {
@@ -390,7 +391,14 @@ void AudioPlaybackCache::UpdateOffsetsFrom(int index)
AudioPlaybackCache::PlaybackDevice *AudioPlaybackCache::CreatePlaybackDevice(QObject* parent) const
{
return new PlaybackDevice(playlist_, params_.bytes_per_sample_per_channel(), parent);
PlaybackDevice *d = new PlaybackDevice(playlist_, params_.bytes_per_sample_per_channel(), parent);
// If we're child of a viewer, set the data limit so audio doesn't play beyond the length
if (ViewerOutput *viewer = viewer_parent()) {
d->SetDataLimit(params_.time_to_bytes_per_channel(viewer->GetAudioLength()));
}
return d;
}
AudioPlaybackCache::Segment::Segment(qint64 size)
@@ -403,7 +411,8 @@ AudioPlaybackCache::PlaybackDevice::PlaybackDevice(const AudioPlaybackCache::Pla
playlist_(playlist),
current_segment_(0),
segment_read_index_(0),
sample_size_(sample_sz)
sample_size_(sample_sz),
limit_(INT64_MAX)
{
}
@@ -437,10 +446,15 @@ qint64 AudioPlaybackCache::PlaybackDevice::readData(char *data, qint64 maxSize)
while (read_size < maxSize
&& current_segment_ >= 0
&& current_segment_ < playlist_.size()) {
&& current_segment_ < playlist_.size()
&& playlist_.at(current_segment_).offset() + segment_read_index_ < limit_) {
const Segment& cs = playlist_.at(current_segment_);
qint64 current_segment_sz = cs.size();
if (cs.offset() + current_segment_sz > limit_) {
current_segment_sz = limit_ - cs.offset();
}
QVector<QFile*> segment_files(cs.channels());
segment_files.fill(nullptr);
@@ -480,13 +494,13 @@ qint64 AudioPlaybackCache::PlaybackDevice::readData(char *data, qint64 maxSize)
// Add to the read index
segment_read_index_ += sample_size_;
}
// If we've reached the end of this segment, tick the counter over to the next segment
if (segment_read_index_ == current_segment_sz) {
// Jump to the next file
segment_read_index_ = 0;
current_segment_++;
}
// If we've reached the end of this segment, tick the counter over to the next segment
if (segment_read_index_ == current_segment_sz) {
// Jump to the next file
segment_read_index_ = 0;
current_segment_++;
}
}
+7
View File
@@ -145,6 +145,11 @@ public:
public:
PlaybackDevice(const Playlist& playlist, int sample_sz, QObject* parent = nullptr);
void SetDataLimit(qint64 limit)
{
limit_ = limit;
}
virtual ~PlaybackDevice() override;
virtual bool isSequential() const override
@@ -178,6 +183,8 @@ public:
int sample_size_;
qint64 limit_;
};
/**