Skip to content

Instantly share code, notes, and snippets.

@jyrkive
Last active July 31, 2024 09:40
Show Gist options
  • Save jyrkive/f61af34798c6597379b751344ed73bf1 to your computer and use it in GitHub Desktop.
Save jyrkive/f61af34798c6597379b751344ed73bf1 to your computer and use it in GitHub Desktop.
int Coalesce(int a, int b)
{
return a != 0 ? a : b;
}
bool CheckStatus(int status)
{
if (status != 0)
{
status = -status;
auto msg = reinterpret_cast<const char*>(&status);
// place a breakpoint here if you're getting failures
return true;
}
return false;
}
struct StreamContext
{
AVFormatContext* formatContext;
AVCodecContext* decoderContext;
AVFrame* buffer;
std::vector<float> sampleBuffer;
StreamContext() :
formatContext(nullptr),
decoderContext(nullptr),
buffer(nullptr),
sampleBuffer(),
{}
};
struct StreamData
{
StreamContext* context;
int length;
int freq;
};
StreamData OpenStream(const char* path)
{
auto context = new StreamContext();
AVFormatContext* formatContext = nullptr;
avformat_open_input(&formatContext, path, nullptr, nullptr);
context->formatContext = formatContext;
StreamData data;
data.context = context;
const AVStream& stream = *formatContext->streams[0];
data.freq = Coalesce(stream.codecpar->sample_rate, stream.time_base.den);
data.length = static_cast<int>(stream.time_base.num * stream.duration * data.freq /
stream.time_base.den);
const AVCodecParameters& parameters = *stream.codecpar;
const AVCodec* decoder = avcodec_find_decoder(parameters.codec_id);
AVCodecContext* decoderContext = avcodec_alloc_context3(decoder);
decoderContext->extradata = static_cast<std::uint8_t*>(av_malloc(parameters.extradata_size));
decoderContext->flags2 = AV_CODEC_FLAG2_SKIP_MANUAL;
memcpy(decoderContext->extradata, parameters.extradata, parameters.extradata_size);
decoderContext->extradata_size = parameters.extradata_size;
AVDictionary* decoderOptions = nullptr;
avcodec_open2(decoderContext, decoder, &decoderOptions);
context->decoderContext = decoderContext;
context->buffer = av_frame_alloc();
return data;
}
// call this every time you need more audio samples
const AVFrame* GetFrame(const StreamData& stream)
{
AVPacket packet;
bool fail;
while (true)
{
while (true)
{
fail = CheckStatus(av_read_frame(stream.context->formatContext, &packet));
if (fail)
{
return nullptr;
}
if (packet.data != nullptr && packet.stream_index == 0)
{
break;
}
else
{
av_packet_unref(&packet);
}
}
fail = CheckStatus(avcodec_send_packet(stream.context->decoderContext, &packet));
if (fail)
{
return nullptr;
}
int status = avcodec_receive_frame(stream.context->decoderContext, stream.context->buffer);
if (status == 0)
{
break;
}
else if (status == AVERROR(EAGAIN))
{
av_packet_unref(&packet);
}
else
{
return nullptr;
}
}
av_packet_unref(&packet);
return stream.context->buffer;
// assuming stereo audio, buffer->data[0] has the left and buffer->data[1] the right channel
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment