142 lines
6.4 KiB
C++
142 lines
6.4 KiB
C++
//
|
||
// FFmpeg 风格的串口解码工具核心定义(带详细中文注释)。
|
||
// 说明:本文件使用了 C++17 引入的 std::optional(可选值容器)与 [[nodiscard]] 属性,
|
||
// 以及 C++11 的 std::initializer_list、基于 enum class 的强类型枚举,用于提高类型安全。
|
||
//
|
||
|
||
#pragma once
|
||
|
||
#include <cstdint> // 固定宽度整数类型,便于协议字段与位宽一致
|
||
#include <cstddef>
|
||
#include <mutex> // 互斥锁,用于注册表线程安全
|
||
#include <optional> // C++17 可选类型,用来表示可能为空的查找结果
|
||
#include <string>
|
||
#include <string_view> // C++17 字符串视图,避免不必要的拷贝
|
||
#include <vector>
|
||
#include <initializer_list> // C++11 可初始化列表,支持批量注册编解码器
|
||
|
||
namespace ffmsep { // 命名空间隔离所有与串口编解码相关的类型
|
||
|
||
// 错误码定义:参考 FFmpeg 的返回值约定,所有负数表示异常。
|
||
inline constexpr int CP_SUCCESS = 0; // 成功
|
||
inline constexpr int CP_ERROR_EOF = -1; // 流结束(End Of File)
|
||
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 使用 C++11 强类型枚举,避免隐式转换导致的错误。
|
||
enum class CPMediaType : std::uint8_t {
|
||
Unknown = 0, // 未知类型:默认值
|
||
Data, // 数据流(例如串口数据)
|
||
Audio, // 音频流(预留扩展)
|
||
Video // 视频流(预留扩展)
|
||
};
|
||
|
||
// 编解码器 ID,用于在注册表中快速定位具体实现。
|
||
enum class CPCodecID : std::uint32_t {
|
||
Unknown = 0, // 未知或未设置
|
||
Tactile = 0x54514354u // 'T','Q','C','T':触觉传感器协议标识
|
||
};
|
||
|
||
// CPPacket 表示输入的“帧包”,与 FFmpeg 中 AVPacket 的思想类似。
|
||
struct CPPacket {
|
||
std::vector<std::uint8_t> payload; // 有效载荷:串口原始数据
|
||
std::int64_t pts = 0; // 显示时间戳(presentation timestamp)
|
||
std::int64_t dts = 0; // 解码时间戳(decode timestamp)
|
||
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(); } // [[nodiscard]] 防止忽略返回值
|
||
};
|
||
|
||
// CPFrame 表示解码后的数据帧,对应业务层可消费的实体。
|
||
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;
|
||
|
||
// CPCodec 用函数指针描述具体编解码器的行为,等价于 FFmpeg 中的 AVCodec。
|
||
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; // 指向编解码器私有状态(大小由 priv_data_size 控制)
|
||
CPMediaType codec_type = CPMediaType::Unknown; // 保存媒体类型,便于外部查询
|
||
bool is_open = false; // 是否已经成功调用 open
|
||
|
||
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; // 私有缓冲区,使用 std::vector 管理生命周期
|
||
|
||
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
|