Blame view

src/decoder/dvpp/FFRecoder.cpp 11.6 KB
09c2d08c   Hu Chunming   arm交付版
1
2
3
4
5
  #include "FFRecoder.h"
  
  #include <tuple>
  #include <array>
  #include <vector>
09c2d08c   Hu Chunming   arm交付版
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  
  
  FFRecoder::FFRecoder()
  	:width_{},
  	height_{},
  	y_size_{},
  	uv_size_{},
  	pts_{},
  	codec_ctx_{ nullptr },
  	fmt_ctx_{ nullptr },
  	out_stream_{ nullptr },
  	yuv_frame_{ nullptr },
  	img_convert_ctx{nullptr}
  {
  	bFirstFrame = true;
  	last_src_pts = 0;
  	last_pts = 0;
  }
  
  FFRecoder::~FFRecoder()
  {
09c2d08c   Hu Chunming   arm交付版
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  }
  
  
  bool FFRecoder::init(int w, int h, AVRational time_base, AVCodecContext* avctx, const char* outfile_name)
  {
  	uninit();
  
  	width_ = w;
  	height_ = h;
  	y_size_ = w * h;
  	uv_size_ = y_size_ / 4;
  
  	// [1] 创建解码器
  	const AVCodec* encoder = avcodec_find_encoder(AV_CODEC_ID_HEVC);
  	if (!encoder) {
e6fa5f85   Hu Chunming   日志优化
42
  		LOG_ERROR("Find encoder AV_CODEC_ID_H264 failed!");
09c2d08c   Hu Chunming   arm交付版
43
44
45
46
47
  		return false;
  	}
  	// 获取解码器上下文
  	codec_ctx_ = avcodec_alloc_context3(encoder);
  	if (!codec_ctx_) {
e6fa5f85   Hu Chunming   日志优化
48
  		LOG_ERROR("Alloc context for encoder contx failed!");
09c2d08c   Hu Chunming   arm交付版
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
  		return false;
  	}
  	// 设置解码器上下文参数
  	codec_ctx_->bit_rate = avctx->bit_rate;
  	codec_ctx_->width = width_;
  	codec_ctx_->height = height_;
  	codec_ctx_->time_base = time_base;
  	codec_ctx_->gop_size = avctx->gop_size;
  	codec_ctx_->max_b_frames = avctx->max_b_frames;
  	codec_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
  	codec_ctx_->thread_count = 4;
  	codec_ctx_->qmin = avctx->qmin;
  	codec_ctx_->qmax = avctx->qmax;
  	codec_ctx_->qcompress = avctx->qcompress;
  	codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  
  	av_opt_set(codec_ctx_->priv_data, "preset", "ultrafast", 0);
  	av_opt_set(codec_ctx_->priv_data, "tune", "zerolatency", 0);
  
  	// 打开解码器
  	int ret = avcodec_open2(codec_ctx_, encoder, nullptr);
  	if (ret < 0) {
e6fa5f85   Hu Chunming   日志优化
71
  		LOG_ERROR("Open encoder failed!");
09c2d08c   Hu Chunming   arm交付版
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  		return false;
  	}
  
  	// [2] 创建输出上下文
  	avformat_alloc_output_context2(&fmt_ctx_, nullptr, nullptr, outfile_name);
  
  	// [3] 添加输出视频流
  	out_stream_ = avformat_new_stream(fmt_ctx_, nullptr);
  	out_stream_->id = 0;
  	out_stream_->codecpar->codec_tag = 0;
  	avcodec_parameters_from_context(out_stream_->codecpar, codec_ctx_);
  	out_stream_->time_base = { 1,30 };
  
  	av_dump_format(fmt_ctx_, out_stream_->id, outfile_name, 1);
  
  	// 创建YUV格式帧
  	yuv_frame_ = av_frame_alloc();
  	yuv_frame_->format = AV_PIX_FMT_YUV420P;
  	yuv_frame_->width = width_;
  	yuv_frame_->height = height_;
  	// 为创建的YUV帧分配内存
  	if (av_frame_get_buffer(yuv_frame_, 0) < 0) {
  		av_frame_free(&yuv_frame_);
  		yuv_frame_ = nullptr;
e6fa5f85   Hu Chunming   日志优化
96
  		LOG_ERROR("Frame get buffer failed!");
09c2d08c   Hu Chunming   arm交付版
97
98
99
100
101
  		return false;
  	}
  
  	// [5] 打开输出视频文件并写入视频头信息
  	if (avio_open(&fmt_ctx_->pb, outfile_name, AVIO_FLAG_WRITE) < 0) {
e6fa5f85   Hu Chunming   日志优化
102
  		LOG_ERROR("avio_open  failed!");
09c2d08c   Hu Chunming   arm交付版
103
104
105
  		return false;
  	}
  	if (avformat_write_header(fmt_ctx_, nullptr) < 0) {
e6fa5f85   Hu Chunming   日志优化
106
  		LOG_ERROR("Write header failed!");
09c2d08c   Hu Chunming   arm交付版
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  		return false;
  	}
  
  	// 计算解码后原始数据所需缓冲区大小,并分配内存空间 Determine required buffer size and allocate buffer
  	int numBytes = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, w, h, 1);
  	out_buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
  
  	//pFrameOut = av_frame_alloc();
  	//av_image_fill_arrays(pFrameOut->data, pFrameOut->linesize, buffer, AV_PIX_FMT_YUV420P, w, h, 1);
  
  	img_convert_ctx = sws_getContext(avctx->width, avctx->height, avctx->pix_fmt, w, h, AV_PIX_FMT_YUV420P,
  		SWS_BICUBIC, nullptr, nullptr, nullptr);
  
  	return true;
  }
  
bf661eb0   Hu Chunming   录像文件保存优化
123
  bool FFRecoder::init(AVStream* stream, AVCodecContext* avctx, const char* outfile_name) {
09c2d08c   Hu Chunming   arm交付版
124
  
a63dd3d2   Hu Chunming   修复recode初始化崩溃问题
125
126
127
128
129
130
131
132
133
134
135
136
  	const AVCodec* encoder = avcodec_find_encoder(avctx->codec_id);
  	if (!encoder) {
  		LOG_ERROR("Find encoder AV_CODEC_ID_H264 failed!");
  		return false;
  	}
  	// 获取解码器上下文
  	codec_ctx_ = avcodec_alloc_context3(encoder);
  	if (!codec_ctx_) {
  		LOG_ERROR("Alloc context for encoder contx failed!");
  		return false;
  	}
  
09c2d08c   Hu Chunming   arm交付版
137
  	avcodec_copy_context(codec_ctx_, avctx);
e01a0397   Hu Chunming   代码优化;
138
  	codec_ctx_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
bf661eb0   Hu Chunming   录像文件保存优化
139
  	m_inStream = stream;
09c2d08c   Hu Chunming   arm交付版
140
141
142
143
144
145
  
  	// [2] 创建输出上下文
  	avformat_alloc_output_context2(&fmt_ctx_, nullptr, nullptr, outfile_name);
  
  	// [3] 添加输出视频流
  	out_stream_ = avformat_new_stream(fmt_ctx_, nullptr);
e01a0397   Hu Chunming   代码优化;
146
  
09c2d08c   Hu Chunming   arm交付版
147
148
149
  	out_stream_->id = 0;
  	out_stream_->codecpar->codec_tag = 0;
  	avcodec_parameters_from_context(out_stream_->codecpar, codec_ctx_);
e01a0397   Hu Chunming   代码优化;
150
  	// out_stream_->time_base = { 1,25 };
bf661eb0   Hu Chunming   录像文件保存优化
151
  	out_stream_->time_base = stream->time_base;
e01a0397   Hu Chunming   代码优化;
152
153
154
155
156
157
158
  	out_stream_->r_frame_rate = stream->r_frame_rate;
  	out_stream_->avg_frame_rate = stream->r_frame_rate;
  
  	codec_ctx_->time_base = out_stream_->time_base;
  
  	av_opt_set(out_stream_->codec->priv_data, "preset", "ultrafast", 0);
  	av_opt_set(out_stream_->codec->priv_data, "tune", "zerolatency", 0);
09c2d08c   Hu Chunming   arm交付版
159
  
fbdee5c4   Hu Chunming   修正recode保存异常问题
160
  	// av_dump_format(fmt_ctx_, out_stream_->id, outfile_name, 1);
09c2d08c   Hu Chunming   arm交付版
161
162
163
  
  	// [5] 打开输出视频文件并写入视频头信息
  	if (avio_open(&fmt_ctx_->pb, outfile_name, AVIO_FLAG_WRITE) < 0) {
e6fa5f85   Hu Chunming   日志优化
164
  		LOG_ERROR("avio_open  failed!");
09c2d08c   Hu Chunming   arm交付版
165
166
167
  		return false;
  	}
  	if (avformat_write_header(fmt_ctx_, nullptr) < 0) {
e6fa5f85   Hu Chunming   日志优化
168
  		LOG_ERROR("Write header failed!");
09c2d08c   Hu Chunming   arm交付版
169
170
  		return false;
  	}
746db74c   Hu Chunming   实现recode
171
172
  
  	return true;
09c2d08c   Hu Chunming   arm交付版
173
174
  }
  
e01a0397   Hu Chunming   代码优化;
175
176
177
178
179
180
181
182
183
184
185
186
  void FFRecoder::release() {
  	av_write_trailer(fmt_ctx_);
  
      avcodec_close(fmt_ctx_->streams[0]->codec);
      av_freep(&fmt_ctx_->streams[0]->codec);
      av_freep(&fmt_ctx_->streams[0]);
  
      avio_close(fmt_ctx_->pb);
      av_free(fmt_ctx_);
  	fmt_ctx_ = nullptr;
  }
  
09c2d08c   Hu Chunming   arm交付版
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  void FFRecoder::uninit()
  {
  	//if (out_buffer) {
  	//	av_free(out_buffer);
  	//}
  
  	if (yuv_frame_) {
  		av_frame_free(&yuv_frame_);
  		yuv_frame_ = nullptr;
  	}
  
  	if (img_convert_ctx) {
  		sws_freeContext(img_convert_ctx);
  		img_convert_ctx = nullptr;
  	}
  
  	if (fmt_ctx_) {
  		av_write_trailer(fmt_ctx_);
  		avio_close(fmt_ctx_->pb);
  		avformat_free_context(fmt_ctx_);
  		fmt_ctx_ = nullptr;
  	}
  
  	if (codec_ctx_) {
  		avcodec_close(codec_ctx_);
  		avcodec_free_context(&codec_ctx_);
  		codec_ctx_ = nullptr;
  	}
  
  	width_ = 0;
  	height_ = 0;
  	y_size_ = 0;
  	uv_size_ = 0;
  	pts_ = 0;
  }
  
  bool FFRecoder::write_image(const uint8_t* bgr)
  {
  	// 分配YUV格式数据的内存
  	thread_local std::vector<uint8_t> yuv_data;
  	if (yuv_data.size() != y_size_ * 3 / 2) {
  		yuv_data.resize(y_size_ * 3 / 2);
  	}
  	// BGR格式转YUV格式
  	bgr_to_yuv420p(bgr, yuv_data.data());
  
  	return write_yuv(yuv_data.data());
  }
  
  bool FFRecoder::write_yuv(const uint8_t* yuv_data)
  {
  	//拷贝YUV数据到帧,由于帧数据存在内存对齐,故需逐行拷贝
  	for (int i = 0; i < height_; i++) {
  		memcpy(yuv_frame_->data[0] + i * yuv_frame_->linesize[0], yuv_data + width_ * i, width_);
  	}
  	const int uv_stride = width_ / 2;
  	for (int i = 0; i < height_ / 2; i++) {
  		memcpy(yuv_frame_->data[1] + i * yuv_frame_->linesize[1], yuv_data + y_size_ + uv_stride * i, uv_stride);
  		memcpy(yuv_frame_->data[2] + i * yuv_frame_->linesize[2], yuv_data + y_size_ + uv_size_ + uv_stride * i, uv_stride);
  	}
  
  	yuv_frame_->pts = pts_++;
  
  	return write_frame(yuv_frame_);
  }
  
  void FFRecoder::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;
  	}
  	
  }
  
  bool FFRecoder::write_pkt(AVPacket *pkt) {
746db74c   Hu Chunming   实现recode
277
278
  	char errbuf[64]{ 0 };
  
bf661eb0   Hu Chunming   录像文件保存优化
279
280
281
282
  	// av_packet_rescale_ts(pkt, codec_ctx_->time_base, out_stream_->time_base);
  	// update_pts(pkt);
  	// pkt->stream_index = out_stream_->index;
  
e01a0397   Hu Chunming   代码优化;
283
  	if(pkt->pts==AV_NOPTS_VALUE) {
e6fa5f85   Hu Chunming   日志优化
284
  		// printf("frame_index:%d", frame_index);
bf661eb0   Hu Chunming   录像文件保存优化
285
286
287
288
289
290
291
292
293
  		//Write PTS
  		AVRational time_base1 = codec_ctx_->time_base;
  		//Duration between 2 frames (us)
  		int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(m_inStream->r_frame_rate);
  		//Parameters
  		pkt->pts = (double)(frame_index*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
  		pkt->dts = pkt->pts;
  		pkt->duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
  		frame_index++;
e01a0397   Hu Chunming   代码优化;
294
  	} 
bf661eb0   Hu Chunming   录像文件保存优化
295
296
297
298
  	// Convert PTS/DTS
  	pkt->pts = av_rescale_q_rnd(pkt->pts, codec_ctx_->time_base, out_stream_->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
  	pkt->dts = av_rescale_q_rnd(pkt->dts, codec_ctx_->time_base, out_stream_->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
  	pkt->duration = av_rescale_q(pkt->duration, codec_ctx_->time_base, out_stream_->time_base);
e01a0397   Hu Chunming   代码优化;
299
  	
bf661eb0   Hu Chunming   录像文件保存优化
300
  	pkt->pos = -1;
09c2d08c   Hu Chunming   arm交付版
301
  	pkt->stream_index = out_stream_->index;
e01a0397   Hu Chunming   代码优化;
302
  	fmt_ctx_->duration += pkt->duration;
bf661eb0   Hu Chunming   录像文件保存优化
303
  	
09c2d08c   Hu Chunming   arm交付版
304
  	// 将数据写入到输出流
fbdee5c4   Hu Chunming   修正recode保存异常问题
305
  	int ret = av_write_frame(fmt_ctx_, pkt);
09c2d08c   Hu Chunming   arm交付版
306
  	if (ret < 0) {
e6fa5f85   Hu Chunming   日志优化
307
  		LOG_ERROR("Error while writing output packet: {}", av_make_error_string(errbuf, sizeof(errbuf), ret));
09c2d08c   Hu Chunming   arm交付版
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
  		return false;
  	}
  	return true;
  }
  
  bool FFRecoder::write_frame(AVFrame* frame)
  {
  	AVFrame *pFrameOut = nullptr;
  	if (frame != nullptr && frame->format != AV_PIX_FMT_YUV420P) {
  		pFrameOut = av_frame_clone(frame);
  		pFrameOut->format = AV_PIX_FMT_YUV420P;
  		av_image_fill_arrays(pFrameOut->data, pFrameOut->linesize, out_buffer, AV_PIX_FMT_YUV420P, frame->width, frame->height, 1);
  		sws_scale(img_convert_ctx, (const unsigned char* const*)frame->data, frame->linesize, 0, frame->height, pFrameOut->data, pFrameOut->linesize);
  	}
  	else {
  		pFrameOut = frame;
  	}
  	char errbuf[64]{ 0 };
  	// 将帧数据发送到编码器
  	int ret = avcodec_send_frame(codec_ctx_, pFrameOut);
  	av_frame_free(&pFrameOut);
  	if (ret < 0) {
e6fa5f85   Hu Chunming   日志优化
330
  		LOG_ERROR("Error sending a frame to the encoder: {}", av_make_error_string(errbuf, sizeof(errbuf), ret));
09c2d08c   Hu Chunming   arm交付版
331
332
333
334
335
336
337
338
339
340
  		return false;
  	}
  
  	while (true) {
  		AVPacket pkt{ 0 };
  		// 获取编码后的数据
  		ret = avcodec_receive_packet(codec_ctx_, &pkt);
  		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
  			return true;
  		else if (ret < 0) {
e6fa5f85   Hu Chunming   日志优化
341
  			LOG_ERROR("Error encoding a frame: {}", av_make_error_string(errbuf, sizeof(errbuf), ret));
09c2d08c   Hu Chunming   arm交付版
342
343
344
345
346
347
348
349
350
351
352
  			return false;
  		}
  		// pts缩放到输出流的time_base
  		av_packet_rescale_ts(&pkt, codec_ctx_->time_base, out_stream_->time_base);
  		pkt.stream_index = out_stream_->index;
  		update_pts(&pkt);
  		// 将数据写入到输出流
  		ret = av_interleaved_write_frame(fmt_ctx_, &pkt);
  		//ret = av_write_frame(fmt_ctx_, &pkt);
  		av_packet_unref(&pkt);
  		if (ret < 0) {
e6fa5f85   Hu Chunming   日志优化
353
  			LOG_ERROR("Error while writing output packet: {}", av_make_error_string(errbuf, sizeof(errbuf), ret));
09c2d08c   Hu Chunming   arm交付版
354
355
356
357
358
359
360
361
362
363
364
365
366
367
  			return false;
  		}
  	/*	av_interleaved_write_frame(fmt_ctx_, nullptr);
  		avio_flush(fmt_ctx_->pb);*/
  	}
  
  	return true;
  }
  
  bool FFRecoder::flush()
  {
  	return write_frame(nullptr);
  }
  
e01a0397   Hu Chunming   代码优化;
368
369
  bool FFRecoder::flush_pkt()
  {
fbdee5c4   Hu Chunming   修正recode保存异常问题
370
  	return av_write_frame(fmt_ctx_, nullptr);
e01a0397   Hu Chunming   代码优化;
371
372
  }
  
09c2d08c   Hu Chunming   arm交付版
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
  bool FFRecoder::bgr_to_yuv420p(const uint8_t* const buf_bgr, uint8_t* const buf_420p)
  {
  	// 分配转换上下文
  	thread_local std::tuple<int,int,int> params{ 0, 0, 0 };
  	thread_local std::unique_ptr<SwsContext, decltype(&sws_freeContext)> sws_context{ nullptr, &sws_freeContext };
  
  	std::tuple<int, int, int> new_params{ width_, height_, av_image_get_linesize(AV_PIX_FMT_YUV420P, width_, 0) };
  	if (!sws_context || params != new_params)
  	{
  		sws_context.reset(sws_getContext(width_, height_, AV_PIX_FMT_BGR24, width_, height_,
  			AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr));
  		params = new_params;
  	}
  	// 转换格式
  	const int stride = std::get<2>(params);//Y平面一行的数据长度
  	//const int ret = sws_scale(sws_context.get(),
  	//	std::array<const uint8_t* const>{ buf_bgr }.data(),/* bgr数据只有一个平面 */
  	//	std::array{ width_ * 3 }.data(),/* BGR所以图像宽度*3 */
  	//	0, height_,
  	//	std::array{ buf_420p, buf_420p + y_size_, buf_420p + y_size_ + uv_size_ }.data(),/* YUV三个平面的起始地址 */
  	//	std::array{ stride, stride / 2, stride / 2 }.data());/* YUV每个平面中一行的宽度 */
  	const int rgba_linesize = width_ * 3;
  	int yuv_linesize[3] = { stride, stride / 2, stride / 2 };
  	int ret = sws_scale(sws_context.get(), (const uint8_t* const*)buf_bgr, &rgba_linesize, 0, height_, (uint8_t* const*)buf_420p, yuv_linesize);
  
  	return 0;
  }