# idl_codegen_agent **Repository Path**: xiaoxiang798/idl_codegen_agent ## Basic Information - **Project Name**: idl_codegen_agent - **Description**: 使用ai agent从.h或.d.ts文件生成taihe文件的自动化工具 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2025-09-08 - **Last Updated**: 2025-10-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # idl_codegen_agent ## 背景介绍 [Taihe介绍](https://github.com/Jemtaly/TaiheCompiler/blob/dev-napi/docs/README.md) Taihe工具生成桥接代码需要手写idl文件,有一定的成本,本项目旨在减少使用Taihe工具的成本,使用AI工具从.h或.d.ts文件生成taihe文件并自动调用taihe工具生成桥接代码。 ## 安装教程 1. 使用uv或pip安装依赖 ``` uv pip install -r requirements.txt ``` ## 使用说明 1. 配置API_KEY,只支持环境变量配置,可以配置.env文件 2. 配置config.json,包含目标文件路径,taihe脚本路径,模型名称等 3. 支持使用命令行参数配置 ## 示例 ### 工具辅助AVCodecVideo项目开发 #### 1.原工程代码结构 ##### 1.1 总体结构 ``` ├──entry/src/main/cpp // Native层 │ ├──capbilities // 能力接口和实现 │ │ ├──include // 能力接口 │ │ ├──AudioCapturer.cpp // 音频采集实现 │ │ ├──AudioDecoder.cpp // 音频解码实现 │ │ ├──AudioEncoder.cpp // 音频编码实现 │ │ ├──Demuxer.cpp // 解封装实现 │ │ ├──Muxer.cpp // 封装实现 │ │ ├──VideoDecoder.cpp // 视频解码实现 │ │ └──VideoEncoder.cpp // 视频编码实现 │ ├──common // 公共模块 │ │ ├──dfx // 日志 │ │ ├──SampleCallback.cpp // 编解码回调实现 │ │ ├──SampleCallback.h // 编解码回调定义 │ │ └──SampleInfo.h // 功能实现公共类 │ ├──render // 送显模块接口和实现 │ │ ├──include // 送显模块接口 │ │ ├──PluginManager.cpp // 送显模块管理实现 │ │ └──PluginRender.cpp // 送显逻辑实现 │ ├──sample // Native层 │ │ ├──player // Native层播放接口和实现 │ │ │ ├──Player.cpp // Native层播放功能调用逻辑的实现 │ │ │ ├──Player.h // Native层播放功能调用逻辑的接口 │ │ │ ├──PlayerNative.cpp // Native层 播放的入口 │ │ │ └──PlayerNative.h │ │ └──recorder // Native层录制接口和实现 │ │ ├──Recorder.cpp // Native层录制功能调用逻辑的实现 │ │ ├──Recorder.h // Native层录制功能调用逻辑的接口 │ │ ├──RecorderNative.cpp // Native层 录制的入口 │ │ └──RecorderNative.h │ ├──types // Native层暴露上来的接口 │ │ ├──libplayer // 播放模块暴露给UI层的接口 │ │ └──librecorder // 录制模块暴露给UI层的接口 │ └──CMakeLists.txt // 编译入口 ├──ets // UI层 │ ├──common // 公共模块 │ │ ├──utils // 共用的工具类 │ │ │ ├──CameraCheck.ets // 检查相机参数是否支持 │ │ │ ├──DateTimeUtils.ets // 获取当前时间 │ │ │ └──Logger.ets // 日志工具 │ │ └──CommonConstants.ets // 参数常量 │ ├──entryability // 应用的入口 │ │ └──EntryAbility.ets │ ├──entrybackupability │ │ └──EntryBackupAbility.ets │ ├──model │ │ └──CameraDataModel.ets // 相机参数数据类 │ └──pages // EntryAbility 包含的页面 │ ├──Index.ets // 首页/播放页面 │ └──Recorder.ets // 录制页面 ├──resources // 用于存放应用所用到的资源文件 │ ├──base // 该目录下的资源文件会被赋予唯一的ID │ │ ├──element // 用于存放字体和颜色 │ │ ├──media // 用于存放图片 │ │ └──profile // 应用入口首页 │ ├──en_US // 设备语言是美式英文时,优先匹配此目录下 │ └──zh_CN // 设备语言是简体中文时,优先匹配此目录下 └──module.json5 // 模块配置信息 ``` ##### 1.2桥接代码结构 ``` ├─sample │ ├─player │ │ Player.cpp │ │ Player.h │ │ PlayerNative.cpp //Player napi实现 │ │ PlayerNative.h │ │ │ └─recorder │ Recorder.cpp │ Recorder.h │ RecorderNative.cpp //Recorder napi实现 │ RecorderNative.h │ ├─types │ ├─libplayer │ │ index.d.ts //Player暴露给UI层接口 │ │ oh-package.json5 │ │ │ └─librecorder │ index.d.ts //Recorder暴露给UI层接口 │ oh-package.json5 ``` #### 2.使用AI工具+Taihe能力生成桥接代码 ##### 2.1 配置config.json ```json { "mapping_rules_path": "./taihe_conversion_rules.json", "project_dir": "D:/AVCodecVideo", "header_dir": "D:/AVCodecVideo/entry/src/main/cpp/types/librecorder", "output_dir": "", "taihec_script": "D:/taihe/bin/taihec.bat", "api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1", "model_name": "qwen-plus-latest" } ``` ##### 2.2 运行idlgen.py 支持命令行参数传入,命令行参数会覆盖config中的值 ``` uv run idlgen.py -H D:\AVCodecVideo\entry\src\main\cpp\types\librecorder ``` ##### 2.3 (可选) 修改.taihe文件,运行taihec脚本重新生成修改后的代码 arkts中使用number映射到c++的类型可能不符合实际功能需求,以及部分复杂情况下可能生成的不符合用户需求可以在相应的taihe文件中修改再重新运行taihec生成 **`recorder.taihe`** ```rust function initNative(fd: i32, videoCodecMime: String, width: i32, height: i32, frameRate: i32, isHDRVivid: i32, bitRate: i32): @dts_type("Promise") Opaque; ``` 修改后 **`recorder.taihe`** ```rust function initNative(fd: i32, videoCodecMime: String, width: i32, height: i32, frameRate: f64, isHDRVivid: i32, bitRate: i64): @dts_type("Promise") Opaque; ``` 重新生成 **`recorder.taihe`** ``` D:/taihe/bin/taihec.bat ./generated/recorder.taihe -O ./generated -G napi-bridge cpp-author ``` ##### 2.4 完善 impl.cpp文件 输出output_dir目录下(默认为目标文件目录下./generated)/temp/recorder.impl.cpp是默认的功能实现文件,可以直接将已有的功能代码复制过来 原始 **`recorder.impl.cpp`**: ```cpp #include "recorder.proj.hpp" #include "recorder.impl.hpp" #include "taihe/runtime.hpp" #include "stdexcept" namespace { // To be implemented. uintptr_t initNative(int32_t fd, ::taihe::string_view videoCodecMime, int32_t width, int32_t height, int32_t frameRate, int32_t isHDRVivid, int32_t bitRate) { TH_THROW(std::runtime_error, "initNative not implemented"); } void startNative() { TH_THROW(std::runtime_error, "startNative not implemented"); } uintptr_t stopNative() { TH_THROW(std::runtime_error, "stopNative not implemented"); } } // namespace // Since these macros are auto-generate, lint will cause false positive. // NOLINTBEGIN TH_EXPORT_CPP_API_initNative(initNative); TH_EXPORT_CPP_API_startNative(startNative); TH_EXPORT_CPP_API_stopNative(stopNative); // NOLINTEND ``` 补充功能 **`recorder.impl.cpp`** ```cpp #include "recorder.proj.hpp" #include "recorder.impl.hpp" #include "stdexcept" #include "RecorderNative.h" #include #include "taihe/napi_runtime.hpp" #undef LOG_DOMAIN #undef LOG_TAG #define LOG_DOMAIN 0xFF00 #define LOG_TAG "recorder" namespace { // To be implemented. struct AsyncCallbackInfo { napi_env env; napi_async_work asyncWork; napi_deferred deferred; int32_t resultCode = 0; std::string surfaceId = ""; SampleInfo sampleInfo; }; void DealCallBack(napi_env env, void *data) { AsyncCallbackInfo *asyncCallbackInfo = static_cast(data); napi_value code; napi_create_int32(env, asyncCallbackInfo->resultCode, &code); napi_value surfaceId; napi_create_string_utf8(env, asyncCallbackInfo->surfaceId.data(), NAPI_AUTO_LENGTH, &surfaceId); napi_value obj; napi_create_object(env, &obj); napi_set_named_property(env, obj, "code", code); napi_set_named_property(env, obj, "surfaceId", surfaceId); napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj); napi_delete_async_work(env, asyncCallbackInfo->asyncWork); delete asyncCallbackInfo; } void SetCallBackResult(AsyncCallbackInfo *asyncCallbackInfo, int32_t code) { asyncCallbackInfo->resultCode = code; } void SurfaceIdCallBack(AsyncCallbackInfo *asyncCallbackInfo, std::string surfaceId) { asyncCallbackInfo->surfaceId = surfaceId; } void NativeInit(napi_env env, void *data) { AsyncCallbackInfo *asyncCallbackInfo = static_cast(data); int32_t ret = Recorder::GetInstance().Init(asyncCallbackInfo->sampleInfo); if (ret != AVCODEC_SAMPLE_ERR_OK) { SetCallBackResult(asyncCallbackInfo, -1); } uint64_t id = 0; ret = OH_NativeWindow_GetSurfaceId(asyncCallbackInfo->sampleInfo.window, &id); if (ret != AVCODEC_SAMPLE_ERR_OK) { SetCallBackResult(asyncCallbackInfo, -1); } asyncCallbackInfo->surfaceId = std::to_string(id); SurfaceIdCallBack(asyncCallbackInfo, asyncCallbackInfo->surfaceId); } uintptr_t initNative(int32_t fd, ::taihe::string_view videoCodecMime, int32_t width, int32_t height, double frameRate, int32_t isHDRVivid, int64_t bitRate) { napi_env env=::taihe::get_env(); SampleInfo sampleInfo; size_t argc = 7; sampleInfo.outputFd=fd; sampleInfo.videoWidth=width; sampleInfo.videoHeight=height; sampleInfo.frameRate=frameRate; sampleInfo.isHDRVivid=isHDRVivid; sampleInfo.bitrate=bitRate; sampleInfo.videoCodecMime = videoCodecMime; if (sampleInfo.isHDRVivid) { sampleInfo.hevcProfile = HEVC_PROFILE_MAIN_10; } sampleInfo.audioCodecMime = OH_AVCODEC_MIMETYPE_AUDIO_AAC; sampleInfo.audioSampleForamt = OH_BitsPerSample::SAMPLE_S16LE; sampleInfo.audioSampleRate = 48000; sampleInfo.audioChannelCount = 2; sampleInfo.audioBitRate = 32000; sampleInfo.audioChannelLayout = OH_AudioChannelLayout::CH_LAYOUT_STEREO; sampleInfo.audioMaxInputSize = sampleInfo.audioSampleRate * 0.02 * sampleInfo.audioChannelCount * sizeof(short); napi_value promise; napi_deferred deferred; napi_create_promise(env, &deferred, &promise); AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo(); asyncCallbackInfo->env = env; asyncCallbackInfo->asyncWork = nullptr; asyncCallbackInfo->deferred = deferred; asyncCallbackInfo->resultCode = -1; asyncCallbackInfo->sampleInfo = sampleInfo; napi_value resourceName; napi_create_string_latin1(env, "recorder", NAPI_AUTO_LENGTH, &resourceName); napi_create_async_work( env, nullptr, resourceName, [](napi_env env, void *data) { NativeInit(env, data); }, [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); }, (void *)asyncCallbackInfo, &asyncCallbackInfo->asyncWork); napi_queue_async_work(env, asyncCallbackInfo->asyncWork); return (uintptr_t)promise; } void startNative() { Recorder::GetInstance().Start(); } void NativeStop(napi_env env, void *data) { AsyncCallbackInfo *asyncCallbackInfo = static_cast(data); int32_t ret = Recorder::GetInstance().Stop(); if (ret != AVCODEC_SAMPLE_ERR_OK) { SetCallBackResult(asyncCallbackInfo, -1); } SetCallBackResult(asyncCallbackInfo, 0); } uintptr_t stopNative() { napi_env env=::taihe::get_env(); napi_value promise; napi_deferred deferred; napi_create_promise(env, &deferred, &promise); AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo(); asyncCallbackInfo->env = env; asyncCallbackInfo->asyncWork = nullptr; asyncCallbackInfo->deferred = deferred; NativeStop(env, asyncCallbackInfo); return (uintptr_t)nullptr; } } // namespace // Since these macros are auto-generate, lint will cause false positive. // NOLINTBEGIN TH_EXPORT_CPP_API_initNative(initNative); TH_EXPORT_CPP_API_startNative(startNative); TH_EXPORT_CPP_API_stopNative(stopNative); // NOLINTEND ``` #### 3.项目生成与运行 ##### 3.1 修改CMakeLists.txt 在output_dir下有CMakeLists.txt包含taihe部分需要添加的内容,可以将其添加到项目构建的CMake文件中 **`./generated/CMakeLists.txt`** ``` set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) add_library( recorder SHARED D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/temp/recorder.impl.cpp D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/temp/napi_register.cpp D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/src/recorder.napi.cpp D:/taihe/src/taihe/runtime/object.cpp D:/taihe/src/taihe/runtime/string.cpp D:/taihe/src/taihe/runtime/napi_runtime.cpp ) target_include_directories( recorder PUBLIC D:/taihe/include D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/include ) ``` 将上述内容添加到项目的CMakeLists中 **`entry/src/main/cpp/CMakeLists.txt`** ``` # the minimum version of CMake. cmake_minimum_required(VERSION 3.4.1) project(videoCodecSample) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${NATIVERENDER_ROOT_PATH} ${NATIVERENDER_ROOT_PATH}/capbilities/include ${NATIVERENDER_ROOT_PATH}/common ${NATIVERENDER_ROOT_PATH}/common/dfx/err ${NATIVERENDER_ROOT_PATH}/common/dfx/log ${NATIVERENDER_ROOT_PATH}/render/include ${NATIVERENDER_ROOT_PATH}/sample/player ${NATIVERENDER_ROOT_PATH}/sample/recorder ) add_library(recorder SHARED D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/temp/recorder.impl.cpp D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/temp/napi_register.cpp D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/src/recorder.napi.cpp D:/taihe/src/taihe/runtime/object.cpp D:/taihe/src/taihe/runtime/string.cpp D:/taihe/src/taihe/runtime/napi_runtime.cpp sample/recorder/Recorder.cpp capbilities/Muxer.cpp capbilities/VideoEncoder.cpp capbilities/AudioCapturer.cpp capbilities/AudioEncoder.cpp common/SampleCallback.cpp ) target_include_directories( recorder PUBLIC D:/taihe/include D:/AVCodecVideo/entry/src/main/cpp/types/librecorder/generated/include ) target_link_libraries(player PUBLIC ${BASE_LIBRARY}) target_link_libraries(recorder PUBLIC ${BASE_LIBRARY}) ``` ##### 3.2 运行 该项目有两个动态库player和recorder,player操作相同,需要注意的是在Init注册处获取了window以绑定后续渲染的窗口,需要在`./generated/src/player.napi.cpp`中添加一行 ``` NativeXComponentSample::PluginManager::GetInstance()->Export(env, exports);``` #### 4. 对比 taihe桥接代码结构 ``` sample/ ├─ player/ // 播放模块相关实现 │ ├─ Player.cpp // 播放功能调用逻辑实现 │ └─ Player.h // 播放功能调用逻辑接口 │ │ │ ├─ recorder/ // 录制模块相关实现 │ ├─ Recorder.cpp // 录制功能调用逻辑实现 │ └─ Recorder.h // 录制功能调用逻辑接口 │ │ │ └─ types/ // 对外暴露的接口类型定义 ├─ libplayer/ // 播放模块暴露给UI层的接口 │ ├─ oh-package.json5 // 模块配置文件 │ │ │ └─ generated/ // 自动生成的代码 │ ├─ CMakeLists.txt // 编译配置 │ ├─ player.d.ts // 生成的声明文件 │ ├─ player.taihe // 生成的IDL文件 │ ├─ processing_report.txt // 生成过程报告 │ │ │ ├─ include/ // 头文件目录 │ │ ├─ player.abi.h │ │ ├─ player.impl.hpp │ │ ├─ player.napi.h │ │ ├─ player.proj.hpp │ │ └─ player.user.hpp │ │ │ ├─ src/ // 源文件目录 │ │ ├─ player.abi.c │ │ └─ player.napi.cpp │ │ │ └─ temp/ // 临时文件目录 │ ├─ napi_register.cpp // NAPI注册代码 │ └─ player.impl.cpp // 实现层临时代码 │ └─ librecorder/ // 录制模块暴露给UI层的接口 ├─ oh-package.json5 // 模块配置文件 │ └─ generated/ // 自动生成的代码 ├─ CMakeLists.txt // 编译配置 ├─ processing_report.txt // 生成过程报告 ├─ recorder.d.ts // 生成的声明文件 ├─ recorder.taihe // 生成的IDL文件 │ ├─ include/ // 头文件目录 │ ├─ recorder.abi.h │ ├─ recorder.impl.hpp │ ├─ recorder.napi.h │ ├─ recorder.proj.hpp │ ├─ recorder.Response.abi.0.h │ ├─ recorder.Response.abi.1.h │ ├─ recorder.Response.abi.2.h │ ├─ recorder.Response.napi.decl.h │ ├─ recorder.Response.napi.impl.h │ ├─ recorder.Response.proj.0.hpp │ ├─ recorder.Response.proj.1.hpp │ ├─ recorder.Response.proj.2.hpp │ └─ recorder.user.hpp // 用户层接口 │ ├─ src/ // 源文件目录 │ ├─ recorder.abi.c // ABI层实现 │ └─ recorder.napi.cpp // NAPI层实现 │ └─ temp/ // 临时文件目录 ├─ napi_register.cpp // NAPI注册代码 └─ recorder.impl.cpp // 实现层临时代码 ``` 假设我们已有完整的c++调用逻辑,转换为napi实现需要的代码量为(仅统计与napi或taihe结构相关的部分) | 文件|手写napi|taihe| | :----| ----:| ----:| | player|39|4| |recorder|66|53| |dts|8|0| *注:目前(2025/9/18)taihe还不支持异步,因此recorder代码减少有限 对于太和已经支持的功能,代码量减少90%,只有少量对象需要更改为taihe对象;