This post was continued from “V4L2 + V4l-utils, Capture for multi-planar #1“
Querying external controls
Getting external control should be an optional job, but it maybe needed to control brightness and gain for good to get excellent balanced image. Little bit difficult to understand in first time but can be understood soon.
Query all user controls should be used as like this,
struct v4l2_query_ext_ctrl* pExtCtl = new struct v4l2_query_ext_ctrl; memset( pExtCtl, 0, sizeof( struct v4l2_query_ext_ctrl ) ); pExtCtl->id = V4L2_CID_USER_BASE; ret = v4l_query_ext_ctrl( &camera_fd, pExtCtl, false, false ); while( ret >= 0 ) { printf( ":::found external control : %X - %s\n", pExtCtl->id, pExtCtl->name ); unsigned tmpid = pExtCtl->id; memset( pExtCtl, 0, sizeof( struct v4l2_query_ext_ctrl ) ) pExtCtl->id = tmpid; // search for next control. ret = v4l_query_ext_ctrl( &camera_fd, pExtCtl, true, false ); if ( pExtCtl->id == tmpid ) break; } delete pExtCtl;
It shows all user controls as below result.
:::found external control : 980900 - :::found external control : 980911 - Exposure :::found external control : 980913 - Gain :::found external control : 980914 - Horizontal Flip :::found external control : 980915 - Vertical Flip :::found external control : 9F0001 - Image Processing Controls :::found external control : 9F0901 - Link Frequency :::found external control : 9F0902 - Pixel Rate :::found external control : 9F0903 - Test Pattern :::found external control : 809F0903 -
Some user controls no name or symbol, so it should filtered out for only named control by checking string length with strlen().
Some tweaks for exposure and gain
In search of looping by v4l_query_ext_ctrl(), it should be export some external control for future, and here some code for getting exposure and gain control.
struct v4l2_query_ext_ctrl* pExtCtlExposure = NULL; struct v4l2_query_ext_ctrl* pExtCtlGain = NULL; struct v4l2_query_ext_ctrl* pExtCtl = new struct v4l2_query_ext_ctrl; memset( pExtCtl, 0, sizeof( struct v4l2_query_ext_ctrl ) ); pExtCtl->id = V4L2_CID_USER_BASE; ret = v4l_query_ext_ctrl( &camera_fd, pExtCtl, false, false ); while( ret >= 0 ) { unsigned tmpid = pExtCtl->id; switch( pExtCtl->id ) { case V4L2_CID_EXPOSURE: pExtCtlExposure = pExtCtl; break; case V4L2_CID_GAIN: pExtCtlGain = pExtCtl; break; default: delete pExtCtl; } pExtCtl = new struct v4l2_query_ext_ctrl; memset( pExtCtl, 0, sizeof( struct v4l2_query_ext_ctrl ) ); pExtCtl->id = tmpid; // search for next control. ret = v4l_query_ext_ctrl( &camera_fd, pExtCtl, true, false ); if ( pExtCtl->id == tmpid ) break; }
Then, exposure should be adjusted as below:
bool adjustExposure( int val ) { struct v4l2_control c = {0}; int maxv = pExtCtlExposure->maximum; int minv = pExtCtlExposure->minimum; int step = pExtCtlExposure->step; if ( val > maxv ) val = maxv; else if ( val < minv ) val = minv; c.id = V4L2_CID_EXPOSURE; c.value = step * ( val / step ); int ret = v4l_ioctl( cfd, VIDIOC_S_CTRL, &c ); if ( ret < 0 ) { perror( "EXPOSURE CONTROL" ); return false; } return true; }
Now, it’s turn to capture
To getting secured image from MIPI CSI-2 camera types with Multiplanar, it should be recommended using memory type for NMAP for quickly approaching to driver side memroy access. And need to following sequence to prepare buffers to be get image data from memory in directly.
unsigned memtype = V4L2_MEMORY_MMAP; v4l_buffer vbuffer; v4l_buffer_init( &vbuffer, captype, memtype, 0 ); v4l_queue vqueue; ret = v4l_buffer_prepare_buf( &cam1, &vbuffer ); if ( ret < 0 ) perror( "v4l_buffer_prepare_buf()" ); v4l_queue_init( &vqueue, captype, memtype ); ret = v4l_queue_querybufs( &cam1, &vqueue, 0 ); if ( ret < 0 ) perror( "v4l_queue_querybufs()" ); ret = v4l_queue_reqbufs( &cam1, &vqueue, REQ_BUFF_SZ ); if ( ret < 0 ) perror( "v4l_queue_reqbufs()" ); ret = v4l_queue_mmap_bufs( &cam1, &vqueue, 0 ); if ( ret < 0 ) perror( "v4l_queue_mmap_bufs()" );
REQ_BUFF_SZ is a pre-definition set for 4. And v4l_queu_reqbufs() count at least or bigger than 4. Now you can make stream on for grab continuously buffers.
Make it streamed on
Before it captures any image from camera, it must be started streaming to βonβ as below:
int ret = v4l_ioctl( &cam1, VIDIOC_STREAMON, &vqueue.type ); if ( ret < 0 ) perror( "v4l_ioctrl(VIDIOC_STREAMON)" );
If it fails or, doesn’t do this, it will impossibly get real captured image β just blacked (NULL) image may receive.
Wait for a sync.
Every camera, monitors has V-Sync signal for complete draw and capture. V4L just simply can make it sens with select() as below code:
fd_set fds; FD_ZERO( &fds ); FD_SET( realfd, &fds ); struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; ret = select( realfd + 1, &fds, NULL, NULL, &tv ); if ( ret <= 0 ) perror( "wait sync failure.\n" );
If it returns to failure, just skip for capturing image.
Getting an image now
When it succeeds to wait a V-Sync, now time to get image as below:
int ret = v4l_buffer_dqbuf( &camera_fd, &vbuffer ); if ( ret < 0 ) perror( "v4l_buffer_dqbuf()" ); unsigned bufflen = v4l_queue_g_length( &vqueue, 0 ); if ( bufflen > 0 ) { size_t bidx = 0; void* ptrData = v4l_queue_g_mmapping( &vqueue, bidx, 0 ); if ( ptrData != NULL ) { // ptrData is RAW pixel array from camera or ISP. } } ret = v4l_buffer_qbuf( &camera_fd, &vbuffer ); if ( ret < 0 ) perror( "v4l_buffer_qbuf()" );
It’s done to get an image from camera with v4l_queue_g_mmapping().
Conclusion
Controlling V4L2 with only native API is very hard to understand, controlling and handling all. But v4l-utils helps to make it easier than before but still hard to understand all because there are not many good examples not existed. So I will continue to writing some articles for understanding multiplanar typed camera accessing methods.
Any question should be let written on guest book.