音视频开发—V4L2介绍,FFmpeg 打开摄像头输出yuv文件

news2024/11/24 14:45:11

实验平台:Ubuntu20.04

摄像头:1080P 监控摄像头,采用V4L2驱动框架

文章目录

    • 1.V4L2相关介绍
      • 1.1. 基本概念
      • 1.2. 主要功能
      • 1.3. V4L2驱动框架
      • 1.4. 主要组件
      • 1.5. 使用V4L2的应用
      • 1.6. 常用V4L2工具
    • 2.ffmpeg命令实现打开摄像头输出yuv文件
    • 3.使用C语言编程实现
    • 4.注意事项
      • 4.1Packet 和 Frame 的区别
      • 4.2为什么读取摄像头时使用 packet而不是 frame?

1.V4L2相关介绍

Video4Linux2(V4L2)是Linux内核中用于视频设备的一个API,旨在提供对视频捕捉、输出和处理设备的支持。它是Video4Linux(V4L)的继任者,具有更强的功能和更好的设计。以下是V4L2的详细介绍:

1.1. 基本概念

  • API: V4L2提供了一个标准的应用程序编程接口(API),使开发者能够在用户空间与视频设备进行交互。
  • 设备文件: 在Linux系统中,视频设备通常表示为 /dev/video0/dev/video1 等设备文件。

1.2. 主要功能

  • 视频捕捉: 从摄像头或其他视频输入设备捕获视频帧。
  • 视频输出: 将视频数据输出到显示设备或其他输出目标。
  • 视频流: 支持视频流的处理和传输,包括实时视频流的捕获和播放。
  • 图像处理: 提供基本的图像处理功能,如缩放、色彩转换等。

1.3. V4L2驱动框架

  • 驱动层: V4L2驱动程序位于内核空间,负责与硬件进行交互,控制视频设备的操作。
  • 用户空间API: 提供给应用程序使用的系统调用接口,如 ioctl 系列函数,用于配置和控制视频设备。

1.4. 主要组件

  • 设备节点: 在 /dev 目录下创建的设备文件,用于用户空间程序访问视频设备。
  • 控制接口: 通过 ioctl 函数设置和获取设备参数,如分辨率、帧率、视频格式等。
  • 缓冲区管理: 支持多种缓冲区管理模式,包括内存映射(mmap)、用户指针(user pointer)和DMA缓冲区(DMABUF)。
  • 格式转换: 支持多种视频格式,如YUV、RGB、MJPEG、H.264等,并提供格式转换功能。

1.5. 使用V4L2的应用

  • 视频采集应用: 使用V4L2 API开发的视频捕捉应用程序,如网络摄像头软件、视频录制软件等。
  • 多媒体框架: GStreamer、FFmpeg等多媒体框架支持V4L2,可以使用这些框架方便地进行视频处理和传输。
  • 嵌入式系统: 在嵌入式Linux系统中,V4L2广泛用于摄像头、视频采集卡等设备的驱动开发。

1.6. 常用V4L2工具

  • v4l2-ctl: 一个命令行工具,用于控制和调试V4L2设备。可以查询设备信息、设置参数、捕获视频帧等。
    v4l2-ctl --list-formats-ext  # 列出设备支持的所有格式
    v4l2-ctl --set-fmt-video=width=1920,height=1080,pixelformat=H264  # 设置视频格式
    v4l2-ctl --stream-mmap --stream-count=100 --stream-to=output.raw  # 捕获视频流
    

比如可以查看本次实验的摄像头的相关参数

marxist@ubuntu:~/Desktop/audio_test/build$ v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
	Type: Video Capture

	[0]: 'MJPG' (Motion-JPEG, compressed)
		Size: Discrete 1920x1080
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 640x480
			Interval: Discrete 0.008s (120.101 fps)
			Interval: Discrete 0.011s (90.000 fps)
			Interval: Discrete 0.017s (60.500 fps)
			Interval: Discrete 0.033s (30.200 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.017s (60.000 fps)
			Interval: Discrete 0.033s (30.500 fps)
		Size: Discrete 1024x768
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.017s (60.000 fps)
		Size: Discrete 1280x1024
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.008s (120.101 fps)
	[1]: 'YUYV' (YUYV 4:2:2)
		Size: Discrete 1920x1080
			Interval: Discrete 0.167s (6.000 fps)
		Size: Discrete 640x480
			Interval: Discrete 0.033s (30.000 fps)
		Size: Discrete 1280x720
			Interval: Discrete 0.111s (9.000 fps)
		Size: Discrete 1024x768
			Interval: Discrete 0.167s (6.000 fps)
		Size: Discrete 800x600
			Interval: Discrete 0.050s (20.000 fps)
		Size: Discrete 1280x1024
			Interval: Discrete 0.167s (6.000 fps)
		Size: Discrete 320x240
			Interval: Discrete 0.033s (30.000 fps)

由上述可知,摄像头一共支持两种格式,一是MJPG格式,已经由硬件压缩好的一种格式,一种就是常见的YUV422格式,YUV同样支持多种分辨率格式。得知这些参数之后,方便编程实现录制输出工作。

2.ffmpeg命令实现打开摄像头输出yuv文件

命令示例:

ffmpeg -f v4l2 -framerate 9 -video_size 1280x720 -pixel_format yuyv422 -i /dev/video0 -c:v rawvideo -pix_fmt yuv420p output.yuv

参数解释:

-f v4l2: 指定输入格式为V4L2(Video4Linux2)。

-framerate 9: 设置帧率为9fps。

-video_size 1280x720: 设置视频分辨率为1280x720(720p)。

-pixel_format yuyv422: 指定像素格式为YUYV422。

-i /dev/video0: 指定输入设备为 /dev/video0

-c:v rawvideo: 指定视频编码器为原始视频(不压缩)。

output.yuv: 指定输出文件名为 output.yuv

播放示例:

注意:需要指定格式和分辨率,原始的数据文件并不包含这些信息,需要手动指定才能播放。

ffplay -pix_fmt yuyv422 -s 1280*720 output.yuv 

效果图:

在这里插入图片描述

此时正常解析出来yuv画面。

3.使用C语言编程实现

主要流程较为简单,如下所示:

在这里插入图片描述

完整代码实现:

extern "C"
{
#include <stdio.h>
#include <stdlib.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
#include <iostream>
using namespace std;
#define OUTPUT_FILE "output.yuv"
#define CAMERA_DEVICE "/dev/video0"
AVFormatContext *format_ctx = NULL;
AVCodecContext *codec_ctx = NULL;
AVCodec *codec = NULL;
AVPacket packet ;
AVFrame *frame = NULL;
FILE *output_file = NULL;
AVInputFormat *input_format = NULL;
AVDictionary *options; //摄像头相关参数
int video_stream_index = -1;

int open_v4l2_cam()
{
    input_format = av_find_input_format("v4l2");

    if (!input_format)
    {
        fprintf(stderr, "Could not find input format 'v4l2'\n");
        return -1;
    }

    // 摄像头支持多种参数,因此使用option 指定参数 最大支持到9帧
    av_dict_set(&options, "video_size", "1280*720", 0);
    av_dict_set(&options, "framerate", "9", 0);
    av_dict_set(&options, "input_format", "yuyv422", 0);
    int ret = avformat_open_input(&format_ctx, CAMERA_DEVICE, input_format, &options);
    if (ret != 0)
    {
        cerr << "open input device fail" << endl;
        return -1;
    }
    ret = avformat_find_stream_info(format_ctx, NULL);
    if (ret < 0)
    {
        cerr << "findding stream info" << endl;
        return -1;
    }
    video_stream_index = -1;
    // 查找视频流
    for (size_t i = 0; i < format_ctx->nb_streams; i++)
    {
        /* code */
        if (format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            video_stream_index = i;
            break;
        }
    }
    if (video_stream_index == -1)
    {
        cerr << "no found video_steam" << endl;
        return -1;
    }
    return 0;
}
int init_codec_env()
{
    // 分配帧和数据包
    frame = av_frame_alloc();
    av_init_packet(&packet);
    // 准备写入文件
    output_file = fopen(OUTPUT_FILE, "wb");
    if (!output_file)
    {
        fprintf(stderr, "Error opening output file\n");
        return -1;
    }
}

void start_ouput_data()
{
    // 读取视频帧并保存为YUV 文件,
    cout << "start record" << endl;
    while (av_read_frame(format_ctx, &packet) >= 0)
    {
        if (packet.stream_index == video_stream_index)
        {
            fwrite(packet.data, 1, packet.size, output_file);
        }
        av_packet_unref(&packet);
    }
}
int main()
{

    avdevice_register_all(); // Ensure that device library is registered

    int ret = open_v4l2_cam();
    if (ret < 0)
    {
        cerr << "open cam fail !" << endl;
        return -1;
    }
    ret = init_codec_env();
    start_ouput_data();
    // 释放资源
    fclose(output_file);
    av_dict_free(&options);
    avformat_close_input(&format_ctx);
    return 0;
}

输出的yuv文件,为没有被编码的原始的数据,需要指定参数才能播放

效果如图:

在这里插入图片描述

4.注意事项

4.1Packet 和 Frame 的区别

  1. Packet(数据包):
    • 一般是指编码后的数据包(如H.264、H.265等编码格式的压缩数据)。
    • 包含元数据和编码数据。
    • 是数据流中的最小单位,可以包含一个完整帧或部分帧的数据。
  2. Frame(帧):
    • 是原始数据的表现形式,未经过编码压缩的原始视频或音频数据。
    • 在视频处理中,一个帧通常是一个完整的视频画面。
    • 在FFmpeg中,帧(AVFrame)是解码后的原始数据或编码前的原始数据。

4.2为什么读取摄像头时使用 packet而不是 frame?

当你通过FFmpeg读取视频数据时,即使没有显式地进行编码,FFmpeg也将视频数据封装在 AVPacket 中。原因如下:

  1. 数据流处理:
    • FFmpeg通过 av_read_frame 函数读取数据流,无论数据是否已经编码,读取到的数据都封装在 AVPacket 结构中。这是因为 AVPacket 是用于传输解码器或编码器之间的数据单位。
  2. 输入格式的处理:
    • 当使用 av_read_frame 从输入设备(如摄像头)读取数据时,FFmpeg将摄像头的原始数据作为 AVPacket 进行处理。这个 AVPacket 包含了从设备获取的原始数据块,即使这些数据是未压缩的。
  3. 统一接口:
    • av_read_frame 提供了一个统一的接口,用于处理各种输入数据流(文件、网络流、设备捕获)。这种设计简化了处理过程,不需要为不同的数据源提供不同的读取机制。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1796179.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法金 | 读者问了个关于深度学习卷积神经网络(CNN)核心概念的问题

​大侠幸会&#xff0c;在下全网同名[算法金] 0 基础转 AI 上岸&#xff0c;多个算法赛 Top [日更万日&#xff0c;让更多人享受智能乐趣] 读者问了个关于卷积神经网络核心概念的问题&#xff0c;如下&#xff0c; 【问】神经元、权重、激活函数、参数、图片尺寸&#xff0c;卷…

Python Excel 指定内容修改

需求描述 在处理Excel 自动化时,财务部门经常有一个繁琐的场景,需要读取分发的Excel文件内容复制到汇总Excel文件对应的单元格内,如下图所示: 这种需求可以延申为,财务同事制作一个模板,将模板发送给各员工,财务同事需收取邮件将员工填写的excel文件下载到本机,再类似…

jenkins的简单使用

2.1.简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 2.4.Jenkins安装 1.下载安装包jenkins.war&#xff1b; 2.在安装…

夕小瑶:资本寒冬下的AI创业一年

几天前我和几位前大厂朋友约了个饭&#xff0c;朋友纷纷向我透露出一种纠结&#xff1a; “GPT-4o将催生一大波创业机会啊&#xff0c;想离职” “但是现在是资本寒冬啊” “好想像你一样勇敢啊” 说起来这两年的大厂打工人确实比较难&#xff0c;受经济大环境影响&#xff0…

unity中animation和animator在使用上的区别

Animation&#xff08;动画&#xff09;&#xff0c;可直接存储在物体上的animation组件中 Animation 组件用于在对象上直接存储和播放动画数据。这些数据通常是通过关键帧动画&#xff08;keyframe animation&#xff09;制作的&#xff0c;其中包含了对象在不同时间点的变换…

28 - 只出现一次的最大数字(高频 SQL 50 题基础版)

28 - 只出现一次的最大数字 select (selectnumfromMyNumbers group bynum havingcount(num)1order by num desc limit 1) as num;

Vue2自定义拖拽指令-元素拖拽

Vue2自定义拖拽指令-元素拖拽-参数传递 v-canDraghtml部分/src/directive/canDrag/index.js然后注册到vue实例上就OK了 v-canDrag html部分 <template><div class"drag-container"><div class"drag-div" v-canDrag"{callback:callbac…

SQL进阶day11——窗口函数

目录 1专用窗口函数 1.1 每类试卷得分前3名 1.2第二快/慢用时之差大于试卷时长一半的试卷 1.3连续两次作答试卷的最大时间窗 1.4近三个月未完成试卷数为0的用户完成情况 1.5未完成率较高的50%用户近三个月答卷情况 2聚合窗口函数 2.1 对试卷得分做min-max归一化 2.2每份…

贪心算法学习一

例题一 解法&#xff08;贪⼼&#xff09;&#xff1a; 贪⼼策略&#xff1a; 分情况讨论&#xff1a; a. 遇到 5 元钱&#xff0c;直接收下&#xff1b; b. 遇到 10 元钱&#xff0c;找零 5 元钱之后&#xff0c;收下&#xff1b; c. 遇到 20 元钱&#xff1a…

LabVIEW齿轮调制故障检测系统

LabVIEW齿轮调制故障检测系统 概述 开发了一种基于LabVIEW平台的齿轮调制故障检测系统&#xff0c;实现齿轮在恶劣工作条件下的故障振动信号的实时在线检测。系统利用LabVIEW的强大图形编程能力&#xff0c;结合Hilbert包络解调技术&#xff0c;对齿轮的振动信号进行精确分析…

【Linux取经路】网络套接字编程——TCP篇

文章目录 前言十、Tcp Server 端代码10.1 socket、bind10.1 listen——监听一个套接字10.2 accept——获取一个新连接10.3 read——从套接字中读取数据10.4 write——向套接字中进行写入10.5 Tcp Service 端完整代码&#xff08;单进程版&#xff09;10.6 Tcp Server 端代码&am…

C++ 11【右值引用】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C修炼之路⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 1.C 11 简介 目录 1.C 11 简介 2. 统一的列表…

JVM之【类的生命周期】

首先&#xff0c;请区分Bean的声明周期和类的声明周期。此处讲的是类的声明周期 可以同步观看另一篇文章JVM之【类加载机制】 概述 在Java中数据类型分为基本数据类型和引用数据类型 基本数据类型由虚拟机预先定义&#xff0c;引用数据类型则需要进行类的加载 按照]ava虚拟机…

如何打造不一样的景区文旅VR体验馆项目?

近年来影院类产品迅速火爆&#xff0c;市面上的产品越来越多&#xff0c;投资者可以说是挑花了眼。为了助力投资者实现持续盈利&#xff0c;今天来给大家分析目前普乐蛙大爆新品悬空球幕飞行影院与其他5D/7D影院有哪些区别&#xff0c;给大家的创业投资之路避避雷~ 那我们正式开…

Android精通值Fragment的使用 —— 不含底层逻辑(五)

文章目录 1. Fragment1.1 Fragment的特性1.2 Fragment的基本使用步骤1.3 动态添加Fragment基本步骤1.4 Fragment与Activity的通信原生方案&#xff1a;Bundle类深入方案&#xff1a;java类与类通信的方案&#xff1a;接口Activity从Fragment获取消息Fragment从Activity获取消息…

德国80%的统计学教授都会答错的6个与P值有关的问题!

小编阅读了一篇发表于2002年关于P值的一项问卷调查研究 [1]&#xff0c;作者在6所德国大学中邀请了3组不同的受试者&#xff0c;分别为: 心理学专业的学生(n 44)&#xff1b;主要从事科学研究但不进行统计相关教学的教授和讲师(n 39)&#xff1b;进行统计相关教学的教授和讲师…

05-控制流(分支结构)

05-控制流(分支结构) 一、二路分支 程序中某一段代码需要满足一定的条件才会被执行。 if 语句&#xff1a;用于表达一种条件&#xff0c;如果条件满足则执行某个代码块。if-else 语句&#xff1a;用于表达一种条件&#xff0c;如果条件满足则执行某个代码块&#xff0c;否则…

微信小程序bindgetphonenumber获取手机号阻止冒泡触发

问题&#xff1a;点击手机号弹出微信的手机号验证组件&#xff0c;这是可以的。但是我点击车牌号&#xff0c;也弹出来了&#xff0c;这就郁闷了。 以下是解决方法 点击手机号时&#xff0c;弹出选择手机号 解决&#xff1a; <view style"display: flex;justify-conte…

Facebook开户|Facebook广告设计与测试优化

早上好家人们~今天Zoey给大家伙带来的是Facebook广告设计与测试优化&#xff0c;需要的家人们看过来啦&#xff01; 一、避免复杂用图和过多的文字 根据Facebook的数据显示&#xff0c;用户平均浏览一个贴文的时间在手机上仅花1.7秒、在电脑上则为2.5秒。因此&#xff0c;广告…

Java1.8 vue版家政服务系统成品源码 家政管家系统源码 家政月嫂系统源码 家政保洁系统源码 在线派单,师傅入驻全套商业源码

Java1.8 vue版家政服务系统成品源码 家政管家系统源码 家政月嫂系统源码 家政保洁系统源码 在线派单&#xff0c;师傅入驻全套商业源码 一、系统定义 家政上门服务系统是一种利用互联网技术&#xff0c;将家政服务需求与专业的家政服务人员进行高效匹配的平台。它允许用户通过…