From e527dbd5746df9cb945d3dccf8ae5fecdd056984 Mon Sep 17 00:00:00 2001 From: lxowalle Date: Wed, 29 May 2024 19:30:45 +0800 Subject: [PATCH] * update rtmp & rtmp demo --- components/basic/include/maix_sys.hpp | 6 + .../basic/port/linux/maix_sys_linux.cpp | 22 ++ .../basic/port/maixcam/maix_sys_maixcam.cpp | 22 ++ components/maixcam_lib/CMakeLists.txt | 4 + components/maixcam_lib/include/maix_avc2flv.h | 23 ++ components/vision/include/maix_rtmp.hpp | 101 +++++- .../vision/port/maixcam/maix_camera_mmf.hpp | 1 + .../vision/port/maixcam/maix_rtmp_maixcam.cpp | 302 +++++++++++++++++- .../vision/src/maix_image_find_qrcodes.cpp | 7 +- examples/rtmp_demo/main/src/main.cpp | 41 ++- examples/rtsp_yolo_demo/main/include/main.h | 2 + 11 files changed, 512 insertions(+), 19 deletions(-) create mode 100644 components/basic/port/linux/maix_sys_linux.cpp create mode 100644 components/basic/port/maixcam/maix_sys_maixcam.cpp create mode 100644 components/maixcam_lib/include/maix_avc2flv.h create mode 100644 examples/rtsp_yolo_demo/main/include/main.h diff --git a/components/basic/include/maix_sys.hpp b/components/basic/include/maix_sys.hpp index f9aee4b7..d72bbc5b 100644 --- a/components/basic/include/maix_sys.hpp +++ b/components/basic/include/maix_sys.hpp @@ -129,6 +129,12 @@ namespace maix::sys */ std::vector> disk_partitions(bool only_disk = true); + /** + * register default signal handle + * @maixpy maix.sys.register_default_signal_handle + */ + void register_default_signal_handle(); + /** * Power off device * @maixpy maix.sys.poweroff diff --git a/components/basic/port/linux/maix_sys_linux.cpp b/components/basic/port/linux/maix_sys_linux.cpp new file mode 100644 index 00000000..8aeceb29 --- /dev/null +++ b/components/basic/port/linux/maix_sys_linux.cpp @@ -0,0 +1,22 @@ +#include "maix_util.hpp" +#include "maix_app.hpp" +#include "signal.h" + +namespace maix::sys +{ + static void signal_handle(int signal) + { + const char *signal_msg = NULL; + switch (signal) { + case SIGINT: + maix::app::set_exit_flag(true); + raise(SIGINT); + break; + default: signal_msg = "UNKNOWN"; break; + } + } + + void register_default_signal_handle() { + signal(SIGILL, signal_handle); + } +} diff --git a/components/basic/port/maixcam/maix_sys_maixcam.cpp b/components/basic/port/maixcam/maix_sys_maixcam.cpp new file mode 100644 index 00000000..0a09a52d --- /dev/null +++ b/components/basic/port/maixcam/maix_sys_maixcam.cpp @@ -0,0 +1,22 @@ +#include "maix_sys.hpp" +#include "maix_app.hpp" +#include "signal.h" + +namespace maix::sys +{ + static void signal_handle(int signal) + { + const char *signal_msg = NULL; + switch (signal) { + case SIGINT: + maix::app::set_exit_flag(true); + raise(SIGINT); + break; + default: signal_msg = "UNKNOWN"; break; + } + } + + void register_default_signal_handle() { + signal(SIGILL, signal_handle); + } +} \ No newline at end of file diff --git a/components/maixcam_lib/CMakeLists.txt b/components/maixcam_lib/CMakeLists.txt index 748d454c..49228f35 100644 --- a/components/maixcam_lib/CMakeLists.txt +++ b/components/maixcam_lib/CMakeLists.txt @@ -17,7 +17,11 @@ if(CONFIG_MAIXCAM_LIB_COMPILE_FROM_SOURCE) append_srcs_dir(ADD_SRCS "${source_dir}/nn") list(APPEND ADD_REQUIREMENTS cvi_tpu) + # media_server + append_srcs_dir(ADD_SRCS "${source_dir}/media_server") + list(APPEND ADD_REQUIREMENTS media_server) + # middleware set(middleware_include_dir . ${middleware_src_path}/v2/component/panel/${CONFIG_SOPHGO_MIDDLEWARE_CHIP} ${middleware_src_path}/v2/include diff --git a/components/maixcam_lib/include/maix_avc2flv.h b/components/maixcam_lib/include/maix_avc2flv.h new file mode 100644 index 00000000..a38d03f2 --- /dev/null +++ b/components/maixcam_lib/include/maix_avc2flv.h @@ -0,0 +1,23 @@ +#ifndef __MAIX_AVC2FLV +#define __MAIX_AVC2FLV + +#ifdef __cplusplus +extern "C" { +#endif +#include "stdint.h" + +int maix_avc2flv_init(int max_buff_size); +int maix_avc2flv_deinit(); +int maix_avc2flv_prepare(uint8_t *data, int data_size); +int maix_avc2flv_iterate(void **nalu, int *size); +int maix_avc2flv(void *nalu, int nalu_size, uint32_t pts, uint32_t dts, uint8_t **flv, int *flv_size); + +// need free data after used +int maix_flv_get_tail(uint8_t **data, int *size); +// need free data after used +int maix_flv_get_header(int audio, int video, uint8_t **data, int *size); +#ifdef __cplusplus +} +#endif + +#endif // __MAIX_AVC2FLV \ No newline at end of file diff --git a/components/vision/include/maix_rtmp.hpp b/components/vision/include/maix_rtmp.hpp index f9e22cb8..884b30f0 100644 --- a/components/vision/include/maix_rtmp.hpp +++ b/components/vision/include/maix_rtmp.hpp @@ -10,12 +10,29 @@ #include "maix_basic.hpp" #include "maix_camera.hpp" +#include "maix_video.hpp" #include #include #include +/** + * @brief maix.rtmp module + * @maixpy maix.rtmp +*/ namespace maix::rtmp { + /** + * Video type + * @maixpy maix.rtmp.TagType + */ + enum TagType + { + TAG_NONE, + TAG_VIDEO, + TAG_AUDIO, + TAG_SCRIPT, + }; + /** * Rtmp class * @maixpy maix.rtmp.Rtmp @@ -25,15 +42,20 @@ namespace maix::rtmp std::string _app; std::string _stream; int _port; + int _bitrate; int _socket; void *_handler; - camera::Camera *_cam; + camera::Camera *_camera; thread::Thread *_thread; + thread::Thread *_push_thread; + thread::Thread *_app_thread; pthread_mutex_t _lock; bool _start; std::string _path; + image::Image *_capture_image; + bool _need_capture; public: /** * @brief Construct a new Video object @@ -45,29 +67,39 @@ namespace maix::rtmp * @param port rtmp port, default is 1935. * @param app rtmp app name * @param stream rtmp stream name + * @param bitrate rtmp bitrate, default is 1000 * 1000 * @maixpy maix.rtmp.Rtmp.__init__ * @maixcdk maix.rtmp.Rtmp.Rtmp */ - Rtmp(std::string host, int port = 1935, std::string app = std::string(), std::string stream = std::string()); + Rtmp(std::string host = "localhost", int port = 1935, std::string app = std::string(), std::string stream = std::string(), int bitrate = 1000 * 1000); ~Rtmp(); + /** + * @brief Get bitrate + * @return bitrate + * @maixpy maix.rtmp.Rtmp.push_video + */ + int bitrate() { + return _bitrate; + } + /** * @brief Push rtmp video data - * @return error code, err::ERR_NONE means success, others means failed + * @return return 0 ok, other error * @maixcdk maix.rtmp.Rtmp.push_video */ int push_video(void *data, size_t data_size, uint32_t timestamp); /** * @brief Push rtmp audio data - * @return error code, err::ERR_NONE means success, others means failed + * @return return 0 ok, other error * @maixcdk maix.rtmp.Rtmp.push_audio */ int push_audio(void *data, size_t data_size, uint32_t timestamp); /** * @brief Push rtmp script data - * @return error code, err::ERR_NONE means success, others means failed + * @return return 0 ok, other error * @maixcdk maix.rtmp.Rtmp.push_script */ int push_script(void *data, size_t data_size, uint32_t timestamp); @@ -80,10 +112,56 @@ namespace maix::rtmp * @maixpy maix.rtmp.Rtmp.bind_camera */ err::Err bind_camera(camera::Camera *cam) { - _cam = cam; + _camera = cam; return err::ERR_NONE; } + /** + * @brief If you bind a camera, return the camera object. + * @return Camera object + * @maixpy maix.rtmp.Rtmp.get_camera + */ + camera::Camera *get_camera() { + return _camera; + } + + /** + * @brief If you bind a camera, capture the image of the camera + * @note The return value may be null, you must check whether the return value is null + * @return Image object + * @maixcdk maix.rtmp.Rtmp.capture + */ + image::Image *capture() { + err::check_raise(err::ERR_NOT_IMPL, "not support now!"); + // lock(300); + // if (!_capture_image || !_capture_image->data()) { + // unlock(); + // return NULL; + // } + // image::Image *new_image = new image::Image(_capture_image->width(), _capture_image->height(), _capture_image->format(), + // (uint8_t *)_capture_image->data(), _capture_image->data_size(), false); + // unlock(); + // return new_image; + return NULL; + } + + /** + * @brief Get pointer of capture image + * @return Image object + */ + image::Image *get_capture_image() { + return _capture_image; + } + + /** + * @brief Set pointer of capture image + * @return Image object + */ + image::Image *set_capture_image(void *img) { + _capture_image = (image::Image *)img; + return _capture_image; + } + /** * @brief Start push stream * @note only support flv file now @@ -94,7 +172,7 @@ namespace maix::rtmp err::Err start(std::string path = std::string()); /** - * @brief Stop push + * @brief Stop push stream * @return error code, err::ERR_NONE means success, others means failed * @maixpy maix.rtmp.Rtmp.stop */ @@ -132,6 +210,15 @@ namespace maix::rtmp bool is_started() { return _start ? true : false; } + + /** + * @brief get handler + * @note DO NOT ADD TO MAIXPY + * @return rtmp handler + */ + void *get_handler() { + return _handler; + } }; } diff --git a/components/vision/port/maixcam/maix_camera_mmf.hpp b/components/vision/port/maixcam/maix_camera_mmf.hpp index 8d8bbfbc..3e34c40e 100644 --- a/components/vision/port/maixcam/maix_camera_mmf.hpp +++ b/components/vision/port/maixcam/maix_camera_mmf.hpp @@ -69,6 +69,7 @@ namespace maix::camera this->width = width; this->height = height; this->buffer_num = buff_num; + this->ch = -1; if (0 != mmf_init()) { err::check_raise(err::ERR_RUNTIME, "mmf init failed"); diff --git a/components/vision/port/maixcam/maix_rtmp_maixcam.cpp b/components/vision/port/maixcam/maix_rtmp_maixcam.cpp index 77e4a377..f48cd639 100644 --- a/components/vision/port/maixcam/maix_rtmp_maixcam.cpp +++ b/components/vision/port/maixcam/maix_rtmp_maixcam.cpp @@ -1,17 +1,27 @@ #include "maix_rtmp.hpp" +#include "maix_video.hpp" +#include "sophgo_middleware.hpp" #include #include "sockutil.h" #include "sys/system.h" #include "rtmp-client.h" #include "flv-reader.h" +#include "flv-writer.h" +#include "flv-header.h" #include "flv-proto.h" +#include "maix_avc2flv.h" #include #include #include #include #include +#define MMF_VENC_CHN 1 +#define AAC_ADTS_HEADER_SIZE 7 +#define FLV_TAG_HEAD_LEN 11 +#define FLV_PRE_TAG_LEN 4 + static int rtmp_client_send(void* param, const void* header, size_t len, const void* data, size_t bytes) { socket_t* socket = (socket_t*)param; @@ -22,12 +32,74 @@ static int rtmp_client_send(void* param, const void* header, size_t len, const v return socket_send_v_all_by_time(*socket, vec, bytes > 0 ? 2 : 1, 0, 5000); } +static uint8_t* h264_startcode(uint8_t* data, size_t bytes) +{ + size_t i; + for (i = 2; i + 1 < bytes; i++) + { + if (0x01 == data[i] && 0x00 == data[i - 1] && 0x00 == data[i - 2]) + return data + i + 1; + } + + return NULL; +} + +int maix_rtmp_client_push_h264(rtmp_client_t *client, struct flv_vec_t *vec, int num, uint32_t timestamp) +{ + int tmp_offset = 0; + int tmp_max_size = 256 * 1024; + uint8_t *tmp = (uint8_t *)malloc(tmp_max_size); + if (!tmp) return -1; + + for (int i = 0; i < num; i ++) { + uint8_t *nalu = h264_startcode((uint8_t *)vec[i].ptr, vec[i].len); + uint8_t *raw = (uint8_t *)vec[i].ptr; + int raw_size = (uint8_t *)vec[i].ptr + vec[i].len - raw; + uint8_t nalutype = nalu[0] & 0x1f; + if (nalutype == 0x7 || nalutype == 0x8 || nalutype == 6) { + if (tmp_offset + raw_size > tmp_max_size) return -1; + memcpy(tmp + tmp_offset, raw, raw_size); + tmp_offset += raw_size; + + } else if (nalutype == 0x5 || nalutype == 0x1) { + if (tmp_offset + raw_size > tmp_max_size) return -1; + memcpy(tmp + tmp_offset, raw, raw_size); + tmp_offset += raw_size; + + uint8_t *flv; + int flv_size; + if (0 != maix_avc2flv(tmp, tmp_offset, timestamp, timestamp, &flv, &flv_size)) { + printf("maix_avc2flv failed!\r\n"); + return -1; + } + // printf("tmp_offset:%d flv:%p flv_size:%d timestamp:%d\r\n", tmp_offset, flv, flv_size, timestamp); + free(tmp); + uint8_t *flv_next = flv; + while (1) { + if (flv_next >= (flv + flv_size)) break; + struct flv_tag_header_t tag_header = {0}; + flv_tag_header_read(&tag_header, flv_next, FLV_TAG_HEAD_LEN); + + // printf("flv:%p flv_next:%p tag len:%d timestamp:%d\r\n", flv, flv_next, tag_header.size, timestamp); + + rtmp_client_push_video(client, flv + FLV_TAG_HEAD_LEN, tag_header.size, timestamp); + flv_next += FLV_PRE_TAG_LEN + FLV_TAG_HEAD_LEN + tag_header.size; + } + + tmp_offset = 0; + } + } + + return 0; +} + namespace maix::rtmp { - Rtmp::Rtmp(std::string host, int port, std::string app, std::string stream) { + Rtmp::Rtmp(std::string host, int port, std::string app, std::string stream, int bitrate) { _host = host; _port = port; _app = app; _stream = stream; + _bitrate = bitrate; char packet[20 * 1024]; snprintf(packet, sizeof(packet), "rtmp://%s/%s", host.c_str(), app.c_str()); // tcurl @@ -49,7 +121,7 @@ namespace maix::rtmp { { int res = rtmp_client_input(rtmp, packet, r); if (res != 0) { - throw std::runtime_error("rtmp_client_input failed!"); + throw std::runtime_error("rtmp client init failed!"); } } @@ -57,18 +129,33 @@ namespace maix::rtmp { throw std::runtime_error("create lock failed!"); } + if (0 != mmf_init()) { + err::check_raise(err::ERR_RUNTIME, "init mmf failed!"); + } + _handler = rtmp; _thread = nullptr; - _cam = nullptr; + _push_thread = nullptr; + _app_thread = nullptr; + _camera = nullptr; + _start = false; + _capture_image = nullptr; + _need_capture = false; _path = std::string(); } Rtmp::~Rtmp() { + if (_start) { + stop(); + } + rtmp_client_t* rtmp = (rtmp_client_t *)_handler; rtmp_client_destroy(rtmp); socket_close(_socket); socket_cleanup(); + mmf_deinit(); + pthread_mutex_destroy(&_lock); } @@ -196,6 +283,185 @@ namespace maix::rtmp { } } + static void _push_camera_thread(void *args) { + Rtmp *rtmp = (Rtmp *)args; + rtmp->lock(-1); + camera::Camera *camera = rtmp->get_camera(); + rtmp_client_t *client = (rtmp_client_t *)rtmp->get_handler(); + rtmp->unlock(); + + uint32_t stream_size = 0; + uint32_t timestamp = 0; + uint64_t curr_ms = time::time_ms(); + uint64_t last_ms = curr_ms; + + bool first_frame = 1; + + mmf_venc_cfg_t cfg = { + .type = 2, //1, h265, 2, h264 + .w = camera->width(), + .h = camera->height(), + .fmt = mmf_invert_format_to_mmf(image::Format::FMT_YVU420SP), + .jpg_quality = 0, // unused + .gop = 50, + .intput_fps = 30, + .output_fps = 30, + .bitrate = rtmp->bitrate() / 1000, + }; + + + if (0 != mmf_add_venc_channel(MMF_VENC_CHN, &cfg)) { + err::check_raise(err::ERR_RUNTIME, "mmf venc init failed!"); + } + + if (0 != maix_avc2flv_init(rtmp->bitrate() / 2)) { + err::check_raise(err::ERR_RUNTIME, "maix avc2flv init failed!"); + } + while (1) { + rtmp->lock(-1); + if (!rtmp->is_started()) { + rtmp->unlock(); + break; + } + + int vi_ch = camera->get_channel(); + void *data; + int data_size, width, height, format; + do { + mmf_h265_stream_t stream = {0}; + if (mmf_venc_pop(MMF_VENC_CHN, &stream)) { + log::error("mmf_venc_pop failed\n"); + mmf_venc_free(MMF_VENC_CHN); + mmf_del_venc_channel(MMF_VENC_CHN); + rtmp->unlock(); + break; + } + + flv_vec_t vec[8]; + for (int i = 0; i < stream.count; i ++) { + vec[i].ptr = stream.data[i]; + vec[i].len = stream.data_size[i]; + stream_size += stream.data_size[i]; + } + + if (first_frame) { + last_ms = time::time_ms(); + timestamp = 0; + first_frame = false; + } + + if (0 != maix_rtmp_client_push_h264(client, vec, stream.count, timestamp)) { + printf("rtmp push failed!\r\n"); + } + + if (mmf_venc_free(MMF_VENC_CHN)) { + printf("mmf_venc_free failed\n"); + mmf_del_venc_channel(MMF_VENC_CHN); + rtmp->unlock(); + break; + } + + if (mmf_vi_frame_pop(vi_ch, &data, &data_size, &width, &height, &format)) { + log::info("camera read image timeout!\r\n"); + rtmp->unlock(); + break; + } + + curr_ms = time::time_ms(); + timestamp = curr_ms - last_ms; + + if (data_size > 2560 * 1440 * 3 / 2) { + log::error("image is too large!\r\n"); + rtmp->unlock(); + break; + } + + if (rtmp->get_camera()) { + image::Image *_capture_image = rtmp->get_capture_image(); + if (_capture_image && _capture_image->data()) { + delete _capture_image; + rtmp->set_capture_image(NULL); + } + + image::Format capture_format = (image::Format)mmf_invert_format_to_maix(format); + bool need_align = (width % mmf_vi_aligned_width(vi_ch) == 0) ? false : true; // Width need align only + switch (capture_format) { + case image::Format::FMT_BGR888: // fall through + case image::Format::FMT_RGB888: + { + _capture_image = rtmp->set_capture_image(new image::Image(width, height, capture_format)); + uint8_t * image_data = (uint8_t *)_capture_image->data(); + if (need_align) { + for (int h = 0; h < height; h++) { + memcpy((uint8_t *)image_data + h * width * 3, (uint8_t *)data + h * width * 3, width * 3); + } + } else { + memcpy(image_data, data, width * height * 3); + } + } + break; + case image::Format::FMT_YVU420SP: + { + _capture_image = rtmp->set_capture_image(new image::Image(width, height, capture_format)); + uint8_t * image_data = (uint8_t *)_capture_image->data(); + if (need_align) { + for (int h = 0; h < height * 3 / 2; h ++) { + memcpy((uint8_t *)image_data + h * width, (uint8_t *)data + h * width, width); + } + } else { + memcpy(image_data, data, width * height * 3 / 2); + } + break; + } + default: + { + rtmp->set_capture_image(NULL); + break; + } + } + } + + mmf_venc_cfg_t cfg = {0}; + if (0 != mmf_venc_get_cfg(MMF_VENC_CHN, &cfg)) { + err::check_raise(err::ERR_RUNTIME, "get venc config failed!\r\n"); + } + + int img_w = width; + int img_h = height; + int mmf_fmt = format; + if (img_w != cfg.w + || img_h != cfg.h + || mmf_fmt != cfg.fmt) { + log::warn("image size or format is incorrect, try to reinit venc!\r\n"); + mmf_del_venc_channel(MMF_VENC_CHN); + cfg.w = img_w; + cfg.h = img_h; + cfg.fmt = mmf_invert_format_to_mmf(mmf_fmt); + if (0 != mmf_add_venc_channel(MMF_VENC_CHN, &cfg)) { + err::check_raise(err::ERR_RUNTIME, "mmf venc init failed!\r\n"); + } + } + + if (mmf_venc_push(MMF_VENC_CHN, (uint8_t *)data, width, height, format)) { + mmf_del_venc_channel(MMF_VENC_CHN); + rtmp->unlock(); + err::check_raise(err::ERR_RUNTIME, "mmf venc push failed!\r\n"); + break; + } + + mmf_vi_frame_free(vi_ch); + } while (stream_size == 0 && rtmp->is_started()); + rtmp->unlock(); + } + + maix_avc2flv_deinit(); + + if (0 != mmf_del_venc_channel(MMF_VENC_CHN)) { + err::check_raise(err::ERR_RUNTIME, "mmf venc init failed!"); + } + + } + err::Err Rtmp::lock(uint32_t time) { uint32_t count = 0; while (0 != pthread_mutex_trylock(&_lock)) { @@ -222,12 +488,14 @@ namespace maix::rtmp { err::Err Rtmp::start(std::string path) { lock(-1); if (_start == true) { + unlock(); return err::ERR_BUSY; } if (path.size() > 0) { if (fs::splitext(path) != ".flv") { log::error("check file path failed!\r\n"); + unlock(); return err::ERR_RUNTIME; } @@ -236,13 +504,18 @@ namespace maix::rtmp { _thread = new thread::Thread(_push_file_thread, this); if (this->_thread == NULL) { log::error("create camera thread failed!\r\n"); + unlock(); return err::ERR_RUNTIME; } } else { - if (_cam == nullptr) { - log::error("you need bind camera first!\r\n"); + _start = true; + _app_thread = new thread::Thread(_push_camera_thread, this); + if (this->_app_thread == NULL) { + log::error("create camera thread failed!\r\n"); + unlock(); return err::ERR_RUNTIME; } + } unlock(); @@ -250,12 +523,23 @@ namespace maix::rtmp { } err::Err Rtmp::stop() { - lock(-1); _start = false; - unlock(); - _thread->join(); - _thread = nullptr; + if (_thread) { + _thread->join(); + _thread = nullptr; + } + + if (_push_thread) { + _push_thread->join(); + _push_thread = nullptr; + } + + if (_app_thread) { + _app_thread->join(); + _app_thread = nullptr; + } + return err::ERR_NONE; } } \ No newline at end of file diff --git a/components/vision/src/maix_image_find_qrcodes.cpp b/components/vision/src/maix_image_find_qrcodes.cpp index a61ea05a..e472bdb4 100644 --- a/components/vision/src/maix_image_find_qrcodes.cpp +++ b/components/vision/src/maix_image_find_qrcodes.cpp @@ -20,7 +20,12 @@ namespace maix::image if (_format == image::FMT_GRAYSCALE) { convert_to_imlib_image(this, &src_img); } else { - gray_img = this->to_format(image::FMT_GRAYSCALE); + if (image::FMT_YVU420SP == _format) { + gray_img = new image::Image(_width, _height, image::FMT_GRAYSCALE); + memcpy(gray_img->data(), _data, _width * _height); + } else { + gray_img = this->to_format(image::FMT_GRAYSCALE); + } convert_to_imlib_image(gray_img, &src_img); } diff --git a/examples/rtmp_demo/main/src/main.cpp b/examples/rtmp_demo/main/src/main.cpp index 669bf917..1bb852f5 100644 --- a/examples/rtmp_demo/main/src/main.cpp +++ b/examples/rtmp_demo/main/src/main.cpp @@ -17,8 +17,9 @@ static int helper(void) printf( "========================\r\n" "Intput param:\r\n" "0 : rtmp client, push file\r\n" - " example: ./test_media_server 0 192.168.0.30 myapp stream ./test.flv" - "1 : rtmp client, push camera image\r\n" + " example: ./rtmp_demo 0 192.168.0.30 myapp stream ./test.flv\r\n" + "1 : rtmp client, push camera image\r\n" + " example: ./rtmp_demo 1 192.168.0.30 1935 live stream 10000000\r\n" "========================\r\n"); fflush(stdin); return 0; @@ -63,6 +64,42 @@ int _main(int argc, char* argv[]) break; } + case 1: + { + if (argc < 6) { + helper(); + break; + } + std::string host = argv[2]; + int port = atoi(argv[3]); + std::string app = argv[4]; + std::string stream = argv[5]; + int bitrate = 1000 * 1000; + if (argc > 6) bitrate = atoi(argv[6]); + printf("push rtmp://%s:%d/%s/%s!\r\n", &host[0], port, &app[0], &stream[0]); + + camera::Camera cam = camera::Camera(1280, 720, image::Format::FMT_YVU420SP); + display::Display disp = display::Display(); + rtmp::Rtmp rtmp = rtmp::Rtmp(host, port, app, stream, bitrate); + + rtmp.bind_camera(&cam); + + log::info("start\r\n"); + rtmp.start(); + while (!app::need_exit()) { + // image::Image *img = rtmp.capture(); // not support now + + // if (img) { + // disp.show(*img); + // delete img; + // } + sleep(1); + } + rtmp.stop(); + log::info("stop\r\n"); + + break; + } default: { helper(); diff --git a/examples/rtsp_yolo_demo/main/include/main.h b/examples/rtsp_yolo_demo/main/include/main.h new file mode 100644 index 00000000..3f59c932 --- /dev/null +++ b/examples/rtsp_yolo_demo/main/include/main.h @@ -0,0 +1,2 @@ +#pragma once +