134 lines
3.6 KiB
C++
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
|