하드웨어
리눅스에서는 카메라 입력을 받기 위한 표준의 인터페이스로 V4L2 가 존재한다.
카메라 역시 디바이스 드라이버이기 때문에 file-operation 을 이용한다.
디바이스 드라이버라면 역시 노드파일(node) 이 있어야 한다.
char-device major=81 minor=0 부터 계속
삼성 S3C6410 보드라면 /dev/cam_codec 81,0 번이며 /dev/cam_preview 는 81,1 번이다.
혹시 보드에 UVC (Usb Video Camera) 를 지원하는 WebCam 을 여기에 연결하면 81,2 번으로 등록된다.
코드에서 알아보자
드라이버를 연다.
cam_fd = open( "/dev/cam_codec", O_RDWR );
if ( 0 > cam_fd )
{
printf( " open fail %s\n", dev_name );
return -1;
}
드라이버를 연 이후에는 다음과 같은 과정을 거친다.
- 드라이버가 overay 를 지원하는가? >> 지원하지 않으면 에러이다.
- 드라이버가 read(), mmap() 함수를 지원하는가? >> 둘중 하나만 지원하면 된다. (정확히는 user-io 란 것도 있다.)
- 카메라 입력 형태를 설정한다. >> YCbCr, RGB 이런 형태와 해상도를 설정한다.
- 카메라 출력 형태를 설정한다. >> 출력형태를 지원하지 않아도 된다. (UVC 는 이런것이 대부분 없다)
- mmap 를 사용하여 메모리를 할당받는다. >> read() 함수를 사용한다면 안해도 된다.
- 카메라 캡쳐를 시작한다.
- 프레임이 만들어 졌는지 감시한다.
- 만들어진 프레임을 화면에 뿌리거나 파일에 저장하거나 압축하거나..
- 다음 프레임 캡쳐를 기다리거나 종료한다.
드라이버가 overay 를 지원하는가?
드라이버가 read(), mmap() 함수를 지원하는가?
대부분의 경우 V4L2_CAP_STREAMING 즉 mmap() 함수를 지원한다. read() 함수를 사용하면 메모리 복사가 한번 더 있어 그 만큼 속도가 떨어진다.
일반적인 CMOS 카메라라면 YCbCr 형태를 지원할 것이다. 이것을 V4L2_PIX_FMT_YUYV 로 표현한다. 카메라 출력 형태를 설정한다
앞에서 언급했듯이 UVC 는 지원하지 않는다. 삼성칩들의 경우 내장 하드웨어를 사용하여 입력의 크기에 관계없이 스케일링하거나 픽셀단위의 YCrCb 형태가 입력되어도 플랜형태의 버퍼에 담아 준다. 만일 우리가 codec 이 아닌 preview 드라이버를 열었다면 출력형태는 반드시 V4L2_PIX_FMT_RGB565 이어야 한다. LCD 에 뿌릴 목적이라면 preview 드라이버를 여는것이 합리적이다. continue ......... 쓰다보니 시간의 압박이,,, 다음강좌로 근래 들어 카메라 관련 프로젝트가 3건이나 물려있다. 별것 아닌듯 한데 안될땐 증말 실타 {
struct v4l2_capability cap;
ret = ioctl( cam_fd, VIDIOC_QUERYCAP, &cap );
if (ret < 0)
{
perror( "VIDIOC_QUERYCAP\n");
goto err_out;
}
/* Check the type - OVERLAY */
if (!(cap.capabilities & V4L2_CAP_VIDEO_OVERLAY))
{
printf("Can not capture(V4L2_CAP_VIDEO_OVERLAY is false)\n");
goto err_out;
}
if ( cap.capabilities & V4L2_CAP_READWRITE )
printf( " preview surport read io\n" );
if ( cap.capabilities & V4L2_CAP_STREAMING )
printf( " preview surport mmap\n" );
}
{
struct v4l2_format vfmt;
CLEAR_STRUCT(vfmt);
vfmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // V4L2_CAP_VIDEO_OVERLAY
vfmt.fmt.pix.width = 640;
vfmt.fmt.pix.height = 480;
vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
vfmt.fmt.pix.priv = V4L2_FMT_IN;
ret = ioctl(cam_fd, VIDIOC_S_FMT, &vfmt);
if (ret < 0)
{
perror("VIDIOC_S_FMT failled");
goto err_out;
}
}
{
struct v4l2_framebuffer vfb;
struct v4l2_pix_format vfb_fmt;
CLEAR_STRUCT(vfb_fmt);
vfb_fmt.width = 640;
vfb_fmt.height = 480;
vfb_fmt.pixelformat = V4L2_PIX_FMT_YUV422P;
/* ref. include/linux/videodev2.h
pixel(interleave) : V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_VYUY, V4L2_PIX_FMT_YYUV
planar : V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUV411P
*/
vfb.base = 0;
vfb.flags = 0;
vfb.capability = 0;
vfb.fmt = vfb_fmt;
ret = ioctl( cam_fd, VIDIOC_S_FBUF, &vfb);
if (ret< 0)
{
perror("VIDIOC_S_BUF\n");
}
}
![]()


