feat: add tactile codec
This commit is contained in:
141
components/comment/cpdecoder.hh
Normal file
141
components/comment/cpdecoder.hh
Normal file
@@ -0,0 +1,141 @@
|
||||
//
|
||||
// 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
|
||||
Reference in New Issue
Block a user