Hi3559AV100外接UVC/MJPEG相机实时采图设计(三):V4L2接口通过MPP平台输出

时间:2022-03-25 12:21:56

可以首先参考前面两篇文章:

Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析:

https://www.cnblogs.com/iFrank/p/14399421.html

Hi3559AV100外接UVC/MJPEG相机实时采图设计(二):V4L2接口的实现(以YUV422为例):

https://www.cnblogs.com/iFrank/p/14403397.html

  下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC、VPSS、VO最后通过HDMI的输出,最后给出(三)V4L2接口通过MPP平台输出。

板载平台:BOXER-8410AI

芯片型号:Hi3559AV100

相机型号:Logitch c270

开发环境:VM15.5+ubuntu16.04+Hilinux

1、V4L2接口经MPP平台输出通路

  这一篇随笔是给出V4L2接口通过MPP平台下VDEC、VPSS、VO最后通过HDMI视频流输出,实现了如下通路:

Hi3559AV100外接UVC/MJPEG相机实时采图设计(三):V4L2接口通过MPP平台输出

  其中的SD Card改为mmap内存映射空间数据,经V4L2接口一帧一帧数据进行传输。其他相关介绍可以参考:

基于Hi3559AV100的视频采集(VDEC-VPSS-VO)整体框图设计

https://www.cnblogs.com/iFrank/p/14370575.html

2、实现方案

Hi3559AV100外接UVC/MJPEG相机实时采图设计(三):V4L2接口通过MPP平台输出

  给出具体的实现代码,下面为main函数:

  1 /******************************************************************************
2 * function : main()
3 * Description : video ouput
4 ******************************************************************************/
5 #ifdef __HuaweiLite__
6 int app_main(int argc, char *argv[])
7 #else
8 int main(int argc, char *argv[])
9 #endif
10 {
11 HI_S32 s32Ret = HI_SUCCESS;
12
13 // ******* V4L2 **********
14 ///*
15 if(init_v4l2() == FALSE)
16 {
17 return(FALSE);
18 }
19
20 v4l2_mmap();
21
22
23 if(start_v4l2() == FALSE)
24 {
25 return(FALSE);
26 }
27
28 //*/
29 //*********** **************
30
31
32 if ((argc < 2) || (1 != strlen(argv[1])))
33 {
34 printf("\nInvaild input! For examples:\n");
35 SAMPLE_VDEC_Usage(argv[0]);
36 return HI_FAILURE;
37 }
38
39 if ((argc > 2) && ('1' == *argv[2]))
40 {
41 g_enIntfSync = VO_OUTPUT_1080P60;
42 }
43 else
44 {
45 g_enIntfSync = VO_OUTPUT_3840x2160_30;
46 }
47
48 #ifndef __HuaweiLite__
49 signal(SIGINT, SAMPLE_VDEC_HandleSig);
50 signal(SIGTERM, SAMPLE_VDEC_HandleSig);
51 #endif
52
53 /******************************************
54 choose the case
55 ******************************************/
56 switch (*argv[1])
57 {
58 case '0':
59 {
60 s32Ret = SAMPLE_H265_VDEC_VPSS_VO();
61 break;
62 }
63 case '1':
64 {
65 s32Ret = SAMPLE_H264_VDEC_VPSS_VO();
66 break;
67 }
68 case '2':
69 {
70 s32Ret = SAMPLE_JPEG_VDEC_VPSS_VO();
71 break;
72 }
73 case '3':
74 {
75 s32Ret = SAMPLE_JPEG_VDEC_To_RGB();
76 break;
77 }
78 case '4':
79 {
80 s32Ret = SAMPLE_H264_VDEC_VPSS_VO_MIPI_Tx();
81 break;
82 }
83 default :
84 {
85 SAMPLE_PRT("the index is invaild!\n");
86 SAMPLE_VDEC_Usage(argv[0]);
87 s32Ret = HI_FAILURE;
88 break;
89 }
90 }
91
92 if (HI_SUCCESS == s32Ret)
93 {
94 SAMPLE_PRT("program exit normally!\n");
95 }
96 else
97 {
98 SAMPLE_PRT("program exit abnormally!\n");
99 }
100
101 return s32Ret;
102 }

  给出细节实现函数代码(其中图片像素为640×480,像素格式为PT_MJPEG,HDMI输出设置为1080p60):

  1 HI_S32 SAMPLE_JPEG_VDEC_VPSS_VO(HI_VOID)
2 {
3 VB_CONFIG_S stVbConfig;
4 HI_S32 i, s32Ret = HI_SUCCESS;
5 VDEC_THREAD_PARAM_S stVdecSend[VDEC_MAX_CHN_NUM];
6 SIZE_S stDispSize;
7 VO_LAYER VoLayer;
8 HI_U32 u32VdecChnNum, VpssGrpNum;
9 VPSS_GRP VpssGrp;
10 pthread_t VdecThread[2*VDEC_MAX_CHN_NUM];
11 PIC_SIZE_E enDispPicSize;
12 SAMPLE_VDEC_ATTR astSampleVdec[VDEC_MAX_CHN_NUM];
13 VPSS_CHN_ATTR_S astVpssChnAttr[VPSS_MAX_CHN_NUM];
14 SAMPLE_VO_CONFIG_S stVoConfig;
15 VPSS_GRP_ATTR_S stVpssGrpAttr;
16 HI_BOOL abChnEnable[VPSS_MAX_CHN_NUM];
17 VO_INTF_SYNC_E enIntfSync;
18
19 u32VdecChnNum = 1;
20 VpssGrpNum = u32VdecChnNum;
21
22
23 /************************************************
24 step1: init SYS, init common VB(for VPSS and VO)
25 *************************************************/
26 if(VO_OUTPUT_3840x2160_30 == g_enIntfSync)
27 {
28 enDispPicSize = PIC_3840x2160;
29 enIntfSync = VO_OUTPUT_3840x2160_30;
30 }
31 else
32 {
33 enDispPicSize = PIC_1080P;
34 enIntfSync = VO_OUTPUT_1080P60;
35 }
36
37 s32Ret = SAMPLE_COMM_SYS_GetPicSize(enDispPicSize, &stDispSize);
38 if(s32Ret != HI_SUCCESS)
39 {
40 SAMPLE_PRT("sys get pic size fail for %#x!\n", s32Ret);
41 goto END1;
42 }
43
44 memset(&stVbConfig, 0, sizeof(VB_CONFIG_S));
45 stVbConfig.u32MaxPoolCnt = 1;
46 stVbConfig.astCommPool[0].u32BlkCnt = 10*u32VdecChnNum;
47 stVbConfig.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(stDispSize.u32Width, stDispSize.u32Height,
48 PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0);
49 s32Ret = SAMPLE_COMM_SYS_Init(&stVbConfig);
50 if(s32Ret != HI_SUCCESS)
51 {
52 SAMPLE_PRT("init sys fail for %#x!\n", s32Ret);
53 goto END1;
54 }
55
56 /************************************************
57 step2: init module VB or user VB(for VDEC)
58 *************************************************/
59 for(i=0; i<u32VdecChnNum; i++)
60 {
61 astSampleVdec[i].enType = PT_MJPEG;
62 astSampleVdec[i].u32Width = 640;
63 astSampleVdec[i].u32Height = 480;
64 astSampleVdec[i].enMode = VIDEO_MODE_FRAME;
65 astSampleVdec[i].stSapmleVdecPicture.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
66 astSampleVdec[i].stSapmleVdecPicture.u32Alpha = 255;
67 astSampleVdec[i].u32DisplayFrameNum = 2;
68 astSampleVdec[i].u32FrameBufCnt = astSampleVdec[i].u32DisplayFrameNum + 1;
69 }
70 s32Ret = SAMPLE_COMM_VDEC_InitVBPool(u32VdecChnNum, &astSampleVdec[0]);
71 if(s32Ret != HI_SUCCESS)
72 {
73 SAMPLE_PRT("init mod common vb fail for %#x!\n", s32Ret);
74 goto END2;
75 }
76
77 /************************************************
78 step3: start VDEC
79 *************************************************/
80 s32Ret = SAMPLE_COMM_VDEC_Start(u32VdecChnNum, &astSampleVdec[0]);
81 if(s32Ret != HI_SUCCESS)
82 {
83 SAMPLE_PRT("start VDEC fail for %#x!\n", s32Ret);
84 goto END3;
85 }
86
87 /************************************************
88 step4: start VPSS
89 *************************************************/
90 stVpssGrpAttr.u32MaxW = 640;
91 stVpssGrpAttr.u32MaxH = 480;
92 stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1;
93 stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
94 stVpssGrpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8;
95 stVpssGrpAttr.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
96 stVpssGrpAttr.bNrEn = HI_FALSE;
97
98 memset(abChnEnable, 0, sizeof(abChnEnable));
99 abChnEnable[0] = HI_TRUE;
100 astVpssChnAttr[0].u32Width = stDispSize.u32Width;
101 astVpssChnAttr[0].u32Height = stDispSize.u32Height;
102 astVpssChnAttr[0].enChnMode = VPSS_CHN_MODE_AUTO;
103 astVpssChnAttr[0].enCompressMode = COMPRESS_MODE_SEG;
104 astVpssChnAttr[0].enDynamicRange = DYNAMIC_RANGE_SDR8;
105 astVpssChnAttr[0].enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
106 astVpssChnAttr[0].stFrameRate.s32SrcFrameRate = -1;
107 astVpssChnAttr[0].stFrameRate.s32DstFrameRate = -1;
108 astVpssChnAttr[0].u32Depth = 0;
109 astVpssChnAttr[0].bMirror = HI_FALSE;
110 astVpssChnAttr[0].bFlip = HI_FALSE;
111 astVpssChnAttr[0].stAspectRatio.enMode = ASPECT_RATIO_NONE;
112 astVpssChnAttr[0].enVideoFormat = VIDEO_FORMAT_LINEAR;
113
114 for(i=0; i<u32VdecChnNum; i++)
115 {
116 VpssGrp = i;
117 s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, &abChnEnable[0], &stVpssGrpAttr, &astVpssChnAttr[0]);
118 if(s32Ret != HI_SUCCESS)
119 {
120 SAMPLE_PRT("start VPSS fail for %#x!\n", s32Ret);
121 goto END4;
122 }
123 }
124
125
126
127 /************************************************
128 step5: start VO
129 *************************************************/
130 stVoConfig.VoDev = SAMPLE_VO_DEV_UHD;
131 stVoConfig.enVoIntfType = VO_INTF_HDMI;
132 stVoConfig.enIntfSync = enIntfSync;
133 stVoConfig.enPicSize = enDispPicSize;
134 stVoConfig.u32BgColor = COLOR_RGB_BLUE;
135 stVoConfig.u32DisBufLen = 3;
136 stVoConfig.enDstDynamicRange = DYNAMIC_RANGE_SDR8;
137 stVoConfig.enVoMode = VO_MODE_1MUX;
138 stVoConfig.enPixFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
139 stVoConfig.stDispRect.s32X = 0;
140 stVoConfig.stDispRect.s32Y = 0;
141 stVoConfig.stDispRect.u32Width = stDispSize.u32Width;
142 stVoConfig.stDispRect.u32Height = stDispSize.u32Height;
143 stVoConfig.stImageSize.u32Width = stDispSize.u32Width;
144 stVoConfig.stImageSize.u32Height = stDispSize.u32Height;
145 stVoConfig.enVoPartMode = VO_PART_MODE_SINGLE;
146 s32Ret = SAMPLE_COMM_VO_StartVO(&stVoConfig);
147 if(s32Ret != HI_SUCCESS)
148 {
149 SAMPLE_PRT("start VO fail for %#x!\n", s32Ret);
150 goto END5;
151 }
152
153 /************************************************
154 step6: VDEC bind VPSS
155 *************************************************/
156 for(i=0; i<u32VdecChnNum; i++)
157 {
158 s32Ret = SAMPLE_COMM_VDEC_Bind_VPSS(i, i);
159 if(s32Ret != HI_SUCCESS)
160 {
161 SAMPLE_PRT("vdec bind vpss fail for %#x!\n", s32Ret);
162 goto END6;
163 }
164 }
165
166 /************************************************
167 step7: VPSS bind VO
168 *************************************************/
169 VoLayer = stVoConfig.VoDev;
170 for(i=0; i<VpssGrpNum; i++)
171 {
172 s32Ret = SAMPLE_COMM_VPSS_Bind_VO(i, 0, VoLayer, i);
173 if(s32Ret != HI_SUCCESS)
174 {
175 SAMPLE_PRT("vpss bind vo fail for %#x!\n", s32Ret);
176 goto END7;
177 }
178 }
179
180
181
182 /************************************************
183 step8: send stream to VDEC
184 *************************************************/
185 for(i=0; i<u32VdecChnNum; i++)
186 {
187 ////snprintf(stVdecSend[i].cFileName, sizeof(stVdecSend[i].cFileName), "3840x2160.jpg");
188 snprintf(stVdecSend[i].cFileName, sizeof(stVdecSend[i].cFileName), "txtjpeg.txt");
189
190 //#define SAMPLE_STREAM_PATH "./source_file"
191 snprintf(stVdecSend[i].cFilePath, sizeof(stVdecSend[i].cFilePath), "%s", "/nfsroot");
192 stVdecSend[i].enType = astSampleVdec[i].enType;
193 stVdecSend[i].s32StreamMode = astSampleVdec[i].enMode;
194 stVdecSend[i].s32ChnId = i;
195 stVdecSend[i].s32IntervalTime = 1000;
196 stVdecSend[i].u64PtsInit = 0;
197 stVdecSend[i].u64PtsIncrease = 0;
198 stVdecSend[i].eThreadCtrl = THREAD_CTRL_START;
199 stVdecSend[i].bCircleSend = HI_TRUE;
200 stVdecSend[i].s32MilliSec = 0;
201 stVdecSend[i].s32MinBufSize = (astSampleVdec[i].u32Width * astSampleVdec[i].u32Height * 3)>>1;
202 }
203
204
205
206 SAMPLE_COMM_VDEC_StartSendStream(u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
207
208 SAMPLE_COMM_VDEC_CmdCtrl(u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
209
210 SAMPLE_COMM_VDEC_StopSendStream(u32VdecChnNum, &stVdecSend[0], &VdecThread[0]);
211
212 END7:
213 for(i=0; i<VpssGrpNum; i++)
214 {
215 s32Ret = SAMPLE_COMM_VPSS_UnBind_VO(i, 0, VoLayer, i);
216 if(s32Ret != HI_SUCCESS)
217 {
218 SAMPLE_PRT("vpss unbind vo fail for %#x!\n", s32Ret);
219 }
220 }
221
222 END6:
223 for(i=0; i<u32VdecChnNum; i++)
224 {
225 s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(i, i);
226 if(s32Ret != HI_SUCCESS)
227 {
228 SAMPLE_PRT("vdec unbind vpss fail for %#x!\n", s32Ret);
229 }
230 }
231
232 END5:
233 SAMPLE_COMM_VO_StopVO(&stVoConfig);
234
235 END4:
236 for(i = VpssGrp; i >= 0; i--)
237 {
238 VpssGrp = i;
239 SAMPLE_COMM_VPSS_Stop(VpssGrp, &abChnEnable[0]);
240 }
241
242 END3:
243 SAMPLE_COMM_VDEC_Stop(u32VdecChnNum);
244
245 END2:
246 SAMPLE_COMM_VDEC_ExitVBPool();
247
248 END1:
249 SAMPLE_COMM_SYS_Exit();
250
251 return s32Ret;
252 }

  参考了sample_vdec.c、sample_common_vdec.c、sample_common_vpss.csample_common_vo.c等,定义了V4L2如下参数:

 1 /*******************************************************
2 function announce
3 *******************************************************/
4
5 //@@@@@@@@@@@@@ V4L2 @@@@@@@@@@@
6 #define TRUE 1
7 #define FALSE 0
8
9
10 #define FILE_VIDEO "/dev/video0"
11
12
13 #define IMAGEWIDTH 640
14 #define IMAGEHEIGHT 480
15
16 int video_fd;
17 struct v4l2_capability cap;
18
19 struct v4l2_fmtdesc fmtdesc;
20 struct v4l2_format fmt;
21 struct v4l2_streamparm setfps;
22 struct v4l2_requestbuffers req;
23 struct v4l2_buffer buf,readbuffer;
24 struct v4l2_buffer queuebuffer;
25
26 int my_type;
27
28
29 //for v4l2_mmap function,to cache data
30 struct buffer
31 {
32 void * start;
33 unsigned int length;
34 }*buffers;
35
36
37
38 int init_v4l2(void);
39 int start_v4l2(void);
40 int v4l2_mmap(void);
41 int stop_v4l2(void);
42 int close_v4l2(void);
43 //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

3、视频流输出结果

  通过上述步骤最终实现了视频流通路,摄像头采集的图形能够实时的显示在显示屏上,具有良好的效果,现象如下(用手机进行了6s视频的录制,因为博客上不能上传视频,所以以截图进行表示,从结果可以看出,很好实现了视频流的输出且不卡帧):

Hi3559AV100外接UVC/MJPEG相机实时采图设计(三):V4L2接口通过MPP平台输出