1. 初始化Camera设备节点

任务说明

本案例目标是在Android/Linux环境下,通过C语言代码打开摄像头设备节点/dev/video0,完成基本的参数配置(包括设置像素格式、分辨率及帧间隔),并通过查询实际生效的参数来验证设备节点是否成功打开且配置是否生效。代码将直接调用V4L2(Video4Linux2)标准接口实现,不依赖任何第三方库。

核心实现

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>

#define DEVICE_PATH "/dev/video0"
#define WIDTH  640
#define HEIGHT 480

static int xioctl(int fd, unsigned long request, void *arg)
{
    int ret;
    do {
        ret = ioctl(fd, request, arg);
    } while (ret == -1 && errno == EINTR);
    return ret;
}

int main(void)
{
    int fd = -1;
    struct v4l2_format fmt;
    struct v4l2_streamparm parm;

    /* 1. 打开设备节点 */
    fd = open(DEVICE_PATH, O_RDWR);
    if (fd == -1) {
        perror("open /dev/video0");
        return EXIT_FAILURE;
    }
    printf("[OK] open %s success\n", DEVICE_PATH);

    /* 2. 查询设备能力,确认是视频采集设备 */
    struct v4l2_capability cap;
    if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
        perror("VIDIOC_QUERYCAP");
        close(fd);
        return EXIT_FAILURE;
    }
    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        fprintf(stderr, "Error: %s is not a video capture device\n", DEVICE_PATH);
        close(fd);
        return EXIT_FAILURE;
    }
    printf("[OK] driver: %s, card: %s\n", cap.driver, cap.card);

    /* 3. 设置采集格式:YUYV, 640x480 */
    memset(&fmt, 0, sizeof(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_YUYV;
    fmt.fmt.pix.field       = V4L2_FIELD_NONE;

    if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
        perror("VIDIOC_S_FMT");
        close(fd);
        return EXIT_FAILURE;
    }
    printf("[OK] set format: %dx%d, fourcc=0x%08x\n",
           fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.pixelformat);

    /* 4. 设置帧间隔为 30fps */
    memset(&parm, 0, sizeof(parm));
    parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    parm.parm.capture.timeperframe.numerator   = 1;
    parm.parm.capture.timeperframe.denominator = 30;

    if (xioctl(fd, VIDIOC_S_PARM, &parm) == -1) {
        perror("VIDIOC_S_PARM");
        close(fd);
        return EXIT_FAILURE;
    }
    printf("[OK] set framerate: %d/%d fps\n",
           parm.parm.capture.timeperframe.denominator,
           parm.parm.capture.timeperframe.numerator);

    /* 5. 回读实际生效的格式,验证配置 */
    struct v4l2_format cur_fmt;
    memset(&cur_fmt, 0, sizeof(cur_fmt));
    cur_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (xioctl(fd, VIDIOC_G_FMT, &cur_fmt) == -1) {
        perror("VIDIOC_G_FMT");
        close(fd);
        return EXIT_FAILURE;
    }

    if (cur_fmt.fmt.pix.width == WIDTH &&
        cur_fmt.fmt.pix.height == HEIGHT &&
        cur_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {
        printf("[PASS] format verification: %dx%d YUYV\n",
               cur_fmt.fmt.pix.width, cur_fmt.fmt.pix.height);
    } else {
        printf("[WARN] driver adjusted format to %dx%d, fourcc=0x%08x\n",
               cur_fmt.fmt.pix.width, cur_fmt.fmt.pix.height,
               cur_fmt.fmt.pix.pixelformat);
    }

    /* 6. 关闭设备 */
    close(fd);
    printf("[OK] device closed\n");
    return EXIT_SUCCESS;
}

编译与运行

将上述代码保存为camera_init.c,在Android NDK环境或Linux交叉编译环境中执行:

# Linux 本机编译
gcc -o camera_init camera_init.c

# Android NDK 交叉编译(示例)
$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android28-clang \
    -o camera_init camera_init.c

# 运行(需要root权限或设备节点可访问)
./camera_init

验证方法与判定标准

在目标设备上运行编译后的程序,观察输出。判定成功的标准如下:

  1. 程序依次打印[OK] open /dev/video0 success和驱动信息,说明设备节点可正常打开且具备视频采集能力;
  2. 打印[OK] set format: 640x480, fourcc=0x56595559(0x56595559为YUYV的ASCII码),说明格式设置调用成功;
  3. 打印[OK] set framerate: 30/1 fps,说明帧间隔配置成功;
  4. 最后打印[PASS] format verification: 640x480 YUYV,表示回读格式与设置一致,验证通过;若驱动调整了参数,会打印[WARN]提示实际值,此时需根据驱动能力调整期望值后重新验证;
  5. 程序退出码为0,且打印[OK] device closed。

若open失败,请检查设备节点是否存在(ls -l /dev/video0)、当前用户是否有读写权限,或设备是否被其他进程占用。若VIDIOC_S_FMT失败,说明驱动不支持YUYV格式或该分辨率,可通过v4l2-ctl --list-formats-ext查询设备实际支持的格式列表后调整代码中的参数。