mirror of
https://github.com/steredbits/pelagia.git
synced 2026-10-17 17:01:33 +02:00
107 lines
3.1 KiB
C++
107 lines
3.1 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// Copyright (C) 2026 steredbits and Pelagia contributors
|
|
#ifndef PELAGIA_CORE_PLAYER_DECODER_H
|
|
#define PELAGIA_CORE_PLAYER_DECODER_H
|
|
|
|
// Video and audio decoders (libavcodec). Used exclusively by the
|
|
// player's decoding threads.
|
|
// - Video: YUV420p output (swscale conversion only if the source
|
|
// is not already YUV420p). No RGB conversion in the core.
|
|
// - Audio: interleaved S16 output, rate/channels of platform::AudioSpec
|
|
// (48 kHz stereo by default), through swresample.
|
|
|
|
#include <cstdint>
|
|
|
|
extern "C" {
|
|
#include <libavcodec/avcodec.h>
|
|
#include <libavformat/avformat.h>
|
|
#include <libswresample/swresample.h>
|
|
#include <libswscale/swscale.h>
|
|
}
|
|
|
|
namespace player {
|
|
|
|
class VideoDecoder {
|
|
public:
|
|
~VideoDecoder() { close(); }
|
|
|
|
bool open(const AVStream* stream);
|
|
// Opening from the parameters of a stream (reopening another stream
|
|
// after a track change, with no AVStream at hand).
|
|
bool open(const AVCodecParameters* par, AVRational time_base, AVRational frame_rate);
|
|
void close();
|
|
|
|
// Sends a packet (nullptr = end-of-stream drain).
|
|
int send(const AVPacket* pkt);
|
|
|
|
// Gets the next decoded frame, converted to YUV420p if needed.
|
|
// Returns nullptr with *status = AVERROR(EAGAIN) (next packet required)
|
|
// or AVERROR_EOF (drain finished). The frame belongs to the caller
|
|
// (av_frame_free).
|
|
AVFrame* receive(int* status);
|
|
|
|
void flush(); // after a seek
|
|
|
|
// PTS of the frame in milliseconds (best_effort, otherwise extrapolation).
|
|
int64_t frame_pts_ms(const AVFrame* frame);
|
|
|
|
// Total time spent in decoding/conversion, in ms (headless stats).
|
|
double busy_ms() const { return busy_ms_; }
|
|
|
|
// Nominal duration of a frame (1 / average frame rate of the stream).
|
|
int64_t frame_duration_ms() const { return frame_duration_ms_; }
|
|
|
|
private:
|
|
AVCodecContext* codec_ = nullptr;
|
|
SwsContext* sws_ = nullptr;
|
|
AVRational time_base_{1, 1000};
|
|
int64_t last_pts_ms_ = 0;
|
|
int64_t frame_duration_ms_ = 0;
|
|
double busy_ms_ = 0.0;
|
|
};
|
|
|
|
// Decoded and converted audio frame: owned interleaved S16 buffer (av_free).
|
|
struct AudioChunk {
|
|
uint8_t* data = nullptr;
|
|
size_t bytes = 0;
|
|
int64_t pts_ms = 0;
|
|
int64_t duration_ms = 0;
|
|
};
|
|
|
|
class AudioDecoder {
|
|
public:
|
|
~AudioDecoder() { close(); }
|
|
|
|
bool open(const AVStream* stream, int out_sample_rate, int out_channels);
|
|
bool open(const AVCodecParameters* par, AVRational time_base, int out_sample_rate,
|
|
int out_channels);
|
|
void close();
|
|
|
|
int send(const AVPacket* pkt);
|
|
|
|
// Gets the next converted chunk. false with *status = EAGAIN/EOF.
|
|
// out->data belongs to the caller (av_free).
|
|
bool receive(AudioChunk* out, int* status);
|
|
|
|
void flush();
|
|
|
|
int out_sample_rate() const { return out_sample_rate_; }
|
|
int out_channels() const { return out_channels_; }
|
|
double busy_ms() const { return busy_ms_; }
|
|
|
|
private:
|
|
bool convert_frame(const AVFrame* frame, AudioChunk* out);
|
|
|
|
AVCodecContext* codec_ = nullptr;
|
|
SwrContext* swr_ = nullptr;
|
|
AVRational time_base_{1, 1000};
|
|
int out_sample_rate_ = 48000;
|
|
int out_channels_ = 2;
|
|
int64_t next_pts_ms_ = 0;
|
|
double busy_ms_ = 0.0;
|
|
};
|
|
|
|
} // namespace player
|
|
|
|
#endif // PELAGIA_CORE_PLAYER_DECODER_H
|