feat: add tactile codec

This commit is contained in:
2025-10-23 17:21:34 +08:00
parent 3d9190e41e
commit d819d40fe1
9 changed files with 1410 additions and 0 deletions

View 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