Blame view

src/FFNvDecoder.cpp 7.76 KB
aac5773f   hucm   功能基本完成,接口待打磨
1
  #include "FFNvDecoder.h"
aac5773f   hucm   功能基本完成,接口待打磨
2
3
4
  
  #include <chrono>
  #include <thread>
8c180bab   hucm   添加是否实时流判断
5
  #include <fstream>
aac5773f   hucm   功能基本完成,接口待打磨
6
  
3d2ab595   Hu Chunming   支持gb28181
7
8
  #include <chrono>
  
f40cc409   Hu Chunming   优化显存占用。当前在3080显卡上...
9
10
  #include "FFCuContextManager.h"
  
3d2ab595   Hu Chunming   支持gb28181
11
12
  #include "logger.hpp"
  
92989af0   ming   更新解码器
13
14
  #include "utiltools.hpp"
  
aac5773f   hucm   功能基本完成,接口待打磨
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  using namespace std;
  
  // 参考博客: https://blog.csdn.net/qq_40116098/article/details/120704340
  
  static AVPixelFormat get_hw_format(AVCodecContext *avctx, const AVPixelFormat *pix_fmts)
  {
  	FFNvDecoder* _this = (FFNvDecoder*)avctx->opaque;
  
  	const AVPixelFormat *p;
  
  	for (p = pix_fmts; *p != -1; p++) {
  		if (*p == _this->getHwPixFmt())
  			return *p;
  	}
  
3d2ab595   Hu Chunming   支持gb28181
30
  	LOG_ERROR("Failed to get HW surface format");
aac5773f   hucm   功能基本完成,接口待打磨
31
32
33
34
35
36
37
38
39
40
41
42
43
  	return AV_PIX_FMT_NONE;
  }
  
  FFNvDecoder::FFNvDecoder()
  {
  	// 初始化解码对象
  	fmt_ctx = nullptr;
  	avctx = nullptr;
  	m_bRunning = false;
  
  	stream = nullptr;
      stream_index = -1;
      hw_pix_fmt = AV_PIX_FMT_NONE;
3d2ab595   Hu Chunming   支持gb28181
44
      m_dec_name = "";
aac5773f   hucm   功能基本完成,接口待打磨
45
46
  
  	m_bPause = false;
8c180bab   hucm   添加是否实时流判断
47
  	m_bReal = true;
e96e6489   Hu Chunming   优化代码;添加isRunning函数
48
49
50
  
  	m_decode_thread = 0;
  	m_post_decode_thread = 0;
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
51
52
  
  	m_bFinished = false;
bc52e542   Hu Chunming   添加关键帧解码功能
53
  	m_dec_keyframe = false;
3d2ab595   Hu Chunming   支持gb28181
54
  	m_fps = 0.0;
aac5773f   hucm   功能基本完成,接口待打磨
55
56
57
58
  }
  
  FFNvDecoder::~FFNvDecoder()
  {
bc52e542   Hu Chunming   添加关键帧解码功能
59
  	m_dec_keyframe = false;
aac5773f   hucm   功能基本完成,接口待打磨
60
61
  }
  
e96e6489   Hu Chunming   优化代码;添加isRunning函数
62
  bool FFNvDecoder::init(FFDecConfig& cfg)
7319ea36   Hu Chunming   多显卡设置
63
64
65
66
67
68
69
70
71
72
73
  {
  	m_cfg = cfg;
  
  	fstream infile(cfg.uri);
  	if (infile.is_open()){
  		m_bReal = false;
  		infile.close();
  	}else {
  		m_bReal = true;
  	}
  
372e629f   ming   gb28181支持TCP数据流
74
75
76
  	post_decoded_cbk = cfg.post_decoded_cbk;
      decode_finished_cbk = cfg.decode_finished_cbk;
  
e96e6489   Hu Chunming   优化代码;添加isRunning函数
77
78
79
80
81
  	return init(cfg.uri.c_str(), cfg.gpuid.c_str(),cfg.force_tcp);
  }
  
  bool FFNvDecoder::init(const char* uri, const char* gpuid, bool force_tcp)
  {
f40cc409   Hu Chunming   优化显存占用。当前在3080显卡上...
82
83
  	// av_log_set_level(AV_LOG_DEBUG);
  
e96e6489   Hu Chunming   优化代码;添加isRunning函数
84
  	avformat_network_init();
7319ea36   Hu Chunming   多显卡设置
85
86
87
88
  
  	// 打开输入视频文件
  	AVDictionary *options = nullptr;
  	av_dict_set( &options, "bufsize", "655360", 0 );
e96e6489   Hu Chunming   优化代码;添加isRunning函数
89
  	av_dict_set( &options, "rtsp_transport", force_tcp ? "tcp" : "udp", 0 );
7319ea36   Hu Chunming   多显卡设置
90
  	// av_dict_set( &options, "listen_timeout", "30", 0 ); // 单位为s
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
91
  	av_dict_set( &options, "stimeout", "30000000", 0 ); // 单位为 百万分之一秒
7319ea36   Hu Chunming   多显卡设置
92
93
  	
  	fmt_ctx = avformat_alloc_context();
e96e6489   Hu Chunming   优化代码;添加isRunning函数
94
  	const char* input_file = uri;
7319ea36   Hu Chunming   多显卡设置
95
  	if (avformat_open_input(&fmt_ctx, input_file, nullptr, &options) != 0) {
3d2ab595   Hu Chunming   支持gb28181
96
  		LOG_ERROR("Cannot open input file:{}",input_file);
7319ea36   Hu Chunming   多显卡设置
97
98
99
100
101
  		return false;
  	}
  
  	// 查找流信息
  	if (avformat_find_stream_info(fmt_ctx, nullptr) < 0) {
3d2ab595   Hu Chunming   支持gb28181
102
  		LOG_ERROR("Cannot find input stream information");
7319ea36   Hu Chunming   多显卡设置
103
104
105
106
107
108
109
  		return false;
  	}
  
  	// 查找视频流信息
  	AVCodec *decoder = nullptr;
  	stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
  	if (stream_index < 0) {
3d2ab595   Hu Chunming   支持gb28181
110
  		LOG_ERROR("Cannot find a video stream in the input file");
7319ea36   Hu Chunming   多显卡设置
111
112
113
  		return false;
  	}
  
7319ea36   Hu Chunming   多显卡设置
114
115
116
117
118
119
120
121
122
123
  	string cuvid_dec_name = string(decoder->name) + "_cuvid";
  	AVCodec *vcodec = avcodec_find_decoder_by_name(cuvid_dec_name.c_str());
  	if (!(avctx = avcodec_alloc_context3(vcodec)))
  		return (bool)AVERROR(ENOMEM);
  
  	// 得到视频流对象
  	stream = fmt_ctx->streams[stream_index];
  	if (avcodec_parameters_to_context(avctx, stream->codecpar) < 0)
  		return false;
  
3d2ab595   Hu Chunming   支持gb28181
124
125
  	m_fps = av_q2d(stream ->avg_frame_rate);
  
7319ea36   Hu Chunming   多显卡设置
126
127
128
129
130
131
  	avctx->opaque = this;
  	// 设置解码器管理器的像素格式回调函数
  	avctx->get_format = get_hw_format;
  
  	hw_pix_fmt = AV_PIX_FMT_CUDA;
  
f40cc409   Hu Chunming   优化显存占用。当前在3080显卡上...
132
  	FFCuContextManager* pCtxMgr = FFCuContextManager::getInstance();
d384f0e9   Hu Chunming   代码优化
133
134
135
136
137
138
139
  
  	AVBufferRef *hw_device_ctx = pCtxMgr->getCuCtx(gpuid);
  	if(nullptr == hw_device_ctx){
  		av_log(nullptr, AV_LOG_ERROR, "create CUDA context failed ! \n");
  		return false;
  	}
  	avctx->hw_device_ctx = av_buffer_ref(hw_device_ctx);
f40cc409   Hu Chunming   优化显存占用。当前在3080显卡上...
140
141
142
143
144
  	if (nullptr == avctx->hw_device_ctx)
  	{
  		return false;
  	}
  
7319ea36   Hu Chunming   多显卡设置
145
146
  	// 打开解码器流
  	AVDictionary *op = nullptr;
e96e6489   Hu Chunming   优化代码;添加isRunning函数
147
  	av_dict_set( &op, "gpu", gpuid, 0 );
3d2ab595   Hu Chunming   支持gb28181
148
  	// av_dict_set( &op, "surfaces", "5", 0 );
7319ea36   Hu Chunming   多显卡设置
149
  	if (avcodec_open2(avctx, vcodec, &op) < 0) {
3d2ab595   Hu Chunming   支持gb28181
150
  		LOG_ERROR("Failed to open codec for stream");
7319ea36   Hu Chunming   多显卡设置
151
152
  		return false;
  	}
3d2ab595   Hu Chunming   支持gb28181
153
  	
7319ea36   Hu Chunming   多显卡设置
154
155
156
  	return true;
  }
  
0573bd98   Hu Chunming   优化代码;添加注释
157
158
159
160
161
162
163
  bool FFNvDecoder::isSurport(FFDecConfig& cfg)
  {
  	bool bRet = init(cfg);
      decode_finished();
      return bRet;
  }
  
3d2ab595   Hu Chunming   支持gb28181
164
  bool FFNvDecoder::start(){
aac5773f   hucm   功能基本完成,接口待打磨
165
166
167
168
169
170
171
172
173
174
175
  
  	m_bRunning = true;
  
  	pthread_create(&m_decode_thread,0,
          [](void* arg)
          {
              FFNvDecoder* a=(FFNvDecoder*)arg;
              a->decode_thread();
              return (void*)0;
          }
      ,this);
3d2ab595   Hu Chunming   支持gb28181
176
177
  
  	return true;
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
178
179
  }
  
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
180
181
182
183
184
  void FFNvDecoder::decode_thread()
  {
  	AVPacket* pkt ;
  	pkt = av_packet_alloc();
  	av_init_packet( pkt );
aac5773f   hucm   功能基本完成,接口待打磨
185
186
187
188
189
190
191
192
193
  
  	pthread_create(&m_post_decode_thread,0,
          [](void* arg)
          {
              FFNvDecoder* a=(FFNvDecoder*)arg;
              a->post_decode_thread();
              return (void*)0;
          }
      ,this);
aac5773f   hucm   功能基本完成,接口待打磨
194
  
92989af0   ming   更新解码器
195
  	// long start_time = UtilTools::get_cur_time_ms();
bc52e542   Hu Chunming   添加关键帧解码功能
196
  
aac5773f   hucm   功能基本完成,接口待打磨
197
198
  	while (m_bRunning)
  	{
8c180bab   hucm   添加是否实时流判断
199
  		if (!m_bReal)
aac5773f   hucm   功能基本完成,接口待打磨
200
  		{
8c180bab   hucm   添加是否实时流判断
201
202
203
204
205
  			if (m_bPause)
  			{
  				std::this_thread::sleep_for(std::chrono::milliseconds(3));
  				continue;
  			}
aac5773f   hucm   功能基本完成,接口待打磨
206
  		}
aac5773f   hucm   功能基本完成,接口待打磨
207
208
  		
  		int result = av_read_frame(fmt_ctx, pkt);
3c7e3e11   Hu Chunming   1.修改日志
209
  		if (result == AVERROR_EOF || result < 0)
aac5773f   hucm   功能基本完成,接口待打磨
210
  		{
3d2ab595   Hu Chunming   支持gb28181
211
  			LOG_ERROR("Failed to read frame!");
aac5773f   hucm   功能基本完成,接口待打磨
212
213
214
  			break;
  		}
  
bc52e542   Hu Chunming   添加关键帧解码功能
215
216
217
218
219
  		if (m_dec_keyframe && !(pkt->flags & AV_PKT_FLAG_KEY)) {
  			av_packet_unref(pkt);
  			continue;
  		}
  
f49bbf3d   Hu Chunming   修正pause逻辑;添加ignore
220
  		if (m_bReal)
8c180bab   hucm   添加是否实时流判断
221
222
223
224
225
226
227
228
229
  		{
  			if (m_bPause)
  			{
  				av_packet_unref(pkt);
  				std::this_thread::sleep_for(std::chrono::milliseconds(3));
  				continue;
  			}
  		}
  
3c7e3e11   Hu Chunming   1.修改日志
230
  		if (stream_index == pkt->stream_index){
aac5773f   hucm   功能基本完成,接口待打磨
231
  			result = avcodec_send_packet(avctx, pkt);
3c7e3e11   Hu Chunming   1.修改日志
232
  			if (result < 0){
92989af0   ming   更新解码器
233
  				av_packet_unref(pkt);
3d2ab595   Hu Chunming   支持gb28181
234
  				LOG_ERROR("{} - Failed to send pkt: {}", m_dec_name, result);
aac5773f   hucm   功能基本完成,接口待打磨
235
236
237
  				continue;
  			}
  
92989af0   ming   更新解码器
238
  			AVFrame* gpuFrame = av_frame_alloc();
aac5773f   hucm   功能基本完成,接口待打磨
239
  			result = avcodec_receive_frame(avctx, gpuFrame);
3c7e3e11   Hu Chunming   1.修改日志
240
  			if ((result == AVERROR(EAGAIN) || result == AVERROR_EOF) || result < 0){
3d2ab595   Hu Chunming   支持gb28181
241
  				LOG_ERROR("{} - Failed to receive frame: {}", m_dec_name, result);
92989af0   ming   更新解码器
242
243
  				av_frame_free(&gpuFrame); 
  				av_packet_unref(pkt);
aac5773f   hucm   功能基本完成,接口待打磨
244
245
  				continue;
  			}
92989af0   ming   更新解码器
246
  			av_packet_unref(pkt);
aac5773f   hucm   功能基本完成,接口待打磨
247
  
92989af0   ming   更新解码器
248
249
250
251
252
253
254
255
256
  			if(gpuFrame != nullptr){
  				m_queue_mutex.lock();
  				if(mFrameQueue.size() <= 10){
  					mFrameQueue.push(gpuFrame);
  				}else{
  					av_frame_free(&gpuFrame); 
  				}
  				m_queue_mutex.unlock();
  			}
aac5773f   hucm   功能基本完成,接口待打磨
257
258
259
260
  		}
  		av_packet_unref(pkt);
  	}
  
aac5773f   hucm   功能基本完成,接口待打磨
261
  	m_bRunning = false;
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
262
  
92989af0   ming   更新解码器
263
  	// long end_time = UtilTools::get_cur_time_ms();
bc52e542   Hu Chunming   添加关键帧解码功能
264
265
  	// cout << "解码用时:" << end_time - start_time << endl;
  
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
266
267
268
269
270
  	if (m_post_decode_thread != 0)
  	{
  		pthread_join(m_post_decode_thread,0);
  	}
  
3d2ab595   Hu Chunming   支持gb28181
271
  	decode_finished_cbk(m_finishedDecArg);
3c7e3e11   Hu Chunming   1.修改日志
272
  
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
273
274
  	decode_finished();
  
92989af0   ming   更新解码器
275
276
277
278
279
280
281
  	// 清空队列
  	while(mFrameQueue.size() > 0){
  		AVFrame * gpuFrame = mFrameQueue.front();
  		av_frame_free(&gpuFrame); 
  		mFrameQueue.pop();
  	}
  
3d2ab595   Hu Chunming   支持gb28181
282
  	LOG_INFO("{} - decode thread exited.", m_dec_name);
aac5773f   hucm   功能基本完成,接口待打磨
283
284
  }
  
3d2ab595   Hu Chunming   支持gb28181
285
  void FFNvDecoder::decode_finished(){
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
286
287
  	if (avctx)
  	{
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
288
289
290
291
292
293
294
295
296
  		avcodec_free_context(&avctx);
  	}
  	
  	if (fmt_ctx)
  	{
  		avformat_close_input(&fmt_ctx);
  	}
  
  	m_bFinished = true;
bc52e542   Hu Chunming   添加关键帧解码功能
297
  	m_dec_keyframe = false;
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
298
299
  }
  
3d2ab595   Hu Chunming   支持gb28181
300
301
302
303
304
305
306
  void FFNvDecoder::post_decode_thread(){
  	int skip_frame = m_cfg.skip_frame;
  	if (skip_frame <= 0){
  		skip_frame = 1;
  	}
  	
  	int index = 0;
92989af0   ming   更新解码器
307
  	while (m_bRunning)
3d2ab595   Hu Chunming   支持gb28181
308
  	{
92989af0   ming   更新解码器
309
310
311
312
313
314
315
316
317
318
319
320
  		if(mFrameQueue.size() > 0){
  			std::lock_guard<std::mutex> l(m_snapshot_mutex);
  			// 取队头数据
  			m_queue_mutex.lock();
  			AVFrame * gpuFrame = mFrameQueue.front();
  			mFrameQueue.pop();
  			m_queue_mutex.unlock();
  			// 跳帧
  			if (skip_frame == 1 || index % skip_frame == 0){
  				post_decoded_cbk(m_postDecArg, gpuFrame);
  				index = 0;
  			}
aac5773f   hucm   功能基本完成,接口待打磨
321
  
92989af0   ming   更新解码器
322
  			av_frame_free(&gpuFrame); 
3c7e3e11   Hu Chunming   1.修改日志
323
  
92989af0   ming   更新解码器
324
325
  			index++;
  		}
3d2ab595   Hu Chunming   支持gb28181
326
327
328
  	}
  
  	LOG_INFO("post decode thread exited.");
aac5773f   hucm   功能基本完成,接口待打磨
329
330
  }
  
3d2ab595   Hu Chunming   支持gb28181
331
  void FFNvDecoder::close(){
aac5773f   hucm   功能基本完成,接口待打磨
332
  	m_bRunning=false;
e96e6489   Hu Chunming   优化代码;添加isRunning函数
333
334
335
  	if(m_decode_thread != 0){
  		pthread_join(m_decode_thread,0);
  	}
bc52e542   Hu Chunming   添加关键帧解码功能
336
  	m_dec_keyframe = false;
aac5773f   hucm   功能基本完成,接口待打磨
337
338
  }
  
3d2ab595   Hu Chunming   支持gb28181
339
  AVPixelFormat FFNvDecoder::getHwPixFmt(){
aac5773f   hucm   功能基本完成,接口待打磨
340
341
342
  	return hw_pix_fmt;
  }
  
3d2ab595   Hu Chunming   支持gb28181
343
  bool FFNvDecoder::isRunning(){
aac5773f   hucm   功能基本完成,接口待打磨
344
345
346
  	return m_bRunning;
  }
  
3d2ab595   Hu Chunming   支持gb28181
347
  bool FFNvDecoder::isFinished(){
48330793   Hu Chunming   修正解码线程自然结束时解码器内存没...
348
349
350
  	return m_bFinished;
  }
  
3c7e3e11   Hu Chunming   1.修改日志
351
352
353
354
  bool FFNvDecoder::isPausing(){
  	return m_bPause;
  }
  
3d2ab595   Hu Chunming   支持gb28181
355
356
  bool FFNvDecoder::getResolution( int &width, int &height ){
  	if (avctx != nullptr)
aac5773f   hucm   功能基本完成,接口待打磨
357
  	{
3d2ab595   Hu Chunming   支持gb28181
358
359
  		width = avctx->width;
  		height = avctx->height;
aac5773f   hucm   功能基本完成,接口待打磨
360
361
362
363
364
365
  		return true;
  	}
  	
  	return false;
  }
  
3d2ab595   Hu Chunming   支持gb28181
366
  void FFNvDecoder::pause(){
aac5773f   hucm   功能基本完成,接口待打磨
367
368
369
  	m_bPause = true;
  }
  
3d2ab595   Hu Chunming   支持gb28181
370
  void FFNvDecoder::resume(){
aac5773f   hucm   功能基本完成,接口待打磨
371
  	m_bPause = false;
bc52e542   Hu Chunming   添加关键帧解码功能
372
373
374
375
376
  }
  
  void FFNvDecoder::setDecKeyframe(bool bKeyframe)
  {
  	m_dec_keyframe = bKeyframe;
3c7e3e11   Hu Chunming   1.修改日志
377
378
379
  }
  
  int FFNvDecoder::getCachedQueueLength(){
92989af0   ming   更新解码器
380
381
382
383
  	m_queue_mutex.lock();
  	int queue_size = mFrameQueue.size(); 
  	m_queue_mutex.lock();
  	return queue_size;
3c7e3e11   Hu Chunming   1.修改日志
384
385
  }
  
3d2ab595   Hu Chunming   支持gb28181
386
387
  float FFNvDecoder::fps(){
  	return m_fps;
d384f0e9   Hu Chunming   代码优化
388
  }