cedarX_venc.c
12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/*
基于aodzip移植的cedarx写的编码文件
拥有 初始化摄像头 释放摄像头 获取拍摄内容3个接口
*/
#include "cedarX_venc.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../CedarX_include/vencoder.h"
#include "../CedarX_include/memoryAdapter.h"
#include "JZsdkLib.h"
#include "version_choose.h"
#if ALLWINNER_CEDAR == VERSION_SWITCH_ON
int g_venc_RecordWidth = 0;
int g_venc_RecordHeight = 0;
int g_venc_RecordCharm = 0;
#define RECORD_FRAME 25
#define RECORD_WIDTH (g_venc_RecordWidth)
#define RECORD_HEIGHT (g_venc_RecordHeight)
/*
编码参数初始化
*/
//设置 H264 的参数
static VencH264Param h264Param;
//编码器设置
static VencBaseConfig VencodeBaseConfig;
//编码器
static VideoEncoder *CedarxVideoEncoder;
//编码的头数据
VencHeaderData sps_pps_data;
//编码器输入队列
VencInputBuffer inputBuffer;
//编码器输出序列
VencOutputBuffer outputBuffer;
//帧数计算
int g_charm_sps;
int g_Venc_charm;
/**********************************************************/
/*********** H264参数设置 ***********************/
/**********************************************************/
static int cedarX_venc_H264param_init()
{
//CABAC: 基于上下文自适应的二进制算术编码(Context-based Adaptive Binary Arithmetic Coding)
//CAVLC: 基于上下文自适应变长编码(Context-based Adaptive Variable Length Coding)
//CABAC 比特率小,但是 压缩时间长于CAVLC
h264Param.bEntropyCodingCABAC = 1;
// 4Mbps码流
h264Param.nBitrate = 4 * 1024 * 1024;
h264Param.nFramerate = RECORD_FRAME; //帧速率
//VENC_FRAME_CODING = 0, 帧编码 适合 静止场景
//VENC_FIELD_CODING = 1, 场编码 适合 运动场景
//但是大疆psdk仅支持帧格式编码,不支持场编码
h264Param.nCodingMode = VENC_FRAME_CODING;
//h264Param.nCodingMode = VENC_FIELD_CODING;
h264Param.nMaxKeyInterval = RECORD_FRAME;
//Profile是用来描述视频压缩特性的,profile越高,就说明采用了越高级的压缩特性
// https://blog.csdn.net/szfhy/article/details/53159869
h264Param.sProfileLevel.nProfile = VENC_H264ProfileBaseline; //baseline压缩 视频只有i p帧
h264Param.sProfileLevel.nLevel = VENC_H264Level31;
//QP:量化参数 quantization parameter。DCT时的量化参数大小。QP数值越大,失真越多,压缩越好,画质越差。
//QP 区间。QP值对应量化步长的序号,对于亮度取值0-51, 对于色度取值0~39。
//值越小,量化步长越小,量化的精度就越高,意味着同样画质的情况下,产生的数据量可能会更大。QP值每增加6,量化步长就增加一倍。
h264Param.sQPRange.nMinqp = 10; // 最小量化步长
h264Param.sQPRange.nMaxqp = 40; // 最大量化步长51
}
/**********************************************************/
/*********** 编码器参数设置 *********************/
/**********************************************************/
static int CedarX_venc_encode_config()
{
//清空参数
memset(&VencodeBaseConfig, 0, sizeof(VencBaseConfig));
//注册编码器缓存
if (VencodeBaseConfig.memops == NULL)
{
VencodeBaseConfig.memops = MemAdapterGetOpsS();
if (VencodeBaseConfig.memops == NULL)
{
printf("MemAdapterGetOpsS failed\n");
return -1;
}
CdcMemOpen(VencodeBaseConfig.memops);
}
//1. nInputWidth:输入图像帧的宽度,以像素为单位;
//2. nInputHeight:输入图像帧的高度,以像素为单位;
VencodeBaseConfig.nInputWidth = RECORD_WIDTH;
VencodeBaseConfig.nInputHeight = RECORD_HEIGHT;
//3. nDstWidth:编码前对输入图像做 scale 后的宽度,以像素为单位; 如果不需要做 scale,nDstWidth 的值保持和 nInputWidth 一致;
//4. nDstHeight:编码前对输入图像做 scale 后的高度,以像素为单位; 如果不需要做 scale,nDstHeight 的值保持和 nInputHeight 一致;
VencodeBaseConfig.nDstWidth = RECORD_WIDTH;
VencodeBaseConfig.nDstHeight = RECORD_HEIGHT;
//5. nStride: 输入图像帧在内存中的行宽,以像素为单位,编码器要求nStride 必须 16 对齐
VencodeBaseConfig.nStride = RECORD_WIDTH;
//VencodeBaseConfig.nStride = ((RECORD_WIDTH + 15) & ~15);
// eInputFormat:输入的颜色格式;
// YUV420SP 即是 NV12 --ookk303
VencodeBaseConfig.eInputFormat = VENC_PIXEL_YUV420SP;
}
/**********************************************************/
/*********** 编码器初始化 *********************/
/**********************************************************/
static int CedarX_venc_encode_init()
{
int EncodeParamValue;
//如果编码器还没使用 注册一个编码器
if (CedarxVideoEncoder == NULL)
{
//编码类型
CedarxVideoEncoder = VideoEncCreate((VENC_CODEC_TYPE)VENC_CODEC_H264);
//编码参数的写入
VideoEncSetParameter(CedarxVideoEncoder, VENC_IndexParamH264Param, &h264Param);
EncodeParamValue = 0;
VideoEncSetParameter(CedarxVideoEncoder, VENC_IndexParamIfilter, &EncodeParamValue);
EncodeParamValue = 0; //degree
VideoEncSetParameter(CedarxVideoEncoder, VENC_IndexParamRotation, &EncodeParamValue);
EncodeParamValue = 0;
VideoEncSetParameter(CedarxVideoEncoder, VENC_IndexParamSetPSkip, &EncodeParamValue);
//编码器初始化
VideoEncInit(CedarxVideoEncoder, &VencodeBaseConfig);
}
}
/**********************************************************/
/*********** h264视频标准帧头创建 *********************/
/**********************************************************/
static int CedarX_venc_encode_H264head()
{
//获取h264帧头部内容
//pEncoder: 通过 VideoEncCreate 函数创建的视频编码器指针;
//indexType:参数类型索引号;
//paramData(输出):参数数据指针
VideoEncGetParameter(CedarxVideoEncoder, VENC_IndexParamH264SPSPPS, &sps_pps_data);
// 写入帧头信息 这里注释掉 将在其他地方写
//fwrite(sps_pps_data.pBuffer, 1, sps_pps_data.nLength, fpH264);
}
/**********************************************************/
/*********** 编码器输入图像缓冲区创建 **********************/
/**********************************************************/
static int CedarX_venc_encode_InputBuffer_init()
{
//创建 输入图像缓冲区,并配置参数
VencAllocateBufferParam bufferParam;
memset(&bufferParam, 0, sizeof(VencAllocateBufferParam));
//图像缓冲区参数 yuv视频的yc分类
bufferParam.nSizeY = VencodeBaseConfig.nInputWidth * VencodeBaseConfig.nInputHeight ;
bufferParam.nSizeC = VencodeBaseConfig.nInputWidth * VencodeBaseConfig.nInputHeight / 2; //yuv420
//bufferParam.nSizeC = VencodeBaseConfig.nInputWidth * VencodeBaseConfig.nInputHeight ; //yuv422
bufferParam.nBufferNum = 1;
//通过编码器 分配 输入图像缓冲区
AllocInputBuffer(CedarxVideoEncoder, &bufferParam);
}
/**********************************************************/
/*********** 编码器的释放 **********************/
/**********************************************************/
int CedarX_encode_uninit()
{
//编码器去初始化
VideoEncUnInit(CedarxVideoEncoder);
//销毁编码器
VideoEncDestroy(CedarxVideoEncoder);
CedarxVideoEncoder = NULL;
//销毁输入输出队列
//FreeOneBitStreamFrame(H3_VideoEncode, &inputBuffer);
//FreeOneBitStreamFrame(H3_VideoEncode, &outputBuffer);
}
/**********************************************************/
/*********** 编码器的初始化 **********************/
/**********************************************************/
int CedarX_venc_init(int height, int width, int charm)
{
g_venc_RecordHeight = height;
g_venc_RecordWidth = width;
g_venc_RecordCharm = charm;
//配置H264流的参数
cedarX_venc_H264param_init();
//配置编码器
CedarX_venc_encode_config();
//创建编码器
CedarX_venc_encode_init();
//创建h264帧头
CedarX_venc_encode_H264head();
//创建编码器的 输入缓冲区
CedarX_venc_encode_InputBuffer_init();
printf("编码器初始化完成\n");
}
int CedarX_NV12_TO_H264_EncodeOneFrame(unsigned char *AddrVirY, unsigned char *AddrVirC, char **EncodeReturnBuffer, int *EncodeReturnLength)
{
//清空输入队列
memset(&inputBuffer, 0, sizeof(VencInputBuffer));
g_charm_sps++;
if(g_charm_sps >g_venc_RecordCharm)
{
//第一帧 关键帧
g_charm_sps = 1;
//标记关键帧
inputBuffer.nFlag = 1;
}
// AllocInputBuffer 申请输入获取队列
GetOneAllocInputBuffer(CedarxVideoEncoder, &inputBuffer);
//写入数据到数列
memcpy(inputBuffer.pAddrVirY, AddrVirY, VencodeBaseConfig.nInputWidth * VencodeBaseConfig.nInputHeight);
memcpy(inputBuffer.pAddrVirC, AddrVirC, VencodeBaseConfig.nInputWidth * VencodeBaseConfig.nInputHeight / 2);
//裁剪使能
inputBuffer.bEnableCorp = 0;
//矩形信息
inputBuffer.sCropInfo.nLeft = 240;
inputBuffer.sCropInfo.nTop = 240;
inputBuffer.sCropInfo.nWidth = 240;
inputBuffer.sCropInfo.nHeight = 240;
//刷 cache 保存数据的一致性
FlushCacheAllocInputBuffer(CedarxVideoEncoder, &inputBuffer);
//添加输入图像帧到编码器
AddOneInputBuffer(CedarxVideoEncoder, &inputBuffer);
//编码一帧
if (VENC_RESULT_OK != VideoEncodeOneFrame(CedarxVideoEncoder))
{
printf("VideoEncodeOneFrame failed.\n");
return -1;
}
//获取 VideoEncodeOneFrame 已经使用过的输入图像帧;
AlreadyUsedInputBuffer(CedarxVideoEncoder, &inputBuffer);
// 还回由 AllocInputBuffer 申请的输入图像帧 buffe
ReturnOneAllocInputBuffer(CedarxVideoEncoder, &inputBuffer);
//功能 获取有效的的输出码流 buffer 的格式
GetOneBitstreamFrame(CedarxVideoEncoder, &outputBuffer);
//1.nID:用来识别不同的 buffer:
//2.nPts:编码器不对时间戳信息做处理,输出 buffer 中的 pts 对应相应输入buffer 中的 pts;
//3.nSize0:输出码流的第一部分的大小;
//4.nSize1:输出码流的第二部分的大小;
//5.pData0:输出码流的第一部分的地址;
//6.pData1:输出码流的第二部分的地址;
//输出的一笔码流可能由两部分组成:nSize0 表示第一部分的大小,nSize1表示第二部分的大小;
//nSize0 一定大于 0,当 nSize1 = 0 的时候,输出码流只在地址 pData0 中;
//当 nSize1 > 0 时,输出码流由两部分组成,第一部分在 pData0 中,第二部分
//在 pData1 中,此时需要外部应用程序把这两部分数据组合成一帧;
//普通帧的处理
if(g_charm_sps != 1)
{
*EncodeReturnLength = outputBuffer.nSize0 + outputBuffer.nSize1;
*EncodeReturnBuffer = (unsigned char*)malloc(*EncodeReturnLength);
if(EncodeReturnBuffer == NULL)
{
JZSDK_LOG_ERROR("malloc uv_tmp_buffer fail\n");
}
//因为outputbuffer会释放掉,所以得memcpy出去
//合成 data1 和data2
//printf("write pData0\n");
memcpy(*EncodeReturnBuffer, outputBuffer.pData0, outputBuffer.nSize0);
//printf("write pData1\n");
memcpy(*EncodeReturnBuffer + outputBuffer.nSize0 , outputBuffer.pData1, outputBuffer.nSize1);
}
//关键帧处理
else if(g_charm_sps == 1)
{
//如果为关键帧,要加上头信息
*EncodeReturnLength = sps_pps_data.nLength + outputBuffer.nSize0 + outputBuffer.nSize1;
*EncodeReturnBuffer = (unsigned char*)malloc(*EncodeReturnLength);
if(EncodeReturnBuffer == NULL)
{
JZSDK_LOG_ERROR("malloc uv_tmp_buffer fail\n");
}
memcpy(*EncodeReturnBuffer, sps_pps_data.pBuffer, sps_pps_data.nLength);
memcpy(*EncodeReturnBuffer + sps_pps_data.nLength, outputBuffer.pData0, outputBuffer.nSize0);
memcpy(*EncodeReturnBuffer + sps_pps_data.nLength + outputBuffer.nSize0 , outputBuffer.pData1, outputBuffer.nSize1);
printf("关键帧处理\n");
}
//释放 输出队列的一帧
FreeOneBitStreamFrame(CedarxVideoEncoder, &outputBuffer);
return 0;
}
#endif