树莓派学习专题<14>:树莓派4B:从V4L2驱动直接获取h264码流
树莓派学习专题<14>:树莓派学习专题<14>:树莓派4B:从V4L2驱动直接获取h264码流
- 0. 项目代码
- 1. 背景
- 2. 代码
0. 项目代码
可以从下面获取到项目中的代码。
https://github.com/cdsmakc/h264_codec_base_rpi4b_rpi5_rv1106_visual_studio.git
1. 背景
之前的文章,记录了在树莓派4上,使用V4L2驱动获取YUV图像,然后通过x264编码。由于sensor本身并不能输出264码流,驱动本身也不负责编码,因此我一直以为从V4L2获取264码流是不可能的。但是在使用V4L2驱动罗列摄像头支持的数据格式时,却可以看到驱动能够输出264码流:
--Format descriptor --------------------------------------
-- 01. Planar YUV 4:2:0 (YU12)
-- 02. YUYV 4:2:2 (YUYV)
-- 03. 24-bit RGB 8-8-8 (RGB3)
-- 04. JFIF JPEG (JPEG)
-- 05. H.264 (H264) --->此处
-- 06. Motion-JPEG (MJPG)
-- 07. YVYU 4:2:2 (YVYU)
-- 08. VYUY 4:2:2 (VYUY)
-- 09. UYVY 4:2:2 (UYVY)
-- 10. Y/UV 4:2:0 (NV12)
-- 11. 24-bit BGR 8-8-8 (BGR3)
-- 12. Planar YVU 4:2:0 (YV12)
-- 13. Y/VU 4:2:0 (NV21)
-- 14. 32-bit XBGR 8-8-8-8 (RX24)
----------------------------------------------------------
也可以用V4L2-CTL工具查看输出支持的格式(注意:需要修改config.txt文件,参看之前的文章):
njl@raspberrypi:~ $ v4l2-ctl -d /dev/video0 --list-formats-ext
ioctl: VIDIOC_ENUM_FMTType: Video Capture[0]: 'YU12' (Planar YUV 4:2:0)Size: Stepwise 32x32 - 3280x2464 with step 2/2[1]: 'YUYV' (YUYV 4:2:2)Size: Stepwise 32x32 - 3280x2464 with step 2/2[2]: 'RGB3' (24-bit RGB 8-8-8)Size: Stepwise 32x32 - 3280x2464 with step 2/2[3]: 'JPEG' (JFIF JPEG, compressed)Size: Stepwise 32x32 - 3280x2464 with step 2/2[4]: 'H264' (H.264, compressed)Size: Stepwise 32x32 - 3280x2464 with step 2/2[5]: 'MJPG' (Motion-JPEG, compressed)Size: Stepwise 32x32 - 3280x2464 with step 2/2[6]: 'YVYU' (YVYU 4:2:2)Size: Stepwise 32x32 - 3280x2464 with step 2/2[7]: 'VYUY' (VYUY 4:2:2)Size: Stepwise 32x32 - 3280x2464 with step 2/2[8]: 'UYVY' (UYVY 4:2:2)Size: Stepwise 32x32 - 3280x2464 with step 2/2[9]: 'NV12' (Y/UV 4:2:0)Size: Stepwise 32x32 - 3280x2464 with step 2/2[10]: 'BGR3' (24-bit BGR 8-8-8)Size: Stepwise 32x32 - 3280x2464 with step 2/2[11]: 'YV12' (Planar YVU 4:2:0)Size: Stepwise 32x32 - 3280x2464 with step 2/2[12]: 'NV21' (Y/VU 4:2:0)Size: Stepwise 32x32 - 3280x2464 with step 2/2[13]: 'RX24' (32-bit XBGR 8-8-8-8)Size: Stepwise 32x32 - 3280x2464 with step 2/2
可以看到,摄像头即可以输出264码流也可以输出MJPEG码流。一上来没有注意到这点,后来看到树莓派论坛上的帖子才知道,树莓派官方已经将摄像头硬件设备、ISP和硬件编码器等做了封装,从而使得可以使用V4L2驱动从 /dev/video0 设备直接获取图像264码流。
2. 代码
使用V4L2获取摄像头的代码和之前的代码相同,只是输出格式需要改一下:
/* 设置输出格式 */
stFormat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
stFormat.fmt.pix.width = g_stRCEConfig.usWidth ;
stFormat.fmt.pix.height = g_stRCEConfig.usHeight ;
stFormat.fmt.pix.pixelformat = (0 == g_stRCEConfig.uiHWEncoder) ? V4L2_PIX_FMT_YUV420 : V4L2_PIX_FMT_H264 ;iRetVal = ioctl(g_stCAMWorkarea.iCameraFile, VIDIOC_S_FMT, &stFormat) ;
如果需要获取264码流,使用VIDIOC_S_FMT命令设置输出格式时,指定V4L2_PIX_FMT_H264即可。
此外,使用硬件编码时,树莓派4B可以输出到60fps;如果获取YUV420的图像,大约只能到47fps(只是获取图像,不考虑软编码的情况下)。另外,在默认情况下,硬件编码的264码流还是很大的,有时候接近10Mbps。这个也可以修改。这需要额外的命令来实现。
INT __RCE_CAM_SetRCParams(VOID)
{struct v4l2_ext_controls stExtCtrls ;struct v4l2_ext_control stExtCtrl ;INT iRetVal ;memset(&stExtCtrls, 0, sizeof(stExtCtrls)) ;memset(&stExtCtrl, 0, sizeof(stExtCtrl)) ;stExtCtrl.id = V4L2_CID_MPEG_VIDEO_BITRATE ;stExtCtrl.value = g_stRCEConfig.uiHWBitrate ;stExtCtrl.size = 0 ;stExtCtrls.controls = &stExtCtrl ;stExtCtrls.count = 1 ;stExtCtrls.ctrl_class = V4L2_CTRL_CLASS_MPEG ;iRetVal = ioctl(g_stCAMWorkarea.iCameraFile, VIDIOC_S_EXT_CTRLS, &stExtCtrls) ;if(-1 == iRetVal){printf("File %s, func %s, line %d : ioctl VIDIOC_S_EXT_CTRLS error. ioctl : %s.\n", __FILE__, __func__, __LINE__, strerror(errno)) ;return -1 ;}return 0 ;
}
其中,g_stRCEConfig.uiHWBitrate即是码流目标值,以bps为单位。例如希望码流是3Mbps左右,则设置3000000。这个码流设置下去,也只是告知编码器按此目标工作,不代表输出就是这个码流。实际上,输出码流是变化的,只是均值尽量接近这个值而已。