Files
ts-qt/components/ffmsep/cpdecoder.hh
2025-10-23 17:22:22 +08:00

134 lines
3.6 KiB
C++

//
// Simple FFmpeg-inspired serial decoding toolkit.
//
#pragma once
#include <cstdint>
#include <cstddef>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include <initializer_list>
namespace ffmsep {
// Error codes loosely mirroring FFmpeg semantics.
inline constexpr int CP_SUCCESS = 0;
inline constexpr int CP_ERROR_EOF = -1;
inline constexpr int CP_ERROR_EAGAIN = -2;
inline constexpr int CP_ERROR_NOT_OPEN = -3;
inline constexpr int CP_ERROR_INVALID_STATE = -4;
inline constexpr int CP_ERROR_INVALID_ARGUMENT = -5;
enum class CPMediaType : std::uint8_t {
Unknown = 0,
Data,
Audio,
Video
};
enum class CPCodecID : std::uint32_t {
Unknown = 0,
Tactile = 0x54514354u // 'T','Q','C','T' marker for tactile quick codec.
};
struct CPPacket {
std::vector<std::uint8_t> payload;
std::int64_t pts = 0;
std::int64_t dts = 0;
bool end_of_stream = false;
bool flush = false;
CPPacket() = default;
CPPacket(std::vector<std::uint8_t> data, std::int64_t pts_value = 0, std::int64_t dts_value = 0) noexcept
: payload(std::move(data)), pts(pts_value), dts(dts_value) {}
[[nodiscard]] bool empty() const noexcept { return payload.empty(); }
};
struct CPFrame {
std::vector<std::uint8_t> data;
std::int64_t pts = 0;
bool key_frame = false;
bool valid = false;
void reset() noexcept {
data.clear();
pts = 0;
key_frame = false;
valid = false;
}
};
struct CPCodecContext;
struct CPCodec {
using InitFn = int (*)(CPCodecContext*);
using CloseFn = void (*)(CPCodecContext*);
using SendPacketFn = int (*)(CPCodecContext*, const CPPacket&);
using ReceiveFrameFn = int (*)(CPCodecContext*, CPFrame&);
const char* name = nullptr;
const char* long_name = nullptr;
CPMediaType type = CPMediaType::Unknown;
CPCodecID id = CPCodecID::Unknown;
std::size_t priv_data_size = 0;
InitFn init = nullptr;
CloseFn close = nullptr;
SendPacketFn send_packet = nullptr;
ReceiveFrameFn receive_frame = nullptr;
};
struct CPCodecContext {
const CPCodec* codec = nullptr;
void* priv_data = nullptr;
CPMediaType codec_type = CPMediaType::Unknown;
bool is_open = false;
void clear() noexcept {
codec = nullptr;
priv_data = nullptr;
codec_type = CPMediaType::Unknown;
is_open = false;
priv_storage.clear();
}
void* ensure_priv_storage(std::size_t size);
void release_priv_storage() noexcept;
template <typename T>
[[nodiscard]] T* priv_as() noexcept {
return static_cast<T*>(priv_data);
}
template <typename T>
[[nodiscard]] const T* priv_as() const noexcept {
return static_cast<const T*>(priv_data);
}
private:
std::vector<std::uint8_t> priv_storage;
friend CPCodecContext* cpcodec_alloc_context3(const CPCodec*);
friend int cpcodec_open2(CPCodecContext*, const CPCodec*);
friend int cpcodec_close(CPCodecContext*);
};
void cpcodec_register(const CPCodec* codec);
void cpcodec_register_many(std::initializer_list<const CPCodec*> codecs);
const CPCodec* cpcodec_find_decoder(CPCodecID id);
const CPCodec* cpcodec_find_decoder_by_name(std::string_view name);
std::vector<const CPCodec*> cpcodec_list_codecs();
CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec);
int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec = nullptr);
int cpcodec_close(CPCodecContext* ctx);
void cpcodec_free_context(CPCodecContext** ctx);
int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet);
int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame);
} // namespace ffmsep