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:
- Capacidad de consulta: consulta las funciones admitidas por el dispositivo, solo VIDIOC_QUERYCAP.
- Relacionado con la prioridad: incluye VIDIOC_G_PRIORITY, VIDIOC_S_PRIORITY, establecer prioridad.
- 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 - 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;
}
}
}