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

AUDIO: Implements the AudioStreamCodec

parent 65d0fa34
Branches
Aucune étiquette associée trouvée
1 requête de fusion!5Add AudioContext to AudioSubDocument
#include "Audio.hh"
using namespace Vivy;
AudioStreamCodec::AudioStreamCodec(AVFormatContext *format, AVStream *stream)
: codecId(stream->codecpar->codec_id)
, codecParams(stream->codecpar)
, audioStream(stream)
, dataFormat(format)
{
codec = avcodec_find_decoder(codecId);
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();
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, "in_channel_layout", static_cast<int64_t>(codecContext->channel_layout), 0);
av_opt_set_int(s, "out_channel_layout", AV_CH_LAYOUT_MONO, 0);
av_opt_set_int(s, "in_sample_rate", codecContext->sample_rate, 0);
av_opt_set_int(s, "out_sample_rate", resamplerSampleRate, 0);
av_opt_set_sample_fmt(s, "in_sample_fmt", codecContext->sample_fmt, 0);
av_opt_set_sample_fmt(s, "out_sample_fmt", AV_SAMPLE_FMT_DBL, 0);
swr_init(s);
if (!swr_is_initialized(s)) {
throw std::runtime_error("failed to initialize SwrContext resampler");
}
}
void
AudioStreamCodec::decodeData()
{
if (isDecoded())
throw std::logic_error("audio stream is already resampled");
AVPacket packet;
av_init_packet(&packet);
/* Iterate through frames */
while (av_read_frame(dataFormat, &packet) >= 0) {
/* Decode one frame */
int response = avcodec_send_packet(codecContext.get(), &packet);
if (response < 0) [[unlikely]] {
// av_err2str(response); <- Compound literals are a C99-specific feature
throw std::runtime_error("error while sending a packet to the decoder");
}
double *buffer = nullptr;
int old_frame_nb_samples = 0;
while (response >= 0) {
response = avcodec_receive_frame(codecContext.get(), dataFrame.get());
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) [[unlikely]] {
break;
} else if (response < 0) [[unlikely]] {
// av_err2str(response); <- Compound literals are a C99-specific feature
throw std::runtime_error("error while receiving a frame from the decoder");
}
/* Resample frames */
if (old_frame_nb_samples < dataFrame->nb_samples) {
if (nullptr != buffer)
av_free(buffer);
old_frame_nb_samples = dataFrame->nb_samples;
av_samples_alloc(reinterpret_cast<uint8_t **>(&buffer), nullptr, 1,
dataFrame->nb_samples, AV_SAMPLE_FMT_DBL, 0);
}
if (const int frame_count_int =
swr_convert(dataSwrContext.get(), reinterpret_cast<uint8_t **>(&buffer),
dataFrame->nb_samples, (const uint8_t **)dataFrame->data,
dataFrame->nb_samples);
frame_count_int < 0) [[unlikely]] {
throw std::runtime_error("error on frame count, is negative but should not be");
}
else [[likely]] {
const size_t frame_count = static_cast<size_t>(frame_count_int);
/* Append resampled frames to data */
dataRealPtr = (double *)realloc(
dataRealPtr, (dataSize + (size_t)dataFrame->nb_samples) * sizeof(double));
memcpy(dataRealPtr + dataSize, buffer, frame_count * sizeof(double));
dataSize += frame_count;
}
}
if (buffer)
av_free(buffer);
av_packet_unref(&packet);
}
}
void
AudioStreamCodec::cleanUpData() noexcept
{
dataPtr.reset();
dataRealPtr = nullptr;
dataSize = 0;
}
int
AudioStreamCodec::getChannels() const noexcept
{
return codecContext->channels;
}
int
AudioStreamCodec::getSampleRate() const noexcept
{
return codecContext->sample_rate;
}
int
AudioStreamCodec::getDataSampleRate() const noexcept
{
return resamplerSampleRate;
}
QString
AudioStreamCodec::getName() const noexcept
{
return QString(codec->name);
}
AVCodecID
AudioStreamCodec::getCodecId() const noexcept
{
return codecId;
}
qint64
AudioStreamCodec::getBitRate() const noexcept
{
return codecContext->bit_rate;
}
bool
AudioStreamCodec::isDecoded() const noexcept
{
return dataPtr != nullptr && dataRealPtr != nullptr;
}
size_t
AudioStreamCodec::getDecodedDataSize() const noexcept
{
return dataSize;
}
AudioStreamCodec::DataWeakPtr
AudioStreamCodec::getDecodedData() const noexcept
{
return DataWeakPtr{ dataPtr };
}
......@@ -17,6 +17,7 @@ extern "C" {
#include <QtGlobal>
#include <QMap>
#include <QVector>
#include <QString>
#include <memory.h>
namespace Vivy
......@@ -54,44 +55,47 @@ class AudioStreamCodec final {
using SwrContextPtr = std::unique_ptr<SwrContext, decltype(swrContenxtDeleter)>;
public:
AudioStreamCodec(AVStream *);
~AudioStreamCodec();
AudioStreamCodec(AVFormatContext *, AVStream *);
~AudioStreamCodec() = default;
// Decode the stream
void decodeData();
void cleanUpData() noexcept;
bool isDecoded() const noexcept;
int getDataSampleRate() const noexcept;
size_t getDecodedDataSize() const noexcept;
DataWeakPtr getDecodedData() const noexcept;
// Some getters
int getChannels() const noexcept;
int getSampleRate() const noexcept;
const char *getName() const noexcept;
QString getName() const noexcept;
AVCodecID getCodecId() const noexcept;
qint64 getBitRate() const noexcept;
private:
bool initFromStream() noexcept; // Init all the object
void cleanupForReuse() noexcept; // Free everything, can now use initFromStream again
// Check if ready
bool isReadyForUse() const noexcept;
bool isResampled() const noexcept;
// Codec related informations
AVCodecID codecId{ AV_CODEC_ID_NONE };
AVCodecContextPtr codecContext{ nullptr };
AVCodecParameters *codecParams{ nullptr };
AVCodec *codec{ nullptr };
AVCodecParameters *codecParams{ nullptr };
AVCodecContextPtr codecContext{ nullptr };
// Stream is held by AudioFormatCtx
AVStream *audioStream{ nullptr };
bool isInitFromStream{ false };
// Resampled frame data
SwrContextPtr dataSwrContext{ nullptr, swrContenxtDeleter };
AVFramePtr dataFrame{ nullptr, avFrameDeleter };
DataPtr dataPtr{ nullptr, dataDeleter };
AVFormatContext *dataFormat;
SwrContextPtr dataSwrContext{ swr_alloc(), swrContenxtDeleter };
AVFramePtr dataFrame{ av_frame_alloc(), avFrameDeleter };
DataPtr dataPtr{ nullptr, dataDeleter }; // Holds dataRealPtr
double *dataRealPtr{ nullptr };
size_t dataSize{ 0 };
// Constants for the resampler
static constexpr uint resamplerChunkSize = 512;
static constexpr uint resamplerOverlap = 128;
static constexpr uint resamplerDecalage = resamplerChunkSize - resamplerOverlap;
static constexpr uint resamplerSampleRate = 44100;
};
/* Facility for pointers to AudioStreamCodec objects */
......
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