首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Linux V4L2 采集 USB 攝像頭 YUV 幀並用 SDL 播放

     編輯:關於C語言

專案架構

  • main.c:SDL 初始化、視窗建立、主迴圈、執行緒同步
  • gather_picture.h/.c:V4L2 設備操作(開啟、設定格式、mmap、佇列/取幀)
  • SDL 執行緒:負責 YUV 轉 RGB 與材質更新

核心流程

  1. 開啟設備:open("/dev/video0", O_RDWR)
  2. 查詢能力:VIDIOC_QUERYCAP 確認支援視訊捕獲與串流
  3. 設定格式:VIDIOC_S_FMT 設定 640x480 YUYV (YUV 4:2:2)
  4. 請求緩衝區:VIDIOC_REQBUFS 申請 4 個 mmap 緩衝區
  5. 記憶體映射:mmap() 將核心緩衝區映射至用戶空間
  6. 佇列緩衝區:VIDIOC_QBUF 將空緩衡區入佇列
  7. 啟動串流:VIDIOC_STREAMON
  8. 取幀迴圈:VIDIOC_DQBUF 取得資料 → 處理 → VIDIOC_QBUF 歸還
  9. SDL 渲染:YUV → RGB (SWS) → SDL_UpdateTexture → SDL_RenderCopy

關鍵代碼片段

V4L2 初始化 (gather_picture.c)


int video_init(void) {
    g_videofd = open(VIDEO_DEV, O_RDWR);
    // ... 查詢能力、列舉格式、設定格式 ...
    struct v4l2_requestbuffers reqbuf = { .count = 4, .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP };
    ioctl(g_videofd, VIDIOC_REQBUFS, &reqbuf);
    // mmap 每個緩衝區
    for (i = 0; i < reqbuf.count; i++) {
        struct v4l2_buffer buf = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP, .index = i };
        ioctl(g_videofd, VIDIOC_QUERYBUF, &buf);
        frame_buf.start[i] = mmap(NULL, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, g_videofd, buf.m.offset);
        frame_buf.length[i] = buf.length;
    }
    // 佇列所有緩衝區
    for (i = 0; i < reqbuf.count; i++) {
        struct v4l2_buffer buf = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP, .index = i };
        ioctl(g_videofd, VIDIOC_QBUF, &buf);
    }
    // 啟動串流
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(g_videofd, VIDIOC_STREAMON, &type);
    return 0;
}

SDL 主迴圈 (main.c)


SDL_Init(SDL_INIT_VIDEO);
screen = SDL_SetVideoMode(WIDTH, HEIGHT, 32, SDL_HWSURFACE|SDL_DOUBLEBUF);
// 建立 YUV Overlay 或 Texture (SDL 2.0 建議用 Texture)
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);

while (running) {
    // 取得幀資料
    struct v4l2_buffer buf = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, .memory = V4L2_MEMORY_MMAP };
    ioctl(g_videofd, VIDIOC_DQBUF, &buf);
    void *yuv_data = frame_buf.start[buf.index];

    // YUV 轉 RGB (使用 FFmpeg swscale)
    sws_scale(sws_ctx, (const uint8_t*[]){(uint8_t*)yuv_data}, linesize, 0, HEIGHT, dst_data, dst_linesize);

    // 更新 SDL Texture
    SDL_UpdateYUVTexture(texture, NULL, y_plane, y_pitch, u_plane, u_pitch, v_plane, v_pitch);
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, texture, NULL, NULL);
    SDL_RenderPresent(renderer);

    // 歸還緩衝區
    ioctl(g_videofd, VIDIOC_QBUF, &buf);
    SDL_Delay(10);
}

常見問題與解決

問題解決方案
畫面顏色異常(偏綠/偏藍)確認 V4L2 格式 (YUYV/UYVY) 與 swscale 輸入格式一致
幀率低、延遲高減少 mmap 緩衝區數量、使用零拷貝、啟用 DMA
SDL 視窗無畫面檢查 SDL_PixelFormat 與 Texture 格式匹配,確認 Renderer 硬體加速
Segmentation fault檢查 mmap 回傳值、緩衝區索引邊界、swscale context 初始化

編譯鏈結


gcc -o camera main.c gather_picture.c `pkg-config --cflags --libs sdl2 libavcodec libavformat libswscale` -lv4l2 -lpthread

學習資源

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved