diff --git a/check_tool/FFCuContextManager.cpp b/check_tool/FFCuContextManager.cpp new file mode 100644 index 0000000..8f5b09a --- /dev/null +++ b/check_tool/FFCuContextManager.cpp @@ -0,0 +1,28 @@ +#include "FFCuContextManager.h" +#include + +using namespace std; + +FFCuContextManager::~FFCuContextManager() +{ + for(auto iter = ctxMap.begin(); iter != ctxMap.end(); iter++){ + av_buffer_unref(&iter->second); + } + ctxMap.clear(); +} + +AVBufferRef *FFCuContextManager::getCuCtx(string gpuid) +{ + AVBufferRef *hw_device_ctx = ctxMap[gpuid]; + if (nullptr == hw_device_ctx) + { + // 初始化硬件解码器 + if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, gpuid.c_str(), nullptr, 0) < 0) + { + av_log(nullptr, AV_LOG_ERROR, "Failed to create specified HW device ! \n"); + return nullptr; + } + ctxMap[gpuid] = hw_device_ctx; + } + return hw_device_ctx; +} \ No newline at end of file diff --git a/check_tool/FFCuContextManager.h b/check_tool/FFCuContextManager.h new file mode 100644 index 0000000..3050641 --- /dev/null +++ b/check_tool/FFCuContextManager.h @@ -0,0 +1,37 @@ + +#include +#include + +extern "C" +{ + #include + #include + #include + #include + #include + #include + #include +} + +using namespace std; + +class FFCuContextManager{ +public: + static FFCuContextManager* getInstance(){ + static FFCuContextManager* singleton = nullptr; + if (singleton == nullptr){ + singleton = new FFCuContextManager(); + } + return singleton; + } + + AVBufferRef *getCuCtx(string gpuid); + +private: + FFCuContextManager(){} + ~FFCuContextManager(); + +private: + map ctxMap; + +}; \ No newline at end of file diff --git a/check_tool/Makefile b/check_tool/Makefile new file mode 100644 index 0000000..ebc427d --- /dev/null +++ b/check_tool/Makefile @@ -0,0 +1,50 @@ + +XX = g++ +NVCC = nvcc + +PROJECT_ROOT= /mnt/e/fiss/FFNvDecoder + +DEPEND_DIR = $(PROJECT_ROOT)/bin +SRC_ROOT = $(PROJECT_ROOT)/check_tool +CUDA_ROOT = /usr/local/cuda + +TARGET= $(DEPEND_DIR)/lib/check_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/check_tool/check_tool.cpp b/check_tool/check_tool.cpp new file mode 100644 index 0000000..03e616f --- /dev/null +++ b/check_tool/check_tool.cpp @@ -0,0 +1,184 @@ +#include"check_tool.h" + +#include "FFCuContextManager.h" + +extern "C" +{ + #include + #include + #include + #include + #include + #include + #include + #include +} + +#include + +static AVPixelFormat get_hw_format(AVCodecContext *avctx, const AVPixelFormat *pix_fmts){ + + const AVPixelFormat *p; + + for (p = pix_fmts; *p != -1; p++) { + if (*p == AV_PIX_FMT_CUDA) + return *p; + } + + av_log(nullptr, AV_LOG_ERROR, "Failed to get HW surface format. \n"); + return AV_PIX_FMT_NONE; +} + +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 evalQuality(const string& uri, const char* gpuid){ + AVFormatContext* ifmt_ctx = nullptr; + AVCodecContext* codec_ctx = nullptr; + AVCodec *decoder = nullptr; + AVCodec* codec = nullptr; + AVPacket* pkt = nullptr; + int video_index = -1; + AVStream* st = 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; + + string cuvid_dec_name = "" ; + FFCuContextManager* pCtxMgr = FFCuContextManager::getInstance(); + AVDictionary *op = nullptr; + AVBufferRef *hw_device_ctx = nullptr; + + + 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 ; + } + + cuvid_dec_name = string(decoder->name) + "_cuvid"; + codec = avcodec_find_decoder_by_name(cuvid_dec_name.c_str()); + if (!codec){ + av_log(nullptr, AV_LOG_ERROR, "Codec not found ! \n"); + goto end_flag ; + } + + //申请AVCodecContext + codec_ctx = avcodec_alloc_context3(codec); + if (!codec_ctx){ + goto end_flag ; + } + + st = ifmt_ctx->streams[video_index]; + if(avcodec_parameters_to_context(codec_ctx, st->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", st->codecpar->width, st->codecpar->height); + + av_log(NULL, AV_LOG_INFO, "frame_rate: %1.0f ", av_q2d(st->r_frame_rate)); + av_log(NULL, AV_LOG_INFO, " --> reference PPS: %f ms\n", 1/av_q2d(st->r_frame_rate) * 1000); + + } + + // 设置解码器管理器的像素格式回调函数 + codec_ctx->get_format = get_hw_format; + + hw_device_ctx = pCtxMgr->getCuCtx(gpuid); + if(nullptr == hw_device_ctx){ + av_log(nullptr, AV_LOG_ERROR, "create CUDA context failed ! \n"); + goto end_flag ; + } + codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); + if (nullptr == codec_ctx->hw_device_ctx){ + goto end_flag ; + } + + // 打开解码器流 + av_dict_set( &op, "gpu", gpuid, 0 ); + if (avcodec_open2(codec_ctx, codec, &op) < 0) { + av_log(nullptr, AV_LOG_ERROR, "Failed to open codec for stream ! \n"); + goto end_flag ; + } + + 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/check_tool/check_tool.h b/check_tool/check_tool.h new file mode 100644 index 0000000..ab31582 --- /dev/null +++ b/check_tool/check_tool.h @@ -0,0 +1,5 @@ +#include + +using namespace std; + +void evalQuality(const string& uri, const char* gpuid); \ No newline at end of file diff --git a/check_tool/main.cpp b/check_tool/main.cpp new file mode 100644 index 0000000..3b981e4 --- /dev/null +++ b/check_tool/main.cpp @@ -0,0 +1,61 @@ +#include"check_tool.h" + +#include + +#include + +using namespace std; + + +#define checkCudaErrors(S) do {CUresult status; \ + status = S; \ + if (status != CUDA_SUCCESS ) {std::cout << __LINE__ <<" checkCudaErrors - status = " << status << std::endl; \ + exit(EXIT_FAILURE);}\ + } while (false) + + + +int CheckCUDAProperty( int devId ) +{ + cuInit(0); + + CUdevice dev = devId; + size_t memSize = 0; + char devName[256] = {0}; + int major = 0, minor = 0; + CUresult rlt = CUDA_SUCCESS; + + checkCudaErrors(cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, dev)); + checkCudaErrors(cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, dev)); + + checkCudaErrors( cuDeviceGetName(devName, sizeof( devName ), dev) ); + + printf( "Using GPU Device %d: %s has SM %d.%d compute capability\n", + dev, devName, major, minor ); + + rlt = cuDeviceTotalMem( &memSize, dev ); + checkCudaErrors( rlt ); + + printf( "Total amount of global memory: %4.4f MB\n", + (float)memSize / ( 1024 * 1024 ) ); + + return 0; +} + + +int main(int argc, char *argv[]) { + printf("start \n"); + if (argc != 3) { + fprintf(stderr, "./xxx uri gpu_id\n"); + return -1; + } + + char* uri = argv[1]; + char* gpuid = argv[2]; + + CheckCUDAProperty(atoi(gpuid)); + + evalQuality(uri,gpuid); + + return 0; +} \ No newline at end of file