render: properly defined "out of bounds time" behavior with footage

If a user extends a clip beyond the footage's length, they can
choose whether they want a black frame, the last frame continued,
or for the footage to loop infinitely (same for extending earlier
than the start of footage).
This commit is contained in:
itsmattkc
2021-04-23 14:49:21 +10:00
parent 7f93ed7fc0
commit 1fca3e1767
9 changed files with 162 additions and 37 deletions
+39 -7
View File
@@ -107,7 +107,7 @@ FramePtr Decoder::RetrieveVideo(const rational &timecode, const int &divider)
return RetrieveVideoInternal(timecode, divider);
}
SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams &params, const QString& cache_path, const QAtomicInt *cancelled)
SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams &params, const QString& cache_path, Footage::LoopMode loop_mode, const QAtomicInt *cancelled)
{
QMutexLocker locker(&mutex_);
@@ -133,7 +133,7 @@ SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams
}
// See if we got the conform
SampleBufferPtr buffer = RetrieveAudioFromConform(conform_filename, range);
SampleBufferPtr buffer = RetrieveAudioFromConform(conform_filename, range, loop_mode);
if (!buffer) {
// We'll need to conform this ourselves
@@ -151,7 +151,7 @@ SampleBufferPtr Decoder::RetrieveAudio(const TimeRange &range, const AudioParams
QFile::rename(working_fn, conform_filename);
// Return audio as planned
buffer = RetrieveAudioFromConform(conform_filename, range);
buffer = RetrieveAudioFromConform(conform_filename, range, loop_mode);
} else {
// Failed
qCritical() << "Failed to conform audio";
@@ -302,16 +302,48 @@ bool Decoder::ConformAudioInternal(const QString& filename, const AudioParams &p
return false;
}
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range)
SampleBufferPtr Decoder::RetrieveAudioFromConform(const QString &conform_filename, const TimeRange& range, Footage::LoopMode loop_mode)
{
WaveInput input(conform_filename);
if (input.open()) {
const AudioParams& input_params = input.params();
// Read bytes from wav
QByteArray packed_data = input.read(input_params.time_to_bytes(range.in()),
input_params.time_to_bytes(range.length()));
QByteArray packed_data(input_params.time_to_bytes(range.length()), Qt::Uninitialized);
qint64 read_index = input_params.time_to_bytes(range.in());
qint64 write_index = 0;
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 < 0) {
read_index += input.data_length();
}
}
qint64 write_count = 0;
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);
} else if (read_index >= input.data_length()) {
// 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);
}
read_index += write_count;
write_index += write_count;
}
input.close();
// Create sample buffer
+2 -2
View File
@@ -163,7 +163,7 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
SampleBufferPtr RetrieveAudio(const TimeRange& range, const AudioParams& params, const QString &cache_path, const QAtomicInt *cancelled);
SampleBufferPtr RetrieveAudio(const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, const QAtomicInt *cancelled);
/**
* @brief Generate a Footage object from a file
@@ -276,7 +276,7 @@ signals:
void IndexProgress(double);
private:
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range);
SampleBufferPtr RetrieveAudioFromConform(const QString& conform_filename, const TimeRange &range, Footage::LoopMode loop_mode);
CodecStream stream_;
+10 -8
View File
@@ -158,38 +158,40 @@ bool WaveInput::is_open() const
return file_.isOpen();
}
QByteArray WaveInput::read(int length)
QByteArray WaveInput::read(qint64 length)
{
if (!is_open()) {
return QByteArray();
}
return file_.read(qMin(calculate_max_read(), static_cast<qint64>(length)));
return file_.read(qMin(calculate_max_read(), length));
}
QByteArray WaveInput::read(int offset, int length)
QByteArray WaveInput::read(qint64 offset, qint64 length)
{
if (!is_open()) {
return QByteArray();
}
seek(offset);
return file_.read(qMin(calculate_max_read(), static_cast<qint64>(length)));
return file_.read(qMin(calculate_max_read(), length));
}
qint64 WaveInput::read(int offset, char *buffer, int 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(), static_cast<qint64>(length)));
return file_.read(buffer, qMin(calculate_max_read(), length));
}
bool WaveInput::seek(qint64 pos)
{
return file_.seek(data_position_ + qMin(pos, static_cast<qint64>(data_size_)));
return file_.seek(data_position_ + qMin(pos, qint64(data_size_)));
}
bool WaveInput::at_end() const
@@ -216,7 +218,7 @@ const quint32 &WaveInput::data_length() const
int WaveInput::sample_count() const
{
return params_.bytes_to_samples(static_cast<int>(data_size_));
return params_.bytes_to_samples(data_size_);
}
bool WaveInput::find_str(QFile *f, const char *str)
+3 -3
View File
@@ -40,9 +40,9 @@ public:
bool is_open() const;
QByteArray read(int length);
QByteArray read(int offset, int length);
qint64 read(int offset, char *buffer, int length);
QByteArray read(qint64 length);
QByteArray read(qint64 offset, qint64 length);
qint64 read(qint64 offset, char *buffer, qint64 length);
bool seek(qint64 pos);