Skip to content
Extraits de code Groupes Projets
Vérifiée Valider 53f310ab rédigé par Kubat's avatar Kubat
Parcourir les fichiers

[WIP] LIB: The audio context and streams are now using the abstract things for code factorisation

parent bf1d5eab
Aucune branche associée trouvée
Aucune étiquette associée trouvée
1 requête de fusion!18Implement the VivyDocument specification
...@@ -9,125 +9,29 @@ using namespace Vivy; ...@@ -9,125 +9,29 @@ using namespace Vivy;
// Create an audio contecxt from a file // Create an audio contecxt from a file
AudioContext::AudioContext(const QString &path) AudioContext::AudioContext(const QString &path)
: filePath(path) : Super(path)
{ {
if (!format)
throw std::runtime_error("out of memory, can't create allocate the AVFormatContext");
const std::string filePathStdHolder = filePath.toStdString();
const char *filename = filePathStdHolder.c_str();
AVFormatContext *formatPtr = format.get();
// Get the format from the audio file
if (avformat_open_input(&formatPtr, filename, nullptr, nullptr) != 0) {
[[maybe_unused]] void *relatedOnFailure = format.release(); // freed by avformat_open_input
throw std::runtime_error("failed to open file");
}
if (avformat_find_stream_info(formatPtr, nullptr) < 0) {
throw std::runtime_error("failed to get audio stream info");
}
// Populate all the stream indexes
for (uint i = 0; i < format->nb_streams; ++i) {
AVStream *itFormat = format->streams[i];
AVCodecParameters *params = itFormat->codecpar;
AVCodec *streamCodec = avcodec_find_decoder(params->codec_id);
if (streamCodec && streamCodec->type == AVMEDIA_TYPE_AUDIO) {
audioStreams.insert(i, std::make_shared<Stream>(streamCodec, formatPtr, itFormat, i));
}
}
// Get the default stream
defaultStreamIndex = av_find_best_stream(formatPtr, AVMEDIA_TYPE_AUDIO,
-1, // Let AV find one stream
-1, // We don't want related streams
nullptr, 0);
if (defaultStreamIndex < 0) {
qCritical() << "Could not find the best audio stream";
}
qDebug() << "Opened audio context for" << path << "with duration" << formatPtr->duration
<< "and default stream index" << defaultStreamIndex;
}
// Get a specific audio stream, try to lock the weak pointer. if the index was
// not present the used_count will be 0 and you won't be able to lock it.
AudioContext::StreamWeakPtr
AudioContext::getStream(int index) const noexcept
{
if (index < 0)
return StreamWeakPtr{ spareNullSreamPtr };
uint unsignedIndex = static_cast<uint>(index);
const auto found = audioStreams.find(unsignedIndex);
if (found != audioStreams.end())
return StreamWeakPtr{ *found };
return StreamWeakPtr{ spareNullSreamPtr };
}
// Get the default stream of this audio context. If no default audio stream is
// present you won't be able to lock the weak pointer.
AudioContext::StreamWeakPtr
AudioContext::getDefaultStream() const noexcept
{
if (defaultStreamIndex < 0)
return StreamWeakPtr{ spareNullSreamPtr };
return getStream(defaultStreamIndex);
} }
QString QString
AudioContext::getElementName() const noexcept AudioContext::getElementName() const noexcept
{ {
QFileInfo file(filePath); return "Audio" + Super::getElementName();
return "AudioContext: " + file.baseName();
} }
QJsonDocument QJsonDocument
AudioContext::getProperties() const noexcept AudioContext::getProperties() const noexcept
{ {
QJsonDocument ret; return Super::getProperties();
QJsonArray streams;
QFileInfo file(filePath);
for (const auto &audioStreamPtr : audioStreams) {
streams.append(audioStreamPtr->getProperties());
}
QJsonObject self{ { "Streams", streams }, { "Base name", file.baseName() } };
ret.setObject(self);
return ret;
} }
// AudioContext::Stream class implementation // AudioStream class implementation
// Constructor, need an AVFormat and an AVStream // Constructor, need an AVFormat and an AVStream
AudioContext::Stream::Stream(AVCodec *streamCodec, AVFormatContext *formatPtr, AVStream *stream, AudioStream::AudioStream(AVCodec *streamCodec, AVFormatContext *formatPtr, AVStream *streamArg,
int index) int index)
: codecId(stream->codecpar->codec_id) : Super(streamCodec, formatPtr, streamArg, index)
, codec(streamCodec)
, codecParams(stream->codecpar)
, audioStream(stream)
, streamIndexInAudioContext(index)
, dataFormat(formatPtr)
{ {
if (codec == nullptr)
throw std::runtime_error("failed to find a decoder for stream");
codecContext.reset(avcodec_alloc_context3(codec));
if (!codecContext)
throw std::runtime_error("failed to allocate codec context");
if (avcodec_parameters_to_context(codecContext.get(), codecParams) < 0)
throw std::runtime_error("failed to copy parameters to codec context");
if (avcodec_open2(codecContext.get(), codec, nullptr) < 0)
throw std::runtime_error("failed to open audio decoder for a stream");
SwrContext *s = dataSwrContext.get(); SwrContext *s = dataSwrContext.get();
av_opt_set_int(s, "in_channel_count", codecContext->channels, 0); av_opt_set_int(s, "in_channel_count", codecContext->channels, 0);
av_opt_set_int(s, "out_channel_count", 1, 0); av_opt_set_int(s, "out_channel_count", 1, 0);
...@@ -140,14 +44,9 @@ AudioContext::Stream::Stream(AVCodec *streamCodec, AVFormatContext *formatPtr, A ...@@ -140,14 +44,9 @@ AudioContext::Stream::Stream(AVCodec *streamCodec, AVFormatContext *formatPtr, A
swr_init(s); swr_init(s);
if (!swr_is_initialized(s)) if (!swr_is_initialized(s))
throw std::runtime_error("failed to initialize SwrContext resampler"); throw std::runtime_error("failed to initialize SwrContext resampler");
qDebug() << "[Stream] Codec" << codec->name << "id:" << codecId;
qDebug() << "[Stream] sample rate:" << codecParams->sample_rate;
qDebug() << "[Stream] bit rate: " << codecParams->bit_rate;
qDebug() << "[Stream] channels: " << codecParams->channels;
} }
AudioContext::Stream::~Stream() noexcept AudioStream::~AudioStream() noexcept
{ {
if (dataPtr) { if (dataPtr) {
qDebug() << "Free data ptr"; qDebug() << "Free data ptr";
...@@ -157,10 +56,9 @@ AudioContext::Stream::~Stream() noexcept ...@@ -157,10 +56,9 @@ AudioContext::Stream::~Stream() noexcept
} }
QJsonObject QJsonObject
AudioContext::Stream::getProperties() const noexcept AudioStream::getProperties() const noexcept
{ {
QJsonObject ret; QJsonObject ret = Super::getProperties();
ret.insert("Codec name", codec->name);
ret.insert("Sample rate", codecParams->sample_rate); ret.insert("Sample rate", codecParams->sample_rate);
ret.insert("Bit rate", static_cast<int>(codecParams->bit_rate)); ret.insert("Bit rate", static_cast<int>(codecParams->bit_rate));
ret.insert("Channels", codecParams->channels); ret.insert("Channels", codecParams->channels);
...@@ -169,11 +67,11 @@ AudioContext::Stream::getProperties() const noexcept ...@@ -169,11 +67,11 @@ AudioContext::Stream::getProperties() const noexcept
// Decode the data, don't call it if data already decoded // Decode the data, don't call it if data already decoded
void void
AudioContext::Stream::decodeData() AudioStream::decodeData()
{ {
if (isDecoded()) if (isDecoded())
throw std::logic_error("audio stream is already resampled"); throw std::logic_error("audio stream is already resampled");
qDebug() << "Launch decoding of stream" << streamIndexInAudioContext; qDebug() << "Launch decoding of stream" << streamIndexInContext;
AVPacket packet; AVPacket packet;
av_init_packet(&packet); av_init_packet(&packet);
...@@ -181,7 +79,7 @@ AudioContext::Stream::decodeData() ...@@ -181,7 +79,7 @@ AudioContext::Stream::decodeData()
// Iterate through frames // Iterate through frames
while (av_read_frame(dataFormat, &packet) >= 0) { while (av_read_frame(dataFormat, &packet) >= 0) {
// Only decode audio // Only decode audio
if (packet.stream_index != streamIndexInAudioContext) { if (packet.stream_index != streamIndexInContext) {
av_packet_unref(&packet); av_packet_unref(&packet);
continue; continue;
} }
...@@ -242,13 +140,13 @@ AudioContext::Stream::decodeData() ...@@ -242,13 +140,13 @@ AudioContext::Stream::decodeData()
av_packet_unref(&packet); av_packet_unref(&packet);
} }
qDebug() << "Decoding data finished for stream" << streamIndexInAudioContext qDebug() << "Decoding data finished for stream" << streamIndexInContext
<< "dataPtr =" << dataPtr << "with dataSize =" << dataSize; << "dataPtr =" << dataPtr << "with dataSize =" << dataSize;
} }
// Delete decoded data, clean up thing // Delete decoded data, clean up thing
void void
AudioContext::Stream::cleanUpData() noexcept AudioStream::cleanUpData() noexcept
{ {
free(dataPtr); free(dataPtr);
dataPtr = nullptr; dataPtr = nullptr;
...@@ -257,14 +155,14 @@ AudioContext::Stream::cleanUpData() noexcept ...@@ -257,14 +155,14 @@ AudioContext::Stream::cleanUpData() noexcept
// Get the number of channels in the audio context stream // Get the number of channels in the audio context stream
int int
AudioContext::Stream::getChannels() const noexcept AudioStream::getChannels() const noexcept
{ {
return codecContext->channels; return codecContext->channels;
} }
// Get the sample rate // Get the sample rate
int int
AudioContext::Stream::getSampleRate() const noexcept AudioStream::getSampleRate() const noexcept
{ {
return codecContext->sample_rate; return codecContext->sample_rate;
} }
...@@ -272,35 +170,21 @@ AudioContext::Stream::getSampleRate() const noexcept ...@@ -272,35 +170,21 @@ AudioContext::Stream::getSampleRate() const noexcept
// Get the sample rate of the decoded data (need to call decodeData for that // Get the sample rate of the decoded data (need to call decodeData for that
// value to have a meaning). // value to have a meaning).
int int
AudioContext::Stream::getDataSampleRate() const noexcept AudioStream::getDataSampleRate() const noexcept
{ {
return resamplerSampleRate; return resamplerSampleRate;
} }
// Get the name of the codec used to decode the stream
QString
AudioContext::Stream::getName() const noexcept
{
return QString::fromUtf8(codec->name);
}
// Get the codec's ID used to decode the stream
AVCodecID
AudioContext::Stream::getCodecId() const noexcept
{
return codecId;
}
// Get the bit rate of the stream // Get the bit rate of the stream
qint64 qint64
AudioContext::Stream::getBitRate() const noexcept AudioStream::getBitRate() const noexcept
{ {
return codecContext->bit_rate; return codecContext->bit_rate;
} }
// Get the information about the decoded state of this stream // Get the information about the decoded state of this stream
bool bool
AudioContext::Stream::isDecoded() const noexcept AudioStream::isDecoded() const noexcept
{ {
return dataPtr != nullptr; return dataPtr != nullptr;
} }
...@@ -308,7 +192,7 @@ AudioContext::Stream::isDecoded() const noexcept ...@@ -308,7 +192,7 @@ AudioContext::Stream::isDecoded() const noexcept
// Get the decoded data's size, will return 0 if the data is not decoded. Don't // Get the decoded data's size, will return 0 if the data is not decoded. Don't
// rely on this behaviour to get the decoded state of the stream! // rely on this behaviour to get the decoded state of the stream!
size_t size_t
AudioContext::Stream::getDecodedDataSize() const noexcept AudioStream::getDecodedDataSize() const noexcept
{ {
return dataSize; return dataSize;
} }
...@@ -316,28 +200,22 @@ AudioContext::Stream::getDecodedDataSize() const noexcept ...@@ -316,28 +200,22 @@ AudioContext::Stream::getDecodedDataSize() const noexcept
// Get the decoded data, safe to call even if the data is not decoded has the // Get the decoded data, safe to call even if the data is not decoded has the
// pointer will be null. You must check it with an `if` statement. // pointer will be null. You must check it with an `if` statement.
double * double *
AudioContext::Stream::getDecodedData() const noexcept AudioStream::getDecodedData() const noexcept
{ {
return dataPtr; return dataPtr;
} }
// Get the chunk size of the decoded data // Get the chunk size of the decoded data
size_t size_t
AudioContext::Stream::getDecodedChunkSize() const noexcept AudioStream::getDecodedChunkSize() const noexcept
{ {
return 512; return 512;
} }
// Get the decalage of the decoded data // Get the decalage of the decoded data
size_t size_t
AudioContext::Stream::getDecodedDecalage() const noexcept AudioStream::getDecodedDecalage() const noexcept
{ {
constexpr size_t overlap = 128; // The overlap constexpr size_t overlap = 128; // The overlap
return getDecodedChunkSize() - overlap; return getDecodedChunkSize() - overlap;
} }
int
AudioContext::Stream::getStreamIndex() const noexcept
{
return streamIndexInAudioContext;
}
...@@ -23,40 +23,15 @@ namespace Vivy ...@@ -23,40 +23,15 @@ namespace Vivy
{ {
class AudioContext; class AudioContext;
// The memory representation of an audio file. It is created inplace and should
// not be moved around. It should be used to get the properties of an audio
// file and its streams. The user can use a stream from the file and get data
// from it.
class AudioContext final {
VIVY_UNMOVABLE_OBJECT(AudioContext)
public:
// Hold all the data for a stream in an audio file. Should only be owned by // Hold all the data for a stream in an audio file. Should only be owned by
// the AudioContext class. // the AudioContext class.
class Stream final { class AudioStream final : public AbstractMediaStream<AVMEDIA_TYPE_AUDIO> {
VIVY_UNMOVABLE_OBJECT(Stream) VIVY_UNMOVABLE_OBJECT(AudioStream)
using Super = AbstractMediaStream<AVMEDIA_TYPE_AUDIO>;
static inline Utils::DeleterFunctionType<double> dataDeleter =
std::bind_front(Utils::freePtrIfNotNull<double>, av_free);
static inline Utils::DeleterFunctionType<AVCodecContext> codecContexteleter =
std::bind_front(Utils::freePPtrIfNotNull<AVCodecContext>, avcodec_free_context);
static inline Utils::DeleterFunctionType<AVFrame> avFrameDeleter =
std::bind_front(Utils::freePPtrIfNotNull<AVFrame>, av_frame_free);
static inline Utils::DeleterFunctionType<SwrContext> swrContenxtDeleter =
std::bind_front(Utils::freePPtrIfNotNull<SwrContext>, swr_free);
// All the used types
using AVCodecContextPtr = std::unique_ptr<AVCodecContext, decltype(codecContexteleter)>;
using DataPtr = std::shared_ptr<double[]>;
using AVFramePtr = std::unique_ptr<AVFrame, decltype(avFrameDeleter)>;
using SwrContextPtr = std::unique_ptr<SwrContext, decltype(swrContenxtDeleter)>;
public: public:
Stream(AVCodec *, AVFormatContext *, AVStream *, int index); AudioStream(AVCodec *, AVFormatContext *, AVStream *, int index);
~Stream() noexcept; ~AudioStream() noexcept override;
// The non-owning view of the stream's data // The non-owning view of the stream's data
using DataWeakPtr = std::weak_ptr<double[]>; using DataWeakPtr = std::weak_ptr<double[]>;
...@@ -74,30 +49,11 @@ public: ...@@ -74,30 +49,11 @@ public:
// Some getters // Some getters
int getChannels() const noexcept; int getChannels() const noexcept;
int getSampleRate() const noexcept; int getSampleRate() const noexcept;
QString getName() const noexcept;
AVCodecID getCodecId() const noexcept;
qint64 getBitRate() const noexcept; qint64 getBitRate() const noexcept;
QJsonObject getProperties() const noexcept override;
// Get the index from the audio context
int getStreamIndex() const noexcept;
QJsonObject getProperties() const noexcept;
private: private:
// Codec related informations
AVCodecID codecId{ AV_CODEC_ID_NONE };
AVCodec *codec{ nullptr };
AVCodecParameters *codecParams{ nullptr };
AVCodecContextPtr codecContext{ nullptr };
// Stream is held by AudioContext
AVStream *audioStream{ nullptr };
// Store the index of this stream
int streamIndexInAudioContext;
// Resampled frame data // Resampled frame data
AVFormatContext *dataFormat;
SwrContextPtr dataSwrContext{ swr_alloc(), swrContenxtDeleter }; SwrContextPtr dataSwrContext{ swr_alloc(), swrContenxtDeleter };
AVFramePtr dataFrame{ av_frame_alloc(), avFrameDeleter }; AVFramePtr dataFrame{ av_frame_alloc(), avFrameDeleter };
double *dataPtr{ nullptr }; double *dataPtr{ nullptr };
...@@ -110,17 +66,23 @@ public: ...@@ -110,17 +66,23 @@ public:
static constexpr uint resamplerSampleRate = 44100; static constexpr uint resamplerSampleRate = 44100;
}; };
using StreamPtr = std::shared_ptr<Stream>; // The memory representation of an audio file. It is created inplace and should
using StreamWeakPtr = std::weak_ptr<Stream>; // not be moved around. It should be used to get the properties of an audio
// file and its streams. The user can use a stream from the file and get data
// from it.
class AudioContext final : public AbstractMediaContext<AudioStream> {
VIVY_UNMOVABLE_OBJECT(AudioContext)
using Super = AbstractMediaContext<AudioStream>;
public:
using StreamPtr = std::shared_ptr<AudioStream>;
using StreamWeakPtr = std::weak_ptr<AudioStream>;
public: public:
AudioContext(const QString &path); AudioContext(const QString &path);
StreamWeakPtr getStream(int) const noexcept; QString getElementName() const noexcept override;
StreamWeakPtr getDefaultStream() const noexcept; QJsonDocument getProperties() const noexcept override;
QString getElementName() const noexcept;
QJsonDocument getProperties() const noexcept;
private: private:
// Regarding the format // Regarding the format
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Veuillez vous inscrire ou vous pour commenter