commit cd788ded0a44aee1df8acf94b39d0c4a3098e1e6 Author: luocai Date: Tue Jun 18 14:27:48 2024 +0800 initial commit. diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..aa2f616 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: LLVM + +ObjCBlockIndentWidth: 4 +IndentWidth: 4 +TabWidth: 4 +AccessModifierOffset: -4 +ColumnLimit: 130 + +#模板声明后换行 +AlwaysBreakTemplateDeclarations: true + +# 是否允许短if单行 If true, if (a) return; 可以放到同一行 +AllowShortIfStatementsOnASingleLine: true + +#短句 while (true) continue; 能被放到单行。 +AllowShortLoopsOnASingleLine: true +AllowShortFunctionsOnASingleLine: false \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7dce0b --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# C++ objects and libs +*.slo +*.lo +*.o +*.a +*.la +*.lai +*.so +*.dll +*.dylib + +# Qt-es +object_script.*.Release +object_script.*.Debug +*_plugin_import.cpp +/.qmake.cache +/.qmake.stash +*.pro.user +*.pro.user.* +*.qbs.user +*.qbs.user.* +*.moc +moc_*.h +*.qmlc +*.jsc +Makefile* +*build-* + +# Qt unit tests +target_wrapper.* + +# QtCreator +*.autosave + +# QtCreator Qml +*.qmlproject.user +*.qmlproject.user.* + +# QtCreator CMake +CMakeLists.txt.user* +build diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..3809836 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,23 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/opt/Libraries/boost_1_84_0/include", + "${workspaceFolder}/3rdparty/arm-linux-gnueabihf/mpp/inc", + "/opt/toolchains/Libraries/opus-1.4/include", + "ThirdParty/libjpegTurbo/inc", + "ThirdParty/libalsa/inc", + "${workspaceFolder}/build/_deps/kylin-src/Universal", + "${workspaceFolder}/build/_deps/kylin-src/HttpProxy" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c17", + "cppStandard": "gnu++17", + "intelliSenseMode": "linux-gcc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..23480ce --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,30 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "FaceAccessDebug", + "type": "cppdbg", + "request": "launch", + "miDebuggerPath": "arm-linux-gnueabihf-gdb", + "miDebuggerServerAddress": "192.168.8.122:8080", + "program": "${workspaceFolder}/build/src/gate_face/GateFace", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "logging": { + "engineLogging": false + }, + "MIMode": "gdb", + "useExtendedRemote": true, + "setupCommands": [ + { + "text": "set remote exec-file /sdcard/GateFace", + "description": "设置嵌入式单板加载的程序", + "ignoreFailures": false + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5c62eb7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "editor.formatOnSaveMode": "modificationsIfAvailable", + "editor.formatOnSave": false, +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f8c76b2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +project(FaceAccess) +cmake_minimum_required(VERSION 3.27) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(OPENSSL_ROOT /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/openssl-1.0.2q) +set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT}/include) + +set(ALSA_ROOT /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/libalsa-1.1.5) +set(ALSA_INCLUDE_DIR ${ALSA_ROOT}/include) +set(ALSA_LIBRARY_DIRS ${ALSA_ROOT}/lib) + +set(FFMPEG_ROOT /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/ffmpeg-4.1.3) +set(FFMPEG_INCLUDE_DIR ${FFMPEG_ROOT}/include) +set(FFMPEG_LIBRARY_DIRS ${FFMPEG_ROOT}/lib) +set(FFMPEG_LIBRARY avcodec avdevice avfilter avformat avutil postproc swresample swscale) + +set(MPP_ROOT /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/rockchip_mpp) +set(MPP_INCLUDE_DIR ${MPP_ROOT}/include) +set(MPP_LIBRARY_DIRS ${MPP_ROOT}/rk-libs) + + +include(FetchContent) +FetchContent_Declare(Kylin + GIT_REPOSITORY https://gitea.amass.fun/amass/Kylin.git +) +FetchContent_MakeAvailable(Kylin) + +add_subdirectory(GatePass) +add_subdirectory(Record) +add_subdirectory(VoucherVerifyServer) \ No newline at end of file diff --git a/GatePass/CMakeLists.txt b/GatePass/CMakeLists.txt new file mode 100644 index 0000000..10421bb --- /dev/null +++ b/GatePass/CMakeLists.txt @@ -0,0 +1,12 @@ +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Boost REQUIRED COMPONENTS json) + +add_executable(GatePass + main.cpp +) + +target_link_libraries(GatePass + PRIVATE Universal +) \ No newline at end of file diff --git a/GatePass/main.cpp b/GatePass/main.cpp new file mode 100644 index 0000000..1dc710d --- /dev/null +++ b/GatePass/main.cpp @@ -0,0 +1,54 @@ +#include "BoostLog.h" +#include +#include +#include +#include +#include + +using boost::asio::ip::udp; + +int main(int argc, char const *argv[]) { + boost::log::initialize("/data/sdcard/logs/GatePass", "/data/sdcard/logs"); + LOG(info) << "app start..."; + std::cout << "hello word." << std::endl; + + constexpr auto txt = R"({"cmd":"gate_status","status":1,"personIn":11638,"personOut":5123,"runCount":115370})"; + + constexpr auto txt1 = R"({"cmd":"gate_status","status":2,"personIn":-12345,"personOut":-12345,"runCount":115370})"; + + constexpr auto error = R"({"cmd":"fault_and_alarm","code":91})"; + + constexpr auto error1 = R"({"cmd":"fault_and_alarm","code":96})"; + + try { + // if (argc != 2) { + // std::cerr << "Usage: client " << std::endl; + // return 1; + // } + + udp::endpoint point(udp::v4(), 6787); + + boost::asio::io_context io_context; + + udp::socket socket(io_context); + socket.open(udp::v4()); + + socket.send_to(boost::asio::buffer(std::string(txt)), point); + LOG(info) << "udp send: " << txt; + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + socket.send_to(boost::asio::buffer(std::string(error)), point); + + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + socket.send_to(boost::asio::buffer(std::string(error1)), point); + + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + socket.send_to(boost::asio::buffer(std::string(txt1)), point); + + } catch (std::exception &e) { + std::cerr << e.what() << std::endl; + } + + return 0; + + return 0; +} diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..c86737b --- /dev/null +++ b/Readme.md @@ -0,0 +1,469 @@ + +```shell +amixer sset 'Master' 50% # 设置音量 +mount -o remount rw /system/ # 读写挂载 +keydata get mac # 操作mac +keydata set mac 6C:5C:3D:B2:27:78 +keydata set hardware HW00000011C3000R -f +MAC存储位置:/data/.mac + +ps -ef | egrep start-app.sh | grep -v grep | awk '{print $2}' | xargs kill -9 2 > /dev/null + +ps -ef | egrep dk_uart_id_reader | grep -v grep | awk '{print $2}' | xargs kill -9 2 > /dev/null + +killall start-app.sh && killall GateFace +killall dk_uart_id_reader +tail -f /sdcard/logs/b020.log + + +mount -o remount rw /system/ && mv /system/bin/start-app.sh /system/bin/start-app.sh.bak +mount -o remount rw /system/ && mv /system/bin/start-app.sh.bak /system/bin/start-app.sh +echo 1 > /dev/watchdog + +dk_uart_id_reader + +keydata set hardware HW00000011C3000U -f +keydata get hardware + + +/etc/init.d/S97_lunch_init +``` + +`script/start-app.sh`有个`write_mac()`函数生成`/data/.mac`文件。 + + +## 隐藏页面参数设置 + +``` + +http://192.168.8.120/otherPage.php?cmd=get_other_page_params + +http://192.168.8.120/otherPage.php?cmd=set_other_page_params&distanceType=1&distance=1&dynamicFeatSimTh=70&dynamicFeatImgQualityTh=90&fast_temp_sw=true&save_reg_img_sw=true&audio_out_enabled=true +``` + +```json +{ + "cmd": "set_other_page_params", + "distanceType": 1, + "save_reg_img_sw": true, + "fast_temp_sw": true, + "dynamicFeatSimTh": 70, + "dynamicFeatImgQualityTh": 90, + "audio_out_enabled": true +} + + +{ + "ret": 0, + "code": 200, + "message": "OK", + "data": { + "distanceType": 1, + "distance": 100, + "dynamicFeatSimTh": 70, + "dynamicFeatImgQualityTh": 90, + "fast_temp_sw": true, + "save_reg_img_sw": true, + "audio_out_enabled": true + } +} +``` + + + +## FacePass平台交互 + +### 连接不成功,UI上图标不对 + +```c++ +void statusbarUi::updatePlatformConnectionStatus(const int status) +{ + QPixmap pm; + switch(status) + { + case UiInterfaceForBackstage::PLATFORM_CONNECTION_STATUS_DISCONNECTED: + pm.load(":/res/image/disconnect.png"); + break; + case UiInterfaceForBackstage::PLATFORM_CONNECTION_STATUS_CONNECTED: + pm.load(":/res/image/display.png"); + break; + case UiInterfaceForBackstage::PLATFORM_CONNECTION_STATUS_LOGGED: + pm.load(":/res/image/display.png"); + break; + default: + break; + } + m_label_platform->setPixmap(pm.scaled(20, 20, Qt::KeepAspectRatio)); +} +``` + + + +## 测试/开发帐号 + +设备业务服务器地址统一都为9002。 + +- FacePass开发地址:http://172.16.7.16/ + + - 账号:luocai + + - 密码:.Luo19961030 + + - 管理员账号:admin + + - 管理员密码:Reconova_123456 + + +- 可视对讲(阳江博物馆项目):https://172.16.100.22/ + - 账号:admin + - 密码:Reconova_123456 + +## 人脸识别函数调用 + +相关模块初始化: + +```c++ +GoThread::taskProc(); // 初始化摄像头 +FaceGate::preLoad(); // 等待摄像头初始化完成 +FaceGate::taskProc(); // 初始化算法 + +AntiSpoof4Rv1109::init(); +AttrDetector4Rv1109::init() +DetectorImpl4Rv1109::init() +LandmarkDetectorRk::init() +``` + + + +```mermaid +classDiagram + RwRecoResult + GRecoID --|> GObject + GRecoID --* RecoIDInfo + + detector + attr_detector + landmark_detector + anti_spoof + GTrackerMng *-- AntiSpoof + GTrackerMng *-- LandmarkDetector + GDetectorHeadAttr *-- AttrDetector + AntiSpoof4Rv1109 ..|> AntiSpoof + LandmarkDetectorRk ..|> LandmarkDetector + AttrDetector4Rv1109 ..|> AttrDetector + GObject <|-- GTrackerLock + GDetectorTracker *-- Detector + RecoAlgorithm *-- Detector + RecoAlgorithm *-- LandmarkDetector + + ParamManage --* GoThread + UdpServer --* GoThread + GCamera --* GoThread + GDetectorTracker --* GoThread + GTrackerMng --* GoThread + GTrackerLock --* GoThread + GDetectorHeadAttr --* GoThread + GReco1vsN --* GoThread + GReco1vs1 --* GoThread + GRecoID --* GoThread + GPeInput --* GoThread +``` + + + + + +逻辑实现都在`src/workflow/output/decision_actuator/decision_actuator.h` + +```mermaid +flowchart TB + A["ADMsg::showDisplayMsg()"] + B["View::showRecoSuccess()"] + C["GDecisionActuator::showRecoResult()"] + D["GDecisionCenter::maskProc()"] + D-->C-->B-->A +``` + +## 人证核验逻辑 + +```mermaid +flowchart TB + A-->B-->C-->D-->E-->F-->G + F-->H-->I-->J + + A["UDPIDCardReader::detect()"] + B["IdReader::isNewCardAvailable()"] + C["IdReader::polling()"] + D["Peripheral::idPolling()"] + E["ReaderThread::taskProc()"] + F["ReaderThread::readerInputAvailNoti()"] + G["GPeInput::readerInputAvailCallback()"] + H["GPeInput::idReaderCallback()"] + I["GDecisionCenter::idCardReaded()"] + J["GDecisionCenter::_idCardProc()"] +``` + +recoUi::slotShowSceneResult() +recoUiRecognize::slotsDrawScanner() +mainUi::notifyFaceInfo() + +`recoUi::slotTimer()`负责将结果页面切回待机页面 + + +### 人证核验限制 + +``` +http://192.168.8.127/deviceAction.php?mask_check_sw=false&no_mask_no_entry=true&mask_no_entry=false&temp_check_sw=true&temp_anomaly_no_entry=true&min=35&max=37.3&sw=false&temp_unit=0&temp_type=1&temp_audio_sw=false&temp_show_sw=true&sim_th=0&reco_distance_type=3&face_sw=true&reco_from=1&fast_access_sw=false&ic_card_sw=false&face_and_id_sw=true&yuejuma=false&succ_interval=3&fail_interval=1&helmet_sw=false&dynamic_feature=false&face_frame_sw=false&dev_local_pwd_type=1&dev_local_pwd=&health_code_sw=false&health_code_allow_stranger_sw=false&health_code_allow_access_color=1&health_code_allow_access_c19t=4&health_code_allow_access_trip=0&health_code_allow_access_abt=0&travel_card_sw=false&disable_face_sw=false&health_code_needed_tip_type=0&health_trip_fail_times=3&health_trip_need_id_sw=false&health_ui_display_time=3&access_condition=[0,1]&light_and_background=[1,1,0,0,2]&health_check_phone_num_sw=false&health_ok_display_time=3&health_white_list_free_sw=false&health_code_c19t_hours_limit=0&health_trip_show_tip_sw=false&disable_dev_setting_sw=false&temp_sensitivity=0.75&cmd=set_device&face_id_card_verify_rule={"age_limit":{"enabled":true,"precision":2,"range":[{"from":40,"to":60},{"from":30,"to":-1},{"from":-1,"to":70}]},"zone_limit":{"enabled":true,"range":["14512","78945","31103"]}} +``` + + + +## 第三方票务平台注册 + +``` +GDecisionCenter::ReconfirmReq +RWProtocol::sendRequestOpenDoorReconfirm() +UdpParse::voucher_verify_result_proc() +RWProtocol::voucherVerifyProc() + +## 读取到身份证之后 +GDecisionCenter::idCardReaded() +GRecoID::onIDCard() 保存图标 +ReaderInfo 保存了nv21的图像编码 +GDecisionCenter::m_idInfo 保存了nv21的图像编码 +``` + + + + + + + + + + + +## 闸机 + +### 故障上报 + +| 功能列表 | 类别 | | +| -------------------- | ------ | -------------- | +| 闸机主机电机故障 | 故障类 | 0x21+100=133 | +| 闸机副机电机故障 | 故障类 | 0x22+100=134 | +| 闸机主机离合器故障 | 故障类 | 2003+100= 2103 | +| 闸机副机离合器故障 | 故障类 | 2004+100=2104 | +| 闸机持续开闸信号故障 | 故障类 | 0x5A+100=190 | +| 闸机红外故障 | 故障类 | 0x5B+100=191 | +| 闸机遇阻停机故障 | 故障类 | 0x0A+100=110 | +| 闸机自检失败故障 | 故障类 | 0x23+100=135 | +| 闸机固件升级失败 | 故障类 | 1001+100=1104 | +| 闸机消防告警 | 告警类 | 0x04+100=104 | +| 闸机门长久未关告警 | 告警类 | 0x5C+100=192 | +| 人员强闯告警 | 告警类 | 0x60+100=196 | +| 人员非法推杆告警 | 告警类 | 2001+100=2101 | +| 人员逆行告警 | 告警类 | 0x61+100=197 | +| 人员尾随告警 | 告警类 | 0x62+100=198 | +| 人员逗留告警 | 告警类 | 0x5F+100=195 | + +### 通行记录 + +```protobuf +//index=593 设备上报人员通过闸机事件 +message PersonAccessedReport{ + Common base=1; + string alarmId=2; //消息id + int64 recordId=3; //记录id + string timestamp=4;//时间戳,格式:"yyyy-MM-dd HH:mm:ss" 举例 "2023-11-30 11:32:45" + int32 direction=5;//方向,1:进,2:出 +} + +//index=594 平台应答人员通过闸机事件 +message ReplyPersonAccessedReport { + Common base=1; + string alarmId=2; //消息id + int64 recordId=3; //记录id +} +``` + + + +参考人脸通行记录 + +``` +createAccessRecord +setAccessRecordsUploadStaByRecordIdRang +deleteAccessRecordByRecordId +getAccessRecordsNumByUploadSta + + +GPeInput::rs485DataProc 处理闸机进出 + +createGateAccessRecord + +# upload thread +bool checkIfSendAccessInfoData(long long curSysSec); +int pushAccessInfoListToSrv(long long curSysSec); +int uploadAccessInfo(long long curSysSec); +``` + + + +## netconfig网络管理 + +```mermaid +classDiagram + NetConfigThread --> NetConfig + NetConfig --> Linker + EthLinker ..|> Linker + WlanLinker ..|> Linker + SIMLinker ..|> Linker + + +``` + + + +wifi_connect 连接wifi耗时过长 + + + +## UI组织 + + +```mermaid +--- +title: FaceGate UI +--- +classDiagram + View *-- Backstage + View *-- mainUi + mainUi ..|> UiInterfaceForBackstage + mainUi *-- SetupUi + mainUi *-- settingUi + mainUi *-- recoUi + recoUi *-- recoUiRecognize + recoUi *-- recoUiRecognizeTypeColor + recoUi *-- recoUiRecognizeTypeAd + recoUi *-- recoUiScreensaver + recoUi *-- recoUiPassword + recoUi *-- recoUiHealthCode + recoUi *-- recoUiSceneResult + + class View { + viewInitBackstage(); + viewCreateUi(); + } +``` +### SetupUi + +仅当`/data/config/need_setup_wizard`文件存在时显示,流程完成时再删除此文件。 + +### settingUi + +```mermaid +classDiagram + SettingUiPage --|> MyWidgetWithMainStyleColor + settingUiDevMngNetWifi --|> SettingUiPage + settingUiDevMngNetWifiAccPoint --|> SettingUiPage + SettingUiPage *-- QListWidget + myListWidget --|> QWidget +``` + +### recoUi + +```mermaid +classDiagram + SceneMsgWidget --|> QWidget + MsgWidget --|> QWidget + recoUiSceneResult *-- SceneMsgWidget + recoUi *-- recoUiSceneResult + recoUiRecognize --|> PageAcceptMouseAndTouch + PageAcceptMouseAndTouch --|> WidgetWithBackstageInterface + recoUiRecognize *-- PersonWidget + recoUiRecognizeTypeBase *-- MsgWidget + recoUiRecognizeTypeBase --|> PageAcceptMouseAndTouch + recoUiRecognizeTypeColor --|> recoUiRecognizeTypeBase + recoUiRecognizeTypeAd --|> recoUiRecognizeTypeBase + +``` + + + + + + +## 硬件、固件版本说明 + +硬件版本说明: http://wiki.reconova.cn/pages/viewpage.action?pageId=36798802 + +### B045 CI构建包存放地址 + +``` +\\172.16.2.222\瑞为版本\Test\shop\B045 +``` + +### 测试机信息记录 + +扫码枪: + +``` +/dev/hidraw0 +``` + +万睿面板机 + +```json +// MAC 6C:5C:3D:B2:27:81 +{ + "mt": 98, +    "ct": "HWA0310722C1110W", +    "mac": "6C5C3DB22781", +    "id": "412d00003263", +    "key": "412d9f3500c79fb3", +    "sct": "b42f6f643aedaf00" +} +``` + +B045 V05带屏幕 + +```json +{"mt": 98,"ct": "HWA0310722C1110U","mac": "6C5C3DB229A0"} + +keydata set hardware HWA0310722C1110W -f +``` + +B033 V05裸板 + +```json +// MAC 6C:5C:3D:B2:27:82 + +{ + "mt": 98, +    "ct": "HW00000011C3000U", +    "mac": "6C5C3DB22782", +    "id": "6c5c3db22782", +    "key": "6d711a5543353b5a", +    "sct": "06b619feadfae838" +} +``` + +B026啪啪运动裸板 + +```json +// keydata set hardware HW00000011C3000R -f +// keydata set mac 6C:5C:3D:B2:27:83 -f +{ + "mt": 98, +    "ct": "HW00000011C3000U", +    "mac": "6C5C3DB22783" +} + +// 带屏幕 +{ + "mt": 98, +    "ct": "HWA0310511C1000R", +    "mac": "6C5C3DB22783" +} +``` + diff --git a/Record/CMakeLists.txt b/Record/CMakeLists.txt new file mode 100644 index 0000000..3080c87 --- /dev/null +++ b/Record/CMakeLists.txt @@ -0,0 +1,81 @@ +add_executable(Record main.cpp + RkAudio.h RkAudio.cpp + OpusCodec.h OpusCodec.cpp + FFmpegResample.h FFmpegResample.cpp + RkRecorder.cpp +) + +target_include_directories(Record + PRIVATE ${ALSA_INCLUDE_DIR} + PRIVATE ${MPP_INCLUDE_DIR} + PRIVATE ${MPP_INCLUDE_DIR}/rkmedia + PRIVATE /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/opus-1.4/include + PRIVATE ${FFMPEG_INCLUDE_DIR} +) + +target_link_directories(Record + PRIVATE ${ALSA_LIBRARY_DIRS} + PRIVATE ${MPP_LIBRARY_DIRS} + PRIVATE ${3rdparty_ROOT}/rkap_3a/lib + PRIVATE ${FFMPEG_LIBRARY_DIRS} + PRIVATE /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/opus-1.4/lib +) + +target_link_libraries(Record + PRIVATE asound + PRIVATE easymedia + PRIVATE drm + PRIVATE rkaiq + PRIVATE rockchip_mpp + PRIVATE v4l2 + PRIVATE v4lconvert + PRIVATE jpeg + PRIVATE png16 + PRIVATE fontconfig + PRIVATE freetype + PRIVATE expat + PRIVATE rga + PRIVATE glib-2.0 + PRIVATE pcre + PRIVATE opus + PRIVATE Universal + PRIVATE stdc++fs + PRIVATE RKAP_ANR + PRIVATE RKAP_Common + PRIVATE uuid + PRIVATE dl + PRIVATE z + PRIVATE ${FFMPEG_LIBRARY} +) + +add_executable(rkmedia_audio_test rkmedia_audio_test.c) + +target_include_directories(rkmedia_audio_test + PRIVATE ${MPP_INCLUDE_DIR}/rkmedia +) + +target_link_directories(rkmedia_audio_test + PRIVATE ${MPP_LIBRARY_DIRS} + PRIVATE ${3rdparty_ROOT}/rkap_3a/lib + PRIVATE ${ALSA_LIBRARY_DIRS} +) + +target_link_libraries(rkmedia_audio_test + PRIVATE asound + PRIVATE easymedia + PRIVATE drm + PRIVATE rkaiq + PRIVATE rockchip_mpp + PRIVATE v4l2 + PRIVATE v4lconvert + PRIVATE jpeg + PRIVATE rga + PRIVATE glib-2.0 + PRIVATE pcre + # PRIVATE opus + # PRIVATE Universal + # PRIVATE stdc++fs + PRIVATE RKAP_ANR + PRIVATE RKAP_Common + PRIVATE dl +) \ No newline at end of file diff --git a/Record/FFmpegResample.cpp b/Record/FFmpegResample.cpp new file mode 100644 index 0000000..af46ed0 --- /dev/null +++ b/Record/FFmpegResample.cpp @@ -0,0 +1,61 @@ +#include "FFmpegResample.h" +#include "BoostLog.h" +#include "FFmpegResample.h" + +extern "C" { +#include +#include +} + +FFmpegResample::~FFmpegResample() { + if (m_buffer != nullptr) { + delete[] m_buffer; + } +} + +void FFmpegResample::initialize(int32_t sampleRateIn, int32_t channelIn, int32_t sampleRateOut, int32_t channelOut, + int32_t period) { + m_sampleRateOut = sampleRateOut; + m_channelOut = channelOut; + m_sampleRateIn = sampleRateIn; + m_channelIn = channelIn; + + int64_t outChLayout = channelOut == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; + int64_t inChLayout = channelIn == 1 ? AV_CH_LAYOUT_MONO : AV_CH_LAYOUT_STEREO; + + m_swrContext = swr_alloc_set_opts(nullptr, outChLayout, AV_SAMPLE_FMT_S16, sampleRateOut, inChLayout, AV_SAMPLE_FMT_S16, + sampleRateIn, 0, nullptr); + if (m_swrContext == nullptr) { + LOG(error) << "swr_alloc_set_opts() failed."; + } + m_buffer = new uint8_t[sampleRateOut / 1000 * period * channelOut * sizeof(int16_t)]; + int status = swr_init(m_swrContext); + if (status < 0) { + LOG(error) << "swr_init() failed."; + } +} + +FFmpegResample::Frame FFmpegResample::resample(const uint8_t *pcm, int32_t size) { + auto begin = std::chrono::system_clock::now(); + FFmpegResample::Frame ret; + // 1. 输入进来的是 1channel 16khz 16bit pcm + // 2. 重采样为 2channel 48khz 16bit pcm + // 3. 编码为 opus + + int32_t inSamples = size / m_channelIn / sizeof(int16_t); + int32_t outSamples = inSamples * m_sampleRateOut / m_sampleRateIn; + uint8_t *outBuffer = buffer.data(); + int samples = swr_convert(m_swrContext, &outBuffer, outSamples, &pcm, inSamples); + if (samples < 0) { + char buffer[512] = {0}; + LOG(error) << "avcodec_receive_frame() failed, error: " << av_make_error_string(buffer, sizeof(buffer), samples); + return ret; + } + + auto elapsed = std::chrono::duration_cast(std::chrono::system_clock::now() - begin); + + ret.data = outBuffer; + ret.byteSize = samples * m_channelOut * sizeof(int16_t); + // LOG(info) << "inSamples: " << inSamples << ", samples: " << samples << ", elapsed: " << elapsed.count(); + return ret; +} diff --git a/Record/FFmpegResample.h b/Record/FFmpegResample.h new file mode 100644 index 0000000..1a99bb1 --- /dev/null +++ b/Record/FFmpegResample.h @@ -0,0 +1,32 @@ +#ifndef __FFMPEGRESAMPLE_H__ +#define __FFMPEGRESAMPLE_H__ + +#include +#include +#include + +struct SwrContext; + +class FFmpegResample { +public: + class Frame { + public: + uint8_t *data = nullptr; + int32_t byteSize = 0; + }; + ~FFmpegResample(); + void initialize(int32_t sampleRateIn, int32_t channelIn, int32_t sampleRateOut, int32_t channelOut,int32_t period); + Frame resample(const uint8_t *pcm, int32_t size); + +private: + SwrContext *m_swrContext = nullptr; + uint8_t *m_buffer; + std::array buffer; + int32_t m_sampleRateOut = 48000; + int32_t m_channelOut = 2; + int32_t m_sampleRateIn = 16000; + int32_t m_channelIn = 1; + int32_t m_period = 20; // ms +}; + +#endif // __FFMPEGRESAMPLE_H__ \ No newline at end of file diff --git a/Record/OpusCodec.cpp b/Record/OpusCodec.cpp new file mode 100644 index 0000000..db8d40e --- /dev/null +++ b/Record/OpusCodec.cpp @@ -0,0 +1,68 @@ +#include "OpusCodec.h" +#include "BoostLog.h" + +namespace Opus { + +bool Encoder::open(int32_t samplerate, int32_t channels, uint32_t sampleSize) { + int error = 0; + + uint32_t maxFrameSize = samplerate / 1000 * 60 * sampleSize * channels; // opus最大60ms一帧编码 + m_buffer.resize(maxFrameSize); + + m_encoder = opus_encoder_create(samplerate, channels, OPUS_APPLICATION_AUDIO, &error); + if (error != OPUS_OK || m_encoder == nullptr) { + LOG(error) << "opus_encoder_create() failed, status: " << error; + return false; + } + + opus_encoder_ctl(m_encoder, OPUS_SET_VBR(0)); // 0:CBR, 1:VBR + opus_encoder_ctl(m_encoder, OPUS_SET_VBR_CONSTRAINT(true)); + opus_encoder_ctl(m_encoder, OPUS_SET_BITRATE(96000)); + opus_encoder_ctl(m_encoder, OPUS_SET_COMPLEXITY(8)); // 8 0~10 + opus_encoder_ctl(m_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); + opus_encoder_ctl(m_encoder, OPUS_SET_LSB_DEPTH(16)); // 每个采样16个bit,2个byte + opus_encoder_ctl(m_encoder, OPUS_SET_DTX(0)); + opus_encoder_ctl(m_encoder, OPUS_SET_INBAND_FEC(0)); + + return true; +} + +void Encoder::close() { + if (m_encoder != nullptr) { + opus_encoder_destroy(m_encoder); + m_encoder = nullptr; + } +} + +Encoder::~Encoder() { + close(); +} + +const Opus::Packet Encoder::encode(const int16_t *pcm, int32_t framesize) { + Packet ret; + if (m_encoder == nullptr) return ret; + ret.byteSize = opus_encode(m_encoder, pcm, framesize, m_buffer.data(), m_buffer.size()); + ret.data = m_buffer.data(); + LOG(info) << "framesize: " << framesize << ", record size: " << ret.byteSize; + return ret; +} + +bool Decoder::open(int32_t samplerate, int32_t channels) { + + int error = 0; + m_decoder = opus_decoder_create(samplerate, channels, &error); + if (error != OPUS_OK || m_decoder == NULL) { + LOG(error) << "opus_decoder_create() failed, status: " << error; + return false; + } + opus_decoder_ctl(m_decoder, OPUS_SET_LSB_DEPTH(16)); + return true; +} + +int32_t Decoder::decode(const uint8_t *data, int32_t dataSize, int16_t *pcm, int32_t pcmBuffer) { + int32_t decodedSize = opus_decode(m_decoder, data, dataSize, pcm, pcmBuffer, 0); + LOG(info) << "dataSize: " << dataSize << ", decodes frame size: " << decodedSize; + return decodedSize; +} + +} // namespace Opus \ No newline at end of file diff --git a/Record/OpusCodec.h b/Record/OpusCodec.h new file mode 100644 index 0000000..f8eb72d --- /dev/null +++ b/Record/OpusCodec.h @@ -0,0 +1,39 @@ +#ifndef __OPUSENCODER_H__ +#define __OPUSENCODER_H__ + +#include "opus/opus.h" +#include +#include + +namespace Opus { + +class Packet { +public: + uint8_t *data = nullptr; + uint32_t byteSize = 0; +}; + +class Encoder { +public: + ~Encoder(); + bool open(int32_t samplerate, int32_t channels, uint32_t sampleSize); + void close(); + const Packet encode(const int16_t *pcm, int32_t framesize); + +private: + OpusEncoder *m_encoder = nullptr; + std::vector m_buffer; +}; + +class Decoder { +public: + bool open(int32_t samplerate, int32_t channels); + int32_t decode(const uint8_t *data, int32_t dataSize, int16_t *pcm, int32_t pcmBuffer); + +private: + OpusDecoder *m_decoder = nullptr; +}; + +} // namespace Opus + +#endif // __OPUSENCODER_H__ \ No newline at end of file diff --git a/Record/RkAudio.cpp b/Record/RkAudio.cpp new file mode 100644 index 0000000..98955e2 --- /dev/null +++ b/Record/RkAudio.cpp @@ -0,0 +1,187 @@ +#include "RkAudio.h" +#include "BoostLog.h" +#include +#include + +namespace RkAudio { + +static SAMPLE_FORMAT_E rkAiFormat(Format::SampleType sampleType) { + SAMPLE_FORMAT_E ret = RK_SAMPLE_FMT_NONE; + switch (sampleType) { + case Format::SampleType::Unknown: + ret = RK_SAMPLE_FMT_NONE; + break; + case Format::SampleType::SignedInt16: + ret = RK_SAMPLE_FMT_S16; + break; + case Format::SampleType::SignedInt: + ret = RK_SAMPLE_FMT_S32; + break; + case Format::SampleType::Float: + ret = RK_SAMPLE_FMT_FLT; + break; + default: + LOG(error) << "unkonwn sample type: " << static_cast(sampleType); + ret = RK_SAMPLE_FMT_NONE; + break; + } + return ret; +} + +Input::Input() { +} + +Input::~Input() { + if (m_channel >= 0) { + stop(); + } +} +bool Input::open(const Format &format) { + bool ret = false; + // RK_MPI_SYS_DumpChn(RK_ID_AI); + m_channel = 0; + + AI_CHN_ATTR_S parameter = {0}; + parameter.pcAudioNode = "default"; + parameter.enAiLayout = AI_LAYOUT_MIC_REF; // remove ref channel, and output mic mono + parameter.enSampleFormat = rkAiFormat(format.sampleType); + parameter.u32Channels = format.channels; + parameter.u32SampleRate = format.sampleRate; + parameter.u32NbSamples = format.sampleRate / 1000 * format.period; + + int status = RK_MPI_AI_SetChnAttr(m_channel, ¶meter); + if (status) { + LOG(error) << "RK_MPI_AI_SetChnAttr() failed, status: " << status; + return ret; + } + + status = RK_MPI_AI_EnableChn(m_channel); + if (status) { + LOG(error) << "RK_MPI_AI_EnableChn() failed, status: " << status; + return ret; + } + + AI_TALKVQE_CONFIG_S config = {0}; + status = RK_MPI_AI_GetTalkVqeAttr(m_channel, &config); + if (status) { + LOG(error) << "RK_MPI_AI_GetTalkVqeAttr() failed, status: " << status; + return ret; + } + // LOG(info) << "param file: " << config.aParamFilePath; + config.s32WorkSampleRate = format.sampleRate; + config.s32FrameSample = format.sampleRate / 1000 * format.period; + config.u32OpenMask = AI_TALKVQE_MASK_AEC | AI_TALKVQE_MASK_ANR | AI_TALKVQE_MASK_AGC; + strncpy(config.aParamFilePath, ParamFilePath, sizeof(config.aParamFilePath)); + + RK_MPI_AI_SetTalkVqeAttr(m_channel, &config); + fprintf(stderr, "end\n"); + if (status) { + LOG(error) << "RK_MPI_AI_SetTalkVqeAttr() failed, status: " << status; + return ret; + } + status = RK_MPI_AI_EnableVqe(m_channel); + if (status) { + LOG(error) << "RK_MPI_AI_EnableVqe() failed, status: " << status; + return ret; + } + + status = RK_MPI_AI_StartStream(0); + if (status) { + LOG(info) << "start AI failed, status: " << status; + return ret; + } + + m_exit = false; + m_thread = std::thread(&Input::run, this); + ret = true; + return ret; +} + +void Input::stop() { + m_exit = true; + if (m_thread.joinable()) m_thread.join(); + + if (m_channel >= 0) { + RK_MPI_AI_DisableVqe(m_channel); + RK_MPI_AI_DisableChn(m_channel); + m_channel = -1; + } +} + +void Input::setDataCallback(const ReadCallback &callback) { + m_callback = callback; +} + +void Input::run() { + while (!m_exit) { + auto mediaBuffer = RK_MPI_SYS_GetMediaBuffer(RK_ID_AI, 0, -1); + if (!mediaBuffer) { + LOG(error) << "RK_MPI_SYS_GetMediaBuffer() failed."; + continue; + } + if (m_callback) { + Frame frame; + frame.data = reinterpret_cast(RK_MPI_MB_GetPtr(mediaBuffer)); + frame.byteSize = RK_MPI_MB_GetSize(mediaBuffer); + frame.frameSize = frame.byteSize / m_format.channels / sizeof(uint16_t); + frame.timestamp = std::chrono::system_clock::now(); + m_callback(frame); + } + RK_MPI_MB_ReleaseBuffer(mediaBuffer); + } +} + +Output::Output() { +} + +Output::~Output() { + close(); +} + +bool Output::open(uint32_t sampleSize, uint32_t sampleRate, uint32_t channels) { + + m_channel = 0; + AO_CHN_ATTR_S parameter = {0}; + parameter.pcAudioNode = "default"; + parameter.enSampleFormat = RK_SAMPLE_FMT_S16; + parameter.u32NbSamples = sampleRate / 1000 * 20; + parameter.u32SampleRate = sampleRate; + parameter.u32Channels = channels; + + RK_MPI_AO_SetChnAttr(m_channel, ¶meter); + RK_MPI_AO_EnableChn(m_channel); + + AO_VQE_CONFIG_S config = {0}; + config.s32WorkSampleRate = sampleRate; + config.s32FrameSample = parameter.u32NbSamples; + config.u32OpenMask = AO_VQE_MASK_ANR | AO_VQE_MASK_AGC; + strncpy(config.aParamFilePath, ParamFilePath, sizeof(config.aParamFilePath)); + + RK_MPI_AO_SetVqeAttr(m_channel, &config); + RK_MPI_AO_EnableVqe(m_channel); + + return true; +} + +void Output::close() { + if (m_channel >= 0) { + RK_MPI_AO_DisableVqe(m_channel); + RK_MPI_AO_DisableChn(m_channel); + m_channel = -1; + } +} + +void Output::write(const uint8_t *data, uint32_t byteSize) { + if (m_channel < 0) return; + auto buffer = RK_MPI_MB_CreateAudioBuffer(byteSize, RK_FALSE); + if (buffer != nullptr) { + memcpy(RK_MPI_MB_GetPtr(buffer), data, byteSize); + RK_MPI_MB_SetSize(buffer, byteSize); + RK_MPI_SYS_SendMediaBuffer(RK_ID_AO, m_channel, buffer); + RK_MPI_MB_ReleaseBuffer(buffer); + } else { + LOG(error) << "RK_MPI_MB_CreateAudioBuffer() failed."; + } +} + +} // namespace RkAudio diff --git a/Record/RkAudio.h b/Record/RkAudio.h new file mode 100644 index 0000000..aeb3e95 --- /dev/null +++ b/Record/RkAudio.h @@ -0,0 +1,73 @@ +#ifndef __RKAUDIO_H__ +#define __RKAUDIO_H__ + +#include +#include + +namespace RkAudio { +// constexpr auto ParamFilePath="/data/sdcard/rw_3a.bin"; +constexpr auto ParamFilePath = "/data/sdcard/RKAP_3A_Para.bin"; + +class Format { +public: + enum Endian { + BigEndian, + LittleEndian, + }; + + enum SampleType { + Unknown, + SignedInt16, + SignedInt, + Float, + }; + SampleType sampleType = SignedInt16; + Endian byteOrder = LittleEndian; + uint32_t sampleRate = 48000; + uint32_t channels = 2; + uint32_t period = 60; +}; + +class Frame { +public: + uint8_t *data = nullptr; + int32_t byteSize = 0; + int32_t frameSize = 0; + std::chrono::system_clock::time_point timestamp; +}; + +class Input { +public: + using ReadCallback = std::function; + Input(); + ~Input(); + void setDataCallback(const ReadCallback &callback); + bool open(const Format &format); + void stop(); + +protected: + void run(); + +private: + int32_t m_channel = -1; + bool m_exit = false; + std::thread m_thread; + ReadCallback m_callback; + Format m_format; +}; + +class Output { +public: + Output(); + ~Output(); + bool open(uint32_t sampleSize, uint32_t sampleRate, uint32_t channels); + void close(); + void write(const uint8_t *data, uint32_t byteSize); + +private: + int32_t m_channel = -1; +}; + +} // namespace RkAudio + +#endif // __RKAUDIO_H__ \ No newline at end of file diff --git a/Record/RkRecorder.cpp b/Record/RkRecorder.cpp new file mode 100644 index 0000000..0c58541 --- /dev/null +++ b/Record/RkRecorder.cpp @@ -0,0 +1,226 @@ +#include "BoostLog.h" +#include +#include +#include +#include + +#define VQEFILE "/data/sdcard/RKAP_3A_Para.bin" +#define ALSA_PATH "default" // get from "arecord -L" + +static bool quit = false; +static void sigterm_handler(int sig) { + fprintf(stderr, "signal %d\n", sig); + quit = true; +} + +std::shared_ptr ofs; + +void run() { + ofs = std::make_shared("/data/sdcard/test.pcm", std::ofstream::binary); + while (!quit) { + auto mediaBuffer = RK_MPI_SYS_GetMediaBuffer(RK_ID_AI, 0, -1); + if (!mediaBuffer) { + LOG(error) << "RK_MPI_SYS_GetMediaBuffer() failed."; + continue; + } + LOG(info) << "get frame, timestamp: " << RK_MPI_MB_GetTimestamp(mediaBuffer) + << ", size: " << RK_MPI_MB_GetSize(mediaBuffer); + ofs->write((const char *)RK_MPI_MB_GetPtr(mediaBuffer), RK_MPI_MB_GetSize(mediaBuffer)); + RK_MPI_MB_ReleaseBuffer(mediaBuffer); + } + ofs.reset(); +} + +void rkDemo() { + AI_CHN_ATTR_S ai_attr; + ai_attr.pcAudioNode = "default"; + ai_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ai_attr.u32NbSamples = 16 * 20; + ai_attr.u32SampleRate = 16000; + ai_attr.u32Channels = 2; + ai_attr.enAiLayout = AI_LAYOUT_REF_MIC; + int status = RK_MPI_AI_SetChnAttr(0, &ai_attr); + status |= RK_MPI_AI_EnableChn(0); + if (status) { + LOG(error) << "enable AI[0] failed, status = " << status; + return; + } + + AI_TALKVQE_CONFIG_S config = {0}; + strcpy(config.aParamFilePath, VQEFILE); + config.s32WorkSampleRate = 16000; + config.s32FrameSample = 16 * 20; + config.u32OpenMask = AI_TALKVQE_MASK_AEC | AI_TALKVQE_MASK_ANR | AI_TALKVQE_MASK_AGC; + status = RK_MPI_AI_SetTalkVqeAttr(0, &config); + if (status) { + LOG(error) << "RK_MPI_AI_SetTalkVqeAttr() failed, status: " << status; + return; + } + + status = RK_MPI_AI_EnableVqe(0); + if (status) { + LOG(error) << "RK_MPI_AI_EnableVqe() failed, status: " << status; + return; + } + + std::thread thread(&run); + + status = RK_MPI_AI_StartStream(0); + if (status) { + LOG(info) << "start AI failed, status: " << status; + return; + } + signal(SIGINT, sigterm_handler); + while (!quit) { + usleep(500000); + } + + if (thread.joinable()) { + thread.join(); + } + + RK_MPI_AI_DisableChn(0); +} + +static RK_U32 g_enWorkSampleRate = 8000; +static RK_U32 g_s32VqeFrameSample = 320; // 20ms; + +// #define VQEFILE "/data/sdcard/3a.bin" +int AI_VqeProcess_AO() { + AI_TALKVQE_CONFIG_S stAiVqeTalkAttr; + AI_RECORDVQE_CONFIG_S stAiVqeRecordAttr; + AO_VQE_CONFIG_S stAoVqeAttr; + MPP_CHN_S mpp_chn_ai, mpp_chn_ao; + + memset(&stAiVqeTalkAttr, 0, sizeof(AI_TALKVQE_CONFIG_S)); + stAiVqeTalkAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAiVqeTalkAttr.s32FrameSample = g_s32VqeFrameSample; + strcpy(stAiVqeTalkAttr.aParamFilePath, VQEFILE); + stAiVqeTalkAttr.u32OpenMask = AI_TALKVQE_MASK_AEC | AI_TALKVQE_MASK_ANR | AI_TALKVQE_MASK_AGC; + // stAiVqeTalkAttr.u32OpenMask = AI_TALKVQE_MASK_AEC ; + + memset(&stAoVqeAttr, 0, sizeof(AO_VQE_CONFIG_S)); + stAoVqeAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAoVqeAttr.s32FrameSample = g_s32VqeFrameSample; + strcpy(stAoVqeAttr.aParamFilePath, VQEFILE); + stAoVqeAttr.u32OpenMask = AO_VQE_MASK_ANR | AO_VQE_MASK_AGC; + + mpp_chn_ai.enModId = RK_ID_AI; + mpp_chn_ai.s32ChnId = 0; + mpp_chn_ao.enModId = RK_ID_AO; + mpp_chn_ao.s32ChnId = 0; + + AI_CHN_ATTR_S ai_attr; + ai_attr.pcAudioNode = "default"; + ai_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ai_attr.u32NbSamples = 320; + ai_attr.u32SampleRate = g_enWorkSampleRate; + ai_attr.u32Channels = 1; + ai_attr.enAiLayout = AI_LAYOUT_MIC_REF; // remove ref channel, and output mic mono + + AO_CHN_ATTR_S ao_attr; + ao_attr.pcAudioNode = "default"; + ao_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ao_attr.u32NbSamples = 320; + ao_attr.u32SampleRate = g_enWorkSampleRate; + ao_attr.u32Channels = 1; + + // 1. create AI + RK_MPI_AI_SetChnAttr(mpp_chn_ai.s32ChnId, &ai_attr); + RK_MPI_AI_EnableChn(mpp_chn_ai.s32ChnId); + + RK_MPI_AI_SetTalkVqeAttr(mpp_chn_ai.s32ChnId, &stAiVqeTalkAttr); + RK_MPI_AI_EnableVqe(mpp_chn_ai.s32ChnId); + + // 2. create AO + RK_MPI_AO_SetChnAttr(mpp_chn_ao.s32ChnId, &ao_attr); + RK_MPI_AO_EnableChn(mpp_chn_ao.s32ChnId); + + RK_MPI_AO_SetVqeAttr(mpp_chn_ao.s32ChnId, &stAoVqeAttr); + RK_MPI_AO_EnableVqe(mpp_chn_ao.s32ChnId); + + // 3. bind AI-AO + RK_MPI_SYS_Bind(&mpp_chn_ai, &mpp_chn_ao); + + printf("%s initial finish\n", __func__); + signal(SIGINT, sigterm_handler); + while (!quit) { + usleep(500000); + } + + printf("%s exit!\n", __func__); + RK_MPI_SYS_UnBind(&mpp_chn_ai, &mpp_chn_ao); + RK_MPI_AI_DisableChn(mpp_chn_ai.s32ChnId); + RK_MPI_AO_DisableChn(mpp_chn_ao.s32ChnId); + + return RK_SUCCESS; +} + +int AI_VqeProcess_AO1() { + AI_TALKVQE_CONFIG_S stAiVqeTalkAttr; + AI_RECORDVQE_CONFIG_S stAiVqeRecordAttr; + AO_VQE_CONFIG_S stAoVqeAttr; + MPP_CHN_S mpp_chn_ai, mpp_chn_ao; + + memset(&stAiVqeTalkAttr, 0, sizeof(AI_TALKVQE_CONFIG_S)); + stAiVqeTalkAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAiVqeTalkAttr.s32FrameSample = g_s32VqeFrameSample; + strcpy(stAiVqeTalkAttr.aParamFilePath, VQEFILE); + stAiVqeTalkAttr.u32OpenMask = AI_TALKVQE_MASK_AEC | AI_TALKVQE_MASK_ANR | AI_TALKVQE_MASK_AGC; + + memset(&stAoVqeAttr, 0, sizeof(AO_VQE_CONFIG_S)); + stAoVqeAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAoVqeAttr.s32FrameSample = g_s32VqeFrameSample; + strcpy(stAoVqeAttr.aParamFilePath, VQEFILE); + stAoVqeAttr.u32OpenMask = AO_VQE_MASK_ANR | AO_VQE_MASK_AGC; + + RK_MPI_SYS_Init(); + mpp_chn_ai.enModId = RK_ID_AI; + mpp_chn_ai.s32ChnId = 0; + mpp_chn_ao.enModId = RK_ID_AO; + mpp_chn_ao.s32ChnId = 0; + + AI_CHN_ATTR_S ai_attr; + ai_attr.pcAudioNode = ALSA_PATH; + ai_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ai_attr.u32NbSamples = 640; + ai_attr.u32SampleRate = g_enWorkSampleRate; + ai_attr.u32Channels = 1; + ai_attr.enAiLayout = AI_LAYOUT_MIC_REF; // remove ref channel, and output mic mono + + AO_CHN_ATTR_S ao_attr; + ao_attr.pcAudioNode = ALSA_PATH; + ao_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ao_attr.u32NbSamples = 640; + ao_attr.u32SampleRate = g_enWorkSampleRate; + ao_attr.u32Channels = 1; + + // 1. create AI + RK_MPI_AI_SetChnAttr(mpp_chn_ai.s32ChnId, &ai_attr); + RK_MPI_AI_EnableChn(mpp_chn_ai.s32ChnId); + + RK_MPI_AI_SetTalkVqeAttr(mpp_chn_ai.s32ChnId, &stAiVqeTalkAttr); + RK_MPI_AI_EnableVqe(mpp_chn_ai.s32ChnId); + + // 2. create AO + RK_MPI_AO_SetChnAttr(mpp_chn_ao.s32ChnId, &ao_attr); + RK_MPI_AO_EnableChn(mpp_chn_ao.s32ChnId); + RK_MPI_AO_SetVqeAttr(mpp_chn_ao.s32ChnId, &stAoVqeAttr); + RK_MPI_AO_EnableVqe(mpp_chn_ao.s32ChnId); + + // 3. bind AI-AO + RK_MPI_SYS_Bind(&mpp_chn_ai, &mpp_chn_ao); + + printf("%s initial finish\n", __func__); + signal(SIGINT, sigterm_handler); + while (!quit) { + usleep(500000); + } + + printf("%s exit!\n", __func__); + RK_MPI_SYS_UnBind(&mpp_chn_ai, &mpp_chn_ao); + RK_MPI_AI_DisableChn(mpp_chn_ai.s32ChnId); + RK_MPI_AO_DisableChn(mpp_chn_ao.s32ChnId); + + return RK_SUCCESS; +} \ No newline at end of file diff --git a/Record/main.cpp b/Record/main.cpp new file mode 100644 index 0000000..130856a --- /dev/null +++ b/Record/main.cpp @@ -0,0 +1,61 @@ +#include "FFmpegResample.h" +#include "OpusCodec.h" +#include +#include + +extern int recorder_demo(); +extern void rkDemo(); +extern int AI_VqeProcess_AO(); +extern int AI_VqeProcess_AO1(); +extern void AecTest(); +extern int opus_test(); + +int main(int argc, char **argv) { + RK_MPI_SYS_Init(); + // std::ifstream ifs("/data/sdcard/test2.pcm", std::ifstream::binary); + // AlsaPcmPlayer player; + // player.open(2, 48000, 2); + + // char buffer[2 * 2 * 48 * 60]; + // while (ifs.read(buffer, sizeof(buffer))) { + // int size = ifs.gcount(); + // player.write((const uint8_t *)buffer, size); + // } + // recorder_demo(); + // rkDemo(); + // AI_VqeProcess_AO(); + // AI_VqeProcess_AO1(); + // AecTest(); + + { + FFmpegResample resample; + resample.initialize(16000, 1, 48000, 2, 20); + + std::ifstream ifs("/sdcard/input.pcm", std::ifstream::binary); + std::ofstream ofs("/sdcard/my_48kz.pcm", std::ifstream::binary); + char buffer[16 * 20 * 2]; + while (ifs.read(buffer, sizeof(buffer))) { + auto frame = resample.resample((uint8_t *)buffer, sizeof(buffer)); + if (frame.data != nullptr) { + ofs.write((char *)frame.data, frame.byteSize); + } + } + } + + { + FFmpegResample resample1; + resample1.initialize(48000, 2, 16000, 1, 20); + + std::ifstream ifs1("/sdcard/my_48kz.pcm", std::ifstream::binary); + std::ofstream ofs1("/sdcard/my_16kz.pcm", std::ifstream::binary); + char buffer1[48 * 20 * 2 * 2]; + while (ifs1.read(buffer1, sizeof(buffer1))) { + auto frame = resample1.resample((uint8_t *)buffer1, sizeof(buffer1)); + if (frame.data != nullptr) { + ofs1.write((char *)frame.data, frame.byteSize); + } + } + } + + return 0; +} \ No newline at end of file diff --git a/Record/rkmedia_audio_test.c b/Record/rkmedia_audio_test.c new file mode 100644 index 0000000..51d7bbc --- /dev/null +++ b/Record/rkmedia_audio_test.c @@ -0,0 +1,411 @@ +// Copyright 2020 Fuzhou Rockchip Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#define MP3_NB_SAMPLES 1024 +#define MP2_NB_SAMPLES 1152 +//#define ALSA_PATH "default:CARD=rockchiprk809co" // get from "arecord -L" +#define ALSA_PATH "hw:0,0" // get from "arecord -L" +#define VQEFILE "/sdcard/RKAP_3A_Para.bin" + +static bool quit = false; +static void sigterm_handler(int sig) { + fprintf(stderr, "signal %d\n", sig); + quit = true; +} + +FILE *fp = NULL; +static RK_U32 g_enWorkSampleRate = 16000; +static RK_U32 g_s32VqeFrameSample = 256; // 20ms; +static RK_U32 g_s32AiLayout = AI_LAYOUT_MIC_REF; + +static void audio_packet_cb(MEDIA_BUFFER mb) { + printf("Get Audio Encoded packet:ptr:%p, fd:%d, size:%zu, mode:%d\n", + RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetFD(mb), RK_MPI_MB_GetSize(mb), + RK_MPI_MB_GetModeID(mb)); + fwrite(RK_MPI_MB_GetPtr(mb), 1, RK_MPI_MB_GetSize(mb), fp); + RK_MPI_MB_ReleaseBuffer(mb); +} + +static RK_VOID AI_AO() { + RK_MPI_SYS_Init(); + MPP_CHN_S mpp_chn_ai, mpp_chn_ao; + mpp_chn_ai.enModId = RK_ID_AI; + mpp_chn_ai.s32ChnId = 0; + mpp_chn_ao.enModId = RK_ID_AO; + mpp_chn_ao.s32ChnId = 0; + + AI_CHN_ATTR_S ai_attr; + ai_attr.pcAudioNode = ALSA_PATH; + ai_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ai_attr.u32NbSamples = 1152; + ai_attr.u32SampleRate = g_enWorkSampleRate; + ai_attr.u32Channels = 1; + ai_attr.enAiLayout = g_s32AiLayout; // chanel layout: [ref:mic]; remove + // ref, output mic mono + + AO_CHN_ATTR_S ao_attr; + ao_attr.pcAudioNode = ALSA_PATH; + ao_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ao_attr.u32NbSamples = 1152; + ao_attr.u32SampleRate = g_enWorkSampleRate; + ao_attr.u32Channels = 1; + + // 1. create AI + RK_MPI_AI_SetChnAttr(mpp_chn_ai.s32ChnId, &ai_attr); + RK_MPI_AI_EnableChn(mpp_chn_ai.s32ChnId); + RK_MPI_AI_SetVolume(mpp_chn_ai.s32ChnId, 100); + + // 2. create AO + RK_MPI_AO_SetChnAttr(mpp_chn_ao.s32ChnId, &ao_attr); + RK_MPI_AO_EnableChn(mpp_chn_ao.s32ChnId); + RK_MPI_AO_SetVolume(mpp_chn_ao.s32ChnId, 100); + + // 3. bind AI-AO + RK_MPI_SYS_Bind(&mpp_chn_ai, &mpp_chn_ao); + + printf("%s initial finish\n", __func__); + signal(SIGINT, sigterm_handler); + while (!quit) { + usleep(500000); + } + + printf("%s exit!\n", __func__); + RK_MPI_SYS_UnBind(&mpp_chn_ai, &mpp_chn_ao); + RK_MPI_AI_DisableChn(mpp_chn_ai.s32ChnId); + RK_MPI_AO_DisableChn(mpp_chn_ao.s32ChnId); +} + +static RK_VOID AI_AENC_FILE(char *file_path) { + fp = fopen(file_path, "w+"); + RK_MPI_SYS_Init(); + MPP_CHN_S mpp_chn_ai, mpp_chn_aenc; + mpp_chn_ai.enModId = RK_ID_AI; + mpp_chn_ai.s32ChnId = 0; + mpp_chn_aenc.enModId = RK_ID_AENC; + mpp_chn_aenc.s32ChnId = 0; + + AI_CHN_ATTR_S ai_attr; + ai_attr.pcAudioNode = ALSA_PATH; + ai_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ai_attr.u32NbSamples = MP3_NB_SAMPLES; + ai_attr.u32SampleRate = g_enWorkSampleRate; + ai_attr.u32Channels = 1; + ai_attr.enAiLayout = g_s32AiLayout; // chanel layout: [ref:mic]; remove + // ref, output mic mono + + AENC_CHN_ATTR_S aenc_attr; + aenc_attr.enCodecType = RK_CODEC_TYPE_MP3; + aenc_attr.u32Bitrate = 64000; + aenc_attr.u32Quality = 1; + aenc_attr.stAencMP3.u32Channels = 1; + aenc_attr.stAencMP3.u32SampleRate = g_enWorkSampleRate; + + // 1. create AI + RK_MPI_AI_SetChnAttr(mpp_chn_ai.s32ChnId, &ai_attr); + RK_MPI_AI_EnableChn(mpp_chn_ai.s32ChnId); + + // 2. create AENC + RK_MPI_AENC_CreateChn(mpp_chn_aenc.s32ChnId, &aenc_attr); + RK_U32 ret = RK_MPI_SYS_RegisterOutCb(&mpp_chn_aenc, audio_packet_cb); + printf("ret = %d.\n", ret); + + // 3. bind AI-AENC + RK_MPI_SYS_Bind(&mpp_chn_ai, &mpp_chn_aenc); + + printf("%s initial finish\n", __func__); + signal(SIGINT, sigterm_handler); + while (!quit) { + usleep(500000); + } + + RK_MPI_SYS_UnBind(&mpp_chn_ai, &mpp_chn_aenc); + RK_MPI_AI_DisableChn(mpp_chn_ai.s32ChnId); + RK_MPI_AENC_DestroyChn(mpp_chn_aenc.s32ChnId); + fclose(fp); +} + +static RK_VOID FILE_ADEC_AO(char *file_path) { + CODEC_TYPE_E codec_type = RK_CODEC_TYPE_MP3; + RK_U32 channels = 2; + RK_U32 sample_rate = g_enWorkSampleRate; + ADEC_CHN_ATTR_S stAdecAttr; + AO_CHN_ATTR_S stAoAttr; + + stAdecAttr.enCodecType = codec_type; + MPP_CHN_S mpp_chn_ao, mpp_chn_adec; + mpp_chn_ao.enModId = RK_ID_AO; + mpp_chn_ao.s32ChnId = 0; + mpp_chn_adec.enModId = RK_ID_ADEC; + mpp_chn_adec.s32ChnId = 0; + + stAoAttr.u32Channels = channels; + stAoAttr.u32SampleRate = sample_rate; + stAoAttr.u32NbSamples = 1024; + stAoAttr.pcAudioNode = ALSA_PATH; + + switch (codec_type) { + case RK_CODEC_TYPE_MP3: + stAoAttr.enSampleFormat = RK_SAMPLE_FMT_S16; + stAoAttr.u32NbSamples = 1024; + break; + case RK_CODEC_TYPE_MP2: + stAoAttr.enSampleFormat = RK_SAMPLE_FMT_S16; + stAoAttr.u32NbSamples = 1152; + break; + case RK_CODEC_TYPE_G711A: + stAdecAttr.stAdecG711A.u32Channels = channels; + stAdecAttr.stAdecG711A.u32SampleRate = sample_rate; + stAoAttr.enSampleFormat = RK_SAMPLE_FMT_S16; + break; + case RK_CODEC_TYPE_G711U: + stAdecAttr.stAdecG711U.u32Channels = channels; + stAdecAttr.stAdecG711U.u32SampleRate = sample_rate; + stAoAttr.enSampleFormat = RK_SAMPLE_FMT_S16; + break; + case RK_CODEC_TYPE_G726: + stAoAttr.enSampleFormat = RK_SAMPLE_FMT_S16; + break; + default: + printf("audio codec type error.\n"); + return; + } + // init MPI + RK_MPI_SYS_Init(); + // create ADEC + RK_MPI_ADEC_CreateChn(mpp_chn_adec.s32ChnId, &stAdecAttr); + // create AO + RK_MPI_AO_SetChnAttr(mpp_chn_ao.s32ChnId, &stAoAttr); + RK_MPI_AO_EnableChn(mpp_chn_ao.s32ChnId); + + RK_MPI_SYS_Bind(&mpp_chn_adec, &mpp_chn_ao); + + RK_S32 buffer_size = 20480; + FILE *read_file = fopen(file_path, "r"); + if (!read_file) { + printf("ERROR: open %s failed!\n", file_path); + exit(0); + } + quit = true; + while (quit) { + MEDIA_BUFFER mb = RK_MPI_MB_CreateAudioBuffer(buffer_size, RK_FALSE); + if (!mb) { + printf("ERROR: no space left!\n"); + break; + } + + RK_S32 s32ReadSize = fread(RK_MPI_MB_GetPtr(mb), 1, buffer_size, read_file); + + RK_MPI_MB_SetSize(mb, s32ReadSize); + RK_MPI_SYS_SendMediaBuffer(RK_ID_ADEC, mpp_chn_adec.s32ChnId, mb); + RK_MPI_MB_ReleaseBuffer(mb); + if (s32ReadSize != buffer_size) { + printf("Get end of file!\n"); + break; + } + } + sleep(2); + { + // flush decoder + printf("start flush decoder.\n"); + MEDIA_BUFFER mb = RK_MPI_MB_CreateAudioBuffer(buffer_size, RK_FALSE); + RK_MPI_MB_SetSize(mb, 0); + RK_MPI_SYS_SendMediaBuffer(RK_ID_ADEC, mpp_chn_adec.s32ChnId, mb); + RK_MPI_MB_ReleaseBuffer(mb); + printf("end flush decoder.\n"); + } + + sleep(10); +} + +/* 0: close, 1: talk, 2: record */ +static RK_U32 u32AiVqeType = 1; +/* 0: close, 1: open */ +static RK_U32 u32AoVqeType = 1; + +/****************************************************************************** + * function : Ai ->VqeProcess-> Ao + ******************************************************************************/ +RK_S32 AI_VqeProcess_AO(RK_VOID) { + AI_TALKVQE_CONFIG_S stAiVqeTalkAttr; + AI_RECORDVQE_CONFIG_S stAiVqeRecordAttr; + AO_VQE_CONFIG_S stAoVqeAttr; + MPP_CHN_S mpp_chn_ai, mpp_chn_ao; + + if (1 == u32AiVqeType) { + memset(&stAiVqeTalkAttr, 0, sizeof(AI_TALKVQE_CONFIG_S)); + stAiVqeTalkAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAiVqeTalkAttr.s32FrameSample = g_s32VqeFrameSample; + strcpy(stAiVqeTalkAttr.aParamFilePath, VQEFILE); + stAiVqeTalkAttr.u32OpenMask = + AI_TALKVQE_MASK_AEC | AI_TALKVQE_MASK_ANR | AI_TALKVQE_MASK_AGC; + } else if (2 == u32AiVqeType) { + memset(&stAiVqeRecordAttr, 0, sizeof(AI_RECORDVQE_CONFIG_S)); + stAiVqeRecordAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAiVqeRecordAttr.s32FrameSample = g_s32VqeFrameSample; + stAiVqeRecordAttr.stAnrConfig.fPostAddGain = 0; + stAiVqeRecordAttr.stAnrConfig.fGmin = -30; + stAiVqeRecordAttr.stAnrConfig.fNoiseFactor = 0.98; + stAiVqeRecordAttr.u32OpenMask = AI_RECORDVQE_MASK_ANR; + } + + if (1 == u32AoVqeType) { + memset(&stAoVqeAttr, 0, sizeof(AO_VQE_CONFIG_S)); + stAoVqeAttr.s32WorkSampleRate = g_enWorkSampleRate; + stAoVqeAttr.s32FrameSample = g_s32VqeFrameSample; + strcpy(stAoVqeAttr.aParamFilePath, VQEFILE); + stAoVqeAttr.u32OpenMask = AO_VQE_MASK_ANR | AO_VQE_MASK_AGC; + } + + RK_MPI_SYS_Init(); + mpp_chn_ai.enModId = RK_ID_AI; + mpp_chn_ai.s32ChnId = 0; + mpp_chn_ao.enModId = RK_ID_AO; + mpp_chn_ao.s32ChnId = 0; + + AI_CHN_ATTR_S ai_attr; + ai_attr.pcAudioNode = ALSA_PATH; + ai_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ai_attr.u32NbSamples = 1024; + ai_attr.u32SampleRate = g_enWorkSampleRate; + ai_attr.u32Channels = 2; + ai_attr.enAiLayout = g_s32AiLayout; // remove ref channel, and output mic mono + + AO_CHN_ATTR_S ao_attr; + ao_attr.pcAudioNode = ALSA_PATH; + ao_attr.enSampleFormat = RK_SAMPLE_FMT_S16; + ao_attr.u32NbSamples = 1024; + ao_attr.u32SampleRate = g_enWorkSampleRate; + ao_attr.u32Channels = 2; + + // 1. create AI + RK_MPI_AI_SetChnAttr(mpp_chn_ai.s32ChnId, &ai_attr); + RK_MPI_AI_EnableChn(mpp_chn_ai.s32ChnId); + //RK_MPI_AI_SetVolume(mpp_chn_ai.s32ChnId, 100); + if (1 == u32AiVqeType) { + RK_MPI_AI_SetTalkVqeAttr(mpp_chn_ai.s32ChnId, &stAiVqeTalkAttr); + RK_MPI_AI_EnableVqe(mpp_chn_ai.s32ChnId); + } else if (2 == u32AiVqeType) { + RK_MPI_AI_SetRecordVqeAttr(mpp_chn_ai.s32ChnId, &stAiVqeRecordAttr); + RK_MPI_AI_EnableVqe(mpp_chn_ai.s32ChnId); + } + // 2. create AO + RK_MPI_AO_SetChnAttr(mpp_chn_ao.s32ChnId, &ao_attr); + RK_MPI_AO_EnableChn(mpp_chn_ao.s32ChnId); + //RK_MPI_AO_SetVolume(mpp_chn_ao.s32ChnId, 100); + if (1 == u32AoVqeType) { + RK_MPI_AO_SetVqeAttr(mpp_chn_ao.s32ChnId, &stAoVqeAttr); + RK_MPI_AO_EnableVqe(mpp_chn_ao.s32ChnId); + } + // 3. bind AI-AO + RK_MPI_SYS_Bind(&mpp_chn_ai, &mpp_chn_ao); + + printf("%s initial finish\n", __func__); + signal(SIGINT, sigterm_handler); + while (!quit) { + usleep(500000); + } + + printf("%s exit!\n", __func__); + RK_MPI_SYS_UnBind(&mpp_chn_ai, &mpp_chn_ao); + RK_MPI_AI_DisableChn(mpp_chn_ai.s32ChnId); + RK_MPI_AO_DisableChn(mpp_chn_ao.s32ChnId); + + return RK_SUCCESS; +} + +static RK_VOID RKMEDIA_AUDIO_Usage() { + printf("\n\n/Usage:./rkmdia_audio [filePath]/ " + "[nbsamples] [ailayout]\n"); + printf("\tindex and its function list below\n"); + printf("\t0: start AI to AO loop\n"); + printf("\t1: send audio frame to AENC channel from AI, save them\n"); + printf("\t2: read audio stream from file, decode and send AO\n"); + printf("\t3: start AI(VQE process), then send to AO\n"); + // printf("\t4: start AI to Extern Resampler\n"); + printf("\n"); + printf("\tsampleRate list:\n"); + printf("\t0 16000 22050 24000 32000 44100 48000\n"); + printf("\n"); + printf("\tfilePath represents the path of audio file to be decoded, only for " + "sample 2.\n"); + printf("\tdefault filePath: /userdata/out.mp2\n"); + printf("\n"); + printf("\tnbsamples, for example: 160 is 10ms at 16kHz\n"); + printf("\n"); + printf("\tailayout:\n"); + printf("\t0: AI_LAYOUT_NORMAL\n"); + printf("\t1: AI_LAYOUT_MIC_REF\n"); + printf("\t2: AI_LAYOUT_REF_MIC\n"); + printf("\t3: AI_LAYOUT_2MIC_REF_NONE\n"); + printf("\t4: AI_LAYOUT_2MIC_NONE_REF\n"); + printf("\t5: AI_LAYOUT_2MIC_2REF\n"); + printf("\t6: AI_LAYOUT_BUTT\n"); + printf("\n"); + printf("\texample: ./rkmdia_audio 0 48000 480 1 /tmp/out_aiao.mp2\n"); +} + +int main(int argc, char *argv[]) { + + RK_U32 u32Index; + RK_CHAR *pFilePath = RK_NULL; + + if (!(argc >= 3 && argc <= 6)) { + RKMEDIA_AUDIO_Usage(); + return -1; + } + + if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-?") == 0) { + RKMEDIA_AUDIO_Usage(); + return -1; + } + + u32Index = atoi(argv[1]); + g_enWorkSampleRate = atoi(argv[2]); + + switch (argc) { + case 6: + pFilePath = argv[5]; + g_s32AiLayout = atoi(argv[4]); + g_s32VqeFrameSample = atoi(argv[3]); + break; + case 5: + g_s32AiLayout = atoi(argv[4]); + g_s32VqeFrameSample = atoi(argv[3]); + break; + case 4: + g_s32VqeFrameSample = atoi(argv[3]); + break; + default: + pFilePath = (char *)"/tmp/out.mp2"; + break; + } + + switch (u32Index) { + case 0: + AI_AO(); + break; + case 1: + AI_AENC_FILE(pFilePath); + break; + case 2: + FILE_ADEC_AO(pFilePath); + break; + case 3: + AI_VqeProcess_AO(); + default: + break; + } + return 0; +} diff --git a/VoucherVerifyServer/CMakeLists.txt b/VoucherVerifyServer/CMakeLists.txt new file mode 100644 index 0000000..843120b --- /dev/null +++ b/VoucherVerifyServer/CMakeLists.txt @@ -0,0 +1,26 @@ +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Boost REQUIRED COMPONENTS container url json) + +add_executable(VoucherVerifyServer + main.cpp + HttpSession.h HttpSession.cpp + Listener.h Listener.cpp + ResponseUtility.h ResponseUtility.cpp + ServiceLogic.h ServiceLogic.cpp + SharedState.h SharedState.cpp + WebsocketSession.h WebsocketSession.cpp +) + +target_link_directories(VoucherVerifyServer + PRIVATE ${OPENSSL_LIBRARY_DIRS} +) + +target_link_libraries(VoucherVerifyServer + PRIVATE Universal + PRIVATE HttpProxy + PRIVATE ${Boost_LIBRARIES} + PRIVATE ${OPENSSL_LIBRARY} + PRIVATE dl +) \ No newline at end of file diff --git a/VoucherVerifyServer/HttpSession.cpp b/VoucherVerifyServer/HttpSession.cpp new file mode 100644 index 0000000..364be5f --- /dev/null +++ b/VoucherVerifyServer/HttpSession.cpp @@ -0,0 +1,107 @@ +#include "HttpSession.h" +#include "WebsocketSession.h" +#include +#include +#include +#include +#include + +HttpSession::HttpSession(boost::asio::ip::tcp::socket &&socket, const std::shared_ptr &state) + : m_stream(std::move(socket)), m_state(state) { + // m_buffer.reserve(1000 * 1000 * 1000); +} + +void HttpSession::run() { + doRead(); +} + +void HttpSession::errorReply(const Request &request, boost::beast::http::status status, + boost::beast::string_view message) { + using namespace boost::beast; + // invalid route + http::response res{status, request.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(request.keep_alive()); + res.body() = message; + res.prepare_payload(); + + reply(std::move(res)); +} + +void HttpSession::doRead() { + // Construct a new parser for each message + m_parser.emplace(); + + // Apply a reasonable limit to the allowed size + // of the body in bytes to prevent abuse. + m_parser->body_limit(std::numeric_limits::max()); + m_parser->header_limit(std::numeric_limits::max()); + m_buffer.clear(); + + // Set the timeout. + m_stream.expires_after(std::chrono::seconds(30)); + boost::beast::http::async_read( + m_stream, m_buffer, *m_parser, + [self{shared_from_this()}](const boost::system::error_code &ec, std::size_t bytes_transferred) { + self->onRead(ec, bytes_transferred); + }); +} + +void HttpSession::onRead(boost::beast::error_code ec, std::size_t) { + using namespace boost::beast; + // This means they closed the connection + if (ec == http::error::end_of_stream) { + m_stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); + return; + } + if (ec) { + if (ec == boost::asio::error::operation_aborted) return; + LOG(info) << ec << " : " << ec.message(); + return; + } + + auto &request = m_parser->get(); + // See if it is a WebSocket Upgrade + if (websocket::is_upgrade(request)) { + // Create a websocket session, transferring ownership + // of both the socket and the HTTP request. + auto session = std::make_shared(m_stream.release_socket(), m_state); + session->run(m_parser->release()); + return; + } + auto path = boost::urls::parse_path(request.target()); + if (!path) { + LOG(error) << request.target() << "failed, error: " << path.error().message(); + errorReply(request, http::status::bad_request, "Illegal request-target"); + return; + } + boost::urls::matches matches; + auto handler = m_state->find(*path, matches); + if (handler) { + (*handler)(*this, request, matches); + } else { + std::ostringstream oss; + oss << "The resource '" << request.target() << "' was not found."; + auto message = oss.str(); + errorReply(request, http::status::not_found, message); + LOG(error) << message; + } +} + +void HttpSession::onWrite(boost::beast::error_code ec, std::size_t, bool close) { + if (ec) { + if (ec == boost::asio::error::operation_aborted) return; + std::cerr << "write: " << ec.message() << "\n"; + } + + if (close) { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + m_stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); + return; + } + + // Read another request + doRead(); +} diff --git a/VoucherVerifyServer/HttpSession.h b/VoucherVerifyServer/HttpSession.h new file mode 100644 index 0000000..d7d249d --- /dev/null +++ b/VoucherVerifyServer/HttpSession.h @@ -0,0 +1,42 @@ +#ifndef HTTPSESSION_H +#define HTTPSESSION_H + +#include "SharedState.h" +#include "boost/beast.hpp" +#include +#include +#include + +/** Represents an established HTTP connection + */ +class HttpSession : public std::enable_shared_from_this { + void doRead(); + void onRead(boost::beast::error_code ec, std::size_t); + void onWrite(boost::beast::error_code ec, std::size_t, bool close); + + // void sendResponse(boost::beast::http::response &&response); + +public: + using Request = boost::beast::http::request; + HttpSession(boost::asio::ip::tcp::socket &&socket, std::shared_ptr const &state); + template + void reply(Response &&response) { + using ResponseType = typename std::decay_t; + auto sp = std::make_shared(std::forward(response)); + boost::beast::http::async_write( + m_stream, *sp, [self = shared_from_this(), sp](boost::beast::error_code ec, std::size_t bytes) { + self->onWrite(ec, bytes, sp->need_eof()); + }); + } + void errorReply(const Request &request, boost::beast::http::status status, boost::beast::string_view message); + + void run(); + +private: + boost::beast::tcp_stream m_stream; + boost::beast::flat_buffer m_buffer{std::numeric_limits::max()}; + SharedStatePtr m_state; + std::optional> m_parser; +}; + +#endif // HTTPSESSION_H diff --git a/VoucherVerifyServer/Listener.cpp b/VoucherVerifyServer/Listener.cpp new file mode 100644 index 0000000..6fcb421 --- /dev/null +++ b/VoucherVerifyServer/Listener.cpp @@ -0,0 +1,61 @@ +#include "Listener.h" +#include "BoostLog.h" +#include "HttpSession.h" +#include "SharedState.h" +#include +#include + +Listener::Listener(boost::asio::io_context &ioContext, const boost::asio::ip::tcp::endpoint &endpoint, + const std::shared_ptr &state) + : m_ioContext(ioContext), m_acceptor(ioContext), m_state(state) { + boost::beast::error_code error; + + // Open the acceptor + m_acceptor.open(endpoint.protocol(), error); + if (error) { + LOG(error) << error.message(); + return; + } + + // Allow address reuse + m_acceptor.set_option(boost::asio::socket_base::reuse_address(true), error); + if (error) { + LOG(error) << error.message(); + return; + } + + // Bind to the server address + m_acceptor.bind(endpoint, error); + if (error) { + LOG(error) << error.message(); + return; + } + + // Start listening for connections + m_acceptor.listen(boost::asio::socket_base::max_listen_connections, error); + if (error) { + LOG(error) << error.message(); + return; + } +} + +void Listener::startAccept() { + auto client = std::make_shared(boost::asio::make_strand(m_ioContext)); + m_acceptor.async_accept(*client, [self{shared_from_this()}, client](const boost::system::error_code &ec) { + self->onAccept(ec, client); + }); +} + +void Listener::onAccept(boost::beast::error_code errorCode, std::shared_ptr socket) { + if (errorCode) { + if (errorCode == boost::asio::error::operation_aborted) return; + LOG(error) << errorCode.message(); + } else { // Launch a new session for this connection + if (!m_state.expired()) { + auto state = m_state.lock(); + auto session = std::make_shared(std::move(*socket), state); + session->run(); + } + } + startAccept(); +} diff --git a/VoucherVerifyServer/Listener.h b/VoucherVerifyServer/Listener.h new file mode 100644 index 0000000..d702a5c --- /dev/null +++ b/VoucherVerifyServer/Listener.h @@ -0,0 +1,26 @@ +#ifndef __LISTENER_H__ +#define __LISTENER_H__ + +#include +#include +#include +#include + +class SharedState; + +class Listener : public std::enable_shared_from_this { +public: + Listener(boost::asio::io_context &ioContext, const boost::asio::ip::tcp::endpoint &endpoint, + const std::shared_ptr &state); + void startAccept(); + +protected: + void onAccept(boost::beast::error_code errorCode, std::shared_ptr socket); + +private: + boost::asio::io_context &m_ioContext; + boost::asio::ip::tcp::acceptor m_acceptor; + std::weak_ptr m_state; +}; + +#endif // __LISTENER_H__ \ No newline at end of file diff --git a/VoucherVerifyServer/ResponseUtility.cpp b/VoucherVerifyServer/ResponseUtility.cpp new file mode 100644 index 0000000..dee717e --- /dev/null +++ b/VoucherVerifyServer/ResponseUtility.cpp @@ -0,0 +1,51 @@ +#include "ResponseUtility.h" +#include "boost/beast.hpp" + +namespace ResponseUtility { + +std::string_view mimeType(std::string_view path) { + using boost::beast::iequals; + auto const ext = [&path] { + auto const pos = path.rfind("."); + if (pos == std::string_view::npos) return std::string_view{}; + return path.substr(pos); + }(); + if (iequals(ext, ".pdf")) return "Application/pdf"; + if (iequals(ext, ".htm")) return "text/html"; + if (iequals(ext, ".html")) return "text/html"; + if (iequals(ext, ".php")) return "text/html"; + if (iequals(ext, ".css")) return "text/css"; + if (iequals(ext, ".txt")) return "text/plain"; + if (iequals(ext, ".js")) return "application/javascript"; + if (iequals(ext, ".json")) return "application/json"; + if (iequals(ext, ".xml")) return "application/xml"; + if (iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if (iequals(ext, ".flv")) return "video/x-flv"; + if (iequals(ext, ".png")) return "image/png"; + if (iequals(ext, ".jpe")) return "image/jpeg"; + if (iequals(ext, ".jpeg")) return "image/jpeg"; + if (iequals(ext, ".jpg")) return "image/jpeg"; + if (iequals(ext, ".gif")) return "image/gif"; + if (iequals(ext, ".bmp")) return "image/bmp"; + if (iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if (iequals(ext, ".tiff")) return "image/tiff"; + if (iequals(ext, ".tif")) return "image/tiff"; + if (iequals(ext, ".svg")) return "image/svg+xml"; + if (iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +std::string pathCat(std::string_view base, std::string_view path) { + if (base.empty()) return std::string(path); + std::string result(base); + char constexpr path_separator = '/'; + if (result.back() == path_separator && path.front() == path_separator) { + result.resize(result.size() - 1); + } else if (result.back() != path_separator && path.front() != path_separator) { + result.append("/"); + } + result.append(path.data(), path.size()); + + return result; +} +} // namespace ResponseUtility diff --git a/VoucherVerifyServer/ResponseUtility.h b/VoucherVerifyServer/ResponseUtility.h new file mode 100644 index 0000000..c0ff08b --- /dev/null +++ b/VoucherVerifyServer/ResponseUtility.h @@ -0,0 +1,19 @@ +#ifndef RESPONSEUTILITY_H +#define RESPONSEUTILITY_H + +#include + +namespace ResponseUtility { +/** + * @brief Return a reasonable mime type based on the extension of a file. + */ +std::string_view mimeType(std::string_view path); + +/** + * @brief Append an HTTP rel-path to a local filesystem path.The returned path is normalized for the + * platform. + */ +std::string pathCat(std::string_view base, std::string_view path); +} // namespace ResponseUtility + +#endif // RESPONSEUTILITY_H diff --git a/VoucherVerifyServer/ServiceLogic.cpp b/VoucherVerifyServer/ServiceLogic.cpp new file mode 100644 index 0000000..ad3f5e5 --- /dev/null +++ b/VoucherVerifyServer/ServiceLogic.cpp @@ -0,0 +1,34 @@ +#include "ServiceLogic.h" +#include + +namespace ServiceLogic { + +boost::beast::http::response +notFound(const boost::beast::http::request &request) { + using namespace boost::beast; + http::response res{http::status::not_found, request.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(request.keep_alive()); + std::ostringstream oss; + oss << "The resource '" << request.target() << "' was not found."; + res.body() = oss.str(); + res.prepare_payload(); + return res; +} + +boost::beast::http::response +serverError(const boost::beast::http::request &request, + std::string_view errorMessage) { + using namespace boost::beast; + http::response res{http::status::internal_server_error, request.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(request.keep_alive()); + std::ostringstream oss; + oss << "An error occurred: '" << errorMessage << "'"; + res.body() = oss.str(); + res.prepare_payload(); + return res; +} +} // namespace ServiceLogic diff --git a/VoucherVerifyServer/ServiceLogic.h b/VoucherVerifyServer/ServiceLogic.h new file mode 100644 index 0000000..5ed8846 --- /dev/null +++ b/VoucherVerifyServer/ServiceLogic.h @@ -0,0 +1,42 @@ +#ifndef SERVICELOGIC_H +#define SERVICELOGIC_H + +#include "ResponseUtility.h" +#include "SharedState.h" +#include "StringUtility.h" +#include +#include +#include +#include +#include +#include +#include + +using StringRequest = boost::beast::http::request; + +namespace ServiceLogic { + +// Returns a not found response +boost::beast::http::response +notFound(const boost::beast::http::request &request); + +// Returns a server error response +boost::beast::http::response +serverError(const boost::beast::http::request &request, std::string_view errorMessage); + +template +boost::beast::http::response make_200(const boost::beast::http::request &request, + typename ResponseBody::value_type body, + boost::beast::string_view content) { + boost::beast::http::response response{boost::beast::http::status::ok, request.version()}; + response.set(boost::beast::http::field::server, BOOST_BEAST_VERSION_STRING); + response.set(boost::beast::http::field::content_type, content); + response.body() = body; + response.prepare_payload(); + response.keep_alive(request.keep_alive()); + + return response; +} +}; // namespace ServiceLogic + +#endif // SERVICELOGIC_H diff --git a/VoucherVerifyServer/SharedState.cpp b/VoucherVerifyServer/SharedState.cpp new file mode 100644 index 0000000..633f0aa --- /dev/null +++ b/VoucherVerifyServer/SharedState.cpp @@ -0,0 +1,87 @@ +#include "SharedState.h" +#include "HttpSession.h" +#include "ServiceLogic.h" +#include "WebsocketSession.h" +#include +#include +#include + +SharedState::SharedState(boost::asio::io_context &ioContext) + : m_ioContext(ioContext), m_router{std::make_shared>()} { + + m_router->insert("/", [](HttpSession &session, const Request &request, const boost::urls::matches &matches) { + // Send content message to client and wait to receive next request + session.reply(ServiceLogic::make_200(request, "Main page\n", "text/html")); + }); + + m_router->insert("/device/access/valid",[](HttpSession &session, const Request &request, const boost::urls::matches &matches) { + LOG(info) << "request body: " << request.body(); + auto requestValue = boost::json::parse(request.body()); + auto &requestObject = requestValue.as_object(); + + boost::json::object root; + root["code"] = 0; + + boost::json::object data; + data["sid"]=requestObject.at("sid").as_string(); + data["vid"]=requestObject.at("sid").as_string(); + data["pass"]=1; + data["code"]=0; + data["voucherPerson"]="amass"; + data["voucherMsg"]="测试验票通过"; + data["register"]=1; + data["effectTime"]="2024-01-15 00:30:35"; + data["expireTime"]="2024-01-20 19:30:35"; + + root["data"] = std::move(data); + using namespace boost::beast; + http::response res{http::status::ok, request.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "application/json"); + res.keep_alive(request.keep_alive()); + res.body() = boost::json::serialize(root); + res.prepare_payload(); + session.reply(std::move(res)); + }); + + m_router->insert( + "/device/access/report", [](HttpSession &session, const Request &request, const boost::urls::matches &matches) { + // Send content message to client and wait to receive next request + session.reply(ServiceLogic::make_200(request, "Main page\n", "text/html")); + }); +} + +const SharedState::Handler *SharedState::find(boost::urls::segments_encoded_view path, + boost::urls::matches_base &matches) const noexcept { + return m_router->find(path, matches); +} + +void SharedState::join(WebSocketSession *session) { + std::lock_guard lock(mutex_); + sessions_.insert(session); +} + +void SharedState::leave(WebSocketSession *session) { + std::lock_guard lock(mutex_); + sessions_.erase(session); +} + +void SharedState::send(std::string message) { + // Put the message in a shared pointer so we can re-use it for each client + auto const ss = std::make_shared(std::move(message)); + + // Make a local list of all the weak pointers representing + // the sessions, so we can do the actual sending without + // holding the mutex: + std::vector> v; + { + std::lock_guard lock(mutex_); + v.reserve(sessions_.size()); + for (auto p : sessions_) v.emplace_back(p->weak_from_this()); + } + + // For each session in our local list, try to acquire a strong + // pointer. If successful, then send the message on that session. + for (auto const &wp : v) + if (auto sp = wp.lock()) sp->send(ss); +} diff --git a/VoucherVerifyServer/SharedState.h b/VoucherVerifyServer/SharedState.h new file mode 100644 index 0000000..90a7037 --- /dev/null +++ b/VoucherVerifyServer/SharedState.h @@ -0,0 +1,50 @@ +#ifndef SHAREDSTATE_H +#define SHAREDSTATE_H + +#include "router.hpp" +#include +#include +#include +#include +#include +#include +#include + +class HttpSession; +class WebSocketSession; + +// Represents the shared server state +class SharedState : public std::enable_shared_from_this { + + // This mutex synchronizes all access to sessions_ + std::mutex mutex_; + + // Keep a list of all the connected clients + std::unordered_set sessions_; + +public: + using Request = boost::beast::http::request; + using Handler = std::function; + SharedState(boost::asio::io_context &ioContext); + + const Handler *find(boost::urls::segments_encoded_view path, boost::urls::matches_base &matches) const noexcept; + + void join(WebSocketSession *session); + void leave(WebSocketSession *session); + + /** + * @brief Broadcast a message to all websocket client sessions + */ + void send(std::string message); + +private: + boost::asio::io_context &m_ioContext; + std::shared_ptr> m_router; + std::string m_docRoot; + std::string m_galleryRoot = "/root/photos"; + std::string m_fileRoot; +}; + +using SharedStatePtr = std::shared_ptr; + +#endif // SHAREDSTATE_H diff --git a/VoucherVerifyServer/WebsocketSession.cpp b/VoucherVerifyServer/WebsocketSession.cpp new file mode 100644 index 0000000..09c528e --- /dev/null +++ b/VoucherVerifyServer/WebsocketSession.cpp @@ -0,0 +1,83 @@ +#include "WebsocketSession.h" +#include + +WebSocketSession::WebSocketSession(boost::asio::ip::tcp::socket &&socket, std::shared_ptr const &state) + : m_ws(std::move(socket)), m_state(state) {} + +WebSocketSession::~WebSocketSession() { + // Remove this session from the list of active sessions + m_state->leave(this); +} + +void WebSocketSession::onAccept(boost::beast::error_code ec) { + // Handle the error, if any + if (ec) { + if (ec == boost::asio::error::operation_aborted || ec == boost::beast::websocket::error::closed) return; + std::cerr << "accept: " << ec.message() << "\n"; + return; + } + + // Add this session to the list of active sessions + m_state->join(this); + + // Read a message + m_ws.async_read(m_buffer, boost::beast::bind_front_handler(&WebSocketSession::on_read, shared_from_this())); +} + +void WebSocketSession::on_read(boost::beast::error_code ec, std::size_t) { + // Handle the error, if any + if (ec) { + // Don't report these + if (ec == boost::asio::error::operation_aborted || ec == boost::beast::websocket::error::closed) return; + LOG(error) << "read: " << ec.message(); + return; + } + LOG(info) << boost::beast::buffers_to_string(m_buffer.data()); + // Send to all connections + m_state->send(boost::beast::buffers_to_string(m_buffer.data())); + + // Clear the buffer + m_buffer.consume(m_buffer.size()); + + // Read another message + m_ws.async_read(m_buffer, boost::beast::bind_front_handler(&WebSocketSession::on_read, shared_from_this())); +} + +void WebSocketSession::send(std::shared_ptr const &ss) { + // Post our work to the strand, this ensures + // that the members of `this` will not be + // accessed concurrently. + m_ws.text(); + boost::asio::post(m_ws.get_executor(), + boost::beast::bind_front_handler(&WebSocketSession::onSend, shared_from_this(), ss)); +} + +void WebSocketSession::onSend(std::shared_ptr const &ss) { + // Always add to queue + m_queue.push_back(ss); + + // Are we already writing? + if (m_queue.size() > 1) return; + + // We are not currently writing, so send this immediately + m_ws.async_write(boost::asio::buffer(*m_queue.front()), + boost::beast::bind_front_handler(&WebSocketSession::on_write, shared_from_this())); +} + +void WebSocketSession::on_write(boost::beast::error_code ec, std::size_t) { + // Handle the error, if any + if (ec) { + // Don't report these + if (ec == boost::asio::error::operation_aborted || ec == boost::beast::websocket::error::closed) return; + std::cerr << "write: " << ec.message() << "\n"; + return; + } + + // Remove the string from the queue + m_queue.erase(m_queue.begin()); + + // Send the next message if any + if (!m_queue.empty()) + m_ws.async_write(boost::asio::buffer(*m_queue.front()), + boost::beast::bind_front_handler(&WebSocketSession::on_write, shared_from_this())); +} diff --git a/VoucherVerifyServer/WebsocketSession.h b/VoucherVerifyServer/WebsocketSession.h new file mode 100644 index 0000000..7f56bd3 --- /dev/null +++ b/VoucherVerifyServer/WebsocketSession.h @@ -0,0 +1,56 @@ +#ifndef WEBSOCKETSESSION_H +#define WEBSOCKETSESSION_H + +#include "BoostLog.h" +#include "SharedState.h" +#include +#include +#include +#include +#include + +class SharedState; + +/** + * @brief Represents an active WebSocket connection to the server + */ +class WebSocketSession : public std::enable_shared_from_this { + +public: + WebSocketSession(boost::asio::ip::tcp::socket &&socket, std::shared_ptr const &state); + + ~WebSocketSession(); + + template + void run(boost::beast::http::request> request) { + using namespace boost::beast::http; + using namespace boost::beast::websocket; + // Set suggested timeout settings for the websocket + m_ws.set_option(stream_base::timeout::suggested(boost::beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + m_ws.set_option(stream_base::decorator([](response_type &response) { + response.set(field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-chat-multi"); + })); + // LOG(info) << request.base().target(); //get path + // Accept the websocket handshake + m_ws.async_accept(request, [self{shared_from_this()}](boost::beast::error_code ec) { self->onAccept(ec); }); + } + + // Send a message + void send(std::shared_ptr const &ss); + +protected: + void onAccept(boost::beast::error_code ec); + void on_read(boost::beast::error_code ec, std::size_t bytes_transferred); + void on_write(boost::beast::error_code ec, std::size_t bytes_transferred); + void onSend(std::shared_ptr const &ss); + +private: + boost::beast::flat_buffer m_buffer; + boost::beast::websocket::stream m_ws; + std::shared_ptr m_state; + std::vector> m_queue; +}; + +#endif // WEBSOCKETSESSION_H diff --git a/VoucherVerifyServer/main.cpp b/VoucherVerifyServer/main.cpp new file mode 100644 index 0000000..8c5c164 --- /dev/null +++ b/VoucherVerifyServer/main.cpp @@ -0,0 +1,17 @@ +#include "BoostLog.h" +#include "IoContext.h" +#include "Listener.h" +#include "SharedState.h" + +int main(int argc, char const *argv[]) +{ + using namespace boost::asio::ip; + boost::log::initialize("/data/sdcard/logs/VoucherVerifyServer", "/data/sdcard/logs"); + auto ioContext = Amass::Singleton::instance(std::thread::hardware_concurrency()); + auto sharedState = Amass::Singleton::instance(*ioContext->ioContext()); + auto listener = + std::make_shared(*ioContext->ioContext(), tcp::endpoint{tcp::v4(), 3392}, sharedState); + listener->startAccept(); + ioContext->run(); + return 0; +} diff --git a/resources/build.sh b/resources/build.sh new file mode 100755 index 0000000..2b8915c --- /dev/null +++ b/resources/build.sh @@ -0,0 +1,230 @@ +#!/bin/bash + +cross_compile=true +build_hisi=false +debug_deploy=true +TARGET_IP="172.16.103.79" +TARGET_PATH="/system/bin" +base_path=$(pwd) +libraries_root="/opt/Libraries" +if [ $base_path==/home/* ]; then + build_path=${base_path}/build +else + build_path=/tmp/build +fi +echo "build directory: $build_path" + +function cmake_scan() { + if [ $cross_compile = true ]; then + toolchain_file=-DCMAKE_TOOLCHAIN_FILE=resources/cmake/toolchain.cmake + else + toolchain_file="-DCROSS_BUILD=OFF" + fi + if [ ! -d ${build_path} ]; then + mkdir ${build_path} + fi + + if [ $debug_deploy = true ]; then + build_debug=-DCMAKE_BUILD_TYPE=Debug + else + build_debug="" + fi + + cmake \ + -G Ninja \ + -S ${base_path} \ + -B ${build_path} \ + $build_debug \ + $toolchain_file +} + +function qtmoc(){ + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/mainUi/mainUi.h -o src/qt/mainUi/moc_mainUi.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/utility/UiTools.h -o src/qt/utility/moc_UiTools.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/videoChatUi/CallWaittingPage.h -o src/qt/videoChatUi/moc_CallWaittingPage.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/videoChatUi/DialPadPage.h -o src/qt/videoChatUi/moc_DialPadPage.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/videoChatUi/VideoChatPage.h -o src/qt/videoChatUi/moc_VideoChatPage.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/recoUi/recoUi.h -o src/qt/recoUi/moc_recoUi.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/recoUi/recoUiRecognize.h -o src/qt/recoUi/moc_recoUiRecognize.cpp + /opt/Qt/5.9.4/gcc_64/bin/moc src/qt/utility/DndModeCountDownItem.h -o src/qt/utility/moc_DndModeCountDownItem.cpp +} + +function build() { + if [ ! -f "${build_path}/CMakeCache.txt" ]; then + cmake_scan + fi + if [ $? -ne 0 ]; then + exit 1 + fi + cmake \ + --build ${build_path} \ + --target all + if [ $? -ne 0 ]; then + exit 1 + fi + + if [ $cross_compile = true ]; then + deploy + fi +} + +function deploy() { + # export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/system/lib + if [ -n "$1" ]; then + TARGET_IP=$1 + fi + + if [ $debug_deploy = true ]; then + TARGET_PATH="/data/sdcard" + fi + + echo "deploy to target $TARGET_IP, path: ${TARGET_PATH} ..." + ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "mount -o remount rw /system/" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "killall start-app.sh; pgrep -f GateFace | xargs kill -s 9" + ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "pgrep -f VoucherVerifyServer | xargs kill -s 9" + ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "pgrep -f Record | xargs kill -s 9" + + scp -i ~/Projects/ssh_host_rsa_key_ok ${build_path}/src/gate_face/GateFace root@${TARGET_IP}:${TARGET_PATH} + # scp -i ~/Projects/ssh_host_rsa_key_ok ${build_path}/ThirdParty/librwSrvProtocol.so root@${TARGET_IP}:/system/lib/ + scp -i resources/ssh_host_rsa_key_ok ${build_path}/Tools/Record/Record root@${TARGET_IP}:/sdcard/ + scp -i resources/ssh_host_rsa_key_ok ${build_path}/Tools/Record/rkmedia_audio_test root@${TARGET_IP}:/sdcard/ + scp -i resources/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/mpp/rk-libs/libeasymedia.so.1.0.1 root@${TARGET_IP}:/system/lib/ + + ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "sync" + + if [ $debug_deploy != true ]; then + echo "reboot remote device." + ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "reboot" + fi +} + +function clean() { + if [ -d ${build_path} ]; then + rm -fr ${build_path} + fi +} + +function copy_ssh() { + if [ -n "$1" ]; then + TARGET_IP=$1 + fi + SSH_KEY=$(cat ~/.ssh/id_rsa.pub) + echo "ssh copy id to ${TARGET_IP} ..." + # chmod 600 ./resources/ssh_host_rsa_key_ok + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "mount -o remount rw /oem/;mount -o remount rw /system/; mount -o remount rw /" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "if [ ! -f /usr/bin/scp ]; then cp /oem/bin/scp /usr/bin/; else echo 'scp exist'; fi" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "echo ${SSH_KEY} >> /oem/.ssh/authorized_keys" + scp -i ~/Projects/ssh_host_rsa_key_ok /mnt/e/Documents/gdb-rk root@${TARGET_IP}:/sdcard/gdb + scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/mpp/rk-libs/libeasymedia.so.1.0.1 root@${TARGET_IP}:/usr/lib/ + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/toolchains/linux-x86/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/bin/gdbserver root@${TARGET_IP}:/sdcard + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_url* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_log* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_program_options* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_date_time* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_filesystem* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_thread* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_regex* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_chrono* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_atomic* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_container* root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_84_0/lib/libboost_json* root@${TARGET_IP}:/system/lib + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "sync" +} + +function deploy_old(){ + if [ -n "$1" ]; then + TARGET_IP=$1 + fi + + if [ $debug_deploy = true ]; then + TARGET_PATH="/data/sdcard" + fi + + # qtmoc + # if [ $? -ne 0 ]; then + # exit 1 + # fi + + if [ $build_hisi = true ]; then + docker run --rm -v /opt:/opt -v .:$(pwd) -w $(pwd) b020_dev make server_protocol + docker run --rm -v /opt:/opt -v .:$(pwd) -w $(pwd) b020_dev make + else + # docker run --rm -v .:$(pwd) -w $(pwd) b020_dev protoc --nanopb_out=./ 3rdparty/rwSrvProtocol/PacketModel_all.proto + # make PLATFORM=rv1109 server_protocol + # if [ $? -ne 0 ]; then + # exit 1 + # fi + make PLATFORM=rv1109 -j4 + fi + if [ $? -ne 0 ]; then + exit 1 + fi + echo "deply to $TARGET_IP, path $TARGET_PATH" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "mount -o remount rw /; mount -o remount rw /system/" + + if [ $build_hisi = true ]; then + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "killall start-app.sh; killall GateFace; sleep 1" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "killall start-app.sh; killall GateFace" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "killall start-app.sh; killall GateFace" + else + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "killall start-app.sh; killall GateFace; killall netconfig" + fi + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/mpp/rk-libs/libeasymedia.so.1.0.1 root@${TARGET_IP}:/usr/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/rkap_3a/lib/libRKAP_3A.so root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/rkap_3a/lib/libRKAP_Common.so root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/rkap_3a/lib/libRKAP_ANR.so root@${TARGET_IP}:/system/lib/ + + # scp -r -i ~/Projects/ssh_host_rsa_key_ok src/web/php/*.php root@${TARGET_IP}:/system/www/web/ + scp -i ~/Projects/ssh_host_rsa_key_ok ./build/GateFace root@${TARGET_IP}:$TARGET_PATH + # scp -i ~/Projects/ssh_host_rsa_key_ok ./build/netconfig root@${TARGET_IP}:$TARGET_PATH + # scp -i ~/Projects/ssh_host_rsa_key_ok ./src/netlink/libRwNetlink.so root@${TARGET_IP}:/system/lib/ + + + if [ $build_hisi = true ]; then + echo "." + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "reboot" + else + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/librwhscheckpw/lib/librwhscheckpw.so root@${TARGET_IP}:/system/lib/ + scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/rwStageProtocol/lib/librwSrvProtocol.so root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/ffmepg/lib/libavdevice.so.58 root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/ffmepg/lib/libavfilter.so.7 root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/ffmepg/lib/libavformat.so.58 root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/ffmepg/lib/libavutil.so.56 root@${TARGET_IP}:/system/lib/ + # scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/ffmepg/lib/libswresample.so.3 root@${TARGET_IP}:/system/lib/ + fi + + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "sync" + + if [ $debug_deploy = false ]; then + if [ $build_hisi = false ]; then + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "echo 1 > /dev/watchdog" + fi + fi +} + +function main() { + local cmd=$1 + shift 1 + case $cmd in + build) + build + ;; + scan) + cmake_scan + ;; + clean) + clean + ;; + old) + deploy_old $@ + ;; + ssh) + copy_ssh $@ + ;; + *) + build + ;; + esac +} + +main $@ diff --git a/resources/cmake/FindQt5.cmake b/resources/cmake/FindQt5.cmake new file mode 100644 index 0000000..aa36498 --- /dev/null +++ b/resources/cmake/FindQt5.cmake @@ -0,0 +1,41 @@ +add_library(Qt5::Core SHARED IMPORTED) +set_target_properties(Qt5::Core PROPERTIES + IMPORTED_LOCATION ${QT5_ROOT}/lib/libQt5Core.so + INTERFACE_INCLUDE_DIRECTORIES ${QT5_ROOT}/include/QtCore +) +target_include_directories(Qt5::Core + INTERFACE ${QT5_ROOT}/include +) + +target_link_libraries(Qt5::Core + INTERFACE rga + INTERFACE drm + INTERFACE z + INTERFACE png16 + INTERFACE pcre + INTERFACE pcre2-16 + INTERFACE gthread-2.0 + INTERFACE glib-2.0 +) + +add_library(Qt5::Gui SHARED IMPORTED) +set_target_properties(Qt5::Gui PROPERTIES + IMPORTED_LOCATION ${QT5_ROOT}/lib/libQt5Gui.so + INTERFACE_INCLUDE_DIRECTORIES ${QT5_ROOT}/include/QtGui +) + +add_library(Qt5::Widgets SHARED IMPORTED) +set_target_properties(Qt5::Widgets PROPERTIES + IMPORTED_LOCATION ${QT5_ROOT}/lib/libQt5Widgets.so + INTERFACE_INCLUDE_DIRECTORIES ${QT5_ROOT}/include/QtWidgets +) + +target_include_directories(Qt5::Widgets + INTERFACE ${QT5_ROOT}/include +) + +add_library(Qt5::Network SHARED IMPORTED) +set_target_properties(Qt5::Network PROPERTIES + IMPORTED_LOCATION ${QT5_ROOT}/lib/libQt5Network.so + INTERFACE_INCLUDE_DIRECTORIES ${QT5_ROOT}/include/QtNetwork +) \ No newline at end of file diff --git a/resources/cmake/hisi_toolchain.cmake b/resources/cmake/hisi_toolchain.cmake new file mode 100644 index 0000000..a53d24e --- /dev/null +++ b/resources/cmake/hisi_toolchain.cmake @@ -0,0 +1,25 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER arm-himix200-linux-gcc) +set(CMAKE_CXX_COMPILER arm-himix200-linux-g++) + +# where is the target environment located +# set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc +# /home/alex/mingw-install) + +# adjust the default behavior of the FIND_XXX() commands: +# search programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# search headers and libraries in the target environment +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(CMAKE_C_COMPILER_FORCED ON) +set(CMAKE_CXX_COMPILER_FORCED ON) + +string(APPEND CMAKE_CXX_FLAGS " -mfloat-abi=softfp -mfpu=neon") + +set(BOOST_ROOT /opt/toolchains/Libraries/boost_1_83_0) \ No newline at end of file diff --git a/resources/cmake/toolchain.cmake b/resources/cmake/toolchain.cmake new file mode 100644 index 0000000..d785f88 --- /dev/null +++ b/resources/cmake/toolchain.cmake @@ -0,0 +1,22 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) +set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) + +# where is the target environment located +# set(CMAKE_FIND_ROOT_PATH /usr/i586-mingw32msvc +# /home/alex/mingw-install) + +# adjust the default behavior of the FIND_XXX() commands: +# search programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# search headers and libraries in the target environment +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +string(APPEND CMAKE_CXX_FLAGS " -mfloat-abi=hard -mfpu=neon") + +set(BOOST_ROOT /opt/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/lib/boost_1_85_0) \ No newline at end of file diff --git a/resources/deploy.sh b/resources/deploy.sh new file mode 100644 index 0000000..ca117aa --- /dev/null +++ b/resources/deploy.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# +TARGET_IP="192.168.8.108" + +function build() { + if [ -n "$1" ]; then + TARGET_IP=$1 + fi + + make PLATFORM=rv1109 server_protocol + make PLATFORM=rv1109 + if [ $? -ne 0 ]; then + exit 1 + fi + echo "deply to $TARGET_IP" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "mount -o remount rw /system/" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "killall start-app.sh; pkill -f GateFace" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "pgrep -f netconfig | xargs kill -s 9" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "pgrep -f voucher_verify | xargs kill -s 9" + scp -r -i ~/Projects/ssh_host_rsa_key_ok src/web/php/*.php root@${TARGET_IP}:/system/www/web/ + scp -i ~/Projects/ssh_host_rsa_key_ok ./build/GateFace root@${TARGET_IP}:/system/bin + scp -i ~/Projects/ssh_host_rsa_key_ok ./build/voucher_verify root@${TARGET_IP}:/system/bin + scp -i ~/Projects/ssh_host_rsa_key_ok ./src/netlink/libRwNetlink.so root@${TARGET_IP}:/system/lib + scp -i ~/Projects/ssh_host_rsa_key_ok ./build/netconfig root@${TARGET_IP}:/system/bin + scp -i ~/Projects/ssh_host_rsa_key_ok ./3rdparty/arm-linux-gnueabihf/rwStageProtocol/lib/librwSrvProtocol.so root@${TARGET_IP}:/system/lib/ + + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "sync" + + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "echo 1 > /dev/watchdog" +} + +function build_debug() { + make PLATFORM=rv1109 server_protocol + scp -r -i ~/Projects/ssh_host_rsa_key_ok 3rdparty/arm-linux-gnueabihf/rwStageProtocol/lib/librwSrvProtocol.so root@${TARGET_IP}:/system/lib/ + make PLATFORM=rv1109 + echo "deply to /sdcard" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "mount -o remount rw /system/" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "pkill -f start-app.sh & pkill -f GateFace & pkill -f dk_uart_id_reader" + scp -r -i ~/Projects/ssh_host_rsa_key_ok src/web/php/*.php root@${TARGET_IP}:/system/www/web/ + scp -i ~/Projects/ssh_host_rsa_key_ok ./build/GateFace root@${TARGET_IP}:/sdcard + scp -i ~/Projects/ssh_host_rsa_key_ok ./build/ryxconnector root@${TARGET_IP}:/sdcard + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "sync" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "amixer sset 'Master' 80%" +} + +function copy_ssh() { + TARGET_IP=$1 + SSH_KEY=$(cat ~/.ssh/id_rsa.pub) + echo "ssh copy id to ${TARGET_IP} ..." + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "mount -o remount rw /oem/; mount -o remount rw /" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "if [ ! -f /usr/bin/scp ]; then cp /oem/bin/scp /usr/bin/; else echo 'scp exist'; fi" + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "echo ${SSH_KEY} >> /oem/.ssh/authorized_keys" + scp -i ~/Projects/ssh_host_rsa_key_ok /mnt/e/Documents/gdb-rk root@${TARGET_IP}:/sdcard + scp -i ~/Projects/ssh_host_rsa_key_ok /opt/toolchains/linux-x86/arm/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/arm-linux-gnueabihf/libc/usr/bin/gdbserver root@${TARGET_IP}:/sdcard + ssh -i ~/Projects/ssh_host_rsa_key_ok root@${TARGET_IP} "sync" +} + +function main() { + local cmd=$1 + shift 1 + case $cmd in + build) + build $@ + ;; + debug) + build_debug + ;; + ssh) + copy_ssh $@ + ;; + *) + build + ;; + esac +} + +main $@ diff --git a/resources/ssh_host_rsa_key_ok b/resources/ssh_host_rsa_key_ok new file mode 100644 index 0000000..82f5eb9 --- /dev/null +++ b/resources/ssh_host_rsa_key_ok @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAnqnYt7KJAQ5bhO/MgyWUg6QV5ICWbQX/cqzxpKKPyQQuJbUz +ML2lKPz5ogsv+pn7Hgh+SvCSN6iF/9Rtr5fD5I8COZxLlWbC3fz6J+2hPtvjCvdI +sMe0PvKxUa7vsujnUzkwpBjRcHG/gBF0P6UWvZBVbtn6jwP9ufYcAI7jYMI3YBk5 +PNOowdcAoRiwTe2KnJzQNj2ztUE6VFgo3U5gyoGKfxNDi8P2aMWW3xARLJHh3hbR +Ztmwz8eDWD9L2c6fn61t9yqpyCRepE33+yC7m1Jdgxx+FWQBBfExwJaSBbWXyT1f +13v4ElSJXk4zWkeB1ddv76MZqDM9kRLJB/1WcQIDAQABAoIBAArexBRBzRUfcLG4 +5CAQEQ67zSwsiyHJCWlH4/5NjOBOwwKeWXV7aLdYLZomfhEDP6sdFdn4hWv+xb9z +RFiFY0xjZ9melacUtUEUNedKZoOx+OolavuBkx3E7tUT0SRGxKJ1BpoXgTHc+SvD +Bl3d56LxIolHsdzefyGgdOas43Mbf6RHbAnXA65VyYf/k7fY4GkrO8/hKTXos32R +fNV0OD8qq8+KTwgnYXBKrjbie6N5XmOS9IcLFNC8F+0PC5NO+xzOk1McnmMux7t9 +EwUj8/VXAYZPFRkEZdhmdzMgs9dpT9n+L13XppJLAAGEvFySFOGjEz2HWT7Hf5qB +z/ct9ukCgYEAzjV7V82ueeUTqMM8Hm6RV8MwpJxVwUABOQ934DuBO/p8oMqy9qlA +V02oJVnA5gMhCKKSr3dZa4+JG3BubrOh9tGEcw4pXIYEAAB6qWxsyHhNPcTrjYNS +mF5TFKgfY9uJ4p2b+YKYAuwyucdQf7kNQTEz/eYHOkardfGF00dlAo8CgYEAxPlo +nCUxaWW+rPI1wiP5F0BMhxrR4H592BajQQ1i/3IBUny43rRO2Sqd0E4xQzmTaGbi +qJtBGvS0DGMHOHfJ/5ha6xPiUFk1TJuRtb5P/uwdtJe6YZHeYz/yEDu07hKdUySs +J7oXlxORxcDphmYiVlM5ARy7h3SlLZEaGocglv8CgYAJWE8gqYeQqpr7eO2Oi4A2 +s/fOXe9c9KOZjGPoW4n9x9pCBy2ZFA6AZlqKZsjhI58pnwR6wzPgNf8GqU+34Jgk +cVZJSIkDUmd4AI2OhAViCGGnvPbX0O27TfsB4qSZh+d1x6K76fktmLqrxKaXQzGg +lLRAe140e+NlUu63pnKjbQKBgB6A1S9hHx7IT9fuhl6zPW7ZWUCO6bOPQyPElbGM +Rily2TuR7vsmBxPgYqIQidZ0Qxym2jDsIg/vlzZX25VmD5USXfoLkT+KIRPk/5Ka +jz7WB9+go49ajIroKy9SX7mCjnSCuFpKmj6eHDP4pPFYPbcKmkhOz8exS6cb0mQL +SwO3AoGAYhzb7EHOTDfhtJQ0d49/8jUXprrGdExs2u88Gv8WjLsLaMm9hmNzY13M +i2wkM1ZOZag1XwvznVdLoM7lt62evtnocnEIysiiNaryoPFw/NCndNNG6mealVut +1Xr3rUQS9HOYeIYW5IFUfCv7aMFnEqd6AWdvWJ69uMIt7Wgp5yo= +-----END RSA PRIVATE KEY-----