// // FFmpeg 风格编解码器注册表与上下文管理实现(中文注释版)。 // 注意:此实现大量使用 std::vector、std::optional 等现代 C++ 容器与 RAII 思想。 // #include "cpdecoder.hh" #include // 提供 std::find_if 等算法,用于查找与遍历 #include // std::mutex + std::lock_guard,保证注册表线程安全 namespace ffmsep { namespace { // 匿名命名空间:限制辅助函数和静态变量作用域在当前编译单元 std::vector& codec_registry() { static std::vector 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(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 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 codecs) { for (const CPCodec* codec : codecs) { cpcodec_register(codec); } } const CPCodec* cpcodec_find_decoder(CPCodecID id) { std::lock_guard 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 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 cpcodec_list_codecs() { std::lock_guard 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