From 3c7e3e11e19bf471a835a17f40ae895783bbf6ab Mon Sep 17 00:00:00 2001
From: fiss <2657262686@qq.com>
Date: Wed, 16 Nov 2022 16:54:05 +0800
Subject: [PATCH] 1.修改日志 2.添加DECODE_FINISHED_CALLBACK 3.添加isPausing接口 4.添加getCachedQueueLength接口 5.添加snapshot和releaseFFImgInfo接口 6.测试代码添加log回调的自定义设置

---
 src/FFNvDecoder.cpp        | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
 src/FFNvDecoder.h          |  21 +++++++++++++++++++--
 src/FFNvDecoderManager.cpp | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
 src/FFNvDecoderManager.h   |  80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/main.cpp               |   8 ++++++++
 5 files changed, 410 insertions(+), 60 deletions(-)

diff --git a/src/FFNvDecoder.cpp b/src/FFNvDecoder.cpp
index fe3d5c4..e89e99e 100644
--- a/src/FFNvDecoder.cpp
+++ b/src/FFNvDecoder.cpp
@@ -1,5 +1,4 @@
 #include "FFNvDecoder.h"
-#include<iostream>
 
 #include <chrono>
 #include <thread>
@@ -24,7 +23,7 @@ static AVPixelFormat get_hw_format(AVCodecContext *avctx, const AVPixelFormat *p
 			return *p;
 	}
 
-	//cout << "Failed to get HW surface format";
+	av_log(NULL, AV_LOG_ERROR, "Failed to get HW surface format. \n");
 	return AV_PIX_FMT_NONE;
 }
 
@@ -86,13 +85,13 @@ bool FFNvDecoder::init(const char* uri, const char* gpuid, bool force_tcp)
 	fmt_ctx = avformat_alloc_context();
 	const char* input_file = uri;
 	if (avformat_open_input(&fmt_ctx, input_file, nullptr, &options) != 0) {
-		cout << "Cannot open input file: " << input_file;
+		av_log(NULL, AV_LOG_ERROR, "Cannot open input file: %s \n", input_file);
 		return false;
 	}
 
 	// 查找流信息
 	if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
-		cout << "Cannot find input stream information";
+		av_log(NULL, AV_LOG_ERROR, "Cannot find input stream information ! \n");
 		return false;
 	}
 
@@ -100,7 +99,7 @@ bool FFNvDecoder::init(const char* uri, const char* gpuid, bool force_tcp)
 	AVCodec *decoder = nullptr;
 	stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
 	if (stream_index < 0) {
-		cout << "Cannot find a video stream in the input file";
+		av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file ! \n");
 		return false;
 	}
 
@@ -130,9 +129,9 @@ bool FFNvDecoder::init(const char* uri, const char* gpuid, bool force_tcp)
 	// 打开解码器流
 	AVDictionary *op = nullptr;
 	av_dict_set( &op, "gpu", gpuid, 0 );
-	// av_dict_set( &op, "surfaces", "3", 0 );
+	av_dict_set( &op, "surfaces", "10", 0 );
 	if (avcodec_open2(avctx, vcodec, &op) < 0) {
-		cout << "Failed to open codec for stream" << stream_index;
+		av_log(NULL, AV_LOG_ERROR, "Failed to open codec for stream ! \n");
 		return false;
 	}
 
@@ -208,14 +207,9 @@ void FFNvDecoder::decode_thread()
 		}
 		
 		int result = av_read_frame(fmt_ctx, pkt);
-		if (result == AVERROR_EOF)
+		if (result == AVERROR_EOF || result < 0)
 		{
-			cout << "Failed to read frame!" << endl;
-			break;
-		}
-		if (result < 0)
-		{
-			cout << "Failed to read frame!" << endl;
+			av_log(NULL, AV_LOG_ERROR, "Failed to read frame! \n");
 			break;
 		}
 
@@ -234,19 +228,16 @@ void FFNvDecoder::decode_thread()
 			}
 		}
 
-		if (stream_index == pkt->stream_index)
-		{
+		if (stream_index == pkt->stream_index){
 			result = avcodec_send_packet(avctx, pkt);
-			if (result < 0)
-			{
-				cout << "Failed to send pkt:" << result << endl;
+			if (result < 0){
+				av_log(NULL, AV_LOG_ERROR, "Failed to send pkt: %d \n",result);
 				continue;
 			}
 
 			result = avcodec_receive_frame(avctx, gpuFrame);
-			if (result == AVERROR(EAGAIN) || result == AVERROR_EOF || result < 0)
-			{
-				cout << "Failed to receive frame"<< endl;
+			if ((result == AVERROR(EAGAIN) || result == AVERROR_EOF) || result < 0){
+				av_log(NULL, AV_LOG_ERROR, "Failed to receive frame: %d \n",result);
 				continue;
 			}
 
@@ -255,6 +246,14 @@ void FFNvDecoder::decode_thread()
 		av_packet_unref(pkt);
 	}
 
+	// 队列中没有数据了再结束
+	while (mFrameQueue.length() > 0){
+		if(!m_bRunning){
+			break;
+		}
+		std::this_thread::sleep_for(std::chrono::milliseconds(10));
+	}
+
 	m_bRunning = false;
 
 	// long end_time = get_cur_time();
@@ -266,9 +265,11 @@ void FFNvDecoder::decode_thread()
 		pthread_join(m_post_decode_thread,0);
 	}
 
+	decode_finished_cbk(m_userPtr);
+
 	decode_finished();
 
-	cout << "decode thread exited." << endl;
+	av_log(NULL, AV_LOG_INFO, "decode thread exited. \n");
 }
 
 void FFNvDecoder::decode_finished()
@@ -289,7 +290,7 @@ void FFNvDecoder::decode_finished()
 
 void FFNvDecoder::post_decode_thread()
 {
-	 while (m_bRunning)
+	 while (m_bRunning || mFrameQueue.length() > 0)
 	 {
 		AVFrame * gpuFrame = mFrameQueue.getHead();
 		if (gpuFrame == nullptr)
@@ -302,8 +303,8 @@ void FFNvDecoder::post_decode_thread()
 
 		mFrameQueue.addHead();
 	 }
-	 
-	 cout << "post decode thread exited." << endl;
+
+	 av_log(NULL, AV_LOG_INFO, "post decode thread exited. \n");
 }
 
 void FFNvDecoder::close()
@@ -338,6 +339,10 @@ bool FFNvDecoder::isFinished()
 	return m_bFinished;
 }
 
+bool FFNvDecoder::isPausing(){
+	return m_bPause;
+}
+
 bool FFNvDecoder::getResolution( int &width, int &height )
 {
 	if (avctx != nullptr)
@@ -363,4 +368,135 @@ void FFNvDecoder::resume()
 void FFNvDecoder::setDecKeyframe(bool bKeyframe)
 {
 	m_dec_keyframe = bKeyframe;
+}
+
+int FFNvDecoder::getCachedQueueLength(){
+	return mFrameQueue.length();
+}
+
+FFImgInfo* FFNvDecoder::snapshot(const string& uri){
+ 
+    AVFormatContext* ifmt_ctx = NULL;
+	AVCodecContext* codec_ctx = nullptr;
+	AVCodec* codec = nullptr;
+	AVPacket* pkt = nullptr;
+	AVFrame *frame = nullptr;
+	AVFrame *pFrameRGB = nullptr;	
+	int video_index = -1;
+	AVStream* st = nullptr;
+	SwsContext *img_convert_ctx = nullptr;
+	uint8_t *buffer = NULL;
+    int numBytes = 0;
+
+	FFImgInfo* imgInfo = nullptr;
+ 
+ 	//av_register_all();
+	avformat_network_init();
+
+	///打开输入的流
+	int ret = avformat_open_input(&ifmt_ctx, uri.c_str(), NULL, NULL);
+	if (ret != 0){
+		av_log(NULL, AV_LOG_ERROR, "Couldn't open input stream ! \n");
+		goto end_flag ;
+	}
+ 
+	//查找流信息
+	if (avformat_find_stream_info(ifmt_ctx, NULL) < 0){
+		av_log(NULL, 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, NULL, 0);
+ 
+    st = ifmt_ctx->streams[video_index];
+    
+    //找到解码器
+    codec = avcodec_find_decoder(st->codecpar->codec_id);
+    if (!codec){
+		av_log(NULL, AV_LOG_ERROR, "Codec not found ! \n");
+        goto end_flag ;
+    }
+ 
+    //申请AVCodecContext
+    codec_ctx = avcodec_alloc_context3(codec);
+    if (!codec_ctx){
+        goto end_flag ;
+    }
+ 
+	avcodec_parameters_to_context(codec_ctx, ifmt_ctx->streams[video_index]->codecpar);
+ 
+    //打开解码器
+    if ((ret = avcodec_open2(codec_ctx, codec, NULL) < 0)){
+        goto end_flag ;
+    }
+	
+    // 计算解码后原始数据所需缓冲区大小,并分配内存空间 Determine required buffer size and allocate buffer
+    numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codec_ctx->width, codec_ctx->height, 1);
+    buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
+    
+	pFrameRGB = av_frame_alloc();
+    av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, buffer, AV_PIX_FMT_BGR24, codec_ctx->width, codec_ctx->height, 1);
+
+	img_convert_ctx = sws_getContext(codec_ctx->width, codec_ctx->height,codec_ctx->pix_fmt, codec_ctx->width, codec_ctx->height, AV_PIX_FMT_BGR24, 
+		   SWS_BICUBIC, NULL, NULL, NULL);
+ 
+	pkt = av_packet_alloc();
+	frame = av_frame_alloc();
+	while (av_read_frame(ifmt_ctx, pkt) >= 0){
+		if (pkt->stream_index == video_index){
+			int ret = avcodec_send_packet(codec_ctx, pkt);
+			if (ret >= 0){
+				ret = avcodec_receive_frame(codec_ctx, frame);
+				if ((ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) || ret < 0){
+					av_log(NULL, AV_LOG_ERROR, "Failed to receive frame: %d \n", ret);
+					continue;
+				}
+
+				sws_scale(img_convert_ctx, (const unsigned char* const*)frame->data, frame->linesize, 0, codec_ctx->height, pFrameRGB->data, pFrameRGB->linesize);
+
+				imgInfo = new FFImgInfo();
+				imgInfo->pData = buffer;
+				imgInfo->height = codec_ctx->height;
+				imgInfo->width = codec_ctx->width;
+
+				break;
+			}
+		}
+	}
+
+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 (frame != nullptr){
+		av_frame_free(&frame);
+	}
+
+	if (pFrameRGB != nullptr){
+		av_frame_free(&pFrameRGB);
+	}
+
+	if (pkt != nullptr){
+		av_packet_free(&pkt);
+	}
+
+	return imgInfo;
+}
+
+void FFNvDecoder::releaseFFImgInfo(FFImgInfo* info){
+	if(nullptr != info){
+		if(info->pData != nullptr){
+			av_free(info->pData);
+			info->pData = nullptr;
+		}
+		delete info;
+		info = nullptr;
+	}
 }
\ No newline at end of file
diff --git a/src/FFNvDecoder.h b/src/FFNvDecoder.h
index 35e016e..5a01dae 100644
--- a/src/FFNvDecoder.h
+++ b/src/FFNvDecoder.h
@@ -12,6 +12,7 @@ extern "C"
 	#include <libavutil/avutil.h> 
     #include <libavutil/pixdesc.h> 
 	#include <libswscale/swscale.h>
+    #include <libavutil/imgutils.h>
 }
 
 using namespace std;
@@ -29,14 +30,22 @@ using namespace std;
 **************************************************/
 typedef void(*POST_DECODE_CALLBACK)(const void * userPtr, AVFrame * gpuFrame);
 
-struct FFDecConfig
-{
+typedef void(*DECODE_FINISHED_CALLBACK)(const void* userPtr);
+
+struct FFDecConfig{
     string uri;                             // 视频地址
     POST_DECODE_CALLBACK post_decoded_cbk;  // 解码数据回调接口
+    DECODE_FINISHED_CALLBACK decode_finished_cbk; // 解码线程结束后的回调接口
     string gpuid;                           // gpu id
     bool force_tcp{true};                   // 是否指定使用tcp连接
 };
 
+struct FFImgInfo{
+    int width;
+    int height;
+    unsigned char * pData;
+};
+
 class FFNvDecoder{
 public:
     FFNvDecoder();
@@ -51,6 +60,7 @@ public:
 
     bool isRunning();
     bool isFinished();
+    bool isPausing();
     bool getResolution( int &width, int &height );
 
     void setName(string nm);
@@ -58,6 +68,12 @@ public:
 
     bool isSurport(FFDecConfig& cfg);
 
+    int getCachedQueueLength();
+
+    static FFImgInfo* snapshot(const string& uri);
+
+    static void releaseFFImgInfo(FFImgInfo*);
+
 public:
     AVPixelFormat getHwPixFmt();
 
@@ -69,6 +85,7 @@ private:
 
 public:
     POST_DECODE_CALLBACK post_decoded_cbk;
+    DECODE_FINISHED_CALLBACK decode_finished_cbk;
     const void * m_userPtr;
     FFDecConfig m_cfg;
 
diff --git a/src/FFNvDecoderManager.cpp b/src/FFNvDecoderManager.cpp
index 2624c39..8f6ebc2 100644
--- a/src/FFNvDecoderManager.cpp
+++ b/src/FFNvDecoderManager.cpp
@@ -1,17 +1,17 @@
 #include "FFNvDecoderManager.h"
-#include<iostream>
 
 using namespace std;
 
 
-FFNvDecoder* FFNvDecoderManager::createDecoder(MgrDecConfig& config){
+FFNvDecoder* FFNvDecoderManager::createDecoder(MgrDecConfig config){
 
     closeAllFinishedDecoder();
 
-    int num = decoderMap.count(config.name);
-    if (num > 0)
-    {
-        cout << "已存在name所标记的解码器" << endl;
+    std::lock_guard<std::mutex> l(m_mutex);
+
+    auto it = decoderMap.find(config.name);
+    if (it != decoderMap.end()){
+        av_log(NULL, AV_LOG_ERROR, "已存在name所标记的解码器 \n");
         return nullptr;
     }
     
@@ -26,6 +26,7 @@ FFNvDecoder* FFNvDecoderManager::createDecoder(MgrDecConfig& config){
     {
         dec->setName(config.name) ;
         dec->post_decoded_cbk = config.cfg.post_decoded_cbk;
+        dec->decode_finished_cbk = config.cfg.decode_finished_cbk;
         decoderMap[config.name] = dec;
         return dec;
     }
@@ -41,10 +42,12 @@ bool FFNvDecoderManager::setUserPtr(const string name, const void * userPtr)
 {
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
@@ -52,7 +55,7 @@ bool FFNvDecoderManager::setUserPtr(const string name, const void * userPtr)
         return true;
     }
 
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
 }
 
@@ -60,17 +63,19 @@ FFNvDecoder* FFNvDecoderManager::getDecoderByName(const string name)
 {
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return nullptr;
     }
     
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
         return dec->second;
     }
 
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return nullptr;
 }
 
@@ -84,10 +89,12 @@ void FFNvDecoderManager::startDecode(FFNvDecoder* dec){
 bool FFNvDecoderManager::startDecodeByName(const string name){
      if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
@@ -95,11 +102,14 @@ bool FFNvDecoderManager::startDecodeByName(const string name){
         return true;
     }
 
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
 }
 
 void FFNvDecoderManager::startAllDecode(){
+
+    std::lock_guard<std::mutex> l(m_mutex);
+
     for(auto iter = decoderMap.begin(); iter != decoderMap.end(); iter++){
         if (!iter->second->isRunning())
         {
@@ -111,11 +121,12 @@ void FFNvDecoderManager::startAllDecode(){
 bool FFNvDecoderManager::closeDecoderByName(const string name){
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
-    m_mutex_erase.lock();
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
@@ -124,30 +135,29 @@ bool FFNvDecoderManager::closeDecoderByName(const string name){
         dec->second = nullptr;
         decoderMap.erase(dec);
 
-        m_mutex_erase.unlock();
         return true;
     }
     
-    m_mutex_erase.unlock();
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
 }
 
 void FFNvDecoderManager::closeAllDecoder()
 {
-    m_mutex_erase.lock();
+    std::lock_guard<std::mutex> l(m_mutex);
+
     for(auto iter = decoderMap.begin(); iter != decoderMap.end(); iter++){
         iter->second->close();
         delete iter->second;
         iter->second = nullptr;
     }
     decoderMap.clear();
-    m_mutex_erase.unlock();
 }
 
 void FFNvDecoderManager::closeAllFinishedDecoder()
 {
-    m_mutex_erase.lock();
+    std::lock_guard<std::mutex> l(m_mutex);
+
      for(auto iter = decoderMap.begin(); iter != decoderMap.end(); ){
         if (iter->second->isFinished())
         {
@@ -160,13 +170,13 @@ void FFNvDecoderManager::closeAllFinishedDecoder()
             iter++ ;
         }
     }
-    m_mutex_erase.unlock();
 }
 
 int FFNvDecoderManager::count()
 {
     closeAllFinishedDecoder();
 
+    std::lock_guard<std::mutex> l(m_mutex);
     return decoderMap.size();
 }
 
@@ -174,10 +184,12 @@ bool FFNvDecoderManager::pauseDecoder(const string name)
 {
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
@@ -185,7 +197,7 @@ bool FFNvDecoderManager::pauseDecoder(const string name)
         return true;
     }
     
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
 }
 
@@ -193,10 +205,12 @@ bool FFNvDecoderManager::resumeDecoder(const string name)
 {
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
@@ -204,7 +218,7 @@ bool FFNvDecoderManager::resumeDecoder(const string name)
         return true;
     }
     
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
 }
 
@@ -217,17 +231,57 @@ bool FFNvDecoderManager::isSurport(FFDecConfig& cfg)
 bool FFNvDecoderManager::isRunning(const string name){
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
         return dec->second->isRunning();
     }
     
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
+    return false;
+}
+
+bool FFNvDecoderManager::isFinished(const string name){
+    if (name.empty())
+    {
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
+        return false;
+    }
+
+    std::lock_guard<std::mutex> l(m_mutex);
+
+    auto dec = decoderMap.find(name);
+    if (dec != decoderMap.end())
+    {
+        return dec->second->isFinished();
+    }
+    
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
+    return false;
+}
+
+bool FFNvDecoderManager::isPausing(const string name){
+    if (name.empty())
+    {
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
+        return false;
+    }
+
+    std::lock_guard<std::mutex> l(m_mutex);
+
+    auto dec = decoderMap.find(name);
+    if (dec != decoderMap.end())
+    {
+        return dec->second->isPausing();
+    }
+    
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
 }
 
@@ -235,10 +289,12 @@ bool FFNvDecoderManager::setDecKeyframe(const string name, bool bKeyframe)
 {
     if (name.empty())
     {
-        cout << "name 为空!"<< endl;
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
         return false;
     }
 
+    std::lock_guard<std::mutex> l(m_mutex);
+
     auto dec = decoderMap.find(name);
     if (dec != decoderMap.end())
     {
@@ -246,6 +302,65 @@ bool FFNvDecoderManager::setDecKeyframe(const string name, bool bKeyframe)
         return true;
     }
     
-    cout << "没有找到name为" << name << "的解码器!" << endl;
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
+    return false;
+}
+
+bool FFNvDecoderManager::getResolution(const string name, int &width, int &height)
+{
+    if (name.empty())
+    {
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
+        return false;
+    }
+
+    std::lock_guard<std::mutex> l(m_mutex);
+
+    auto dec = decoderMap.find(name);
+    if (dec != decoderMap.end())
+    {
+        dec->second->getResolution(width, height);
+        return true;
+    }
+    
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
     return false;
+}
+
+vector<string> FFNvDecoderManager::getAllDecodeName(){
+    
+    closeAllFinishedDecoder();
+
+    std::lock_guard<std::mutex> l(m_mutex);
+
+    vector<string> decode_names;
+    for(auto it = decoderMap.begin(); it != decoderMap.end(); ++it){
+        decode_names.push_back(it->first);
+    }
+    return decode_names;
+}
+
+int FFNvDecoderManager::getCachedQueueLength(const string name){
+    if (name.empty()){
+        av_log(NULL, AV_LOG_ERROR, "name 为空! \n");
+        return -1;
+    }
+
+    std::lock_guard<std::mutex> l(m_mutex);
+
+    auto dec = decoderMap.find(name);
+    if (dec != decoderMap.end()){
+        return dec->second->getCachedQueueLength();
+    }
+    
+    av_log(NULL, AV_LOG_ERROR, "没有找到name为 %s 的解码器! \n", name.c_str());
+    return -1;
+}
+
+FFImgInfo* FFNvDecoderManager::snapshot(const string& uri){
+    return FFNvDecoder::snapshot(uri);
+}
+
+void FFNvDecoderManager::releaseFFImgInfo(FFImgInfo* info){
+    FFNvDecoder::releaseFFImgInfo(info);
 }
\ No newline at end of file
diff --git a/src/FFNvDecoderManager.h b/src/FFNvDecoderManager.h
index 0f6f075..bae7838 100644
--- a/src/FFNvDecoderManager.h
+++ b/src/FFNvDecoderManager.h
@@ -45,7 +45,7 @@ public:
 	* 返回:成功返回解码器, 失败返回 nullptr
 	* 备注:
 	**************************************************/
-    FFNvDecoder* createDecoder(MgrDecConfig& config);
+    FFNvDecoder* createDecoder(MgrDecConfig config);
 
     /**************************************************
 	* 接口:setUserPtr
@@ -111,6 +111,15 @@ public:
 	**************************************************/
     void closeAllDecoder();
 
+	/**************************************************
+	* 接口:closeAllDecoderByGpuid
+	* 功能:关闭某张显卡撒花姑娘的全部解码器
+	* 参数:const string gpuid gpu的id
+	* 返回:void
+	* 备注:
+	**************************************************/
+    void closeAllDecoderByGpuid(const string gpuid);
+
     /**************************************************
 	* 接口:pauseDecoder
 	* 功能:暂停指定名称的解码器
@@ -147,6 +156,24 @@ public:
 	**************************************************/
     bool isRunning(const string name);
 
+	/**************************************************
+	* 接口:isFinished
+	* 功能:根据解码器名称判断解码器是否已经结束
+	* 参数:const string name 解码器名称
+	* 返回:正在运行返回true,否则返回false
+	* 备注:
+	**************************************************/
+    bool isFinished(const string name);
+
+	/**************************************************
+	* 接口:isPausing
+	* 功能:根据解码器名称判断解码器是否暂停
+	* 参数:const string name 解码器名称
+	* 返回:正在运行返回true,否则返回false
+	* 备注:
+	**************************************************/
+    bool isPausing(const string name);
+
     /**************************************************
 	* 接口:count
 	* 功能:获取正在运行的解码器数量
@@ -161,11 +188,58 @@ public:
 	* 功能:设置是否只解码关键帧。默认全解
 	* 参数:const string name 解码器名称
 	*		bool bKeyframe   是否只解码关键帧。true,只解码关键帧;false,普通的全解码
-	* 返回:void
+	* 返回:bool 成功返回true,失败返回false
 	* 备注:
 	**************************************************/
 	bool setDecKeyframe(const string name, bool bKeyframe);
 
+	/**************************************************
+	* 接口:getResolution
+	* 功能:获取视频分辨率
+	* 参数:const string name 解码器名称
+	*		int &width   从 width 返回视频宽度
+	*		int &height	 从 height 返回视频高度
+	* 返回:bool 成功获取返回true,失败返回false
+	* 备注:
+	**************************************************/
+	bool getResolution(const string name, int &width, int &height);
+
+	/**************************************************
+	* 接口:getAllDecodeName
+	* 功能:获取全部解码器名称
+	* 参数:void
+	* 返回:vector<string> 返回全部解码器名称
+	* 备注:
+	**************************************************/
+	vector<string> getAllDecodeName();
+
+	/**************************************************
+	* 接口:getCachedQueueLength
+	* 功能:获取解码缓冲队列当前长度
+	* 参数:const string name 解码器名称
+	* 返回:int 解码缓冲队列当前长度
+	* 备注:
+	**************************************************/
+	int getCachedQueueLength(const string name);
+
+	/**************************************************
+	* 接口:snapshot
+	* 功能:获取视频快照
+	* 参数:const string& uri 视频地址
+	* 返回:FFImgInfo* 快照信息
+	* 备注:
+	**************************************************/
+	FFImgInfo* snapshot(const string& uri);
+
+	/**************************************************
+	* 接口:releaseFFImgInfo
+	* 功能:释放视频快照信息
+	* 参数:FFImgInfo* info 视频快照信息
+	* 返回:void
+	* 备注:
+	**************************************************/
+	void releaseFFImgInfo(FFImgInfo* info);
+
 private:
     FFNvDecoderManager(){}
     
@@ -174,5 +248,5 @@ private:
 private:
     map<string, FFNvDecoder*> decoderMap;
 
-    mutex m_mutex_erase;
+    mutex m_mutex;
 };
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 00a61d9..1d08668 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -225,8 +225,16 @@ int CheckCUDAProperty( int devId )
 	return 0;
 }
 
+void logFF(void *, int level, const char *fmt, va_list ap)
+{
+    vfprintf(stdout, fmt, ap);
+}
+
+
 int main(){
 
+    // av_log_set_callback(&logFF);
+
     CheckCUDAProperty(0);
 
     FFNvDecoderManager* pDecManager = FFNvDecoderManager::getInstance();
--
libgit2 0.21.4