diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 48219b1..29d13e4 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,7 +4,7 @@ "name": "Linux", "includePath": [ "${workspaceFolder}/**", - "${workspaceFolder}/3rdparty/ds_pedestrian_mot_hisi/include", + "/opt/aarch64-v01c01-linux-gnu-gcc/lib/libdatachannel-0.22.5/include", "/opt/aarch64-v01c01-linux-gnu-gcc/lib/boost_1_87_0/include", "/opt/aarch64-v01c01-linux-gnu-gcc/lib/libdatachannel-0.22.5", "/opt/aarch64-v01c01-linux-gnu-gcc/lib/ZLMediaKit/include", diff --git a/3rdparty/fsan_sensorsdk/include/sensorsdk_api.h b/3rdparty/fsan_sensorsdk/include/sensorsdk_api.h new file mode 100644 index 0000000..463da0a --- /dev/null +++ b/3rdparty/fsan_sensorsdk/include/sensorsdk_api.h @@ -0,0 +1,156 @@ +#ifndef __SENSOR_SDK_API_H__ +#define __SENSOR_SDK_API_H__ + +enum { + SENSOR_SDK_SUCC = 0, + SENSOR_SDK_INVALID_INPARAM, //参数非法 + SENSOR_SDK_NOT_INIT, //未初始化 + SENSOR_SDK_LICENSE_INIT_FAIL, //license 库初始化失败 + SENSOR_SDK_NOT_AUTH, //未授权 + SENSOR_SDK_MUTI_PROCESS_INIT, //多个實例初始化 + SENSOR_SDK_AUTOLENS_GET_PARAM, //获取参数失败 + SENSOR_SDK_AUTOLENS_SET_PARAM, //设置参数失败 + SENSOR_SDK_AUTOLENS_SET_AF_PARAM,//设置AF参数失败 + SENSOR_SDK_ERROR, //其他错误 + +}SENSOR_SDK_ERROR_TYPE; +//autolens +enum { + + SensorSDK_AutoLens_Type_ZeroCheck=0x0, //零点矫正, 无参数 + SensorSDK_AutoLens_Type_ZoomIn , // 参数 1-10为速度 ,-1 为停止 ,以下无特殊时同此 + SensorSDK_AutoLens_Type_ZoomOut , + SensorSDK_AutoLens_Type_FocusNear , + SensorSDK_AutoLens_Type_FocusFar , + SensorSDK_AutoLens_Type_FocusAuto , //自动聚焦 + SensorSDK_AutoLens_Type_EnableAuto , // 设置是否启用自动聚焦 + SensorSDK_AutoLens_Type_ZoomFocus , // 精确定位到指定的位置, + SensorSDK_AutoLens_Type_ZoomPos , // int 精确定位 + SensorSDK_AutoLens_Type_ZoomRange , // int 定位范围 + SensorSDK_AutoLens_Type_FocusPos , // int 精确定位 + SensorSDK_AutoLens_Type_FocusRange , // int 获取 取值范围,精确定位时用 + SensorSDK_AutoLens_Type_ZoomRate , // int 300 代表3.00倍 + SensorSDK_AutoLens_Type_IRCUT , // 0 全波段, 1 可见光模式, 2 红外模式 + + //new + SensorSDK_AutoLens_Type_Iris_OPEN, // 光圈大 //需要带步长参数1-64 光圈范围固定0-0X3FF + SensorSDK_AutoLens_Type_Iris_CLOSE, // 光圈小 //需要带步长参数1-64 + SensorSDK_AutoLens_Type_IrisPos, // 获取光圈位置 + + SensorSDK_AutoLens_Type_Fov, //获取对角线视场角精确到0.01度 60度 6000(表示60度) + SensorSDK_AutoLens_Type_Auto_Check, //设置镜头曲线校准 + SensorSDK_AutoLens_Type_AF_Status, //获取 AF状态 + SensorSDK_AutoLens_Type_ZoomPos_By_Fov, //获取 输入FOV输出ZOOMPOS 调用get接口 + SensorSDK_AutoLens_Type_Fov_By_ZoomPos, //获取 输入ZOOMPOS的值输出FOV 调用get接口 + SensorSDK_AutoLens_Type_Efi, //获取焦距 + SensorSDK_AutoLens_Type_Lens_Offset, //获取镜头中心点偏移量输出水平偏移量和垂直偏移量(**保留暂不使用**) + SensorSDK_AutoLens_Type_Check_Offset, //设置镜头中心偏移量校准 + SensorSDK_AutoLens_Type_IRCUTLensAuto=0x100, //设置 0 :夜晚1:白天 + SensorSDK_AutoLens_Type_Motor_Hysteresis=0x101, //马达间隙(1-100)步(**保留暂不使用**) + + +}SENSORSDK_AUTOLENS_TYPE_E; + +typedef struct +{ + int nStop; // 1- 停止 + int nSpeed; // 速度 1-10, nStop==0时有效 + +}SensorSDK_AutoLens_Param_Base_T; + + + +typedef struct +{ + union + { + + SensorSDK_AutoLens_Param_Base_T Param_Base; + int nZoomRate; // 300 代表 3.00倍 + int nIrCut; // 0-全波段,1-可见光,2红外模式 + int nZoomPos; // 定位, 先获取范围,再定位,定位必须是有效范围内 + int nZoomRange; // 范围 + int nFocusPos; // 定位 + int nFocusRange; // 聚焦定位取值范围 + int nEnableAuto; // 使能 自动 聚焦功能 + int nZoomFocusPos[2]; // nZoomFocusPos[0]:focuspos,nZoomFocusPos[1]:zoompos + int nIrStep; // 光圈步长1-64 + int nIrPos; // 定位 + int nFOV; // 对角线视场角[ 1, 6000]表示 0.01度到60度 + int nEFI; // 焦距 + int nAFStatus; // 自动镜头状态 + int nFOVZoomPos; // nFOVZoomPos 输入输出 + int nIRCUTLensAuto; // 0-夜晚 1-白天 + int ress[4]; + }Params; +}SENSOR_SDK_AUTOLENS_PARAM_T; + +enum SENSOR_SDK_AF_STATUS_E +{ + SENSOR_SDK_AF_STATUS_IDLE = 0x1, + SENSOR_SDK_AF_STATUS_DO_ZOOM, + SENSOR_SDK_AF_STATUS_DO_FOCUS, + SENSOR_SDK_AF_STATUS_DO_AUTO_FOCUS, + SENSOR_SDK_AF_STATUS_ZOOM_ING, + SENSOR_SDK_AF_STATUS_FOCUS_ING, + SENSOR_SDK_AF_STATUS_AUTO_FOCUS_ING, + SENSOR_SDK_AF_STATUS_DETECT, + SENSOR_SDK_AF_STATUS_TEST, +}; + +typedef struct _SensorSDK_CameraLens +{ + int AFControlModel; //对焦控制方式 0----自动 1----半自动 2----手动 + int AFArea; //对焦区域选择 (保留)0----全区域对焦 1----中心对焦 + int AFSearchModel; //对焦区搜索方式 0 --- 全景, 1 --- 1.5m, 2 --- 3m, 3 --- 6m, 4 --- 无穷远 + int AFSensitivity; //对焦灵敏度 0 ~255 + int DigitZoomEnable; //数字ZOOM允许 + int IrisControl; //光圈调节0 自动 1手动 +} SensorSDK_CameraLens; + +#ifdef __cplusplus +extern "C" { +#endif +// SDK 初始化接口 +// nFlashPartIdx : 软件授权信息存放的分区索引号,注意分区需要自己提前创建好 +// 例如分区为/dev/mmcblk0p5,此处参数取值为5 +// 分区为/dev/mtd6,此处参数取值为6 +// BoardLine 板接口定义,000,第一位代表zoomfocus顺序,后面两个代表两个电机方向;每位取值为 0或者1 +// szLensType 镜头型号,可取值如下 + /* + "MUXIA_5X", + "RICOM_AF3610_4K","YS_15_3_XM02","RICOM_3312_4K","RICOM_27135_6MP", "RICOM_2812_5MP","RICOM_2812_2MP", + "YT_6022_3MP","YT_3610_3MP","YT_3611_4K","YT_27135_HD","YT_27135_4MP","YT_2712_HD","YT_2812_4MP","YT_2812_2MP","RICOM_2812_3MP", + "YT_550_2MP","RICOM_5M_50MM_1_27F","YT6022","YT3013","YT2712","YT3610","3.6-10mm","YT2812","YT3015","RICON2814","RICON2812","YT2808", + "2.8-8mm","RICOM_4K_AF2710DC","RICOM_4K_33_120MM_14F","RICOM_3M_28_120MM_14F","RICOM_5M_38_100MM","RICOM_2M_22_120MM_14F","RICOM_3M_28_80MM" + */ +// szAutoLensParmPath: 保存当前变焦状态用的路径,路径必须可写 +// pCameralens: 自动聚焦镜头参数 + +//return 错误码见 SENSOR_SDK_ERROR_TYPE + +//初始化接口 +int SensorSdk_Init(int nFlashPartIdx,char *szBoardLine,char *szLensType , + char *szAutoLensParmPath /* 保存当前变焦状态用的路径 */, SensorSDK_CameraLens *pCameralens = 0); + +//获取授权状态 SENSOR_SDK_SUCC-授权成功 +int SensorSdk_GetAuthStatus(); + +// 镜头变焦变倍 接口 +// 设置 动作 +// nType : 见 SensorSDK_AutoLens_Type_XXX 宏定义 +// nParam : SENSOR_SDK_AUTOLENS_PARAM_T +int SensorSdk_AutoLens_SetParam(int nDev,int nType,SENSOR_SDK_AUTOLENS_PARAM_T *pParam); +// 获取当前 信息 +int SensorSdk_AutoLens_GetParam(int nDev,int nType,SENSOR_SDK_AUTOLENS_PARAM_T *pParam); +//設置自动聚焦镜头参数 +int SensorSdk_AutoLens_SetAFParam(SensorSDK_CameraLens *pCameralens); + +#ifdef __cplusplus +} +#endif + + + +#endif + diff --git a/3rdparty/fsan_sensorsdk/ko/ants_spi_ms41908_ex.ko b/3rdparty/fsan_sensorsdk/ko/ants_spi_ms41908_ex.ko new file mode 100644 index 0000000..f0b356b Binary files /dev/null and b/3rdparty/fsan_sensorsdk/ko/ants_spi_ms41908_ex.ko differ diff --git a/3rdparty/fsan_sensorsdk/libs/libsensorsdk.so b/3rdparty/fsan_sensorsdk/libs/libsensorsdk.so new file mode 100644 index 0000000..19fe62e Binary files /dev/null and b/3rdparty/fsan_sensorsdk/libs/libsensorsdk.so differ diff --git a/CMakeLists.txt b/CMakeLists.txt index e7b0c2f..f32551d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ option(CROSS_BUILD "build for embeded product." ON) if(CROSS_BUILD) set(Libraries_ROOT /opt/aarch64-v01c01-linux-gnu-gcc/lib) set(OpenCV_DIR ${Libraries_ROOT}/opencv-4.11.0/lib/cmake/opencv4) + set(LibDataChannel_DIR ${Libraries_ROOT}/libdatachannel-0.22.5/lib/cmake/LibDataChannel) set(OPENSSL_ROOT_DIR ${Libraries_ROOT}/openssl-3.4.1) set(OPENSSL_LIBRARY_DIRS ${OPENSSL_ROOT_DIR}/libs) set(SCTP_ROOT ${Libraries_ROOT}/usrsctp-0.9.5.0) diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index e900c6f..6d44bf0 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -1,28 +1,39 @@ find_package(OpenSSL REQUIRED) find_package(OpenCV REQUIRED) +find_package(LibDataChannel REQUIRED) +find_package(Boost COMPONENTS json REQUIRED) add_executable(PassengerStatistics main.cpp + Camera.h Camera.cpp ImageUtilities.h ImageUtilities.cpp RtspServer.h RtspServer.cpp VideoInput.h VideoInput.cpp + + WebRTC/Streamer.h WebRTC/Streamer.cpp + WebRTC/Helpers.h WebRTC/Helpers.cpp ) target_include_directories(PassengerStatistics PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/rw_mpp/include + PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/fsan_sensorsdk/include PRIVATE ${ZLMediaKit_INCLUDE_DIR} ) target_link_directories(PassengerStatistics PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/rw_mpp/lib + PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/fsan_sensorsdk/libs PRIVATE ${ZLMediaKit_LIBRARY_DIRS} PRIVATE ${OPENSSL_LIBRARY_DIRS} ) target_link_libraries(PassengerStatistics PRIVATE Kylin::Core + PRIVATE LibDataChannel::LibDataChannel PRIVATE OpenSSL::SSL PRIVATE OpenSSL::Crypto + PRIVATE Boost::json PRIVATE rw_mpp + PRIVATE sensorsdk PRIVATE mk_api PRIVATE ${OpenCV_LIBS} PRIVATE ${SCTP_LIBRARIES} diff --git a/Main/Camera.cpp b/Main/Camera.cpp new file mode 100644 index 0000000..81a9555 --- /dev/null +++ b/Main/Camera.cpp @@ -0,0 +1,48 @@ +#include "Camera.h" +#include "Core/Logger.h" +#include "sensorsdk_api.h" + +Camera::Camera() { + SensorSDK_CameraLens cameraLens = {}; + cameraLens.AFControlModel = 2; + + char boardLine[] = "000"; + char lensType[] = "YT_2812_2MP"; + char autoLensParmPath[] = "/data"; + int status = SensorSdk_Init(7, boardLine, lensType, autoLensParmPath, &cameraLens); + if (status == 0) { + LOG(info) << "SensorSdk_Init() successed."; + } else { + LOG(info) << "SensorSdk_Init() failed."; + } + + SENSOR_SDK_AUTOLENS_PARAM_T parameter = {0}; + parameter.Params.nIrCut = 1; + status = SensorSdk_AutoLens_SetParam(0, SensorSDK_AutoLens_Type_IRCUT, ¶meter); + if (status == 0) { + LOG(info) << "SensorSdk_AutoLens_SetParam() successed."; + } else { + LOG(info) << "SensorSdk_AutoLens_SetParam() failed."; + } + initIrCutGpio(); +} + +void Camera::initIrCutGpio() { + constexpr auto initCommand = R"( + /system/bin/bspmm 0x179f0014 0x1200; + echo 84 > /sys/class/gpio/export; + echo out > /sys/class/gpio/gpio84/direction; + echo 83 > /sys/class/gpio/export; + echo out > /sys/class/gpio/gpio83/direction; + )"; + system(initCommand); + + constexpr auto dayCommand = R"( + echo 0 > /sys/class/gpio/gpio84/value; + echo 1 > /sys/class/gpio/gpio83/value; + sleep 1; + echo 0 > /sys/class/gpio/gpio84/value; + echo 0 > /sys/class/gpio/gpio83/value; + )"; + system(dayCommand); +} diff --git a/Main/Camera.h b/Main/Camera.h new file mode 100644 index 0000000..259cf45 --- /dev/null +++ b/Main/Camera.h @@ -0,0 +1,12 @@ +#ifndef __CAMERA_H__ +#define __CAMERA_H__ + +class Camera { +public: + Camera(); + +protected: + void initIrCutGpio(); +}; + +#endif // __CAMERA_H__ \ No newline at end of file diff --git a/Main/VideoInput.cpp b/Main/VideoInput.cpp index 38325bf..a081b1f 100644 --- a/Main/VideoInput.cpp +++ b/Main/VideoInput.cpp @@ -87,8 +87,8 @@ bool VideoInput::startEncode() { config.raw_max_height = 1664; config.width = 1280; config.height = 720; - config.gop = 15; - config.framerate = 15; + config.gop = 5; + config.framerate = 25; config.rc_type = RC_VBR; S_venc_rc_vbr vbr; diff --git a/Main/WebRTC/Helpers.cpp b/Main/WebRTC/Helpers.cpp new file mode 100644 index 0000000..3e5df91 --- /dev/null +++ b/Main/WebRTC/Helpers.cpp @@ -0,0 +1,32 @@ +#include "Helpers.h" +#include <mutex> +#include <sys/time.h> + +Client::Client(std::shared_ptr<rtc::PeerConnection> pc) { + m_peerConnection = pc; +} + +Client::State Client::state() const { + std::shared_lock lock(m_mutex); + return m_state; +} + +void Client::setState(State state) { + std::unique_lock lock(m_mutex); + m_state = state; +} + +std::shared_ptr<rtc::PeerConnection> Client::peerConnection() const { + return m_peerConnection; +} + +ClientTrack::ClientTrack(std::string id, std::shared_ptr<ClientTrackData> trackData) { + this->id = id; + this->trackData = trackData; +} + +uint64_t currentTimeInMicroSeconds() { + struct timeval time; + gettimeofday(&time, NULL); + return uint64_t(time.tv_sec) * 1000 * 1000 + time.tv_usec; +} diff --git a/Main/WebRTC/Helpers.h b/Main/WebRTC/Helpers.h new file mode 100644 index 0000000..dd41d0f --- /dev/null +++ b/Main/WebRTC/Helpers.h @@ -0,0 +1,48 @@ +#ifndef __HELPERS_H__ +#define __HELPERS_H__ + +#include <memory> +#include <optional> +#include <rtc/rtc.hpp> +#include <shared_mutex> + +template <class T> +std::weak_ptr<T> make_weak_ptr(std::shared_ptr<T> ptr) { + return ptr; +} + +uint64_t currentTimeInMicroSeconds(); + +struct ClientTrackData { + std::shared_ptr<rtc::Track> track; + std::shared_ptr<rtc::RtcpSrReporter> sender; +}; + +class Client { +public: + enum class State { + Waiting, + WaitingForVideo, + WaitingForAudio, + Ready, + }; + Client(std::shared_ptr<rtc::PeerConnection> pc); + State state() const; + void setState(State state); + std::shared_ptr<rtc::PeerConnection> peerConnection() const; + std::optional<std::shared_ptr<ClientTrackData>> video; + std::optional<std::shared_ptr<rtc::DataChannel>> dataChannel; + +private: + mutable std::shared_mutex m_mutex; + State m_state = State::Waiting; + std::shared_ptr<rtc::PeerConnection> m_peerConnection; +}; + +struct ClientTrack { + std::string id; + std::shared_ptr<ClientTrackData> trackData; + ClientTrack(std::string id, std::shared_ptr<ClientTrackData> trackData); +}; + +#endif // __HELPERS_H__ \ No newline at end of file diff --git a/Main/WebRTC/Streamer.cpp b/Main/WebRTC/Streamer.cpp new file mode 100644 index 0000000..253a0b8 --- /dev/null +++ b/Main/WebRTC/Streamer.cpp @@ -0,0 +1,187 @@ +#include "Streamer.h" +#include "Core/Logger.h" +#include "Helpers.h" +#include <boost/asio/io_context.hpp> +#include <boost/asio/strand.hpp> +#include <boost/json/parse.hpp> +#include <boost/json/serialize.hpp> +#include <rtc/rtc.hpp> + +class WebRTCStreamerPrivate { +public: + WebRTCStreamerPrivate(boost::asio::io_context &ioContext) : strand{ioContext.get_executor()} { + } + + std::shared_ptr<ClientTrackData> addVideo(const std::shared_ptr<rtc::PeerConnection> pc, const uint8_t payloadType, + const uint32_t ssrc, const std::string cname, const std::string msid, + const std::function<void(void)> onOpen) { + using namespace std::chrono; + auto video = rtc::Description::Video(cname); + video.addH264Codec(payloadType); + video.addSSRC(ssrc, cname, msid, cname); + auto track = pc->addTrack(video); + + auto rtpConfig = std::make_shared<rtc::RtpPacketizationConfig>( + ssrc, cname, payloadType, rtc::H264RtpPacketizer::defaultClockRate); // create RTP configuration + rtpConfig->startTimestamp = rtpConfig->secondsToTimestamp( + static_cast<double>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count()) / 1000); + + auto packetizer = std::make_shared<rtc::H264RtpPacketizer>(rtc::NalUnit::Separator::StartSequence, + rtpConfig); // create packetizer + + auto srReporter = std::make_shared<rtc::RtcpSrReporter>(rtpConfig); // add RTCP SR handler + packetizer->addToChain(srReporter); + + auto nackResponder = std::make_shared<rtc::RtcpNackResponder>(); // add RTCP NACK handler + packetizer->addToChain(nackResponder); + + track->setMediaHandler(packetizer); // set handler + track->onOpen(onOpen); + auto trackData = std::make_shared<ClientTrackData>(); + trackData->track = track; + trackData->sender = srReporter; + return trackData; + } + + std::shared_ptr<Client> createPeerConnection(const rtc::Configuration &config, std::string id) { + auto pc = std::make_shared<rtc::PeerConnection>(config); + auto client = std::make_shared<Client>(pc); + + pc->onStateChange([this, id](rtc::PeerConnection::State state) { + LOG(info) << "State: " << state; + if (state == rtc::PeerConnection::State::Disconnected || state == rtc::PeerConnection::State::Failed || + state == rtc::PeerConnection::State::Closed) { + boost::asio::post(strand, [this, id]() { m_clients.erase(id); }); // remove disconnected client + } + }); + + pc->onGatheringStateChange([this, wpc = make_weak_ptr(pc), id](rtc::PeerConnection::GatheringState state) { + LOG(info) << "Gathering State: " << state; + if (state == rtc::PeerConnection::GatheringState::Complete) { + if (auto pc = wpc.lock()) { + auto description = pc->localDescription(); + boost::json::object message; + message["id"] = id; + message["type"] = description->typeString(); + message["sdp"] = std::string(description.value()); + websocket->send(boost::json::serialize(message)); // Gathering complete, send answer + } + } + }); + + client->video = addVideo(pc, 102, 1, "video-stream", "stream", [this, id, wc = make_weak_ptr(client)]() { + boost::asio::post(strand, [this, wc]() { + if (auto c = wc.lock()) { + c->setState(Client::State::Ready); + } + }); + LOG(info) << "Video from " << id << " opened"; + }); + + auto dc = pc->createDataChannel("ping-pong"); + dc->onOpen([id, wdc = make_weak_ptr(dc)]() { + if (auto dc = wdc.lock()) { + dc->send("Ping"); + } + }); + + dc->onMessage(nullptr, [id, wdc = make_weak_ptr(dc)](std::string msg) { + LOG(info) << "Message from " << id << " received: " << msg; + if (auto dc = wdc.lock()) { + dc->send("Ping"); + } + }); + client->dataChannel = dc; + + pc->setLocalDescription(); + return client; + } + void onWebSocketMesssage(const boost::json::object &message) { + if (!message.contains("id") || !message.contains("type")) return; + std::string id = std::string(message.at("id").as_string()); + std::string type = std::string(message.at("type").as_string()); + if (type == "request") { + m_clients.emplace(id, createPeerConnection(configuration, id)); + } else if (type == "answer") { + if (m_clients.count(id)) { + auto &peer = m_clients.at(id); + std::string sdp = std::string(message.at("sdp").as_string()); + auto pc = peer->peerConnection(); + auto description = rtc::Description(sdp, type); + pc->setRemoteDescription(description); + } + } + } + boost::asio::strand<boost::asio::io_context::executor_type> strand; + rtc::Configuration configuration; + std::shared_ptr<rtc::WebSocket> websocket; + std::unordered_map<std::string, std::shared_ptr<Client>> m_clients; +}; + +Streamer::~Streamer() { + if (m_d != nullptr) { + delete m_d; + } +} + +void Streamer::start(const std::string &signalServerAddress, uint16_t signalServerPort) { + using namespace std::chrono_literals; + + std::string localId = "server"; + LOG(info) << "The local ID is: " << localId; + + rtc::WebSocket::Configuration c; + c.disableTlsVerification = true; + m_d->websocket = std::make_shared<rtc::WebSocket>(c); + m_d->websocket->onOpen([]() { LOG(info) << "WebSocket connected, signaling ready"; }); + m_d->websocket->onClosed([]() { LOG(info) << "WebSocket closed"; }); + m_d->websocket->onError([](const std::string &error) { LOG(error) << "WebSocket failed: " << error; }); + + m_d->websocket->onMessage([this](std::variant<rtc::binary, std::string> data) { + if (!std::holds_alternative<std::string>(data)) return; + auto &text = std::get<std::string>(data); + LOG(info) << "ws received: " << std::endl << text; + auto value = boost::json::parse(text); + boost::asio::post(m_d->strand, + [this, value = std::move(value)]() { m_d->onWebSocketMesssage(value.as_object()); }); + }); + + const std::string url = + "wss://" + signalServerAddress + ":" + std::to_string(signalServerPort) + "/api/v1/webrtc/signal/" + localId; + LOG(info) << "URL is " << url; + m_d->websocket->open(url); + LOG(info) << "Waiting for signaling to be connected..."; +} + +void Streamer::push(const uint8_t *data, uint32_t size) { + using namespace std::chrono; + boost::asio::post(m_d->strand, [this, frame = rtc::binary(reinterpret_cast<const rtc::byte *>(data), + reinterpret_cast<const rtc::byte *>(data) + size)]() { + for (auto &[id, client] : m_d->m_clients) { + if (client->state() != Client::State::Ready) continue; + auto sender = (*client->video)->sender; + auto track = (*client->video)->track; + + sender->rtpConfig->timestamp = sender->rtpConfig->secondsToTimestamp( + static_cast<double>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count()) / + 1000); + auto reportElapsedTimestamp = sender->rtpConfig->timestamp - sender->lastReportedTimestamp(); + if (sender->rtpConfig->timestampToSeconds(reportElapsedTimestamp) > 1) { + sender->setNeedsToReport(); + } + track->send(frame); + } + }); +} + +Streamer::Streamer(boost::asio::io_context &ioContext) : m_d{new WebRTCStreamerPrivate(ioContext)} { + rtc::InitLogger(rtc::LogLevel::Debug); + std::string stunServer = "stun:amass.fun:5349"; // ssl + m_d->configuration.iceServers.emplace_back(stunServer); + LOG(info) << "STUN server is " << stunServer; + + rtc::IceServer turnServer("amass.fun", 5349, "amass", "88888888"); + m_d->configuration.iceServers.emplace_back(turnServer); + + m_d->configuration.disableAutoNegotiation = true; +} diff --git a/Main/WebRTC/Streamer.h b/Main/WebRTC/Streamer.h new file mode 100644 index 0000000..d91ca75 --- /dev/null +++ b/Main/WebRTC/Streamer.h @@ -0,0 +1,25 @@ +#ifndef __WEBRTCSTREAMER_H__ +#define __WEBRTCSTREAMER_H__ + +#include <memory> +#include <string> + +namespace boost { +namespace asio { +class io_context; +} +} // namespace boost +class WebRTCStreamerPrivate; + +class Streamer { +public: + Streamer(boost::asio::io_context &ioContext); + ~Streamer(); + void start(const std::string &signalServerAddress, uint16_t signalServerPort); + void push(const uint8_t *data, uint32_t size); + +private: + WebRTCStreamerPrivate *m_d = nullptr; +}; + +#endif // __WEBRTCSTREAMER_H__ \ No newline at end of file diff --git a/Main/main.cpp b/Main/main.cpp index 2c79506..9b659c3 100644 --- a/Main/main.cpp +++ b/Main/main.cpp @@ -1,9 +1,11 @@ +#include "Camera.h" #include "Core/DateTime.h" #include "Core/IoContext.h" #include "Core/Logger.h" #include "Core/Singleton.h" #include "RtspServer.h" #include "VideoInput.h" +#include "WebRTC/Streamer.h" #include "rw_mpp_api.h" #include <boost/asio/signal_set.hpp> #include <fstream> @@ -17,21 +19,16 @@ int main(int argc, char const *argv[]) { return -1; } try { + auto camera = Singleton<Camera>::construct(); auto ioContext = Singleton<IoContext>::construct(std::thread::hardware_concurrency()); auto rtsp = std::make_shared<RtspServer>(); - - std::shared_ptr<std::ofstream> ofs; - std::ostringstream oss; - oss << "/data/sdcard/video/record_" << DateTime::currentDateTime().toString("%Y%m%d%H%M%S") << ".h264"; - auto path = oss.str(); - LOG(info) << "write h264 to " << path; - ofs = std::make_shared<std::ofstream>(path, std::ofstream::binary); + auto streamer = std::make_shared<Streamer>(*ioContext->ioContext()); + streamer->start("amass.fun", 443); auto video = std::make_shared<VideoInput>(2592, 1536); video->setPacketHandler([&](const uint8_t *data, uint32_t size) { - // ofs->write(reinterpret_cast<const char *>(data), size); - // pusher->push(data, size); rtsp->push(data, size); + streamer->push(data, size); }); video->start(); // video->startFileInput("/data/sdcard/HM1.264", 1280, 720); diff --git a/resources/build.sh b/resources/build.sh index b505dbb..f27521b 100755 --- a/resources/build.sh +++ b/resources/build.sh @@ -70,8 +70,9 @@ function init() { echo "put /opt/aarch64-v01c01-linux-gnu-gcc/lib/ZLMediaKit/lib/libmk_api.so /data/sdcard/PassengerStatistics/lib" | sftp danki echo "put /opt/aarch64-v01c01-linux-gnu-gcc/lib/opencv-4.11.0/lib/libopencv_world.so.4.11.0 /data/sdcard/PassengerStatistics/lib" | sftp danki echo "put /opt/aarch64-v01c01-linux-gnu-gcc/lib/usrsctp-0.9.5.0/lib/libusrsctp.so.2.0.0 /data/sdcard/PassengerStatistics/lib" | sftp danki + echo "put /opt/aarch64-v01c01-linux-gnu-gcc/lib/libdatachannel-0.22.5/lib/libdatachannel.so.0.22.5 /data/sdcard/PassengerStatistics/lib" | sftp danki # echo "put /opt/aarch64-v01c01-linux-gnu-gcc/lib/LeakTracer/libleaktracer.so /data/sdcard/PassengerStatistics/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} - # echo "put ${BOOST_LIBDIR}/libboost_regex.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} + echo "put ${BOOST_LIBDIR}/libboost_container.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki echo "put ${BOOST_LIBDIR}/libboost_log_setup.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki echo "put ${BOOST_LIBDIR}/libboost_log.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki echo "put ${BOOST_LIBDIR}/libboost_atomic.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki @@ -80,7 +81,7 @@ function init() { echo "put ${BOOST_LIBDIR}/libboost_filesystem.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki echo "put ${BOOST_LIBDIR}/libboost_thread.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki # echo "put ${BOOST_LIBDIR}/libboost_url.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} - # echo "put ${BOOST_LIBDIR}/libboost_json.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} + echo "put ${BOOST_LIBDIR}/libboost_json.so.1.87.0 /data/sdcard/PassengerStatistics/lib" | sftp danki # echo "put ${BOOST_LIBDIR}/libboost_program_options.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} # ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "echo 'mount -o remount rw /' >> /etc/profile"