Programación de aplicaciones en entorno Linux (9): Cámara

Uno: Introducción a V4L2

1. Concepto

V4L2 es la abreviatura de Video para linux2, que es el controlador del kernel de los equipos de video en Linux y proporciona un conjunto de especificaciones de interfaz unificadas para controladores y aplicaciones. En Linux, un dispositivo de video es un archivo de dispositivo, que se puede leer y escribir como un archivo normal.

Puede admitir varios dispositivos y puede tener las siguientes interfaces:

(1)  Interfaz de captura de video : El equipo para esta aplicación puede ser un cabezal de alta frecuencia o una cámara. El diseño original de V4L2 se aplicó a esta función.

(2) Interfaz de salida de video (interfaz de salida de video): puede controlar el equipo de imagen de video periférico de la computadora que puede emitir formato de señal de TV.

(3) Interfaz de video de transmisión directa (interfaz de superposición de video): su trabajo principal es enviar directamente la señal recopilada desde el dispositivo de captura de video al dispositivo de salida sin pasar por la CPU del sistema.

(4) Interfaz de señal de supresión de intervalo de video (interfaz VBI): permite que las aplicaciones accedan a la señal de video en el período de supresión de transmisión.

(5) Interfaz de radio: se puede utilizar para procesar la transmisión de audio recibida desde el equipo sintonizador de AM o FM.

El diagrama de estructura de V4L2 en el sistema Linux:

2. Modo de programación

v4L2 es un marco de programación para dispositivos usb sin unidad uvc, que se utiliza principalmente para recolectar cámaras usb, etc. El modo de programación es el siguiente:

(1) Encienda el dispositivo, configure los parámetros iniciales y configure la ventana de recopilación de imágenes de video, el tamaño y el formato de la matriz de puntos recopilada a través de la interfaz V4L2;

(2) Solicite búferes de marcos de imágenes y realice el mapeo de memoria, y mapee estos búferes de marcos desde el espacio del kernel al espacio del usuario, para que la aplicación pueda leer y procesar datos de imágenes;

(3) Ponga en cola el búfer de cuadros y comience la captura de video;

(4) El controlador inicia la recopilación de datos de video. El programa de aplicación saca el búfer de fotogramas de la cola de salida de captura de video y, después del procesamiento, vuelve a colocar el búfer de fotogramas en la cola de entrada de captura de video y recopila datos de video continuos de forma cíclica;

(5) Liberar recursos y dejar de recolectar trabajo.

Dos: instrucciones y flujo de trabajo de V4L2

Puede verse en el diagrama de estructura que V4L2 es un dispositivo de caracteres, y la mayoría de las funciones de V4L2 se derivan del archivo ioctl del dispositivo.

1. Clasificación de comandos ioctl comunes:

  1. Capacidad de consulta: consulta las funciones admitidas por el dispositivo, solo VIDIOC_QUERYCAP.
  2. Relacionado con la prioridad: incluye VIDIOC_G_PRIORITY, VIDIOC_S_PRIORITY, establecer prioridad.
  3. Relacionado con la captura: Ioctl relacionado con la captura de vídeo.
    CARNÉ DE IDENTIDAD descripción
    VIDIOC_ENUM_FMT Enumere todos los formatos de datos admitidos por el dispositivo
    VIDIOC_S_FMT Establecer el formato de datos
    VIDIOC_G_FMT Obtener formato de datos
    VIDIOC_TRY_FMT Igual que VIDIOC_S_FMT, pero no cambia el estado del dispositivo
    VIDIOC_REQBUFS Solicitar un búfer de video del dispositivo, es decir, inicializar el búfer de video
    VIDIOC_QUERYBUF Consultar el estado del búfer
    VIDIOC_QBUF Obtenga un fotograma de datos de video del dispositivo
    VIDIOC_DQBUF Devuelva el búfer de video al dispositivo,
    VIDIOC_OVERLAY Iniciar o detener superposición
    VIDIOC_G_FBUF Obtenga los parámetros de framebuffer del dispositivo de superposición de video o dispositivo OSD
    VIDIOC_S_FBUF Establecer parámetros de framebuffer
    VIDIOC_STREAMON Inicie la operación de E / S, captura o dispositivo de salida
    VIDIOC_STREAMOFF Cerrar las operaciones de E / S de flujo
  4. Estándar de video de TV:
    CARNÉ DE IDENTIDAD descripción
    VIDIOC_ENUMSTD Enumere todos los estándares admitidos por el dispositivo
    VIDIOC_G_STD Obtenga el estándar actualmente en uso
    VIDIOC_S_STD Establecer estándares de video
    VIDIOC_QUERYSTD Algunos dispositivos admiten la detección automática del estándar de video de la fuente de entrada. En este momento, use este ioctl para consultar el estándar de video detectado

2. Diagrama de flujo específico

Tres: marco de software

1. Abra el dispositivo para ver las propiedades.

Use el comando VIDIOC_QUERYCAP para ver la estructura de las propiedades del dispositivo utilizadas. El prototipo de tV4l2Cap es v4l2_capability para describir la información del controlador del dispositivo de captura de video. Según las capacidades, se puede juzgar si el dispositivo actual es un dispositivo de captura y compruebe si desea utilizar el mapeo de memoria o la lectura directa para obtener datos de imagen.

struct v4l2_capability {
	__u8	driver[16];     // 驱动名字
	__u8	card[32];       // 设备名字
	__u8	bus_info[32];   // 设备在系统中的位置
	__u32   version;        // 驱动版本号
	__u32	capabilities;   // 设备支持的操作
	__u32	device_caps;
	__u32	reserved[3];    // 保留字段
};

struct v4l2_capability tV4l2Cap;

iFd = open(strDevName, O_RDWR);
if (iFd < 0)
{
    DBG_PRINTF("can not open %s\n", strDevName);
    return -1;
}
ptVideoDevice->iFd = iFd;

iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);
memset(&tV4l2Cap, 0, sizeof(struct v4l2_capability));
iError = ioctl(iFd, VIDIOC_QUERYCAP, &tV4l2Cap);
if (iError) {
	DBG_PRINTF("Error opening device %s: unable to query device.\n", strDevName);
	goto err_exit;
}

if (!(tV4l2Cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
	DBG_PRINTF("%s is not a video capture device\n", strDevName);
    goto err_exit;
}

if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING) {
    DBG_PRINTF("%s supports streaming i/o\n", strDevName);
}

if (tV4l2Cap.capabilities & V4L2_CAP_READWRITE) {
    DBG_PRINTF("%s supports read i/o\n", strDevName);
}

2. Mostrar todos los formatos compatibles

 La estructura v4l2_fmtdesc describe la información de formato admitida por la cámara actual. Utilice el comando VIDIOC_ENUM_FMT para realizar consultas. El índice en la estructura v4l2_fmtdesc debe establecerse, comenzando desde 0; el tipo v4l2_buf_type también debe establecerse. Si se utiliza el dispositivo de cámara, v4l2_buf_TYPE type debe establecerse en V4L2_BUF_CAPTURE, porque la cámara es un dispositivo CAPTURE. El resto del contenido de la estructura lo llena el controlador.

struct v4l2_fmtdesc {
	__u32		    index;             // 要查询的格式序号,应用程序设置
	__u32		    type;              // 帧类型,应用程序设置
	__u32               flags;         // 是否为压缩格式
	__u8		    description[32];   // 格式名称
	__u32		    pixelformat;       //所支持的格式
	__u32		    reserved[4];
};

struct v4l2_fmtdesc tFmtDesc;

memset(&tFmtDesc, 0, sizeof(tFmtDesc));
tFmtDesc.index = 0;
tFmtDesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
while ((iError = ioctl(iFd, VIDIOC_ENUM_FMT, &tFmtDesc)) == 0) {
    if (isSupportThisFormat(tFmtDesc.pixelformat))
    {
        ptVideoDevice->iPixelFormat = tFmtDesc.pixelformat;
        break;
    }
    tFmtDesc.index++;
}

3. Configure el formato del marco de la imagen

La estructura struct v4l2_format necesita establecer enum v4l2_buf_type type y struct v4l2_pix_format pix en union fmt. Enum v4l2_buf_type type se establece en V4L2_BUF_TYPE_VIDEO_CAPTURE porque usa un dispositivo de cámara, que es un dispositivo CAPTURE. struct v4l2_pix_format pix establece la longitud, el ancho y el formato de un marco de imagen. Dado que necesita adaptarse a la salida de la pantalla LCD, la longitud y el ancho se establecen en la longitud y el ancho admitidos por la pantalla LCD.

struct v4l2_format {
	__u32	 type;
	union {
		struct v4l2_pix_format		pix;     /* V4L2_BUF_TYPE_VIDEO_CAPTURE */
		struct v4l2_pix_format_mplane	pix_mp;  /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */
		struct v4l2_window		win;     /* V4L2_BUF_TYPE_VIDEO_OVERLAY */
		struct v4l2_vbi_format		vbi;     /* V4L2_BUF_TYPE_VBI_CAPTURE */
		struct v4l2_sliced_vbi_format	sliced;  /* V4L2_BUF_TYPE_SLICED_VBI_CAPTURE */
		struct v4l2_sdr_format		sdr;     /* V4L2_BUF_TYPE_SDR_CAPTURE */
		struct v4l2_meta_format		meta;    /* V4L2_BUF_TYPE_META_CAPTURE */
		__u8	raw_data[200];                   /* user-defined */
	} fmt;
};
struct v4l2_format  tV4l2Fmt;

/* set format in */
GetDispResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);
memset(&tV4l2Fmt, 0, sizeof(struct v4l2_format));
tV4l2Fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2Fmt.fmt.pix.pixelformat = ptVideoDevice->iPixelFormat;
tV4l2Fmt.fmt.pix.width       = iLcdWidth;
tV4l2Fmt.fmt.pix.height      = iLcdHeigt;
tV4l2Fmt.fmt.pix.field       = V4L2_FIELD_ANY;

iError = ioctl(iFd, VIDIOC_S_FMT, &tV4l2Fmt);

4. Solicite la zona de amortiguamiento

Solicite un búfer con marcos en búfer NB_BUFFER, __u32 count es el número de marcos en búfer; enum v4l2_buf_type type es el mismo que antes, y también se establece en V4L2_BUF_TYPE_VIDEO_CAPTURE; enum v4l2_memorymemory se usa para distinguir entre el mapeo de memoria y el usuario Método de mapeo de memoria, por lo tanto, configúrelo en V4L2_MEMORY_MMAP.

struct v4l2_requestbuffers {
	__u32			count;
	__u32			type;		/* enum v4l2_buf_type */
	__u32			memory;		/* enum v4l2_memory */
	__u32			reserved[2];
};
struct v4l2_requestbuffers tV4l2ReqBuffs;

/* request buffers */
memset(&tV4l2ReqBuffs, 0, sizeof(struct v4l2_requestbuffers));
tV4l2ReqBuffs.count = NB_BUFFER;
tV4l2ReqBuffs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
tV4l2ReqBuffs.memory = V4L2_MEMORY_MMAP;

iError = ioctl(iFd, VIDIOC_REQBUFS, &tV4l2ReqBuffs);
if (iError) 
{
	DBG_PRINTF("Unable to allocate buffers.\n");
    goto err_exit;        
}

5. Asigne el marco de búfer solicitado desde el espacio del kernel al espacio del usuario y coloque el marco de búfer en una cola

struct v4l2_buffer {
	__u32			index;
	__u32			type;
	__u32			bytesused;
	__u32			flags;
	__u32			field;
	struct timeval		timestamp;
	struct v4l2_timecode	timecode;
	__u32			sequence;

	/* memory location */
	__u32			memory;
	union {
		__u32           offset;
		unsigned long   userptr;
		struct v4l2_plane *planes;
		__s32		fd;
	} m;
	__u32			length;
	__u32			reserved2;
	__u32			reserved;
};

struct v4l2_buffer tV4l2Buf;

if (tV4l2Cap.capabilities & V4L2_CAP_STREAMING)
{
    /* map the buffers */
    for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++) 
    {
    	memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
    	tV4l2Buf.index = i;
    	tV4l2Buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	tV4l2Buf.memory = V4L2_MEMORY_MMAP;
    	iError = ioctl(iFd, VIDIOC_QUERYBUF, &tV4l2Buf);
    	if (iError) 
        {
    	    DBG_PRINTF("Unable to query buffer.\n");
    	    goto err_exit;
    	}

        ptVideoDevice->iVideoBufMaxLen = tV4l2Buf.length;
    	ptVideoDevice->pucVideBuf[i] = mmap(0 /* start anywhere */ ,
    			  tV4l2Buf.length, PROT_READ, MAP_SHARED, iFd,
    			  tV4l2Buf.m.offset);
    	if (ptVideoDevice->pucVideBuf[i] == MAP_FAILED) 
        {
    	    DBG_PRINTF("Unable to map buffer\n");
    	    goto err_exit;
    	}
    }        

    /* Queue the buffers. */
    for (i = 0; i < ptVideoDevice->iVideoBufCnt; i++) 
    {
    	memset(&tV4l2Buf, 0, sizeof(struct v4l2_buffer));
    	tV4l2Buf.index = i;
    	tV4l2Buf.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    	tV4l2Buf.memory = V4L2_MEMORY_MMAP;
    	iError = ioctl(iFd, VIDIOC_QBUF, &tV4l2Buf);
    	if (iError)
        {
    	    DBG_PRINTF("Unable to queue buffer.\n");
    	    goto err_exit;
    	}
    }
}

 

 

 

 

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/qq_34968572/article/details/107554310
Recomendado
Clasificación