From d713ba3be779546d8a34a6815a8a024293c2632b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 8 Aug 2021 17:05:41 -0700 Subject: [PATCH] fixed 32-bit integer overflow on long audio --- app/codec/CMakeLists.txt | 4 - app/codec/conformmanager.cpp | 18 +-- app/codec/decoder.cpp | 25 ++- app/codec/decoder.h | 3 +- app/codec/ffmpeg/ffmpegdecoder.cpp | 5 +- app/codec/ffmpeg/ffmpegdecoder.h | 1 - app/codec/waveinput.cpp | 244 ----------------------------- app/codec/waveinput.h | 75 --------- app/codec/waveoutput.cpp | 180 --------------------- app/codec/waveoutput.h | 68 -------- 10 files changed, 20 insertions(+), 603 deletions(-) delete mode 100644 app/codec/waveinput.cpp delete mode 100644 app/codec/waveinput.h delete mode 100644 app/codec/waveoutput.cpp delete mode 100644 app/codec/waveoutput.h diff --git a/app/codec/CMakeLists.txt b/app/codec/CMakeLists.txt index 94d6b4062..42f456059 100644 --- a/app/codec/CMakeLists.txt +++ b/app/codec/CMakeLists.txt @@ -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 ) diff --git a/app/codec/conformmanager.cpp b/app/codec/conformmanager.cpp index 38b5874a5..340f1e763 100644 --- a/app/codec/conformmanager.cpp +++ b/app/codec/conformmanager.cpp @@ -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 ¶ms) { - 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) diff --git a/app/codec/decoder.cpp b/app/codec/decoder.cpp index 5647d1519..cac7d553f 100644 --- a/app/codec/decoder.cpp +++ b/app/codec/decoder.cpp @@ -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; diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 5a7382444..3b9a64ac9 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -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 ¶ms); CodecStream stream_; diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 7b8659603..ccee600aa 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -38,7 +38,6 @@ extern "C" { #include #include -#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) { diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index 62572a904..1239cc421 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -37,7 +37,6 @@ extern "C" { #include #include "codec/decoder.h" -#include "codec/waveoutput.h" #include "ffmpegframepool.h" namespace olive { diff --git a/app/codec/waveinput.cpp b/app/codec/waveinput.cpp deleted file mode 100644 index b7342a88c..000000000 --- a/app/codec/waveinput.cpp +++ /dev/null @@ -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 . - -***/ - -#include "waveinput.h" - -extern "C" { -#include -} - -#include -#include - -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(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_ ); -} - -} diff --git a/app/codec/waveinput.h b/app/codec/waveinput.h deleted file mode 100644 index d8b863da1..000000000 --- a/app/codec/waveinput.h +++ /dev/null @@ -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 . - -***/ - -#ifndef WAVEINPUT_H -#define WAVEINPUT_H - -#include - -#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 diff --git a/app/codec/waveoutput.cpp b/app/codec/waveoutput.cpp deleted file mode 100644 index d5fd431c5..000000000 --- a/app/codec/waveoutput.cpp +++ /dev/null @@ -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 . - -***/ - -#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(&file_, 0); - - // File type header - file_.write("WAVE"); - - // Begin format descriptor chunk - file_.write("fmt "); - - // Format chunk size - write_int(&file_, 16); - - // Type of format - switch (params_.format()) { - case AudioParams::kFormatUnsigned8: - case AudioParams::kFormatSigned16: - case AudioParams::kFormatSigned32: - case AudioParams::kFormatSigned64: - write_int(&file_, kWAVIntegerFormat); - break; - case AudioParams::kFormatFloat32: - case AudioParams::kFormatFloat64: - write_int(&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(&file_, static_cast(params_.channel_count())); - - // Sample rate - write_int(&file_, params_.sample_rate()); - - // Bytes per second - write_int(&file_, params_.samples_to_bytes(params_.sample_rate())); - - // Bytes per sample - write_int(&file_, static_cast(params_.samples_to_bytes(1))); - - // Bits per sample per channel - write_int(&file_, static_cast(params_.bits_per_sample())); - - // Data chunk header - file_.write("data"); - - // Size of data chunk (filled in later) - write_int(&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(&file_, data_length_ + 36); - - file_.seek(40); - write_int(&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 -void WaveOutput::write_int(QFile *file, T integer) -{ - QByteArray bytes; - bytes.resize(sizeof(T)); - memcpy(bytes.data(), &integer, static_cast(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); -} - -} diff --git a/app/codec/waveoutput.h b/app/codec/waveoutput.h deleted file mode 100644 index 1626a207c..000000000 --- a/app/codec/waveoutput.h +++ /dev/null @@ -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 . - -***/ - -#ifndef WAVEAUDIO_H -#define WAVEAUDIO_H - -#include -#include - -#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 - void write_int(QFile* file, T integer); - - void switch_endianness(QByteArray &array); - - QFile file_; - - AudioParams params_; - - int data_length_; - -}; - -} - -#endif // WAVEAUDIO_H