当前位置: 首页 > article >正文

Linux中,使用C++获取网络摄像头视频流的方式【附带源码示例】

在Linux中,使用C++获取网络摄像头视频流通常可以通过以下两种方式实现:

  1. 使用Video4Linux2 (V4L2) API
    V4L2是Linux内核提供的用于视频设备(如摄像头)的API,支持直接访问摄像头硬件并获取视频流。

  2. 使用FFmpeg库
    FFmpeg是一个功能强大的多媒体处理库,支持从摄像头设备(如 /dev/video0)或网络流(如RTSP)获取视频流。

下面分别介绍这两种方式的实现方法。


方法一:使用Video4Linux2 (V4L2) API

1. 安装依赖

确保系统已安装V4L2开发库:

sudo apt-get install libv4l-dev
2. 示例代码

以下代码展示了如何使用V4L2 API从摄像头获取视频流并保存为YUV格式的文件。

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/videodev2.h>
#include <cstring>

#define DEVICE "/dev/video0"
#define WIDTH 640
#define HEIGHT 480
#define OUTPUT_FILE "output.yuv"

int main() {
    int fd = open(DEVICE, O_RDWR);
    if (fd == -1) {
        std::cerr << "Failed to open device: " << DEVICE << std::endl;
        return -1;
    }

    // 设置视频格式
    v4l2_format format = {};
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = WIDTH;
    format.fmt.pix.height = HEIGHT;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // YUYV格式
    format.fmt.pix.field = V4L2_FIELD_NONE;

    if (ioctl(fd, VIDIOC_S_FMT, &format) == -1) {
        std::cerr << "Failed to set video format" << std::endl;
        close(fd);
        return -1;
    }

    // 申请缓冲区
    v4l2_requestbuffers req = {};
    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
        std::cerr << "Failed to request buffers" << std::endl;
        close(fd);
        return -1;
    }

    // 映射缓冲区
    v4l2_buffer buffer = {};
    buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buffer.memory = V4L2_MEMORY_MMAP;
    buffer.index = 0;

    if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) == -1) {
        std::cerr << "Failed to query buffer" << std::endl;
        close(fd);
        return -1;
    }

    void* bufferStart = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buffer.m.offset);
    if (bufferStart == MAP_FAILED) {
        std::cerr << "Failed to map buffer" << std::endl;
        close(fd);
        return -1;
    }

    // 开始捕获
    if (ioctl(fd, VIDIOC_STREAMON, &buffer.type) == -1) {
        std::cerr << "Failed to start streaming" << std::endl;
        close(fd);
        return -1;
    }

    // 打开输出文件
    FILE* outputFile = fopen(OUTPUT_FILE, "wb");
    if (!outputFile) {
        std::cerr << "Failed to open output file" << std::endl;
        close(fd);
        return -1;
    }

    // 捕获帧并写入文件
    for (int i = 0; i < 100; ++i) { // 捕获100帧
        if (ioctl(fd, VIDIOC_QBUF, &buffer) == -1) {
            std::cerr << "Failed to enqueue buffer" << std::endl;
            break;
        }

        if (ioctl(fd, VIDIOC_DQBUF, &buffer) == -1) {
            std::cerr << "Failed to dequeue buffer" << std::endl;
            break;
        }

        fwrite(bufferStart, buffer.bytesused, 1, outputFile);
    }

    // 停止捕获
    ioctl(fd, VIDIOC_STREAMOFF, &buffer.type);

    // 释放资源
    munmap(bufferStart, buffer.length);
    fclose(outputFile);
    close(fd);

    std::cout << "Video capture completed. Saved to " << OUTPUT_FILE << std::endl;
    return 0;
}
3. 编译与运行

编译代码:

g++ -o v4l2_capture v4l2_capture.cpp

运行程序:

./v4l2_capture

方法二:使用FFmpeg库

1. 安装FFmpeg开发库

确保已安装FFmpeg开发库:

sudo apt-get install libavformat-dev libavcodec-dev libavutil-dev libswscale-dev
2. 示例代码

以下代码展示了如何使用FFmpeg从摄像头获取视频流并解码为帧。

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}

#include <iostream>

int main() {
    av_register_all();
    avformat_network_init();

    AVFormatContext* formatContext = avformat_alloc_context();
    if (!formatContext) {
        std::cerr << "Failed to allocate format context" << std::endl;
        return -1;
    }

    // 打开摄像头设备
    const char* device = "/dev/video0";
    if (avformat_open_input(&formatContext, device, nullptr, nullptr) != 0) {
        std::cerr << "Failed to open device: " << device << std::endl;
        return -1;
    }

    // 获取流信息
    if (avformat_find_stream_info(formatContext, nullptr) < 0) {
        std::cerr << "Failed to find stream information" << std::endl;
        return -1;
    }

    // 查找视频流
    int videoStreamIndex = -1;
    for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            break;
        }
    }

    if (videoStreamIndex == -1) {
        std::cerr << "Could not find video stream" << std::endl;
        return -1;
    }

    // 获取解码器
    AVCodecParameters* codecParameters = formatContext->streams[videoStreamIndex]->codecpar;
    AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);
    if (!codec) {
        std::cerr << "Unsupported codec" << std::endl;
        return -1;
    }

    // 创建解码器上下文
    AVCodecContext* codecContext = avcodec_alloc_context3(codec);
    if (!codecContext) {
        std::cerr << "Failed to allocate codec context" << std::endl;
        return -1;
    }

    if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
        std::cerr << "Failed to copy codec parameters to context" << std::endl;
        return -1;
    }

    if (avcodec_open2(codecContext, codec, nullptr) < 0) {
        std::cerr << "Failed to open codec" << std::endl;
        return -1;
    }

    // 分配帧和包
    AVFrame* frame = av_frame_alloc();
    AVPacket* packet = av_packet_alloc();
    if (!frame || !packet) {
        std::cerr << "Failed to allocate frame or packet" << std::endl;
        return -1;
    }

    // 读取帧
    while (av_read_frame(formatContext, packet) >= 0) {
        if (packet->stream_index == videoStreamIndex) {
            if (avcodec_send_packet(codecContext, packet) == 0) {
                while (avcodec_receive_frame(codecContext, frame) == 0) {
                    // 处理解码后的帧(frame)
                    std::cout << "Decoded frame: " << frame->pts << std::endl;
                }
            }
        }
        av_packet_unref(packet);
    }

    // 释放资源
    av_frame_free(&frame);
    av_packet_free(&packet);
    avcodec_free_context(&codecContext);
    avformat_close_input(&formatContext);
    avformat_free_context(formatContext);

    return 0;
}
3. 编译与运行

编译代码:

g++ -o ffmpeg_capture ffmpeg_capture.cpp -lavformat -lavcodec -lavutil -lswscale

运行程序:

./ffmpeg_capture

总结

方法优点缺点适用场景
V4L2直接访问硬件,低延迟代码复杂,功能有限本地摄像头访问
FFmpeg功能强大,支持多种格式和协议依赖库较大,延迟稍高网络流或复杂视频处理

根据需求选择合适的方式:

  • 如果需要直接访问摄像头硬件,推荐使用 V4L2
  • 如果需要支持多种格式或网络流,推荐使用 FFmpeg

http://www.kler.cn/a/538596.html

相关文章:

  • 前端 CSS 动态设置样式::class、:style 等技巧详解
  • ZU47DR 100G光纤 高性能板卡
  • STM32G474--Whetstone程序移植(单精度)笔记
  • ChunkKV:优化 KV 缓存压缩,让 LLM 长文本推理更高效
  • ChatGPT提问技巧:行业热门应用提示词案例-文案写作
  • Dockerfile 文件详解
  • 配置 VS Code 调试 ROS Python 脚本:完整步骤
  • 保研考研机试攻略:python笔记(4)
  • 设计模式-生产者消费者模型
  • RPC 如何做到 服务调⽤者可以像调⽤本地接⼝⼀样调⽤远程的服务提供者
  • Rust 测试指南:从入门到进阶
  • DeepSeek-R1-V3(深度求索)和GPT-4o(OpenAI)作为两种不同的AI模型,在PoC编写场景中表现差异显著
  • (五)C++的类继承、多态、组合
  • MYSQL第四次
  • 网络安全-HSTS
  • python3中字符编码的问题
  • c#序列化和反序列化(一)
  • golang 开启HTTP代理认证
  • Spring-RetryTemplate
  • 解构赋值在 TypeScript 中的妙用:以 Babylon.js 的 loadModel 函数为例
  • 优惠券平台(一):基于责任链模式创建优惠券模板
  • 侯捷C++课程学习笔记:从内存管理到面向对象编程的深度探索
  • 分享2款 .NET 开源且强大的翻译工具
  • 在线SQL转JSON-GO在线工具集
  • Visual Studio(VS)初始配置环境(scanf异常)
  • Spring(26) spring-security-oauth2 官方表结构解析