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

227 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 风格编解码器注册表与上下文管理实现(中文注释版)。
// 注意:此实现大量使用 std::vector、std::optional 等现代 C++ 容器与 RAII 思想。
//
#include "cpdecoder.hh"
#include <algorithm> // 提供 std::find_if 等算法,用于查找与遍历
#include <mutex> // std::mutex + std::lock_guard保证注册表线程安全
namespace ffmsep {
namespace { // 匿名命名空间:限制辅助函数和静态变量作用域在当前编译单元
std::vector<const CPCodec*>& codec_registry() {
static std::vector<const CPCodec*> registry; // 使用静态局部变量 + vector 保留所有注册的编解码器
return registry;
}
std::mutex& registry_mutex() {
static std::mutex m; // 构建一个全局互斥量,用于守护注册表操作
return m;
}
void attach_codec(CPCodecContext* ctx, const CPCodec* codec) {
if (!ctx) {
return;
}
ctx->codec = codec;
if (!codec) {
ctx->codec_type = CPMediaType::Unknown;
ctx->priv_data = nullptr;
ctx->release_priv_storage();
return;
}
ctx->codec_type = codec->type;
ctx->priv_data = ctx->ensure_priv_storage(codec->priv_data_size);
}
bool codec_name_equals(const CPCodec* codec, std::string_view name) {
if (!codec || !codec->name) {
return false;
}
return std::string_view(codec->name) == name; // std::string_view 避免额外拷贝
}
} // namespace
void* CPCodecContext::ensure_priv_storage(std::size_t size) {
// 将 vector 当作动态缓冲区,确保大小满足编解码器私有数据需求
if (size == 0U) {
priv_storage.clear();
priv_data = nullptr;
return nullptr;
}
if (priv_storage.size() != size) {
priv_storage.assign(size, static_cast<std::uint8_t>(0));
}
priv_data = priv_storage.data();
return priv_data;
}
void CPCodecContext::release_priv_storage() noexcept {
// 与 ensure 配套,释放时直接清空 vector
priv_storage.clear();
priv_data = nullptr;
}
// 将单个编解码器指针注册到全局列表,重复注册时作安全检查。
void cpcodec_register(const CPCodec* codec) {
if (!codec || !codec->name) {
return;
}
std::lock_guard<std::mutex> lock(registry_mutex());
auto& reg = codec_registry();
auto already = std::find(reg.begin(), reg.end(), codec);
if (already != reg.end()) {
return;
}
auto same_id = std::find_if(reg.begin(), reg.end(), [codec](const CPCodec* entry) {
return entry && codec && entry->id == codec->id && codec->id != CPCodecID::Unknown;
});
if (same_id != reg.end()) {
*same_id = codec; // 如果 ID 已存在,则替换为新实现,以最新注册者为准
return;
}
reg.push_back(codec);
}
// 批量注册函数:利用 C++11 std::initializer_list 便捷传入多个指针。
void cpcodec_register_many(std::initializer_list<const CPCodec*> codecs) {
for (const CPCodec* codec : codecs) {
cpcodec_register(codec);
}
}
const CPCodec* cpcodec_find_decoder(CPCodecID id) {
std::lock_guard<std::mutex> lock(registry_mutex());
const auto& reg = codec_registry();
auto it = std::find_if(reg.begin(), reg.end(), [id](const CPCodec* codec) {
return codec && codec->id == id;
});
return it == reg.end() ? nullptr : *it; // 使用可空返回值:找不到时返回 nullptr
}
const CPCodec* cpcodec_find_decoder_by_name(std::string_view name) {
std::lock_guard<std::mutex> lock(registry_mutex());
const auto& reg = codec_registry();
auto it = std::find_if(reg.begin(), reg.end(), [name](const CPCodec* codec) {
return codec_name_equals(codec, name);
});
return it == reg.end() ? nullptr : *it;
}
std::vector<const CPCodec*> cpcodec_list_codecs() {
std::lock_guard<std::mutex> lock(registry_mutex());
return codec_registry(); // 返回当前注册表的浅拷贝
}
// 分配一个新的上下文,并可选绑定已有编解码器。
CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec) {
auto* ctx = new CPCodecContext();
if (codec) {
attach_codec(ctx, codec);
}
return ctx;
}
// 打开上下文:允许调用方在此时指定(或替换)具体编解码器。
int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec) {
if (!ctx) {
return CP_ERROR_INVALID_ARGUMENT;
}
if (ctx->is_open) {
return CP_ERROR_INVALID_STATE;
}
if (codec) {
attach_codec(ctx, codec);
}
if (!ctx->codec) {
return CP_ERROR_INVALID_ARGUMENT;
}
ctx->is_open = true;
if (ctx->codec->init) {
int rc = ctx->codec->init(ctx);
if (rc < 0) {
ctx->is_open = false;
if (ctx->codec->close) {
ctx->codec->close(ctx); // 初始化失败时调用 close 进行善后
}
return rc;
}
}
return CP_SUCCESS;
}
// 关闭上下文:调用编解码器收尾逻辑并释放私有状态。
int cpcodec_close(CPCodecContext* ctx) {
if (!ctx) {
return CP_ERROR_INVALID_ARGUMENT;
}
if (!ctx->is_open) {
return CP_SUCCESS;
}
if (ctx->codec && ctx->codec->close) {
ctx->codec->close(ctx);
}
ctx->is_open = false;
ctx->release_priv_storage();
ctx->codec_type = CPMediaType::Unknown;
ctx->codec = nullptr;
ctx->priv_data = nullptr;
return CP_SUCCESS;
}
// 释放上下文指针,防止悬挂引用。
void cpcodec_free_context(CPCodecContext** ctx) {
if (!ctx || !*ctx) {
return;
}
cpcodec_close(*ctx);
delete *ctx;
*ctx = nullptr;
}
// 输入侧 API推送一帧串口数据内部队列通常会缓冲。
int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet) {
if (!ctx || !packet) {
return CP_ERROR_INVALID_ARGUMENT;
}
if (!ctx->is_open || !ctx->codec) {
return CP_ERROR_NOT_OPEN;
}
if (!ctx->codec->send_packet) {
return CP_ERROR_INVALID_STATE;
}
return ctx->codec->send_packet(ctx, *packet);
}
// 输出侧 API尝试从编解码器读取一帧解码结果。
int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame) {
if (!ctx || !frame) {
return CP_ERROR_INVALID_ARGUMENT;
}
if (!ctx->is_open || !ctx->codec) {
return CP_ERROR_NOT_OPEN;
}
if (!ctx->codec->receive_frame) {
return CP_ERROR_INVALID_STATE;
}
return ctx->codec->receive_frame(ctx, *frame);
}
} // namespace ffmsep