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

142 lines
6.4 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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