From ecb0badb0d3b7ba6b4175c174d63d78e118597e0 Mon Sep 17 00:00:00 2001 From: ming Date: Mon, 25 Mar 2024 10:29:34 +0800 Subject: [PATCH] 保存jpg图片 --- .vscode/launch.json | 38 +++++++++++++++++++++++++++++++++++++- build/Makefile | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ nv-codec-headers/Makefile | 2 +- save_tool/Makefile | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ save_tool/check_tool.cpp1 | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ save_tool/check_tool.h1 | 5 +++++ save_tool/main.cpp | 24 ++++++++++++++++++++++++ save_tool/save_tool.cpp | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ save_tool/save_tool.h | 5 +++++ src/FFNvDecoderManager.cpp | 1 + src/FFNvDecoderManager.h | 1 + src/FFSaveImg.cpp | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/FFSaveImg.h | 6 ++++++ src/Makefile | 67 ------------------------------------------------------------------- src/gb28181/FFGB28181Decoder.cpp | 26 +++++++++++++++++--------- src/main.cpp | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 16 files changed, 928 insertions(+), 88 deletions(-) create mode 100644 build/Makefile create mode 100644 save_tool/Makefile create mode 100644 save_tool/check_tool.cpp1 create mode 100644 save_tool/check_tool.h1 create mode 100644 save_tool/main.cpp create mode 100644 save_tool/save_tool.cpp create mode 100644 save_tool/save_tool.h create mode 100644 src/FFSaveImg.cpp create mode 100644 src/FFSaveImg.h delete mode 100644 src/Makefile diff --git a/.vscode/launch.json b/.vscode/launch.json index cc4d00d..517f8c5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/bin/lib/test", - "args": ["rtsp","3", "30012"], + "args": ["rtsp://192.168.10.4:8554/street","3", "30012"], "stopAtEntry": false, "cwd": "${workspaceFolder}/bin/lib", "environment": [], @@ -55,6 +55,42 @@ "ignoreFailures": true } ] + },{ + "name": "save_tool", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/lib/save_tool", + "args": ["rtsp://admin:ad123456@192.168.60.165:554/cam/realmonitor?channel=1&subtype=0"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/lib", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + },{ + "name": "gb28181", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/bin/lib/test", + "args": ["rtsp://192.168.10.4:8554/street","3", "30026","sipId"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/bin/lib", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] } ] } \ No newline at end of file diff --git a/build/Makefile b/build/Makefile new file mode 100644 index 0000000..9f642c4 --- /dev/null +++ b/build/Makefile @@ -0,0 +1,70 @@ +XX = g++ + +CUDA_ROOT = /usr/local/cuda-11.1 +NVCC = $(CUDA_ROOT)/bin/nvcc + + +PROJECT_ROOT= /mnt/data/cmhu/FFNvDecoder + +DEPEND_DIR = $(PROJECT_ROOT)/bin +SRC_ROOT = $(PROJECT_ROOT)/src +THIRDPARTY_ROOT = $(PROJECT_ROOT)/3rdparty + + +TARGET= $(DEPEND_DIR)/lib/test + + +SPDLOG_ROOT = $(THIRDPARTY_ROOT)/spdlog-1.9.2/release +JRTP_ROOT = $(THIRDPARTY_ROOT)/jrtp_export +CURL_ROOT = $(THIRDPARTY_ROOT)/curl/bin + + +INCLUDE= -I $(DEPEND_DIR)/include \ + -I $(CUDA_ROOT)/include \ + -I $(SRC_ROOT)/common/inc \ + -I $(SRC_ROOT)/common/UtilNPP \ + -I $(SRC_ROOT)\ + -I $(SPDLOG_ROOT)/include \ + -I $(SRC_ROOT)/gb28181 \ + -I $(JRTP_ROOT)/jrtplib/include/jrtplib3 \ + -I $(JRTP_ROOT)/jthread/include/jthread \ + -I $(CURL_ROOT)/include \ + +LIBSPATH= -L $(DEPEND_DIR)/lib -lavformat -lavcodec -lswscale -lavutil -lavfilter -lswresample -lavdevice \ + -L $(CUDA_ROOT)/lib64 -lcuda -lcudart -lnvcuvid -lcurand -lcublas -lnvjpeg \ + -L $(SPDLOG_ROOT) -l:libspdlog.a \ + -L $(JRTP_ROOT)/jthread/lib -l:libjthread.a \ + -L $(JRTP_ROOT)/jrtplib/lib -l:libjrtp.a \ + -L $(CURL_ROOT)/lib -l:libcurl.a \ + +CFLAGS= -g -fPIC -O0 $(INCLUDE) -pthread -lrt -lz -std=c++11 -fvisibility=hidden -Wl,-Bsymbolic -ldl + # -DUNICODE -D_UNICODE + +NFLAGS_LIB=-g -c -shared -Xcompiler -fPIC -Xcompiler -fvisibility=hidden +NFLAGS = $(NFLAGS_LIB) $(INCLUDE) -std=c++11 + +SRCS:=$(wildcard $(SRC_ROOT)/*.cpp) \ + $(wildcard $(SRC_ROOT)/gb28181/*.cpp) +OBJS = $(patsubst %.cpp, %.o, $(notdir $(SRCS))) + +CU_SOURCES = $(wildcard ${SRC_ROOT}/*.cu) +CU_OBJS = $(patsubst %.cu, %.o, $(notdir $(CU_SOURCES))) + + +$(TARGET):$(OBJS) $(CU_OBJS) + rm -f $(TARGET) + $(XX) -o $@ $^ $(CFLAGS) $(LIBSPATH) $(LIBS) -Wwrite-strings + rm -f *.o + +%.o:$(SRC_ROOT)/%.cpp + $(XX) $(CFLAGS) -c $< + +%.o:$(SRC_ROOT)/gb28181/%.cpp + $(XX) $(CFLAGS) -c $< + +%.o:$(SRC_ROOT)/%.cu + @echo "#######################CU_OBJS:$@###############" + $(NVCC) $(NFLAGS) -o $@ $< + +clean: + rm -f *.o $(TARGET) \ No newline at end of file diff --git a/nv-codec-headers/Makefile b/nv-codec-headers/Makefile index a51c2c9..42f71ad 100644 --- a/nv-codec-headers/Makefile +++ b/nv-codec-headers/Makefile @@ -1,4 +1,4 @@ -PREFIX = /usr/local +PREFIX = /mnt/data/cmhu/nv_headers LIBDIR = lib INSTALL = install SED = sed diff --git a/save_tool/Makefile b/save_tool/Makefile new file mode 100644 index 0000000..d6bbedd --- /dev/null +++ b/save_tool/Makefile @@ -0,0 +1,50 @@ + +XX = g++ +NVCC = nvcc + +PROJECT_ROOT= /mnt/data/cmhu/FFNvDecoder + +DEPEND_DIR = $(PROJECT_ROOT)/bin +SRC_ROOT = $(PROJECT_ROOT)/save_tool +CUDA_ROOT = /usr/local/cuda + +TARGET= $(DEPEND_DIR)/lib/save_tool + + +INCLUDE= -I $(DEPEND_DIR)/include \ + -I $(CUDA_ROOT)/include \ + -I $(SRC_ROOT)\ + +LIBSPATH= -L $(DEPEND_DIR)/lib -lavformat -lavcodec -lswscale -lavutil -lavfilter -lswresample -lavdevice \ + -L $(CUDA_ROOT)/lib64 -lcudart -lcurand -lcublas -lnvjpeg \ + -L /usr/lib/wsl/lib -lcuda -lnvcuvid\ + +CFLAGS= -g -fPIC -O0 $(INCLUDE) -pthread -lrt -lz -std=c++11 -fvisibility=hidden -Wl,-Bsymbolic -ldl + # -DUNICODE -D_UNICODE + +NFLAGS_LIB=-g -c -shared -Xcompiler -fPIC -Xcompiler -fvisibility=hidden +NFLAGS = $(NFLAGS_LIB) $(INCLUDE) -std=c++11 + +SRCS:=$(wildcard $(SRC_ROOT)/*.cpp) +OBJS = $(patsubst %.cpp, %.o, $(notdir $(SRCS))) + +CU_SOURCES = $(wildcard ${SRC_ROOT}/*.cu) +CU_OBJS = $(patsubst %.cu, %.o, $(notdir $(CU_SOURCES))) + + +$(TARGET):$(OBJS) $(CU_OBJS) + rm -f $(TARGET) + $(XX) -o $@ $^ $(CFLAGS) $(LIBSPATH) $(LIBS) -Wwrite-strings + rm -f *.o + +%.o:$(SRC_ROOT)/%.cpp + $(XX) $(CFLAGS) -c $< + +%.o:$(SRC_ROOT)/%.cu + @echo "#######################CU_OBJS:$@###############" + $(NVCC) $(NFLAGS) -o $@ $< + +clean: + rm -f *.o $(TARGET) + + diff --git a/save_tool/check_tool.cpp1 b/save_tool/check_tool.cpp1 new file mode 100644 index 0000000..7401e42 --- /dev/null +++ b/save_tool/check_tool.cpp1 @@ -0,0 +1,179 @@ +#include"check_tool.h" + +extern "C" +{ + #include + #include + #include + #include + #include + #include + #include + #include +} + +#include + + +static long long get_cur_time(){ + // 获取操作系统当前时间点(精确到微秒) + chrono::time_point tpMicro + = chrono::time_point_cast(chrono::system_clock::now()); + // (微秒精度的)时间点 => (微秒精度的)时间戳 + time_t currentTime = tpMicro.time_since_epoch().count(); + + return (long long )currentTime; +} + +void save_pkt(const string& uri){ + AVFormatContext* ifmt_ctx = nullptr; + AVCodecContext* codec_ctx = nullptr; + AVCodec *decoder = nullptr; + AVCodec* codec = nullptr; + AVPacket* pkt = nullptr; + int video_index = -1; + AVStream* i_video_stream = nullptr; + SwsContext *img_convert_ctx = nullptr; + uint8_t *buffer = nullptr; + int numBytes = 0; + + long start_time = 0; + long end_time = 0; + long s_time = 0; + long e_time = 0; + long long sum = 0; + double avg_time = 0.0; + + int sep = 0; + + AVFormatContext *o_fmt_ctx; + AVStream *o_video_stream; + + const char *filename = "test.mp4"; + + + avformat_network_init(); + + // 打开输入视频文件 + AVDictionary *options = nullptr; + av_dict_set( &options, "bufsize", "655360", 0 ); + av_dict_set( &options, "rtsp_transport", "tcp", 0 ); + // av_dict_set( &options, "listen_timeout", "30", 0 ); // 单位为s + av_dict_set( &options, "stimeout", "30000000", 0 ); // 单位为 百万分之一秒 + + ///打开输入的流 + ifmt_ctx = avformat_alloc_context(); + int ret = avformat_open_input(&ifmt_ctx, uri.c_str(), nullptr, &options); + if (ret != 0){ + av_log(nullptr, AV_LOG_ERROR, "Couldn't open input stream ! \n"); + goto end_flag ; + } + + //查找流信息 + if (avformat_find_stream_info(ifmt_ctx, nullptr) < 0){ + av_log(nullptr, AV_LOG_ERROR, "Couldn't find stream information ! \n"); + goto end_flag ; + } + + // 查找视频流信息 + video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); + if (video_index < 0) { + av_log(nullptr, AV_LOG_ERROR, "Cannot find a video stream in the input file ! \n"); + goto end_flag ; + } + + //申请AVCodecContext + codec_ctx = avcodec_alloc_context3(decoder); + if (!codec_ctx){ + goto end_flag ; + } + + i_video_stream = ifmt_ctx->streams[video_index]; + if(avcodec_parameters_to_context(codec_ctx, i_video_stream->codecpar) < 0){ + goto end_flag ; + } + + { + av_log(nullptr, AV_LOG_INFO, "path: %s \n", ifmt_ctx->url); + av_log(nullptr, AV_LOG_INFO, "format: %s \n", ifmt_ctx->iformat->name); + const char* profile = avcodec_profile_name(codec_ctx->codec_id, codec_ctx->profile); + av_log(nullptr, AV_LOG_INFO, "codec: %s(%s) \n", decoder->name, profile); + const char* pix_fmt_name = av_get_pix_fmt_name(codec_ctx->pix_fmt); + av_log(nullptr, AV_LOG_INFO, "pix_fmt_name: %s \n", pix_fmt_name); + av_log(nullptr, AV_LOG_INFO, "width x height: %dX%d \n", i_video_stream->codecpar->width, i_video_stream->codecpar->height); + + av_log(NULL, AV_LOG_INFO, "frame_rate: %1.0f ", av_q2d(i_video_stream->r_frame_rate)); + av_log(NULL, AV_LOG_INFO, " --> reference PPS: %f ms\n", 1/av_q2d(i_video_stream->r_frame_rate) * 1000); + } + + + avformat_alloc_output_context2(&o_fmt_ctx, NULL, NULL, filename); + + /* + * since all input files are supposed to be identical (framerate, dimension, color format, ...) + * we can safely set output codec values from first input file + */ + o_video_stream = avformat_new_stream(o_fmt_ctx, NULL); + { + AVCodecContext *c; + c = o_video_stream->codec; + c->bit_rate = 400000; + c->codec_id = i_video_stream->codec->codec_id; + c->codec_type = i_video_stream->codec->codec_type; + c->time_base.num = i_video_stream->time_base.num; + c->time_base.den = i_video_stream->time_base.den; + fprintf(stderr, " = time_base.num = %d time_base.den = %d\n", c->time_base.num, c->time_base.den); + c->width = i_video_stream->codec->width; + c->height = i_video_stream->codec->height; + c->pix_fmt = i_video_stream->codec->pix_fmt; + printf(" = width: %d height: %d pix_fmt: %d\n", c->width, c->height, c->pix_fmt); + c->flags = i_video_stream->codec->flags; + c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + c->me_range = i_video_stream->codec->me_range; + c->max_qdiff = i_video_stream->codec->max_qdiff; + + c->qmin = i_video_stream->codec->qmin; + c->qmax = i_video_stream->codec->qmax; + + c->qcompress = i_video_stream->codec->qcompress; + } + + avio_open(&o_fmt_ctx->pb, filename, AVIO_FLAG_WRITE); + + avformat_write_header(o_fmt_ctx, NULL); + + + s_time = get_cur_time(); + start_time = get_cur_time(); + pkt = av_packet_alloc(); + while (av_read_frame(ifmt_ctx, pkt) >= 0){ + end_time = get_cur_time(); + sum ++ ; + sep ++ ; + if(end_time - start_time > 1000){ + avg_time = double(end_time - start_time) / sep; + start_time = get_cur_time(); + sep = 0; + av_log(nullptr, AV_LOG_INFO, "PPS: %f ms\n", avg_time); + } + av_packet_unref(pkt); + } + + e_time = get_cur_time(); + avg_time = double(e_time - s_time) / sum; + av_log(nullptr, AV_LOG_INFO, "TOOTAL PPS: %f ms\n", avg_time); + +end_flag: + if (codec_ctx != nullptr){ + avcodec_close(codec_ctx); + avcodec_free_context(&codec_ctx); + } + + if (ifmt_ctx != nullptr){ + avformat_close_input(&ifmt_ctx); + } + + if (pkt != nullptr){ + av_packet_free(&pkt); + } +} \ No newline at end of file diff --git a/save_tool/check_tool.h1 b/save_tool/check_tool.h1 new file mode 100644 index 0000000..22812a8 --- /dev/null +++ b/save_tool/check_tool.h1 @@ -0,0 +1,5 @@ +#include + +using namespace std; + +void save_pkt(const string& uri); \ No newline at end of file diff --git a/save_tool/main.cpp b/save_tool/main.cpp new file mode 100644 index 0000000..d9f9d25 --- /dev/null +++ b/save_tool/main.cpp @@ -0,0 +1,24 @@ +#include"save_tool.h" + + +#include + +using namespace std; + + + +int main(int argc, char *argv[]) { + printf("start \n"); + if (argc != 2) { + fprintf(stderr, "./xxx uri\n"); + return -1; + } + + char* uri = "rtsp://admin:ad123456@192.168.60.165:554/cam/realmonitor?channel=1&subtype=0"; + + cout << uri << endl; + + save_tool(uri); + + return 0; +} \ No newline at end of file diff --git a/save_tool/save_tool.cpp b/save_tool/save_tool.cpp new file mode 100644 index 0000000..4d57676 --- /dev/null +++ b/save_tool/save_tool.cpp @@ -0,0 +1,160 @@ +extern "C" +{ + #include + #include + #include + #include + #include + #include + #include + #include +} + +#include +#include + +using namespace std; + +bool bFirstFrame = false; +long last_src_pts = 0; +long last_pts = 0; + +void update_pts(AVPacket* pkt) { + if (pkt->pts > 0) { + if (bFirstFrame) { + bFirstFrame = false; + last_src_pts = pkt->pts; + } + int64_t pkt_pts = pkt->pts; + pkt->pts = last_pts + (pkt_pts - last_src_pts); + last_src_pts = pkt_pts; + last_pts = pkt->pts; + pkt->dts = pkt->pts; + } + else { + if (bFirstFrame) { + bFirstFrame = false; + last_pts = 0; + } + pkt->pts = last_pts + 512; + last_pts = pkt->pts; + } + +} + +void *save_tool(const string uri) +{ + AVFormatContext *i_fmt_ctx; + AVCodecContext* codec_ctx = nullptr; + AVCodec *decoder = nullptr; + AVStream *i_video_stream; + int video_index; + + AVFormatContext *o_fmt_ctx; + AVStream *o_video_stream; + + bool bStop = false; + int frame_nums = 0; + + avcodec_register_all(); + av_register_all(); + avformat_network_init(); + + /* should set to NULL so that avformat_open_input() allocate a new one */ + i_fmt_ctx = NULL; + + const char *filename = "2.mp4"; + + if (avformat_open_input(&i_fmt_ctx, uri.c_str(), NULL, NULL)!=0) + { + fprintf(stderr, " = could not open input file\n"); + return nullptr; + } + + if (avformat_find_stream_info(i_fmt_ctx, NULL)<0) + { + fprintf(stderr, " = could not find stream info\n"); + return nullptr; + } + + video_index = av_find_best_stream(i_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); + if (video_index < 0) { + av_log(nullptr, AV_LOG_ERROR, "Cannot find a video stream in the input file ! \n"); + return nullptr; + } + + codec_ctx = avcodec_alloc_context3(decoder); + if (!codec_ctx){ + return nullptr; + } + + i_video_stream = i_fmt_ctx->streams[video_index]; + if(avcodec_parameters_to_context(codec_ctx, i_video_stream->codecpar) < 0){ + return nullptr; + } + + avformat_alloc_output_context2(&o_fmt_ctx, NULL, NULL, filename); + + o_video_stream = avformat_new_stream(o_fmt_ctx, NULL); + { + AVCodecContext *c; + c = o_video_stream->codec; + c->bit_rate = 400000; + c->codec_id = i_video_stream->codec->codec_id; + c->codec_type = i_video_stream->codec->codec_type; + c->time_base.num = i_video_stream->time_base.num; + c->time_base.den = i_video_stream->time_base.den; + fprintf(stderr, " = time_base.num = %d time_base.den = %d\n", c->time_base.num, c->time_base.den); + c->width = i_video_stream->codec->width; + c->height = i_video_stream->codec->height; + c->pix_fmt = i_video_stream->codec->pix_fmt; + printf(" = width: %d height: %d pix_fmt: %d\n", c->width, c->height, c->pix_fmt); + c->flags = i_video_stream->codec->flags; + c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; + c->me_range = i_video_stream->codec->me_range; + c->max_qdiff = i_video_stream->codec->max_qdiff; + + c->qmin = i_video_stream->codec->qmin; + c->qmax = i_video_stream->codec->qmax; + + c->qcompress = i_video_stream->codec->qcompress; + } + + avio_open(&o_fmt_ctx->pb, filename, AVIO_FLAG_WRITE); + + avformat_write_header(o_fmt_ctx, NULL); + + AVPacket* i_pkt = av_packet_alloc(); + av_init_packet(i_pkt); + + while (av_read_frame(i_fmt_ctx, i_pkt) >= 0){ + + update_pts(i_pkt); + + static int num = 1; + printf(" = frame %d\n", num++); + av_interleaved_write_frame(o_fmt_ctx, i_pkt); + + av_packet_unref(i_pkt); + + if(frame_nums > 750){ + break; + } + frame_nums++; + } + + av_packet_free(&i_pkt); + + avformat_close_input(&i_fmt_ctx); + + av_write_trailer(o_fmt_ctx); + + avcodec_close(o_fmt_ctx->streams[0]->codec); + av_freep(&o_fmt_ctx->streams[0]->codec); + av_freep(&o_fmt_ctx->streams[0]); + + avio_close(o_fmt_ctx->pb); + av_free(o_fmt_ctx); + + printf("end. \n"); +} \ No newline at end of file diff --git a/save_tool/save_tool.h b/save_tool/save_tool.h new file mode 100644 index 0000000..e75fd9c --- /dev/null +++ b/save_tool/save_tool.h @@ -0,0 +1,5 @@ +#include + +using namespace std; + +void *save_tool(const string uri); \ No newline at end of file diff --git a/src/FFNvDecoderManager.cpp b/src/FFNvDecoderManager.cpp index b15ef22..69a1b9b 100644 --- a/src/FFNvDecoderManager.cpp +++ b/src/FFNvDecoderManager.cpp @@ -40,6 +40,7 @@ AbstractDecoder* FFNvDecoderManager::createDecoder(MgrDecConfig config){ if (bRet) { dec->setName(config.name) ; + dec->setSnapTimeInterval(config.snap_time_interval); decoderMap[config.name] = dec; LOG_INFO("[{}][{}]- 解码器初始化成功",config.name, config.cfg.uri); diff --git a/src/FFNvDecoderManager.h b/src/FFNvDecoderManager.h index 685b1f9..744c83b 100644 --- a/src/FFNvDecoderManager.h +++ b/src/FFNvDecoderManager.h @@ -12,6 +12,7 @@ struct MgrDecConfig DECODER_TYPE dec_type; // 解码器类型 FFDecConfig cfg; // 解码器配置 string name{""}; // 解码器名称 + long snap_time_interval; // 定时抓拍时间间隔 }; /** diff --git a/src/FFSaveImg.cpp b/src/FFSaveImg.cpp new file mode 100644 index 0000000..cb7bcf4 --- /dev/null +++ b/src/FFSaveImg.cpp @@ -0,0 +1,134 @@ +#include "FFSaveImg.h" + +extern "C" +{ + #include + #include + #include + #include + #include + #include + #include + #include +} + +int saveJpg(AVFrame *pFrame, const char *out_file) { + + int width = pFrame->width; + int height = pFrame->height; + AVCodecContext *pCodecCtx = NULL; + + AVFormatContext *pFormatCtx = avformat_alloc_context(); + // 设置输出文件格式 + pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL); + + // 创建并初始化输出AVIOContext + if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) { + printf("Couldn't open output file."); + return -1; + } + + // 构建一个新stream + AVStream *video_st = avformat_new_stream(pFormatCtx, 0); + if (video_st == NULL) { + return -1; + } + + pCodecCtx = video_st->codec; + pCodecCtx->codec_id = pFormatCtx->oformat->video_codec; + pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; + pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P; + pCodecCtx->width = width; + pCodecCtx->height = height; + + pCodecCtx->time_base = (AVRational) {1, 25}; + + AVCodec *pCodec = avcodec_find_encoder(pCodecCtx->codec_id); + + if (!pCodec) { + printf("Could not find encoder\n"); + return -1; + } + + if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { + printf("Could not open codec."); + return -1; + } + + int ret = avformat_write_header(pFormatCtx, NULL); + if (ret < 0) { + printf("write_header fail\n"); + return -1; + } + + AVPacket* pkt = av_packet_alloc(); + av_init_packet(pkt); + + // 编码数据 + ret = avcodec_send_frame(pCodecCtx, pFrame); + if (ret < 0) { + printf("Could not avcodec_send_frame."); + return -1; + } + + // 得到编码后数据 + ret = avcodec_receive_packet(pCodecCtx, pkt); + if (ret < 0) { + printf("Could not avcodec_receive_packet"); + return -1; + } + + ret = av_write_frame(pFormatCtx, pkt); + + if (ret < 0) { + printf("Could not av_write_frame"); + return -1; + } + + av_frame_free(&pFrame); + av_packet_free(&pkt); + + //Write Trailer + av_write_trailer(pFormatCtx); + + + avcodec_close(pCodecCtx); + avio_close(pFormatCtx->pb); + avformat_free_context(pFormatCtx); + + return 0; +} + +AVFrame* convert2yuv(int width, int height, unsigned char * rgbbuf, AVPixelFormat src_pix_fmt){ + + AVPixelFormat dst_pix_fmt = AV_PIX_FMT_YUV420P; + + AVFrame *pFrameYUV = av_frame_alloc(); + uint8_t *out_buffer = new uint8_t[avpicture_get_size(dst_pix_fmt, width, height)]; + + avpicture_fill((AVPicture *)pFrameYUV, out_buffer, dst_pix_fmt, width, height); + + AVFrame *rgbFrame = av_frame_alloc(); + + avpicture_fill((AVPicture *)rgbFrame, rgbbuf, src_pix_fmt, width, height); + + SwsContext *sws_ctx = sws_getContext( + width, height, src_pix_fmt, + width, height, dst_pix_fmt, + SWS_BILINEAR, NULL, NULL, NULL); + + sws_scale(sws_ctx, rgbFrame->data, rgbFrame->linesize, 0, height, pFrameYUV->data, pFrameYUV->linesize); + sws_freeContext(sws_ctx); + av_frame_free(&rgbFrame); + + pFrameYUV->width = width; + pFrameYUV->height = height; + pFrameYUV->format = dst_pix_fmt; + + return pFrameYUV; +} + +void saveJpg(int width, int height, unsigned char * pData, string file_name){ + AVFrame *pFrame = convert2yuv(width, height, pData, AV_PIX_FMT_BGR24); + saveJpg(pFrame, file_name.c_str()); +} \ No newline at end of file diff --git a/src/FFSaveImg.h b/src/FFSaveImg.h new file mode 100644 index 0000000..d4fd40d --- /dev/null +++ b/src/FFSaveImg.h @@ -0,0 +1,6 @@ + +#include + +using namespace std; + +void saveJpg(int width, int height, unsigned char * pData, string file_name); \ No newline at end of file diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 3da0ec5..0000000 --- a/src/Makefile +++ /dev/null @@ -1,67 +0,0 @@ -XX = g++ - -CUDA_ROOT = /usr/local/cuda-11.1 -NVCC = $(CUDA_ROOT)/bin/nvcc - - -PROJECT_ROOT= /mnt/data/cmhu/FFNvDecoder - -DEPEND_DIR = $(PROJECT_ROOT)/bin -SRC_ROOT = $(PROJECT_ROOT)/src -THIRDPARTY_ROOT = $(PROJECT_ROOT)/3rdparty - - -TARGET= $(DEPEND_DIR)/lib/test - - -SPDLOG_ROOT = $(THIRDPARTY_ROOT)/spdlog-1.9.2/release -JRTP_ROOT = $(THIRDPARTY_ROOT)/jrtp_export - - -INCLUDE= -I $(DEPEND_DIR)/include \ - -I $(CUDA_ROOT)/include \ - -I $(SRC_ROOT)/common/inc \ - -I $(SRC_ROOT)/common/UtilNPP \ - -I $(SRC_ROOT)\ - -I $(SPDLOG_ROOT)/include \ - -I $(SRC_ROOT)/gb28181 \ - -I $(JRTP_ROOT)/jrtplib/include/jrtplib3 \ - -I $(JRTP_ROOT)/jthread/include/jthread - -LIBSPATH= -L $(DEPEND_DIR)/lib -lavformat -lavcodec -lswscale -lavutil -lavfilter -lswresample -lavdevice \ - -L $(CUDA_ROOT)/lib64 -lcuda -lcudart -lnvcuvid -lcurand -lcublas -lnvjpeg \ - -L $(SPDLOG_ROOT) -l:libspdlog.a \ - -L $(JRTP_ROOT)/jthread/lib -l:libjthread.a \ - -L $(JRTP_ROOT)/jrtplib/lib -l:libjrtp.a - -CFLAGS= -g -fPIC -O0 $(INCLUDE) -pthread -lrt -lz -std=c++11 -fvisibility=hidden -Wl,-Bsymbolic -ldl - # -DUNICODE -D_UNICODE - -NFLAGS_LIB=-g -c -shared -Xcompiler -fPIC -Xcompiler -fvisibility=hidden -NFLAGS = $(NFLAGS_LIB) $(INCLUDE) -std=c++11 - -SRCS:=$(wildcard $(SRC_ROOT)/*.cpp) \ - $(wildcard $(SRC_ROOT)/gb28181/*.cpp) -OBJS = $(patsubst %.cpp, %.o, $(notdir $(SRCS))) - -CU_SOURCES = $(wildcard ${SRC_ROOT}/*.cu) -CU_OBJS = $(patsubst %.cu, %.o, $(notdir $(CU_SOURCES))) - - -$(TARGET):$(OBJS) $(CU_OBJS) - rm -f $(TARGET) - $(XX) -o $@ $^ $(CFLAGS) $(LIBSPATH) $(LIBS) -Wwrite-strings - rm -f *.o - -%.o:$(SRC_ROOT)/%.cpp - $(XX) $(CFLAGS) -c $< - -%.o:$(SRC_ROOT)/gb28181/%.cpp - $(XX) $(CFLAGS) -c $< - -%.o:$(SRC_ROOT)/%.cu - @echo "#######################CU_OBJS:$@###############" - $(NVCC) $(NFLAGS) -o $@ $< - -clean: - rm -f *.o $(TARGET) \ No newline at end of file diff --git a/src/gb28181/FFGB28181Decoder.cpp b/src/gb28181/FFGB28181Decoder.cpp index 2207c98..4cea111 100644 --- a/src/gb28181/FFGB28181Decoder.cpp +++ b/src/gb28181/FFGB28181Decoder.cpp @@ -173,11 +173,11 @@ void FFGB28181Decoder::stream_callback(int videoType, char* data, int len, int i return; } - AVPacket framePacket = {}; - av_init_packet(&framePacket); + AVPacket* pkt = av_packet_alloc(); + av_init_packet(pkt); - framePacket.size = len; - framePacket.data = (uint8_t*)data; + pkt->size = len; + pkt->data = (uint8_t*)data; if (m_pAVCodecCtx == nullptr) { LOG_INFO("frame data is zero --{}", m_dec_name); @@ -232,11 +232,12 @@ void FFGB28181Decoder::stream_callback(int videoType, char* data, int len, int i } //开始解码 - int ret = avcodec_send_packet(m_pAVCodecCtx, &framePacket); + int ret = avcodec_send_packet(m_pAVCodecCtx, pkt); if (ret < 0) { //send_exception(RunMessageType::E2002, e_msg); LOG_ERROR("Real stream视频解码失败,请检查视频设备{}: avcodec_send_packet failed. ret={}", m_dec_name, ret); - av_packet_unref(&framePacket); + av_packet_free(&pkt); + pkt = nullptr; return; } @@ -245,7 +246,8 @@ void FFGB28181Decoder::stream_callback(int videoType, char* data, int len, int i frameH = m_pAVCodecCtx->height; if (frameW <= 0 || frameH <= 0) { LOG_ERROR("[{}] frame W or H is error! ({},{})", m_dec_name, frameW, frameH); - av_packet_unref(&framePacket); + av_packet_free(&pkt); + pkt = nullptr; return; } } @@ -257,16 +259,20 @@ void FFGB28181Decoder::stream_callback(int videoType, char* data, int len, int i ret = avcodec_receive_frame(m_pAVCodecCtx, gpuFrame); if ((ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) || ret < 0){ LOG_ERROR("{} - Failed to receive frame: {}", m_dec_name, ret); - av_packet_unref(&framePacket); + av_packet_free(&pkt); + pkt = nullptr; av_frame_free(&gpuFrame); + gpuFrame = nullptr; return; } - av_packet_unref(&framePacket); + av_packet_free(&pkt); + pkt = nullptr; if (gpuFrame->width != frameW || gpuFrame->height != frameH){ LOG_INFO("AVFrame is inconsistent: width is {}, height is {}; original frameW is {}, frameH is {}--{}", gpuFrame->width, gpuFrame->height, frameW, frameH , m_dec_name); av_frame_free(&gpuFrame); + gpuFrame = nullptr; return; } @@ -275,6 +281,7 @@ void FFGB28181Decoder::stream_callback(int videoType, char* data, int len, int i mFrameQueue.push(gpuFrame); }else{ av_frame_free(&gpuFrame); + gpuFrame = nullptr; } m_queue_mutex.unlock(); } @@ -297,6 +304,7 @@ void FFGB28181Decoder::post_decode_thread(){ } av_frame_free(&gpuFrame); + gpuFrame = nullptr; index++; if(index >= 100000){ diff --git a/src/main.cpp b/src/main.cpp index d24e8f4..bfc1241 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,7 @@ #include +#include "FFSaveImg.h" #ifdef _WIN32 #include "Winsock2.h" @@ -24,6 +25,9 @@ #include "utiltools.hpp" +#include "curl/curl.h" + + #define MIN_RTP_PORT 10000 #define MAX_RTP_PORT 60000 @@ -155,10 +159,34 @@ int CheckCUDAProperty( int devId ) return 0; } +static long get_cur_time_ms() { + chrono::time_point tpMicro + = chrono::time_point_cast(chrono::system_clock::now()); + return tpMicro.time_since_epoch().count(); +} + /** * 注意: gpuFrame 在解码器设置的显卡上,后续操作要十分注意这一点,尤其是多线程情况 * */ void postDecoded(const void * userPtr, AVFrame * gpuFrame){ + + // long first_time = get_cur_time_ms(); + // long second_time = 0; + // int a = 1; + // while(true){ + // a = a * 99; + // if(a > 1000000){ + // a = 1; + // } + // second_time = get_cur_time_ms(); + // if(second_time - first_time > 100){ + // break; + // } + // } + // return ; + + // std::this_thread::sleep_for(std::chrono::milliseconds(100)); + AbstractDecoder* decoder = (AbstractDecoder*)userPtr; if (decoder!= nullptr) { @@ -235,7 +263,8 @@ static int sum = 0; unsigned char *pHwData = nullptr; void postDecoded0(const void * userPtr, AVFrame * gpuFrame){ - // std::this_thread::sleep_for(std::chrono::milliseconds(30000)); + + std::this_thread::sleep_for(std::chrono::milliseconds(100)); AbstractDecoder* decoder = (AbstractDecoder*)userPtr; if (decoder!= nullptr) @@ -287,12 +316,196 @@ void postDecoded0(const void * userPtr, AVFrame * gpuFrame){ } } +//get请求和post请求数据响应函数 +size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream) +{ + //在注释的里面可以打印请求流,cookie的信息 + //cout << "----->reply" << endl; + string *str = (string*)stream; + //cout << *str << endl; + (*str).append((char*)ptr, size*nmemb); + return size * nmemb; +} + +//http POST请求 +CURLcode curl_post_body_getVideoRealStream(const string &url, const string &postParams, string &response, string devid, string ip, string port) +{ + // curl初始化 + CURL *curl = curl_easy_init(); + // curl返回值 + CURLcode res; + if (curl) + { + // set params + //设置curl的请求头 + struct curl_slist* header_list = NULL; + header_list = curl_slist_append(header_list, "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"); + // header_list = curl_slist_append(header_list, "Content-Type:application/x-www-form-urlencoded; charset=UTF-8"); + + header_list = curl_slist_append(header_list, "Accept: application/json"); + header_list = curl_slist_append(header_list, "Content-Type: application/json");//text/html + header_list = curl_slist_append(header_list, "charsets: utf-8"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list); + + //不接收响应头数据0代表不接收 1代表接收 + curl_easy_setopt(curl, CURLOPT_HEADER, 0); + + //设置请求为post请求 + // curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + + //设置请求的URL地址 + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + //设置post请求的参数 + // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postParams.c_str()); + + //设置ssl验证 + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); + + //CURLOPT_VERBOSE的值为1时,会显示详细的调试信息 + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0); + + string strResult = "{ \ + \"authinfo\": \"hisense|hisense123|201807311630\", \ + \"method\": \"getVideoRealStream\", \ + \"deviceId\": \""+devid+"\", \ + \"streamFormat\": \"0\", \ + \"recvIp\": \""+ip+"\", \ + \"recvPort\": \""+port+"\", \ + \"remark\": \"\", \ + \"protocol\": \"1\" \ + }"; + + curl_easy_setopt(curl,CURLOPT_POSTFIELDS,strResult.c_str()); + + curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); + + //设置数据接收和写入函数 + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); + + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + + //设置超时时间 + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 6); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 6); + + // 开启post请求 + res = curl_easy_perform(curl); + } + //释放curl + curl_easy_cleanup(curl); + return res; +} + +//http POST请求 +CURLcode curl_post_body_stopVideoRealStream(const string &url, const string &postParams, string &response, string handleId) +{ + + // curl初始化 + CURL *curl = curl_easy_init(); + // curl返回值 + CURLcode res; + if (curl) + { + // set params + //设置curl的请求头 + struct curl_slist* header_list = NULL; + header_list = curl_slist_append(header_list, "User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"); + // header_list = curl_slist_append(header_list, "Content-Type:application/x-www-form-urlencoded; charset=UTF-8"); + + header_list = curl_slist_append(header_list, "Accept: application/json"); + header_list = curl_slist_append(header_list, "Content-Type: application/json");//text/html + header_list = curl_slist_append(header_list, "charsets: utf-8"); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list); + + //不接收响应头数据0代表不接收 1代表接收 + curl_easy_setopt(curl, CURLOPT_HEADER, 0); + + //设置请求为post请求 + // curl_easy_setopt(curl, CURLOPT_POST, 1); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); + + //设置请求的URL地址 + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + //设置post请求的参数 + // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postParams.c_str()); + + //设置ssl验证 + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); + + //CURLOPT_VERBOSE的值为1时,会显示详细的调试信息 + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0); + + string strResult = "{ \ + \"authinfo\": \"hisense|hisense123|201807311630\", \ + \"method\": \"stopVideoRealStream\", \ + \"handleId\": \""+ handleId + "\" \ + }"; + + curl_easy_setopt(curl,CURLOPT_POSTFIELDS,strResult.c_str()); + + curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); + + //设置数据接收和写入函数 + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response); + + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + + //设置超时时间 + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 6); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 6); + + // 开启post请求 + res = curl_easy_perform(curl); + } + //释放curl + curl_easy_cleanup(curl); + return res; +} + void decode_finished_cbk(const void* userPtr){ cout << "当前时间戳: " << UtilTools::get_cur_time_ms() << endl; } +string recv_port = "13012"; + bool decode_request_stream_cbk(const char* deviceId){ - cout << "需在此请求流" << endl; + // cout << "需在此请求流" << endl; + + string url_post = "http://172.16.6.129:7000/httpRequest"; + string paramsLogin = "key1=value1&key2=value2"; + string resPost; + + static string last_handleid = ""; + if(!last_handleid.empty()){ + auto res3 = curl_post_body_stopVideoRealStream(url_post, paramsLogin, resPost, last_handleid); + if (res3 == CURLE_OK) + { + cout << resPost << endl; + } + } + + auto res = curl_post_body_getVideoRealStream(url_post, paramsLogin, resPost, deviceId, "172.16.6.129", recv_port); + if (res == CURLE_OK) + { + cout << resPost << endl; + + size_t start = resPost.find_last_of(":") + 3; + size_t end = resPost.find_last_of("\""); + if (start == end){ + return false; + } + last_handleid = resPost.substr(start,end - start); + + cout << last_handleid << endl; + } + return true; } @@ -301,7 +514,7 @@ bool decode_request_stream_cbk(const char* deviceId){ // string test_uri = "/home/cmhu/data/output_1920x1080.mp4"; // string test_uri = "rtsp://176.10.0.2:8554/stream"; // string test_uri = "/mnt/f/fiss/test_data/h265.mp4"; -string test_uri = "rtsp://176.10.0.4:8554/stream"; +string test_uri = "rtsp://192.168.10.4:8554/street"; void createDecode(int index, const char* gpu_id){ FFNvDecoderManager* pDecManager = FFNvDecoderManager::getInstance(); @@ -312,6 +525,7 @@ void createDecode(int index, const char* gpu_id){ config.cfg.decode_finished_cbk = decode_finished_cbk; config.cfg.force_tcp = true; config.dec_type = DECODER_TYPE_FFMPEG; + config.snap_time_interval = 100; config.cfg.gpuid = gpu_id; // if (index % 2 == 0) @@ -333,10 +547,10 @@ void createDecode(int index, const char* gpu_id){ pDecManager->startDecodeByName(config.name); } -void createGB28181Decode(int index, char* gpu_id, int port){ +void createGB28181Decode(char* devid, char* gpu_id, int port){ FFNvDecoderManager* pDecManager = FFNvDecoderManager::getInstance(); MgrDecConfig config; - config.name = "dec" + to_string(index); + config.name = devid; config.cfg.uri = config.name; config.cfg.post_decoded_cbk = postDecoded; config.cfg.decode_finished_cbk = decode_finished_cbk; @@ -345,8 +559,11 @@ void createGB28181Decode(int index, char* gpu_id, int port){ config.dec_type = DECODER_TYPE_GB28181; config.cfg.port = port;//allocRtpPort(); - + config.cfg.gpuid = gpu_id; + config.snap_time_interval = 100; + + recv_port = to_string(port); AbstractDecoder* decoder = pDecManager->createDecoder(config); if (!decoder) @@ -366,10 +583,11 @@ void logFF(void *, int level, const char *fmt, va_list ap) int main(int argc, char* argv[]){ - test_uri = "rtsp://admin:admin@123456@192.168.60.176:554/cam/realmonitor?channel=1&subtype=0";//argv[1]; + test_uri = argv[1]; char* gpuid = argv[2]; int port = atoi(argv[3]); - cout << test_uri << " gpu_id:" << gpuid << " port:" << port << endl; + char* devId = argv[4]; + cout << test_uri << " gpu_id:" << gpuid << " port:" << port << " devId:" << devId << endl; // av_log_set_callback(&logFF); @@ -382,10 +600,20 @@ int main(int argc, char* argv[]){ // cudaSetDevice(atoi(gpuid)); while (true) { - std::this_thread::sleep_for(std::chrono::minutes(1)); + std::this_thread::sleep_for(std::chrono::seconds(10)); FFNvDecoderManager* pDecManager = FFNvDecoderManager::getInstance(); int count = pDecManager->count(); cout << "当前时间:" << UtilTools::get_cur_time_ms() << " 当前运行路数: " << pDecManager->count() << endl; + vector vec_img = pDecManager->timing_snapshot_all(); + for (auto imgInfo : vec_img){ + auto task_id = imgInfo->dec_name; + + std::string fpath_ori = "./time_snapshot/" + task_id + "_" + std::to_string(imgInfo->timestamp) + ".jpg"; + + saveJpg(imgInfo->width, imgInfo->height, imgInfo->pData, fpath_ori.c_str()); + + pDecManager->releaseFFImgInfo(imgInfo); + } } return (void*)0; @@ -413,7 +641,7 @@ int main(int argc, char* argv[]){ break; case 'g': case 'G': - createGB28181Decode(i, gpuid, port); + createGB28181Decode(devId, gpuid, port); i++; break; case 'r': -- libgit2 0.21.4