decoder: move conform function to base class
There's no reason the Conform() function can't live in the base class since its functionality isn't necessarily specific to FFmpeg.
This commit is contained in:
+139
-4
@@ -24,8 +24,11 @@
|
||||
#include <QDebug>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include "codec/ffmpeg/ffmpegcommon.h"
|
||||
#include "codec/ffmpeg/ffmpegdecoder.h"
|
||||
#include "codec/oiio/oiiodecoder.h"
|
||||
#include "codec/waveinput.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "render/indexmanager.h"
|
||||
#include "task/index/index.h"
|
||||
#include "task/taskmanager.h"
|
||||
@@ -172,12 +175,144 @@ DecoderPtr Decoder::CreateFromID(const QString &id)
|
||||
|
||||
void Decoder::Conform(const AudioRenderingParams ¶ms, const QAtomicInt* cancelled)
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
qCritical() << "Conform called on an audio decoder that does not have a handler for it:" << id();
|
||||
abort();
|
||||
if (stream()->type() != Stream::kAudio) {
|
||||
// Nothing to be done
|
||||
return;
|
||||
}
|
||||
|
||||
Index(cancelled);
|
||||
|
||||
// Get indexed WAV file
|
||||
WaveInput input(GetIndexFilename());
|
||||
|
||||
// FIXME: No handling if input failed to open/is corrupt
|
||||
if (input.open()) {
|
||||
// If the parameters are equal, nothing to be done
|
||||
// FIXME: Technically we only need to conform if the SAMPLE RATE is not equal. Format and channel layout conversion
|
||||
// could be done on the fly so we could perhaps conform less often at some point.
|
||||
if (input.params() == params) {
|
||||
input.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, let's start converting the format
|
||||
|
||||
// Generate destination filename for this conversion to see if it exists
|
||||
QString conformed_fn = GetConformedFilename(params);
|
||||
|
||||
if (QFileInfo::exists(conformed_fn)) {
|
||||
// We must have already conformed this format
|
||||
input.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up resampler
|
||||
SwrContext* resampler = swr_alloc_set_opts(nullptr,
|
||||
static_cast<int64_t>(params.channel_layout()),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(params.format()),
|
||||
params.sample_rate(),
|
||||
static_cast<int64_t>(input.params().channel_layout()),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(input.params().format()),
|
||||
input.params().sample_rate(),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
swr_init(resampler);
|
||||
|
||||
WaveOutput conformed_output(conformed_fn, params);
|
||||
if (!conformed_output.open()) {
|
||||
qWarning() << "Failed to open conformed output:" << conformed_fn;
|
||||
input.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert one second of audio at a time
|
||||
int input_buffer_sz = input.params().time_to_bytes(1);
|
||||
|
||||
while (!input.at_end()) {
|
||||
if (cancelled && *cancelled) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read up to one second of audio from WAV file
|
||||
QByteArray read_samples = input.read(input_buffer_sz);
|
||||
|
||||
// Determine how many samples this is
|
||||
int in_sample_count = input.params().bytes_to_samples(read_samples.size());
|
||||
|
||||
ConformInternal(resampler, &conformed_output, read_samples.data(), in_sample_count);
|
||||
}
|
||||
|
||||
// Flush resampler
|
||||
ConformInternal(resampler, &conformed_output, nullptr, 0);
|
||||
|
||||
// Clean up
|
||||
swr_free(&resampler);
|
||||
conformed_output.close();
|
||||
input.close();
|
||||
|
||||
// If we cancelled, the conform didn't finish so remove it
|
||||
if (cancelled && *cancelled) {
|
||||
QFile(conformed_fn).remove();
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to conform file:" << stream()->footage()->filename();
|
||||
}
|
||||
}
|
||||
|
||||
void Decoder::Index(const QAtomicInt *cancelled)
|
||||
void Decoder::ConformInternal(SwrContext* resampler, WaveOutput* output, const char* in_data, int in_sample_count)
|
||||
{
|
||||
// Determine how many samples the output will be
|
||||
int out_sample_count = swr_get_out_samples(resampler, in_sample_count);
|
||||
|
||||
// Allocate array for the amount of samples we'll need
|
||||
QByteArray out_samples;
|
||||
out_samples.resize(output->params().samples_to_bytes(out_sample_count));
|
||||
|
||||
char* out_data = out_samples.data();
|
||||
|
||||
// Convert samples
|
||||
int convert_count = swr_convert(resampler,
|
||||
reinterpret_cast<uint8_t**>(&out_data),
|
||||
out_sample_count,
|
||||
reinterpret_cast<const uint8_t**>(&in_data),
|
||||
in_sample_count);
|
||||
|
||||
if (convert_count != out_sample_count) {
|
||||
out_samples.resize(output->params().samples_to_bytes(convert_count));
|
||||
}
|
||||
|
||||
output->write(out_samples);
|
||||
}
|
||||
|
||||
QString Decoder::GetConformedFilename(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
QString index_fn = GetIndexFilename();
|
||||
WaveInput input(GetIndexFilename());
|
||||
|
||||
// FIXME: No handling if input failed to open/is corrupt
|
||||
if (input.open()) {
|
||||
// If the parameters are equal, nothing to be done
|
||||
AudioRenderingParams index_params = input.params();
|
||||
input.close();
|
||||
|
||||
if (index_params == params) {
|
||||
// Source file matches perfectly, no conform required
|
||||
return 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;
|
||||
}
|
||||
|
||||
void Decoder::Index(const QAtomicInt *)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+19
-1
@@ -25,6 +25,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "codec/frame.h"
|
||||
#include "codec/waveoutput.h"
|
||||
#include "common/constructors.h"
|
||||
#include "common/rational.h"
|
||||
#include "project/item/footage/footage.h"
|
||||
@@ -32,6 +33,8 @@
|
||||
class Decoder;
|
||||
using DecoderPtr = std::shared_ptr<Decoder>;
|
||||
|
||||
struct SwrContext;
|
||||
|
||||
/**
|
||||
* @brief A decoder's is the main class for bringing external media into Olive
|
||||
*
|
||||
@@ -221,7 +224,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.
|
||||
*/
|
||||
virtual void Conform(const AudioRenderingParams& params, const QAtomicInt* cancelled);
|
||||
void Conform(const AudioRenderingParams& params, const QAtomicInt* cancelled);
|
||||
|
||||
/**
|
||||
* @brief Create an index for this media
|
||||
@@ -243,10 +246,25 @@ signals:
|
||||
protected:
|
||||
void SignalIndexProgress(const int64_t& ts);
|
||||
|
||||
/**
|
||||
* @brief Returns the filename for the index
|
||||
*
|
||||
* Retrieves the absolute filename of the index file for this stream. Decoder must be open for this to work correctly.
|
||||
*/
|
||||
virtual QString GetIndexFilename() = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the destination filename of an audio stream conformed to a set of parameters
|
||||
*/
|
||||
QString GetConformedFilename(const AudioRenderingParams ¶ms);
|
||||
|
||||
bool open_;
|
||||
|
||||
private:
|
||||
void ConformInternal(SwrContext *resampler, WaveOutput *output, const char *in_data, int in_sample_count);
|
||||
|
||||
StreamPtr stream_;
|
||||
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(Decoder::RetrieveState)
|
||||
|
||||
@@ -416,93 +416,6 @@ QString FFmpegDecoder::id()
|
||||
return "ffmpeg";
|
||||
}
|
||||
|
||||
void FFmpegDecoder::Conform(const AudioRenderingParams ¶ms, const QAtomicInt* cancelled)
|
||||
{
|
||||
if (avstream_->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) {
|
||||
// Nothing to be done
|
||||
return;
|
||||
}
|
||||
|
||||
Index(cancelled);
|
||||
|
||||
// Get indexed WAV file
|
||||
WaveInput input(GetIndexFilename());
|
||||
|
||||
// FIXME: No handling if input failed to open/is corrupt
|
||||
if (input.open()) {
|
||||
// If the parameters are equal, nothing to be done
|
||||
// FIXME: Technically we only need to conform if the SAMPLE RATE is not equal. Format and channel layout conversion
|
||||
// could be done on the fly so we could perhaps conform less often at some point.
|
||||
if (input.params() == params) {
|
||||
input.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, let's start converting the format
|
||||
|
||||
// Generate destination filename for this conversion to see if it exists
|
||||
QString conformed_fn = GetConformedFilename(params);
|
||||
|
||||
if (QFileInfo::exists(conformed_fn)) {
|
||||
// We must have already conformed this format
|
||||
input.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up resampler
|
||||
SwrContext* resampler = swr_alloc_set_opts(nullptr,
|
||||
static_cast<int64_t>(params.channel_layout()),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(params.format()),
|
||||
params.sample_rate(),
|
||||
static_cast<int64_t>(input.params().channel_layout()),
|
||||
FFmpegCommon::GetFFmpegSampleFormat(input.params().format()),
|
||||
input.params().sample_rate(),
|
||||
0,
|
||||
nullptr);
|
||||
|
||||
swr_init(resampler);
|
||||
|
||||
WaveOutput conformed_output(conformed_fn, params);
|
||||
if (!conformed_output.open()) {
|
||||
qWarning() << "Failed to open conformed output:" << conformed_fn;
|
||||
input.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert one second of audio at a time
|
||||
int input_buffer_sz = input.params().time_to_bytes(1);
|
||||
|
||||
while (!input.at_end()) {
|
||||
if (cancelled && *cancelled) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Read up to one second of audio from WAV file
|
||||
QByteArray read_samples = input.read(input_buffer_sz);
|
||||
|
||||
// Determine how many samples this is
|
||||
int in_sample_count = input.params().bytes_to_samples(read_samples.size());
|
||||
|
||||
ConformInternal(resampler, &conformed_output, read_samples.data(), in_sample_count);
|
||||
}
|
||||
|
||||
// Flush resampler
|
||||
ConformInternal(resampler, &conformed_output, nullptr, 0);
|
||||
|
||||
// Clean up
|
||||
swr_free(&resampler);
|
||||
conformed_output.close();
|
||||
input.close();
|
||||
|
||||
// If we cancelled, the conform didn't finish so remove it
|
||||
if (cancelled && *cancelled) {
|
||||
QFile(conformed_fn).remove();
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Failed to conform file:" << stream()->footage()->filename();
|
||||
}
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::SupportsVideo()
|
||||
{
|
||||
return true;
|
||||
@@ -518,31 +431,6 @@ void FFmpegDecoder::SetMultithreading(bool e)
|
||||
multithreading_ = e;
|
||||
}
|
||||
|
||||
void FFmpegDecoder::ConformInternal(SwrContext* resampler, WaveOutput* output, const char* in_data, int in_sample_count)
|
||||
{
|
||||
// Determine how many samples the output will be
|
||||
int out_sample_count = swr_get_out_samples(resampler, in_sample_count);
|
||||
|
||||
// Allocate array for the amount of samples we'll need
|
||||
QByteArray out_samples;
|
||||
out_samples.resize(output->params().samples_to_bytes(out_sample_count));
|
||||
|
||||
char* out_data = out_samples.data();
|
||||
|
||||
// Convert samples
|
||||
int convert_count = swr_convert(resampler,
|
||||
reinterpret_cast<uint8_t**>(&out_data),
|
||||
out_sample_count,
|
||||
reinterpret_cast<const uint8_t**>(&in_data),
|
||||
in_sample_count);
|
||||
|
||||
if (convert_count != out_sample_count) {
|
||||
out_samples.resize(output->params().samples_to_bytes(convert_count));
|
||||
}
|
||||
|
||||
output->write(out_samples);
|
||||
}
|
||||
|
||||
bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
|
||||
{
|
||||
if (open_) {
|
||||
@@ -742,33 +630,6 @@ QString FFmpegDecoder::GetIndexFilename()
|
||||
.append(QString::number(avstream_->index));
|
||||
}
|
||||
|
||||
QString FFmpegDecoder::GetConformedFilename(const AudioRenderingParams ¶ms)
|
||||
{
|
||||
QString index_fn = GetIndexFilename();
|
||||
WaveInput input(GetIndexFilename());
|
||||
|
||||
// FIXME: No handling if input failed to open/is corrupt
|
||||
if (input.open()) {
|
||||
// If the parameters are equal, nothing to be done
|
||||
AudioRenderingParams index_params = input.params();
|
||||
input.close();
|
||||
|
||||
if (index_params == params) {
|
||||
// Source file matches perfectly, no conform required
|
||||
return 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;
|
||||
}
|
||||
|
||||
void FFmpegDecoder::UnconditionalAudioIndex(AVPacket *pkt, AVFrame *frame, const QAtomicInt* cancelled)
|
||||
{
|
||||
// Iterate through each audio frame and extract the PCM data
|
||||
|
||||
@@ -57,8 +57,6 @@ public:
|
||||
|
||||
virtual QString id() override;
|
||||
|
||||
virtual void Conform(const AudioRenderingParams& params, const QAtomicInt *cancelled) override;
|
||||
|
||||
virtual bool SupportsVideo() override;
|
||||
virtual bool SupportsAudio() override;
|
||||
|
||||
@@ -67,8 +65,6 @@ public:
|
||||
virtual void Index(const QAtomicInt *cancelled) override;
|
||||
|
||||
private:
|
||||
void ConformInternal(SwrContext *resampler, WaveOutput *output, const char *in_data, int in_sample_count);
|
||||
|
||||
/**
|
||||
* @brief Handle an error
|
||||
*
|
||||
@@ -97,17 +93,7 @@ private:
|
||||
*/
|
||||
int GetFrame(AVPacket* pkt, AVFrame* frame);
|
||||
|
||||
/**
|
||||
* @brief Returns the filename for the index
|
||||
*
|
||||
* Retrieves the absolute filename of the index file for this stream. Decoder must be open for this to work correctly.
|
||||
*/
|
||||
QString GetIndexFilename();
|
||||
|
||||
/**
|
||||
* @brief Get the destination filename of an audio stream conformed to a set of parameters
|
||||
*/
|
||||
QString GetConformedFilename(const AudioRenderingParams ¶ms);
|
||||
virtual QString GetIndexFilename() override;
|
||||
|
||||
void UnconditionalAudioIndex(AVPacket* pkt, AVFrame* frame, const QAtomicInt* cancelled);
|
||||
void UnconditionalVideoIndex(AVPacket* pkt, AVFrame* frame, const QAtomicInt* cancelled);
|
||||
|
||||
@@ -187,3 +187,8 @@ bool OIIODecoder::SupportsVideo()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString OIIODecoder::GetIndexFilename()
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@ public:
|
||||
|
||||
virtual bool SupportsVideo() override;
|
||||
|
||||
virtual QString GetIndexFilename() override;
|
||||
|
||||
private:
|
||||
#if OIIO_VERSION < 10903
|
||||
OIIO::ImageInput* image_;
|
||||
|
||||
Reference in New Issue
Block a user