攝像頭畫面顯示的程序比較簡單,友善之臂的光盤裏面已經提供了相關的代碼,這裏對其進行簡單的封裝,以便後續工程的使用。
首先從main函數看起,代碼如下。
/*
* main.cpp
*
* Created on: 2015年12月4日
* Author: Westlor
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "camera.h"
#include "Fb.h"
#define CAM_DEV "/dev/video0"
#define FB_DEV "/dev/fb0"
Camera *camera;
Fb *fb;
void sign_func(int sign_num)
{
switch(sign_num)
{
case SIGINT:
printf("I have get SIGINT<Ctrl+c>, I'm going now..\n");
camera->CloseDevice();
fb->CloseDevice();
exit(0);
break;
}
}
int main(void) {
int width=640;
int height=480;
unsigned char* image;
camera=new Camera(CAM_DEV, width, height);
if(!camera->OpenDevice()){
printf("Cam Open error\n");
return -1;
}
fb = new Fb(FB_DEV, 80, 0, width, height);
if(!fb->OpenDevice()){
printf("Fb Open error\n");
return -1;
}
fb->Trans(&image);
printf("Waiting for signal SIGINT..\n");
signal(SIGINT, sign_func);
while(1){
if(!camera->GetBuffer(image)){
break;
}
fb->Draw();
}
return 0;
}
這裏定義了兩個類,Camera類的構造函數裏定義了攝像頭輸入圖像的寬和高,Fb類的構造函數裏定義了液晶屏上顯示圖像的位置(x,y)以及大小。首先定義一個緩衝區,用來存儲要顯示的數據。在主循環中,不斷讀取攝像頭採集度圖像數據,並將其進行格式轉換後放到緩衝區中,然後將緩衝區的數據拷貝到framebuffer中即可。
接下來看攝像頭初始化部分,對攝像頭配置代碼如下。
bool Camera::init_device(void) {
v4l2_input input;
memset(&input, 0, sizeof(struct v4l2_input));
input.index = 0;
if (ioctl(fd, VIDIOC_ENUMINPUT, &input) != 0) {
fprintf(stderr, "No matching index found\n");
return false;
}
if (!input.name) {
fprintf(stderr, "No matching index found\n");
return false;
}
if (ioctl(fd, VIDIOC_S_INPUT, &input) < 0) {
fprintf(stderr, "VIDIOC_S_INPUT failed\n");
return false;
}
struct v4l2_format fmt;
CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = width;
fmt.fmt.pix.height = height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV12;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
return false;
if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
return false;
//原始攝像頭數據每幀的大小
cap_image_size = fmt.fmt.pix.sizeimage;
init_mmap();
return true;
}
這裏配置攝像頭圖像格式爲NV12,因爲後面h264編碼是需要攝像頭圖像格式爲NV12。NV12爲YUV420格式,每個像素佔12位,平均是Y分量佔8位,UV分量各佔兩位,存儲時在線性模式下先存儲Y分量,佔前2/3,然後在剩餘1/3空間裏交叉存儲UV分量。
然後看液晶屏初始化的代碼,對屏幕顯示配置的代碼如下。
bool Fb::init_device(void) {
struct fb_fix_screeninfo Fix;
struct fb_var_screeninfo Var;
if (ioctl(fd, FBIOGET_FSCREENINFO, bitand Fix) < 0 or ioctl(fd, FBIOGET_VSCREENINFO, bitand Var) < 0) {
printf("cannot get frame buffer information\n");
}
BPP = Var.bits_per_pixel;
if (BPP not_eq 32) {
printf("support 32 BPP frame buffer only\n");
}
Width = Var.xres;
Height = Var.yres;
LineLen = Fix.line_length;
Size = LineLen * Height;
Addr = (unsigned char *)mmap(NULL, Size, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
if (Addr == (unsigned char *)MAP_FAILED) {
printf("map frame buffer failed\n");
return false;
}
show_buffer = (unsigned char*)malloc(show_width*show_height*BPP/8);
if(show_buffer == NULL){
printf("buffers malloc failed\n");
return false;
}
Clear(); //清空屏幕
return true;
}
屏幕所顯示的圖像格式爲rgb,每個像素爲32位的,rgb分量各佔8位,還有8位是透明度的分量。從攝像頭取來的圖像轉換到屏幕上顯示時需要做格式轉換,即NV12->rgb。下面是圖像格式轉換代碼。申請內存的時候需要注意存儲空間的大小,NV12的對應爲 H*W*12/8,rgb的對應爲 H*W*32/8。
void Camera::DecodeYUV420SP(unsigned int* rgbBuf, unsigned char* yuv420sp, int width, int height) {
int frameSize = width * height;
int i = 0, y = 0;
int uvp = 0, u = 0, v = 0;
int y1192 = 0, r = 0, g = 0, b = 0;
unsigned int xrgb8888;
int xrgb8888Index = 0;
for (int j = 0, yp = 0; j < height; j++) {
uvp = frameSize + (j >> 1) * width;
u = 0;
v = 0;
for (i = 0; i < width; i++, yp++) {
y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
y1192 = 1192 * y;
r = (y1192 + 1634 * u);
g = (y1192 - 833 * u - 400 * v);
b = (y1192 + 2066 * v);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
r = (unsigned char)(r >> 10);
g = (unsigned char)(g >> 10);
b = (unsigned char)(b >> 10);
xrgb8888 = (unsigned int)((r << 16) | (g << 8) | b);
rgbBuf[xrgb8888Index++] = xrgb8888;
}
}
}
程序可以在http://download.csdn.net/detail/westlor/9389478下載。