USB摄像头使用V4L2采集图像\视频

背景

V4L2(Video for Linux Two)是Linux内核自带的一部分,专门用于处理视频设备的管理和控制。‌ V4L2框架提供了统一的API和抽象层,使得开发者可以编写通用的视频驱动程序,同时使用户空间的应用程序能够轻松地访问和控制视频设备‌。在linux的开发板上,为了对符合UVC协议的摄像头进行视频或图像采集时,若不方便安装第三库,可以使用linux内核自带的V4L2框架进行处理。

V4L2视频采集

如下代码来自电子论坛,能够帮助我们采集uvc摄像头的图像,保存为.bmp。
在虚拟机中aarch64交叉编译得到可执行文件 V4L2_VideoCapture,不知道如何配置交叉编译的,可以查看我的其他博文。执行函数

#videoXX是你的设备号,我的是video36,使用命令gst-device-monitor-1.0可进行查询
./V4L2_VideoCapture /dev/videoXX

在这里插入图片描述
主要过程包含了

  1. v4l2设备的初始化。初始化中包括了打开相机,设置格式、分辨率、帧率,申请buffer空间等准备工作
  2. 数据采集。使用ioctl从队列中取出数据,拷贝副本,然后把原始数据再放回去
  3. 数据格式转换。如YUYV格式转换为RGB,源代码出处的转换公式有一处错误,本文已进行了修正。
  4. 保存图像
  5. 释放环境
// V4L2_VideoCapture.cpp
#include <fcntl.h>
#include <linux/videodev2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>#define _UVC_CAM_HEIGHT (480)
#define _UVC_CAM_WIDTH  (640)
#define IMAGEHEIGHT     _UVC_CAM_HEIGHT
#define IMAGEWIDTH      _UVC_CAM_WIDTH#define NB_BUFFER 4struct vdIn {int   fd;char* videodevice;// v4l2struct v4l2_capability     cap;struct v4l2_format         fmt;struct v4l2_fmtdesc        fmtdesc;struct v4l2_streamparm     setfps;struct v4l2_requestbuffers rb;void* mem[NB_BUFFER];int   memlength[NB_BUFFER];unsigned char* framebuffer;int            framesizeIn;int width;int height;int fps;FILE* fp_bmp;
};//14byte文件头
typedef struct
{unsigned char cfType[2];  //文件类型,"BM"(0x4D42)unsigned int  cfSize;     //文件大小(字节)unsigned int  cfReserved; //保留,值为0unsigned int  cfoffBits;  //数据区相对于文件头的偏移量(字节)
} __attribute__((packed)) BITMAPFILEHEADER;//40byte信息头
typedef struct
{unsigned int       ciSize;            //BITMAPFILEHEADER所占的字节数unsigned int       ciWidth;           //宽度unsigned int       ciHeight;          //高度unsigned short int ciPlanes;          //目标设备的位平面数,值为1unsigned short int ciBitCount;        //每个像素的位数char               ciCompress[4];     //压缩说明unsigned int       ciSizeImage;       //用字节表示的图像大小,该数据必须是4的倍数unsigned int       ciXPelsPerMeter;   //目标设备的水平像素数/米unsigned int       ciYPelsPerMeter;   //目标设备的垂直像素数/米unsigned int       ciClrUsed;         //位图使用调色板的颜色数unsigned           intciClrImportant; //指定重要的颜色数,当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要
} __attribute__((packed)) BITMAPINFOHEADER;typedef struct
{unsigned char blue;unsigned char green;unsigned char red;
} __attribute__((packed)) PIXEL; //颜色模式RGB/* Private function prototypes -----------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static struct vdIn   uvc_cam;
static unsigned char rgb888_buffer[IMAGEWIDTH * IMAGEHEIGHT * 3];/* Global  variables ---------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/void yuyv_to_rgb888(void)
{int            i, j;unsigned char  y1, y2, u, v;int            r1, g1, b1, r2, g2, b2;unsigned char* pointer;double         rbase = 0;double         gbase = 0;double         bbase = 0;pointer = uvc_cam.framebuffer;for (i = 0; i < IMAGEHEIGHT; i++) {for (j = 0; j < (IMAGEWIDTH / 2); j++) {y1 = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2));u  = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2) + 1);y2 = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2) + 2);v  = *(pointer + ((i * (IMAGEWIDTH / 2) + j) << 2) + 3);rbase = 1.042 * (v - 128);gbase = 0.34414 * (u - 128) + 0.71414 * (v - 128);bbase = 1.772 * (u - 128);r1 = y1 + rbase;g1 = y1 - gbase;b1 = y1 + bbase;r2 = y2 + rbase;g2 = y2 - gbase;b2 = y2 + bbase;if (r1 > 255)r1 = 255;else if (r1 < 0)r1 = 0;if (b1 > 255)b1 = 255;else if (b1 < 0)b1 = 0;if (g1 > 255)g1 = 255;else if (g1 < 0)g1 = 0;if (r2 > 255)r2 = 255;else if (r2 < 0)r2 = 0;if (b2 > 255)b2 = 255;else if (b2 < 0)b2 = 0;if (g2 > 255)g2 = 255;else if (g2 < 0)g2 = 0;*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6)     = (unsigned char)b1;*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 1) = (unsigned char)g1;*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 2) = (unsigned char)r1;*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 3) = (unsigned char)b2;*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 4) = (unsigned char)g2;*(rgb888_buffer + ((IMAGEHEIGHT - 1 - i) * (IMAGEWIDTH / 2) + j) * 6 + 5) = (unsigned char)r2;}}printf("yuyv to rgb888 done\n");
}int v4l2_init(void)
{int                i = 0;int                ret;struct v4l2_buffer buf;// 1. open camif ((uvc_cam.fd = open(uvc_cam.videodevice, O_RDWR)) == -1) {printf("ERROR opening V4L interface\n");return -1;}// 2. querycapmemset(&uvc_cam.cap, 0, sizeof(struct v4l2_capability));ret = ioctl(uvc_cam.fd, VIDIOC_QUERYCAP, &uvc_cam.cap);if (ret < 0) {printf("Error opening device %s: unable to query device.\n", uvc_cam.videodevice);return -1;}else {printf("driver:\t\t%s\n", uvc_cam.cap.driver);printf("card:\t\t%s\n", uvc_cam.cap.card);printf("bus_info:\t%s\n", uvc_cam.cap.bus_info);printf("version:\t%d\n", uvc_cam.cap.version);printf("capabilities:\t%x\n", uvc_cam.cap.capabilities);if ((uvc_cam.cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE) {printf("%s: \tsupports capture.\n", uvc_cam.videodevice);}if ((uvc_cam.cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) {printf("%s: \tsupports streaming.\n", uvc_cam.videodevice);}}// 3. set format in// 3.1 enum fmtprintf("\nSupport format:\n");memset(&uvc_cam.fmtdesc, 0, sizeof(struct v4l2_fmtdesc));uvc_cam.fmtdesc.index = 0;uvc_cam.fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;while (ioctl(uvc_cam.fd, VIDIOC_ENUM_FMT, &uvc_cam.fmtdesc) != -1) {printf("\t%d.%s\n", uvc_cam.fmtdesc.index + 1, uvc_cam.fmtdesc.description);uvc_cam.fmtdesc.index++;}// 3.2 set fmtmemset(&uvc_cam.fmt, 0, sizeof(struct v4l2_format));uvc_cam.fmt.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;uvc_cam.fmt.fmt.pix.width       = uvc_cam.width;uvc_cam.fmt.fmt.pix.height      = uvc_cam.height;uvc_cam.fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;uvc_cam.fmt.fmt.pix.field       = V4L2_FIELD_ANY;ret = ioctl(uvc_cam.fd, VIDIOC_S_FMT, &uvc_cam.fmt);if (ret < 0) {printf("Unable to set format\n");return -1;}// 3.3 get fmtret = ioctl(uvc_cam.fd, VIDIOC_G_FMT, &uvc_cam.fmt);if (ret < 0) {printf("Unable to get format\n");return -1;}else {printf("\nfmt.type:\t\t%d\n", uvc_cam.fmt.type);printf("pix.pixelformat:\t%c%c%c%c\n", uvc_cam.fmt.fmt.pix.pixelformat & 0xFF, (uvc_cam.fmt.fmt.pix.pixelformat >> 8) & 0xFF, (uvc_cam.fmt.fmt.pix.pixelformat >> 16) & 0xFF, (uvc_cam.fmt.fmt.pix.pixelformat >> 24) & 0xFF);printf("pix.height:\t\t%d\n", uvc_cam.fmt.fmt.pix.height);printf("pix.width:\t\t%d\n", uvc_cam.fmt.fmt.pix.width);printf("pix.field:\t\t%d\n", uvc_cam.fmt.fmt.pix.field);}// 4. set fpsmemset(&uvc_cam.setfps, 0, sizeof(struct v4l2_streamparm));uvc_cam.setfps.type                                  = V4L2_BUF_TYPE_VIDEO_CAPTURE;uvc_cam.setfps.parm.capture.timeperframe.numerator   = 1;uvc_cam.setfps.parm.capture.timeperframe.denominator = 25;ret = ioctl(uvc_cam.fd, VIDIOC_S_PARM, &uvc_cam.setfps);if (ret < 0) {printf("Unable to set frame rate\n");return -1;}else {printf("set fps OK!\n");}ret = ioctl(uvc_cam.fd, VIDIOC_G_PARM, &uvc_cam.setfps);if (ret < 0) {printf("Unable to get frame rate\n");return -1;}else {printf("get fps OK:\n");printf("timeperframe.numerator  : %d\n", uvc_cam.setfps.parm.capture.timeperframe.numerator);printf("timeperframe.denominator: %d\n", uvc_cam.setfps.parm.capture.timeperframe.denominator);printf("set fps : %d\n", 1 * uvc_cam.setfps.parm.capture.timeperframe.denominator / uvc_cam.setfps.parm.capture.timeperframe.numerator);}// 5. enum framesizeswhile (1) {struct v4l2_fmtdesc fmtdesc;memset(&fmtdesc, 0, sizeof(struct v4l2_fmtdesc));fmtdesc.index = i++;fmtdesc.type  = V4L2_BUF_TYPE_VIDEO_CAPTURE;if (ioctl(uvc_cam.fd, VIDIOC_ENUM_FMT, &fmtdesc) < 0) {break;}printf("Supported format: %s\n", fmtdesc.description);struct v4l2_frmsizeenum fsenum;memset(&fsenum, 0, sizeof(struct v4l2_frmsizeenum));fsenum.pixel_format = uvc_cam.fmtdesc.pixelformat;int j = 0;while (1) {fsenum.index = j;j++;if (ioctl(uvc_cam.fd, VIDIOC_ENUM_FRAMESIZES, &fsenum) == 0) {if (uvc_cam.fmt.fmt.pix.pixelformat == fmtdesc.pixelformat) {printf("\tSupported size with the current format: %dx%d\n", fsenum.discrete.width, fsenum.discrete.height);}else {printf("\tSupported size: %dx%d\n", fsenum.discrete.width, fsenum.discrete.height);}}else {break;}}}// 6. request buffersmemset(&uvc_cam.rb, 0, sizeof(struct v4l2_requestbuffers));uvc_cam.rb.count  = NB_BUFFER;uvc_cam.rb.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;uvc_cam.rb.memory = V4L2_MEMORY_MMAP;ret = ioctl(uvc_cam.fd, VIDIOC_REQBUFS, &uvc_cam.rb);if (ret < 0) {printf("Unable to allocate buffers\n");return -1;}// 6.1 map the buffersfor (i = 0; i < NB_BUFFER; i++) {memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index  = i;buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;ret = ioctl(uvc_cam.fd, VIDIOC_QUERYBUF, &buf);if (ret < 0) {printf("Unable to query buffer\n");return -1;}uvc_cam.mem[i] = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, uvc_cam.fd, buf.m.offset);if (uvc_cam.mem[i] == MAP_FAILED) {printf("Unable to map buffer\n");return -1;}uvc_cam.memlength[i] = buf.length;}// 6.2 queue the buffers.for (i = 0; i < NB_BUFFER; i++) {memset(&buf, 0, sizeof(struct v4l2_buffer));buf.index  = i;buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;ret        = ioctl(uvc_cam.fd, VIDIOC_QBUF, &buf);if (ret < 0) {printf("Unable to queue buffer\n");return -1;}}// 7. malloc yuyv bufuvc_cam.framesizeIn = uvc_cam.width * uvc_cam.height << 1; // w * h * 2uvc_cam.framebuffer = (unsigned char*)calloc(1, (size_t)uvc_cam.framesizeIn);if (uvc_cam.framebuffer == NULL) {printf("err calloc memory\n");return -1;}printf("init %s \t[OK]\n", uvc_cam.videodevice);return 0;
}void v4l2_exit(void)
{free(uvc_cam.framebuffer);close(uvc_cam.fd);
}int v4l2_enable(void)
{int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;int ret;ret = ioctl(uvc_cam.fd, VIDIOC_STREAMON, &type);if (ret < 0) {printf("Unable to start capture\n");return ret;}printf("start capture\n");return 0;
}int v4l2_disable(void)
{int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;int ret;ret = ioctl(uvc_cam.fd, VIDIOC_STREAMOFF, &type);if (ret < 0) {printf("Unable to stop capture\n");return ret;}printf("stop capture\n");return 0;
}int v4l2_uvc_grap(void)
{int                ret;struct v4l2_buffer buf;memset(&buf, 0, sizeof(struct v4l2_buffer));buf.type   = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;ret = ioctl(uvc_cam.fd, VIDIOC_DQBUF, &buf);if (ret < 0) {printf("Unable to dequeue buffer\n");exit(1);}memcpy(uvc_cam.framebuffer, uvc_cam.mem[buf.index], uvc_cam.framesizeIn);ioctl(uvc_cam.fd, VIDIOC_QBUF, &buf);printf("buf index: %d\n", buf.index);return 0;
}int save_bmp(char* bmp_name)
{FILE*            fp;BITMAPFILEHEADER bf;BITMAPINFOHEADER bi;printf("save bmp  function\n");fp = fopen(bmp_name, "wb");if (fp == NULL) {printf("open errror\n");return (-1);}//Set BITMAPINFOHEADERmemset(&bi, 0, sizeof(BITMAPINFOHEADER));bi.ciSize      = 40;bi.ciWidth     = IMAGEWIDTH;bi.ciHeight    = IMAGEHEIGHT;bi.ciPlanes    = 1;bi.ciBitCount  = 24;bi.ciSizeImage = IMAGEWIDTH * IMAGEHEIGHT * 3;//Set BITMAPFILEHEADERmemset(&bf, 0, sizeof(BITMAPFILEHEADER));bf.cfType[0]  = 'B';bf.cfType[1]  = 'M';bf.cfSize     = 54 + bi.ciSizeImage;bf.cfReserved = 0;bf.cfoffBits  = 54;fwrite(&bf, 14, 1, fp);fwrite(&bi, 40, 1, fp);fwrite(rgb888_buffer, bi.ciSizeImage, 1, fp);printf("save %s done\n", bmp_name);fclose(fp);return 0;
}int main(int argc, char const* argv[])
{char vdname[20];printf("\n----- v4l2 savebmp app start ----- \n");if (argc < 2) {printf("need:/dev/videox\n");printf("like:%s /dev/video1\n", argv[0]);printf("app exit.\n\n");exit(1);}snprintf(vdname, 20, argv[1]);memset(&uvc_cam, 0, sizeof(struct vdIn));uvc_cam.videodevice = vdname;printf("using: \t\t%s\n", uvc_cam.videodevice);uvc_cam.width  = _UVC_CAM_WIDTH;uvc_cam.height = _UVC_CAM_HEIGHT;// 1. init camif (v4l2_init() < 0) {goto app_exit;}v4l2_enable();usleep(5 * 1000);// 2. grap uvcv4l2_uvc_grap();yuyv_to_rgb888();// 3. save bmpsave_bmp("./uvc_grap.bmp");app_exit:printf("app exit.\n\n");v4l2_exit();return 0;
}

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

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

相关文章

栈和队列(三)

队列的链式存储表示和实现 链队的类型定义 typedef struct qnode{char data;struct qnode *next; }qnode,*queneptr;typedef struct{queneptr front;queneptr rear; }linkqueue; typedef struct qnode{}&#xff1a; 定义了一个名为qnode的结构体。结构体成员包括&#xff1a…

vmvare启动freebsd操作系统密码忘记了怎么办?

本章教程,主要介绍,通过vmvare安装的freebsd操作系统,密码忘记了,如何重置密码。 一、重启虚拟机 在重启过程中,按键盘中是数字2,进入单用户模式。 二、进入到shell界面 在出现“Enter full pathname of shell or RETURN for /bin/sh:”直接按回车键。 三、输入命令 mou…

设计模式之结构型模式---装饰器模式

目录 1.概述2.类图3.应用场景及优缺点3.1 应用场景3.2 优缺点3.2.1 优点3.2.2 缺点 4.实现4.1 案例类图4.2 代码实现4.2.1 定义抽象构建角色4.2.2 定义具体构建角色4.2.3 定义抽象装饰器角色4.2.4 定义具体装饰角色4.2.5 装饰器模式的使用 1.概述 装饰器模式是指在不改变现有对…

SQL,力扣题目1709,访问日期之间最大的空档期

一、力扣链接 LeetCode_1709 二、题目描述 表&#xff1a; UserVisits ------------------- | Column Name | Type | ------------------- | user_id | int | | visit_date | date | ------------------- 该表没有主键&#xff0c;它可能有重复的行 该表包含用户访问…

极市平台 | NeurIPS 2024|浙大/微信/清华提出:彻底解决扩散模型反演问题

本文来源公众号“极市平台”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;NeurIPS 2024&#xff5c;浙大/微信/清华提出&#xff1a;彻底解决扩散模型反演问题 极市导读 本文介绍了浙江大学、微信和清华大学联合提出的BELM算法…

心觉:人每日60000念头,如何让你的时间精力只专注于核心目标?

Hi&#xff0c;我是心觉&#xff0c;带你用潜意识化解各种焦虑、内耗&#xff0c;建立无敌自信&#xff1b;教你财富精准显化的实操方法&#xff1b;关注我,伴你一路成长&#xff01; 每日一省写作220/1000天 据说一个人每天会产生60000个念头 有些我们的意识能察觉到&#x…

89.冒泡算法(代码编写)

目录 一.代码编写 二.视频教程 一.代码编写 #include <stdio.h>void main(void) {int data[10];int j,i;int temp;printf("Please input data:\n");for(i0;i<10;i){scanf("%d",&data[i]);}for(i0;i<10;i){for(j0;j<9-i;j){if(data[j…

SQL CASE表达式与窗口函数

CASE 表达式是一种通用的条件表达式&#xff0c;类似于其他编程语言中的if/else语句。 窗口函数类似于group by&#xff0c;但是不会改变记录行数&#xff0c;能扫描所有行&#xff0c;能对每一行执行聚合计算或其他复杂计算&#xff0c;并把结果填到每一行中。 1 CASE 表达式…

ubuntu22-安装vscode-配置shell命令环境-mac安装

文章目录 1.安装vscode2.修改语言为中文3.配置bash调试环境3.1.安装插件3.2.添加配置文件 4.调试bash4.1.新建tmp.sh文件4.2.运行启动 5.mac安装6.mac卸载 1.安装vscode 从官网下载安装包Code_1.93.1-1726079302_amd64.deb。 在ubuntu系统中&#xff0c;安装包所在目录打开命令…

Chromium127编译指南 Linux篇 - 同步第三方库以及Hooks(六)

引言 在成功克隆 Chromium 源代码仓库并建立新分支之后&#xff0c;配置开发环境成为至关重要的下一步。这一过程涉及获取必要的第三方依赖库以及设置钩子&#xff08;hooks&#xff09;&#xff0c;这些步骤对于确保后续的编译和开发工作能够顺利进行起着决定性作用。本指南旨…

【NOIP提高组】虫食算

【NOIP提高组】虫食算 C语言C &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 所谓虫食算&#xff0c;就是原先的算式中有一部分被虫子啃掉了&#xff0c;需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子&#xff1a; 43#98…

练习LabVIEW第三十题

学习目标&#xff1a; 刚学了LabVIEW&#xff0c;在网上找了些题&#xff0c;练习一下LabVIEW&#xff0c;有不对不好不足的地方欢迎指正&#xff01; 第三十题&#xff1a; 用labview写一个获取当前系统时间的程序 开始编写&#xff1a; 前面板添加一个字符串显示控件&am…

书生大模型实战营 L0 入门岛

书生大模型训练营入门岛任务——训练营链接 1. Linux前置知识 任务&#xff1a;端口转发 当使用vscode远程连接服务器时&#xff0c;在服务器运行的任务&#xff0c;vscode会自动帮忙进行端口映射&#xff0c;方便本地进行访问。 2. Python前置知识 任务1&#xff1a;Leec…

【本科毕业设计】基于单片机的智能家居防火防盗报警系统

基于单片机的智能家居防火防盗报警系统 源码下载摘要Abstract第1章 绪论1.1课题的背景1.2 研究的目的和意义 第2章 系统总体方案设计2.1 设计要求2.2 方案选择和论证2.2.1 单片机的选择2.2.2 显示方案的选择 第3章 系统硬件设计3.1 整体方案设计3.1.1 系统概述3.1.2 系统框图 3…

<项目代码>YOLOv8 猫狗识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

前端项目使用高德地图插件

高德开放平台 | 高德地图API 1、注册成为开发者 登录 高德开放平台控制台&#xff0c;如果没有开发者账号&#xff0c;请 注册开发者。 2. 创建key&#xff0c;项目里面要用 进入应用管理&#xff0c;创建新应用&#xff0c;新应用中添加 key&#xff0c;服务平台选择 Web端…

统信UOS开发环境支持php

UOS对PHP开发环境提供了灵活的选择,在这里开发者可以轻松搭建开发环境,是开发者最理想的选择。 文章目录 一、环境部署php开发环境安装二、代码示例PHP开发案例三、常见问题1. 权限问题2. PHP-FPM服务未正确启动或配置错误一、环境部署 php开发环境安装 php为服务器开发语言…

word转ppt软件哪个好?这些工具你值得拥有

在日常工作和学习中&#xff0c;我们经常需要将word文档转换为ppt幻灯片&#xff0c;以便于展示和汇报。 为了提高效率&#xff0c;市场上涌现了许多word转ppt工具&#xff0c;它们能够自动排版&#xff0c;帮助我们快速完成转换工作。 一、迅捷PPT &#x1f525;优势——多样…

idm扩展自动更新,导致不能正常使用处理方法

idm扩展自动更新&#xff0c;导致不能正常使用处理方法 针对于edge和chrome浏览器的设置 处理思路&#xff0c;设置权限&#xff0c;让浏览器的没有权限修改扩展的文件&#xff0c;从而关闭自动更新 具体做法 1找到idm安装路径&#xff0c;里面有IDMGCE.crx的文件就是扩展文…

Spring框架的声明式事务

目录 一.配置文件的方式 1.配置文件 2.业务层 3.持久层 4.测试类 5.运行 6.查看数据库 7.出现异常运行 二.半注解的方式 1.配置文件 2.db.properties 3.持久层 4.业务层 5.测试类 6.运行 7.查看数据库 8.加上异常 三.纯注解的方式 1.持久层 2.业务层 3.配置…