#pragma once #include "FfmpegGuards.h" #include extern "C" { #include #include } namespace engine { struct DecoderPipeline { FmtCtxIn fmtCtx; // owns AVFormatContext* CodecCtxGuard ctx; // owns AVCodecContext* AVStream *stream = nullptr; // non-owning; lifetime == fmtCtx // open filelPath find audiostream and open decoder for it, returns false and sets errorout on any failure bool open(const QString &filePath, QString *errorOut) { AVDictionary *opts = nullptr; av_dict_set(&opts, "probesize", "5000000", 0); av_dict_set(&opts, "analyzeduration", "5000000", 0); int err = avformat_open_input(&fmtCtx.p, filePath.toUtf8().constData(), nullptr, &opts); av_dict_free(&opts); if (err < 0) { if (errorOut) *errorOut = "Cannot open input file"; return false; } if (avformat_find_stream_info(fmtCtx.p, nullptr) < 0) { if (errorOut) *errorOut = "Cannot read stream info"; return false; } const int audioIdx = av_find_best_stream( fmtCtx.p, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0); if (audioIdx < 0) { if (errorOut) *errorOut = "No audio stream found"; return false; } stream = fmtCtx.p->streams[audioIdx]; const AVCodec *decoder = avcodec_find_decoder(stream->codecpar->codec_id); if (!decoder) { if (errorOut) *errorOut = "No decoder for input codec"; return false; } ctx.p = avcodec_alloc_context3(decoder); if (!ctx.p) { if (errorOut) *errorOut = "OOM: decoder context"; return false; } avcodec_parameters_to_context(ctx.p, stream->codecpar); ctx.p->pkt_timebase = stream->time_base; if (avcodec_open2(ctx.p, decoder, nullptr) < 0) { if (errorOut) *errorOut = "Cannot open decoder"; return false; } return true; } // accessors after successful open() int audioStreamIndex() const { return stream ? static_cast(stream - fmtCtx.p->streams[0]) : -1; } int channels() const { return ctx.p ? ctx.p->ch_layout.nb_channels : 0; } int sampleRate() const { return ctx.p ? ctx.p->sample_rate : 0; } }; } // namespace engine