可以首先参考前面两篇文章:
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视频流输出,实现了如下通路:
其中的SD Card改为mmap内存映射空间数据,经V4L2接口一帧一帧数据进行传输。其他相关介绍可以参考:
基于Hi3559AV100的视频采集(VDEC-VPSS-VO)整体框图设计
https://www.cnblogs.com/iFrank/p/14370575.html
2、实现方案
给出具体的实现代码,下面为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视频的录制,因为博客上不能上传视频,所以以截图进行表示,从结果可以看出,很好实现了视频流的输出且不卡帧):