GStreamer开发笔记(二):GStreamer在ubnutn平台部署安装,测试gstreamer/cheese/ffmpeg/fmplayer打摄像头延迟

前言

  本篇介绍ubuntu平台的GStreamer部署安装,然后测试摄像头,进行性能延迟对比。

准备ubuntu虚拟机

  略。

注意

  由于在虚拟机ubuntu上也会测试usb摄像头,因为不是直接硬件到ubuntu,而是先到windows然后虚拟机桥接到ubuntu,可能会到usb摄像头的延迟产生一定影响,但根据经验,大概率几毫秒到几十毫秒之间。

关于ubuntu离线安装

  离线安装比较难,首先就要安装几十个依赖工具和库,离线没有这些的话,就是套娃+套娃+套娃,需要费很大劲逐一安装,且解决各种依赖问题,所以就算离线安装也需要先把ubuntu的辅助工具都打上做成个镜像,再去离线安装gstreamer,这里不建议没依赖库的时候直接离线安装。

GStreamer在ubuntu部署

步骤一:下载解压

  

步骤二:更新,并安装gstreamer

shell 复制代码
sudo apt-get update
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgstreamer-plugins-bad1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-libav gstreamer1.0-doc gstreamer1.0-tools gstreamer1.0-x gstreamer1.0-alsa gstreamer1.0-gl gstreamer1.0-gtk3 gstreamer1.0-qt5 gstreamer1.0-pulseaudio

步骤三:运行gstreamer测试

  命令行打开usb摄像头

shell 复制代码
gst-launch-1.0.exe -v ksvideosrc do-stats=TRUE ! videoconvert ! autovideosink

  命令行打开播放文件

shell 复制代码
gst-launch-1.0 playbin uri=file:///<你的视频>
gst-launch-1.0 playbin uri=file:////home/yang/Desktop/test.avi

  

  命令行打开usb摄像头

shell 复制代码
gst-launch-1.0 v4l2src device=/dev/video0 ! video/x-raw,framerate=30/1 ! videoconvert ! autovideosink

  

延迟和内存对比

步骤一:gstreamer延迟和内存

  使用gstreamer:

shell 复制代码
gst-launch-1.0 v4l2src device=/dev/video0 ! video/x-raw,framerate=30/1 ! videoconvert ! autovideosink

     内存占用:   

步骤二:cheese延迟和内存

  cheese相机:

shell 复制代码
sudo apt-get install cheese
sudo cheese

     内存占用:   

  Ubuntu打开usb摄像头   

步骤三:ffmpeg的ffplay延迟和内存

cpp 复制代码
sudo apt-get install ffmpeg
ffplay -f v4l2 /dev/video0

     对比内存   

步骤四:fmplayer延迟和内存

shell 复制代码
sudo apt-get install fmplayer

     对比内存:   

Demo

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <errno.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_pixels.h>

#define WIDTH 640
#define HEIGHT 480

int main() {

    setbuf(stdout, NULL);

    int fd;
    struct v4l2_format fmt;
    struct v4l2_requestbuffers req;
    struct v4l2_buffer buf;
    void *buffer_start;
    unsigned int buffer_length;

    // 打开摄像头设备
    fd = open("/dev/video0", O_RDWR);
    if (fd == -1) {
        perror("打开摄像头设备失败");
        return EXIT_FAILURE;
    }

    // 设置视频格式
    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_INTERLACED;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
        perror("设置视频格式失败");
        close(fd);
        return EXIT_FAILURE;
    }

    // 请求缓冲区
    memset(&req, 0, sizeof(req));
    req.count = 1;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
        perror("请求缓冲区失败");
        close(fd);
        return EXIT_FAILURE;
    }

    // 映射缓冲区
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 0;

    if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
        perror("查询缓冲区失败");
        close(fd);
        return EXIT_FAILURE;
    }

    buffer_length = buf.length;
    buffer_start = mmap(NULL, buffer_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
    if (buffer_start == MAP_FAILED) {
        perror("映射缓冲区失败");
        close(fd);
        return EXIT_FAILURE;
    }

    // 将缓冲区放入队列
    if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
        perror("缓冲区入队失败");
        munmap(buffer_start, buffer_length);
        close(fd);
        return EXIT_FAILURE;
    }

    // 开始视频捕获
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1) {
        perror("开始视频捕获失败");
        munmap(buffer_start, buffer_length);
        close(fd);
        return EXIT_FAILURE;
    }

    // 初始化 SDL
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "SDL 初始化失败: %s\n", SDL_GetError());
        munmap(buffer_start, buffer_length);
        close(fd);
        return EXIT_FAILURE;
    }

    SDL_Window *window = SDL_CreateWindow("V4L2 Camera", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIDTH, HEIGHT, 0);
    if (!window) {
        fprintf(stderr, "创建 SDL 窗口失败: %s\n", SDL_GetError());
        SDL_Quit();
        munmap(buffer_start, buffer_length);
        close(fd);
        return EXIT_FAILURE;
    }

    SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);

    // SDL_PIXELFORMAT_YV12 =      /**< Planar mode: Y + V + U  (3 planes) */
    // SDL_PIXELFORMAT_IYUV =      /**< Planar mode: Y + U + V  (3 planes) */
    // SDL_PIXELFORMAT_YUY2 =      /**< Packed mode: Y0+U0+Y1+V0 (1 plane) */
    // SDL_PIXELFORMAT_UYVY =      /**< Packed mode: U0+Y0+V0+Y1 (1 plane) */
    // SDL_PIXELFORMAT_YVYU =      /**< Packed mode: Y0+V0+Y1+U0 (1 plane) */

//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YUY2, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_UYVY, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);
//    SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YVYU, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);

    int running = 1;
    SDL_Event event;
    while (running) {
        // 处理事件
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                running = 0;
            }
        }

        // 捕获帧
        memset(&buf, 0, sizeof(buf));
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;

        if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
            perror("出队缓冲区失败");
            break;
        }

        // 更新 SDL 纹理
        SDL_UpdateTexture(texture, NULL, buffer_start, WIDTH);

        // 渲染纹理
        SDL_RenderClear(renderer);
        SDL_RenderCopy(renderer, texture, NULL, NULL);
        SDL_RenderPresent(renderer);

        // 将缓冲区重新入队
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
            perror("缓冲区入队失败");
            break;
        }
    }

    // 清理资源
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();
    munmap(buffer_start, buffer_length);
    close(fd);

    return EXIT_SUCCESS;
}

总结

  到这里,我们得出结论,gstreamer基本是最优秀的框架之一了,初步测试不是特别严谨,但是基本能反应情况(比如ffmpeg得fmplay本轮测试是最差,但是ffmpeg写代码可以进行ffmpeg源码和编程代码的优化,达到150ms左右,诸如这类情况不考虑)。   gstreamer优于ffmplayer优于cheese优于ffmpeg。   下一篇将使用代码v4l2+SDL以及v4l2+QtOpenGL做进一步测试延迟内存。

入坑

入坑一:摄像头使用cheese打开为黑色

问题

  打开黑色,有设备,拔了就提示没设备   

尝试

  更新安装驱动,未解决,排除驱动问题。   测试sudo cheese,还是黑色,排除权限问题。   修改虚拟机usb兼容性,换不是自己当前的就好了:   

  然后可以了。

解决

  虚拟机兼容性从2.0改成USB3.1,然后:   

相关推荐
kiracrimson6 分钟前
STL:List的使用和模拟实现
开发语言·c++·list
2601_9494800610 分钟前
开源鸿蒙平台 CMP 三方库「reveal」适配全流程
华为·开源·harmonyos
二十七日秋13 分钟前
算法刷题笔记「Day 1|四道 OJ 入门题里的深坑」
c语言·汇编·数据结构·c++·笔记·python·算法
2401_zq136y0314 分钟前
开源鸿蒙平台 CMP 三方库「KToast」适配全流程
华为·开源·harmonyos
流年如夢35 分钟前
继承与多态_继承篇
c++·继承·面向对象·c++进阶
沫璃染墨1 小时前
从零入门计算机网络系列(十):HTTP协议进阶——从资源路径解析到状态码与请求头详解
linux·运维·服务器·网络·c++·http
海盗12341 小时前
AI 新闻日报 2026-10-29:Gemini 4 Carbon 登顶、Clover 35B 扩散模型开源、Copilot Fusion 接手选模型
人工智能·机器人·开源·copilot·人工智能aigc
xiaoye-duck1 小时前
《C++ 工具库实战》高性能日志 spdlog:从基础使用到工程化封装
c++·第三方库
努力努力再努力wz1 小时前
【Lua入门系列】从 C++ 迭代器到 Lua 迭代器:一文深入理解 Lua 有状态以及无状态迭代器与泛型for、闭包、ipairs、pairs 和 next
开发语言·c++·lua
努力实干2 小时前
第二阶段 · 第 7 课:C++ 多线程与线程安全
开发语言·c++·安全