diff --git a/.vscode/launch.json b/.vscode/launch.json index ea535e5..b044159 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,11 +5,11 @@ "name": "AppDebug", "type": "cppdbg", "request": "launch", - "miDebuggerPath": "/opt/aarch64-v01c01-linux-gnu-gcc/lib/gdb-14.1/bin/aarch64-linux-gnu-gdb", - "miDebuggerServerAddress": "192.168.8.149:8080", - "program": "${workspaceFolder}/build/Tools/SensorCli/SensorCli", + "miDebuggerPath": "/opt/aarch64-v01c01-linux-gnu-gcc/lib/gdb-10.2/bin/aarch64-linux-gnu-gdb", + "miDebuggerServerAddress": "192.168.8.115:8080", + "program": "${workspaceFolder}/build/Main/PassengerStatistics", "args": [], - "stopAtEntry": true, + "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], "externalConsole": false, @@ -20,7 +20,7 @@ "useExtendedRemote": true, "setupCommands": [ { - "text": "set remote exec-file /data/sdcard/SensorCli", + "text": "set remote exec-file /data/sdcard/PassengerStatistics/PassengerStatistics", "description": "设置嵌入式单板加载的程序", "ignoreFailures": false } diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt index d40dffb..1cafb17 100644 --- a/Main/CMakeLists.txt +++ b/Main/CMakeLists.txt @@ -1,5 +1,17 @@ -add_executable(PassengerStatistics main.cpp) +add_executable(PassengerStatistics main.cpp + Live555RtspPusher.h Live555RtspPusher.cpp + VideoInput.h VideoInput.cpp +) + +target_include_directories(PassengerStatistics + PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/rw_mpp/include +) + +target_link_directories(PassengerStatistics + PRIVATE ${CMAKE_SOURCE_DIR}/3rdparty/rw_mpp/lib +) target_link_libraries(PassengerStatistics PRIVATE Universal + PRIVATE rw_mpp ) \ No newline at end of file diff --git a/Main/Live555RtspPusher.cpp b/Main/Live555RtspPusher.cpp new file mode 100644 index 0000000..0ba1061 --- /dev/null +++ b/Main/Live555RtspPusher.cpp @@ -0,0 +1,12 @@ +#include "Live555RtspPusher.h" + +Live555RtspPusher::Live555RtspPusher(boost::asio::io_context &ioContext) : m_ioContext(ioContext), m_socket(ioContext) { + using boost::asio::ip::udp; + m_socket.open(udp::v4()); +} + +void Live555RtspPusher::push(const uint8_t *data, uint32_t size) { + using boost::asio::ip::udp; + udp::endpoint point(udp::v4(), 6666); + m_socket.send_to(boost::asio::const_buffer(data, size), point); +} diff --git a/Main/Live555RtspPusher.h b/Main/Live555RtspPusher.h new file mode 100644 index 0000000..9d80f92 --- /dev/null +++ b/Main/Live555RtspPusher.h @@ -0,0 +1,16 @@ +#ifndef __LIVE555RTSPPUSHER_H__ +#define __LIVE555RTSPPUSHER_H__ + +#include +#include + +class Live555RtspPusher { +public: + Live555RtspPusher(boost::asio::io_context &ioContext); + void push(const uint8_t *data, uint32_t size); + +private: + boost::asio::io_context &m_ioContext; + boost::asio::ip::udp::socket m_socket; +}; +#endif // __LIVE555RTSPPUSHER_H__ \ No newline at end of file diff --git a/Main/VideoInput.cpp b/Main/VideoInput.cpp new file mode 100644 index 0000000..bcc6edc --- /dev/null +++ b/Main/VideoInput.cpp @@ -0,0 +1,128 @@ +#include "VideoInput.h" +#include "BoostLog.h" +#include "rw_mpp_api.h" +#include + +VideoInput::VideoInput(int32_t width, int32_t height) : m_width(width), m_height(height) { +} + +VideoInput::~VideoInput() { + if (isStarted()) { + stop(); + } +} + +bool VideoInput::start() { + if (!m_exit) { + LOG(warning) << "camera already started..."; + return false; + } + S_video_cfg vcfg; + vcfg.sns_w = -1; + vcfg.sns_h = -1; + vcfg.img_w = m_width; + vcfg.img_h = m_height; + m_vid = 0; + m_exit = false; + int status = rw_mpp__video_start(m_vid, &vcfg); + if (status != 0) { + LOG(error) << "rw_mpp__video_start() failed, status: " << status; + m_vid = -1; + } else { + m_thread = std::thread(&VideoInput::run, this); + } + return status == 0; +} + +void VideoInput::stop() { + m_exit = true; + if (m_encodeThread.joinable()) { + m_encodeThread.join(); + } + if (m_thread.joinable()) { + m_thread.join(); + } + rw_mpp__video_stop(m_vid); + rw_mpp__venc_stop(m_encodeChannel); + m_vid = -1; + m_encodeChannel = -1; +} + +bool VideoInput::isStarted() const { + return !m_exit; +} + +bool VideoInput::startEncode() { + m_encodeChannel = 0; + S_venc_config config; + memset(&config, 0, sizeof(S_venc_config)); + config.codec = CODEC_H264; + config.profile = H264_main; + config.raw_max_width = 2960; + config.raw_max_height = 1664; + config.width = 1280; + config.height = 720; + config.gop = 15; + config.framerate = 15; + config.rc_type = RC_VBR; + + S_venc_rc_vbr vbr; + memset(&vbr, 0, sizeof(S_venc_rc_vbr)); + vbr.max_bitrate = 1024; + vbr.stats_time = 1; + config.rc_param = &vbr; + int status = rw_mpp__venc_start(m_encodeChannel, &config, -1); + if (status != 0) { + LOG(error) << "rw_mpp__venc_start() failed, status: " << status; + } else { + m_encodeThread = std::thread(&VideoInput::encodeRun, this); + } + return status == 0; +} + +void VideoInput::setPacketHandler(const PacketHandler &hanlder) { + m_handler = hanlder; +} + +void VideoInput::run() { + using namespace std::chrono_literals; + S_mpp_img img; + while (!m_exit) { + int status = rw_mpp__video_recv(m_vid, &img, 2000); + if (status != 0) { + LOG(error) << "rw_mpp__video_recv() failed, status: " << status; + std::this_thread::sleep_for(500ms); + continue; + } + // LOG(info) << "camera frame size: " << img.width << "x" << img.height; + if (m_encodeChannel >= 0) { + rw_mpp__venc_send(m_encodeChannel, &img); + } + + status = rw_mpp__video_free(m_vid, &img); + if (status != 0) { + LOG(error) << "rw_mpp__video_free() failed, status: " << status; + } + } +} + +void VideoInput::encodeRun() { + using namespace std::chrono_literals; + S_mpp_venc_frame frame; + while (!m_exit) { + int status = rw_mpp__venc_recv(m_encodeChannel, &frame, 1000); + if (status != 0) { + std::this_thread::sleep_for(500ms); + LOG(error) << "rw_mpp__venc_recv() failed, status: " << status; + continue; + } + LOG(info) << "encode frame data size: " << frame.len << ", is key frame: " << frame.is_key_frame; + if (m_handler) { + m_handler(frame.data, frame.len); + } + status = rw_mpp__venc_free(m_encodeChannel, &frame); + if (status != 0) { + LOG(error) << "rw_mpp__venc_free() failed, status: " << status; + } + } +} diff --git a/Main/VideoInput.h b/Main/VideoInput.h new file mode 100644 index 0000000..61f3e76 --- /dev/null +++ b/Main/VideoInput.h @@ -0,0 +1,34 @@ +#ifndef __VIDEOINPUT_H__ +#define __VIDEOINPUT_H__ + +#include +#include +#include + +class VideoInput { +public: + using PacketHandler = std::function; + VideoInput(int32_t width, int32_t height); + ~VideoInput(); + bool start(); + void stop(); + bool isStarted() const; + bool startEncode(); + void setPacketHandler(const PacketHandler &hanlder); + +protected: + void run(); + void encodeRun(); + +private: + int32_t m_width; + int32_t m_height; + + int32_t m_vid = -1; + int32_t m_encodeChannel = -1; + bool m_exit = true; + std::thread m_thread; + std::thread m_encodeThread; + PacketHandler m_handler; +}; +#endif // __VIDEOINPUT_H__ \ No newline at end of file diff --git a/Main/main.cpp b/Main/main.cpp index 8b81a14..f93e53e 100644 --- a/Main/main.cpp +++ b/Main/main.cpp @@ -1,7 +1,52 @@ #include "BoostLog.h" +#include "DateTime.h" +#include "IoContext.h" +#include "Live555RtspPusher.h" +#include "VideoInput.h" +#include "rw_mpp_api.h" +#include +#include -int main(int argc, char const *argv[]) -{ - LOG(info)<<"app start..."; +int main(int argc, char const *argv[]) { + LOG(info) << "app start..."; + int status = rw_mpp__init(); + if (status != 0) { + LOG(error) << "rw_mpp__init() failed, status: " << status; + return -1; + } + try { + auto ioContext = Amass::Singleton::instance(); + auto pusher = std::make_shared(*ioContext->ioContext()); + + std::shared_ptr 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(path, std::ofstream::binary); + + auto video = std::make_shared(2592, 1536); + video->setPacketHandler([&](const uint8_t *data, uint32_t size) { + ofs->write(reinterpret_cast(data), size); + pusher->push(data, size); + }); + video->start(); + video->startEncode(); + boost::asio::signal_set signals(*ioContext->ioContext(), SIGINT); + signals.async_wait([&](boost::system::error_code const &, int signal) { + LOG(info) << "capture " << (signal == SIGINT ? "SIGINT" : "SIGTERM") << ",stop!"; + video.reset(); + rw_mpp__finalize(); + ioContext->ioContext()->stop(); + }); + + ioContext->run(); + } catch (const boost::exception &e) { + LOG(error) << "error"; + } catch (const std::exception &e) { + LOG(error) << e.what(); + } + + rw_mpp__finalize(); return 0; } diff --git a/resources/build.sh b/resources/build.sh index f4e00ad..a57f992 100755 --- a/resources/build.sh +++ b/resources/build.sh @@ -29,6 +29,9 @@ function cmake_scan() { } function build() { + if [ -n "$1" ]; then + TARGET_IP=$1 + fi if [ ! -f "${build_path}/CMakeCache.txt" ]; then cmake_scan fi @@ -58,17 +61,17 @@ function init() { echo "put ${base_path}/resources/authorized_keys /system/.ssh" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} echo "put /opt/aarch64-v01c01-linux-gnu-gcc/lib/gdb-10.2/bin/gdbserver /system/bin" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} # echo "put ${BOOST_LIBDIR}/libboost_date_time* /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} - # echo "put ${BOOST_LIBDIR}/libboost_regex* /system/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_log_setup.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} # echo "put ${BOOST_LIBDIR}/libboost_log.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} # echo "put ${BOOST_LIBDIR}/libboost_chrono.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} # echo "put ${BOOST_LIBDIR}/libboost_filesystem.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} - # echo "put ${BOOST_LIBDIR}/libboost_atomic.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.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} - # echo "put ${BOOST_LIBDIR}/libboost_thread.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} - # 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_program_options.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} + echo "put ${BOOST_LIBDIR}/libboost_atomic.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.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} + echo "put ${BOOST_LIBDIR}/libboost_thread.so.1.84.0 /system/lib" | sftp -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} + 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_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" ssh -i resources/ssh_host_rsa_key_ok root@${TARGET_IP} "echo 'mount -o remount rw /system/' >> /etc/profile" @@ -98,7 +101,7 @@ function main() { shift 1 case $cmd in build) - build + build $@ ;; scan) cmake_scan