支持YUV和RGB格式两路视频同时播放

1.头文件:

sdlqtrgb.h


#pragma once
#include <QtWidgets/QWidget>
#include "ui_sdlqtrgb.h"
#include <thread>
class SdlQtRGB : public QWidget
{Q_OBJECTpublic:SdlQtRGB(QWidget* parent = Q_NULLPTR);~SdlQtRGB(){is_exit_ = true;//等待渲染线程退出th_.join();}void timerEvent(QTimerEvent* ev) override;void resizeEvent(QResizeEvent* ev) override;//线程函数,用于刷新视频void Main();signals:void ViewS();   //信号函数,将任务放入列表
public slots:void View();    //显示的槽函数void Open1();void Open2();void Open(int i);
private:std::thread th_;bool is_exit_ = false;//处理线程退出Ui::SdlQtRGBClass ui;
};

xvideoview.h

#ifndef XVIDEO_VIEW_H
#define XVIDEO_VIEW_H
#include <mutex>
#include <fstream>
struct AVFrame;void MSleep(unsigned int ms);//获取当前时间戳 毫秒
long long NowMs();/// 视频渲染接口类
/// 隐藏SDL实现
/// 渲染方案可替代
// 线程安全
class XVideoView
{
public:enum Format  //枚举的值和ffmpeg中一致{YUV420P = 0,ARGB = 25,RGBA = 26,BGRA = 28};enum RenderType{SDL = 0};static XVideoView* Create(RenderType type = SDL);/// 初始化渲染窗口 线程安全 可多次调用/// @para w 窗口宽度/// @para h 窗口高度/// @para fmt 绘制的像素格式/// @para win_id 窗口句柄,如果为空,创建新窗口/// @return 是否创建成功virtual bool Init(int w, int h,Format fmt = RGBA) = 0;//清理所有申请的资源,包括关闭窗口virtual void Close() = 0;//处理窗口退出事件virtual bool IsExit() = 0;///// 渲染图像 线程安全///@para data 渲染的二进制数据///@para linesize 一行数据的字节数,对于YUV420P就是Y一行字节数/// linesize<=0 就根据宽度和像素格式自动算出大小/// @return 渲染是否成功virtual bool Draw(const unsigned  char* data, int linesize = 0) = 0;virtual bool Draw(const unsigned  char* y, int y_pitch,const unsigned  char* u, int u_pitch,const unsigned  char* v, int v_pitch) = 0;//显示缩放void Scale(int w, int h){scale_w_ = w;scale_h_ = h;}bool DrawFrame(AVFrame* frame);int render_fps() { return render_fps_; }//打开文件bool Open(std::string filepath);///// 读取一帧数据,并维护AVFrame空间/// 每次调用会覆盖上一次数据AVFrame* Read();void set_win_id(void* win) { win_id_ = win; }
protected:void* win_id_ = nullptr; //窗口句柄int render_fps_ = 0;       //显示帧率int width_ = 0;             //材质宽高int height_ = 0;Format fmt_ = RGBA;         //像素格式std::mutex mtx_;            //确保线程安全int scale_w_ = 0;           //显示大小int scale_h_ = 0;long long beg_ms_ = 0;       //计时开始时间int count_ = 0;              //统计显示次数
private:std::ifstream ifs_;AVFrame* frame_ = nullptr;
};#endif

xsdl.h

#pragma once#include "xvideo_view.h"
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Texture;
class XSDL :public XVideoView
{
public:void Close() override;/// 初始化渲染窗口 线程安全/// @para w 窗口宽度/// @para h 窗口高度/// @para fmt 绘制的像素格式/// @para win_id 窗口句柄,如果为空,创建新窗口/// @return 是否创建成功bool Init(int w, int h,Format fmt = RGBA) override;///// 渲染图像 线程安全///@para data 渲染的二进制数据///@para linesize 一行数据的字节数,对于YUV420P就是Y一行字节数/// linesize<=0 就根据宽度和像素格式自动算出大小/// @return 渲染是否成功bool Draw(const unsigned  char* data,int linesize = 0) override;bool Draw(const unsigned  char* y, int y_pitch,const unsigned  char* u, int u_pitch,const unsigned  char* v, int v_pitch) override;bool IsExit() override;
private:SDL_Window* win_ = nullptr;SDL_Renderer* render_ = nullptr;SDL_Texture* texture_ = nullptr;
};

2.源文件:

sdlqtrgb.cpp


#include "sdlqtrgb.h"
#include <fstream>
#include <iostream>
#include <QMessageBox>
#include <thread>
#include <sstream>
#include <iostream>
#include <QSpinBox>
#include <QFileDialog>
#include "xvideo_view.h"
#include <vector>
#include <sstream>
using namespace std;static std::vector<XVideoView*> views;
SdlQtRGB::SdlQtRGB(QWidget* parent): QWidget(parent)
{ui.setupUi(this);//绑定渲染信号槽connect(this, SIGNAL(ViewS()), this, SLOT(View()));views.push_back(XVideoView::Create());// 创建一个对象并用vector 存储views.push_back(XVideoView::Create());views[0]->set_win_id((void*)ui.video1->winId());//将video1的窗口句柄赋值给xvideoview对象的窗口句柄views[1]->set_win_id((void*)ui.video2->winId());th_ = std::thread(&SdlQtRGB::Main, this);
}void SdlQtRGB::timerEvent(QTimerEvent* ev)
{
}void SdlQtRGB::View()
{static int last_pts[32] = { 0 };//last_pts 用于存放每个视图上次渲染的时间戳,lase_pts[0]表示第一个播放窗口static int fps_arr[32] = { 0 };//用于存放每个视图的帧率值。fps_arr[0] = ui.set_fps1->value();//获取第一个播放窗口的fps,用fps_arr[0]存储fps_arr[1] = ui.set_fps2->value();for (int i = 0; i < views.size(); i++)//依次渲染容器中的xvideoview对象{if (fps_arr[i] <= 0) continue;//invaluable data then pass//需要间隔时间int ms = 1000 / fps_arr[i];// video1's fps = 100,then ms = 10,意思是100fps,一秒渲染100次,每10ms渲染一次//判断是否到了可渲染时间if (NowMs() - last_pts[i] < ms)//一秒渲染100次,每10ms渲染一次,若时间没达到10ms那么不继续进行渲染continue;last_pts[i] = NowMs();//记录下此次的渲染时间auto frame = views[i]->Read();//read()执行完后 返回读取了yuv数据的avframe对象给frame,此时frame中已经进行了文件读取有了yuv数据,参考read函数即可if (!frame)continue;views[i]->DrawFrame(frame);//计算了当前帧率并且进行了渲染//显示fpsstringstream ss;ss << "fps:" << views[i]->render_fps();if (i == 0)ui.fps1->setText(ss.str().c_str());elseui.fps2->setText(ss.str().c_str());}
}void SdlQtRGB::Main()
{while (!is_exit_){ViewS();//调用一次view槽函数,再没进行Open函数之前是不能把view槽函数执行完的,因为进行draw之前要进行材质的初始化 Init函数,在Open函数之中//一旦执行了Open函数,Init即进行完毕,则一直在执行的线程函数Main则会调用view函数,此时view函数中的draw函数可以进行渲染了MSleep(10);//每调用一次槽函数就休眠10ms,用于控制帧率}
}void SdlQtRGB::Open1()
{Open(0);
}
void SdlQtRGB::Open2()
{Open(1);
}
void SdlQtRGB::Open(int i)
{QFileDialog fd;auto filename = fd.getOpenFileName();if (filename.isEmpty())return;cout << filename.toLocal8Bit().data() << endl;//打开文件if (!views[i]->Open(filename.toLocal8Bit().toStdString())){return;}int w = 0;int h = 0;QString pix = 0;  //YUV420P RGBAif (i == 0)//下拉框的下标0,表示YUV420P,下标1表示RGBA以此类推{w = ui.width1->value();h = ui.height1->value();pix = ui.pix1->currentText(); //像素格式}else{w = ui.width2->value();h = ui.height2->value();pix = ui.pix2->currentText();}XVideoView::Format fmt = XVideoView::YUV420P;if (pix == "YUV420P"){}else if (pix == "RGBA"){fmt = XVideoView::RGBA;}else if (pix == "ARGB"){fmt = XVideoView::ARGB;}else if (pix == "BGRA"){fmt = XVideoView::BGRA;}//初始化窗口和材质views[i]->Init(w, h, fmt);
}void SdlQtRGB::resizeEvent(QResizeEvent* ev)
{//ui.label->resize(size());//ui.label->move(0, 0);//view->Scale(width(), height());
}

xvideoview.cpp

#include "xsdl.h"
#include <thread>
#include <iostream>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
}
#pragma comment(lib,"avutil.lib")void MSleep(unsigned int ms)
{auto beg = clock();for (int i = 0; i < ms; i++){this_thread::sleep_for(1ms);if ((clock() - beg) / (CLOCKS_PER_SEC / 1000) >= ms)break;}
}
long long NowMs()
{return clock() / (CLOCKS_PER_SEC / 1000);//获取当前时间戳 毫秒
}
AVFrame* XVideoView::Read()
{if (width_ <= 0 || height_ <= 0 || !ifs_)return NULL;//AVFrame空间已经申请,如果参数发生变化,需要释放空间if (frame_)//若分配了{if (frame_->width != width_|| frame_->height != height_|| frame_->format != fmt_){//释放AVFrame对象空间,和buf引用计数减一av_frame_free(&frame_);}}if (!frame_)//若没分配则分配{//分配对象空间和像素空间frame_ = av_frame_alloc();frame_->width = width_;frame_->height = height_;frame_->format = fmt_;frame_->linesize[0] = width_ * 4;if (frame_->format == AV_PIX_FMT_YUV420P)//YUV420P单独处理,因为他不是平面存储是多层存储需要三个数组存储三个平面{frame_->linesize[0] = width_; // Yframe_->linesize[1] = width_ / 2;//Uframe_->linesize[2] = width_ / 2;//V}//生成AVFrame空间,使用默认对齐auto re = av_frame_get_buffer(frame_, 0);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf) - 1);cout << buf << endl;av_frame_free(&frame_);return NULL;}}if (!frame_)return NULL;//读取一帧数据if (frame_->format == AV_PIX_FMT_YUV420P){ifs_.read((char*)frame_->data[0],frame_->linesize[0] * height_);	//Yifs_.read((char*)frame_->data[1],frame_->linesize[1] * height_ / 2);	//Uifs_.read((char*)frame_->data[2],frame_->linesize[2] * height_ / 2);	//V}//执行完后 avframe中就保存了从2.yuv中读取的yuv数据了else	//RGBA ARGB BGRA 32{ifs_.read((char*)frame_->data[0], frame_->linesize[0] * height_);}if (ifs_.gcount() == 0)return NULL;return frame_;}//打开文件
bool XVideoView::Open(std::string filepath)
{if (ifs_.is_open()){ifs_.close();}ifs_.open(filepath, ios::binary);return ifs_.is_open();//ifs_绑定了所选的文件 如 2.yuv
}
XVideoView* XVideoView::Create(RenderType type)
{switch (type){case XVideoView::SDL:return new XSDL();break;default:break;}return nullptr;
}bool XVideoView::DrawFrame(AVFrame* frame)
{if (!frame || !frame->data[0])return false;count_++;if (beg_ms_ <= 0){beg_ms_ = clock();}//计算显示帧率else if ((clock() - beg_ms_) / (CLOCKS_PER_SEC / 1000) >= 1000) //一秒计算一次fps{render_fps_ = count_;count_ = 0;beg_ms_ = clock();}switch (frame->format){case AV_PIX_FMT_YUV420P:return Draw(frame->data[0], frame->linesize[0],//Yframe->data[1], frame->linesize[1],	//Uframe->data[2], frame->linesize[2]	//V);case AV_PIX_FMT_BGRA:case AV_PIX_FMT_ARGB:case AV_PIX_FMT_RGBA:return Draw(frame->data[0], frame->linesize[0]);default:break;}return false;
}

xsdl.cpp

#include "xsdl.h"
#include "sdl/SDL.h"
#include <iostream>
using namespace std;
#pragma comment(lib,"SDL2.lib")
static bool InitVideo()
{static bool is_first = true;static mutex mux;unique_lock<mutex> sdl_lock(mux);if (!is_first)return true;is_first = false;if (SDL_Init(SDL_INIT_VIDEO)){cout << SDL_GetError() << endl;return false;}//设定缩放算法,解决锯齿问题,线性插值算法SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");return true;
}
bool XSDL::IsExit()
{SDL_Event ev;SDL_WaitEventTimeout(&ev, 1);if (ev.type == SDL_QUIT)return true;return false;
}
void XSDL::Close()
{//确保线程安全unique_lock<mutex> sdl_lock(mtx_);if (texture_){SDL_DestroyTexture(texture_);texture_ = nullptr;}if (render_){SDL_DestroyRenderer(render_);render_ = nullptr;}if (win_){SDL_DestroyWindow(win_);win_ = nullptr;}
}bool XSDL::Init(int w, int h, Format fmt)
{if (w <= 0 || h <= 0)return false;//初始化SDL 视频库InitVideo();//确保线程安全unique_lock<mutex> sdl_lock(mtx_);width_ = w;height_ = h;fmt_ = fmt;if (texture_)SDL_DestroyTexture(texture_);if (render_)SDL_DestroyRenderer(render_);///1 创建窗口if (!win_){if (!win_id_){//新建窗口win_ = SDL_CreateWindow("",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);}else{//渲染到控件窗口win_ = SDL_CreateWindowFrom(win_id_);}}if (!win_){cerr << SDL_GetError() << endl;return false;}/// 2 创建渲染器render_ = SDL_CreateRenderer(win_, -1, SDL_RENDERER_ACCELERATED);if (!render_){cerr << SDL_GetError() << endl;return false;}//创建材质 (显存)unsigned int sdl_fmt = SDL_PIXELFORMAT_RGBA8888;switch (fmt){case XVideoView::RGBA:sdl_fmt = SDL_PIXELFORMAT_RGBA32;break;case XVideoView::BGRA:sdl_fmt = SDL_PIXELFORMAT_BGRA32;break;case XVideoView::ARGB:sdl_fmt = SDL_PIXELFORMAT_ARGB32;break;case XVideoView::YUV420P:sdl_fmt = SDL_PIXELFORMAT_IYUV;break;default:break;}texture_ = SDL_CreateTexture(render_,sdl_fmt,                        //像素格式SDL_TEXTUREACCESS_STREAMING,    //频繁修改的渲染(带锁)w, h                            //材质大小);if (!texture_){cerr << SDL_GetError() << endl;return false;}return true;
}
bool XSDL::Draw(const unsigned  char* y, int y_pitch,const unsigned  char* u, int u_pitch,const unsigned  char* v, int v_pitch
)
{//参数检查if (!y || !u || !v)return false;unique_lock<mutex> sdl_lock(mtx_);if (!texture_ || !render_ || !win_ || width_ <= 0 || height_ <= 0)return false;//复制内存到显显存auto re = SDL_UpdateYUVTexture(texture_,NULL,y, y_pitch,u, u_pitch,v, v_pitch);if (re != 0){cout << SDL_GetError() << endl;return false;}//清空屏幕SDL_RenderClear(render_);//材质复制到渲染器SDL_Rect rect;SDL_Rect* prect = nullptr;if (scale_w_ > 0)  //用户手动设置缩放{rect.x = 0; rect.y = 0;rect.w = scale_w_;//渲染的宽高,可缩放rect.h = scale_w_;prect = &rect;}re = SDL_RenderCopy(render_, texture_, NULL, prect);if (re != 0){cout << SDL_GetError() << endl;return false;}SDL_RenderPresent(render_);return true;
}
bool XSDL::Draw(const unsigned char* data, int linesize)
{if (!data)return false;unique_lock<mutex> sdl_lock(mtx_);if (!texture_ || !render_ || !win_ || width_ <= 0 || height_ <= 0)return false;if (linesize <= 0){switch (fmt_){case XVideoView::RGBA:case XVideoView::ARGB:linesize = width_ * 4;break;case XVideoView::YUV420P:linesize = width_;break;default:break;}}if (linesize <= 0)return false;//复制内存到显显存auto re = SDL_UpdateTexture(texture_, NULL, data, linesize);if (re != 0){cout << SDL_GetError() << endl;return false;}//清空屏幕SDL_RenderClear(render_);//材质复制到渲染器SDL_Rect rect;SDL_Rect* prect = nullptr;if (scale_w_ > 0)  //用户手动设置缩放{rect.x = 0; rect.y = 0;rect.w = scale_w_;//渲染的宽高,可缩放rect.h = scale_w_;prect = &rect;}re = SDL_RenderCopy(render_, texture_, NULL, prect);if (re != 0){cout << SDL_GetError() << endl;return false;}SDL_RenderPresent(render_);return true;
}

main.cpp

#include "sdlqtrgb.h"
#include <QtWidgets/QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);SdlQtRGB w;w.show();return a.exec();
}

3.运行结果:

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

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

相关文章

十大排序

本文将以「 通俗易懂」的方式来描述排序的基本实现。 &#x1f9d1;‍&#x1f4bb;阅读本文前&#xff0c;需要一点点编程基础和一点点数据结构知识 本文的所有代码以cpp实现 文章目录 排序的定义 插入排序 ⭐ &#x1f9d0;算法描述 &#x1f496;具体实现 &#x1f…

LabVIEW硬件与仿真双模式设计液压系统

在实际项目中&#xff0c;结合LabVIEW与液压运动控制器&#xff0c;通过设计两种运行方式&#xff1a;硬件运行和仿真运行&#xff0c;使得系统既能进行实际操作又能进行仿真测试。此设计不仅方便了开发调试&#xff0c;也为教学培训和展示提供了极大的便利。本文将从项目背景、…

【机器学习】基于卷积LSTM的视频预测

1. 引言 1.1 LSTM是什么 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种特殊的循环神经网络&#xff08;RNN&#xff09;变体&#xff0c;旨在解决传统RNN在处理长序列数据时遇到的梯度消失和梯度爆炸问题。LSTM通过引入门控机制和细胞状态的概念&#xff0c;使得…

QPS,平均时延和并发数

我们当前有两个服务A和B&#xff0c;想要知道哪个服务的性能更好&#xff0c;该用什么指标来衡量呢&#xff1f; 1. 单次请求时延 一种最简单的方法就是使用同一请求体同时请求两个服务&#xff0c;性能越好的服务时延越短&#xff0c;即 R T 返回结果的时刻 − 发送请求的…

【Python教程】4-字符串、列表、字典、元组与集合操作

在整理自己的笔记的时候发现了当年学习python时候整理的笔记&#xff0c;稍微整理一下&#xff0c;分享出来&#xff0c;方便记录和查看吧。个人觉得如果想简单了解一名语言或者技术&#xff0c;最简单的方式就是通过菜鸟教程去学习一下。今后会从python开始重新更新&#xff0…

AI大模型探索之路-实战篇16:优化决策流程:Agent智能数据分析平台中Planning功能实践

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;深入DB-GPT数据应用开发框架调研 AI大模型探索之路-实战篇5&#xff1a;探索Open Interpreter开放代码解释器调研 AI大模型探索之路-实战篇6&#xff1a;掌握Function Calling的详细流程 AI大模型探索之路-实战篇7…

现代x86汇编-环境安装

今天端午节&#xff0c;独自在家&#xff0c;翻阅了张银奎老师编写的《现代x86汇编语言程序设计》一书&#xff0c;前言部分说明书中示例代码都是用微软visual C工具编写并使用微软宏汇编&#xff08;著名的MASM&#xff09;编译的&#xff0c;好久没有用微软vc了&#xff0c;假…

详解FedProx:FedAvg的改进版 Federated optimization in heterogeneous networks

FedProx&#xff1a;2020 FedAvg的改进 论文&#xff1a;《Federated Optimization in Heterogeneous Networks》 引用量&#xff1a;4445 源码地址&#xff1a; 官方实现&#xff08;tensorflow&#xff09;https://github.com/litian96/FedProx 几个pytorch实现&#xff1a;…

基于STM32的595级联的Proteus仿真

文章目录 一、595级联1.题目要求2.思路3.仿真图3.1 未仿真时3.2 模式A3.2 模式B3.3 故障模式 二、总结 一、595级联 1.题目要求 STM32单片机&#xff0c;以及三个LED灯对应红黄绿灯&#xff0c;IIC的OLED显示屏&#xff0c;温湿度传感器DHT11&#xff0c;两个独立按键和两个5…

MySQL时间和日期类型详解(零基础入门篇)

目录 1. DATE 2. DATETIME 3. TIMESTAMP 4. TIME 5. YEAR 6. 日期和时间的使用示例 以下SQL语句的测试可以使用命令行&#xff0c;或是使用SQL工具比如MySQL Workbench或SQLynx等。 在 MySQL 中&#xff0c;时间和日期数据类型用于存储与时间相关的数据&#xff0c;如何…

重温共射放大电路

1、放大概念 小功率信号变成一个大功率信号&#xff0c;需要一个核心器件做这件事&#xff0c;核心器件的能量由电源提供&#xff0c;通过核心器件用小功率的信号去控制大电源&#xff0c;来实现能量的转换和控制&#xff0c;前提是不能失真&#xff0c;可以用一系列正弦波进行…

Running Gradle task ‘assembleDebug‘ Flutter项目

基于Android方面运行Flutter项目一直卡在 Launching lib\main.dart on Android SDK built for x86 in debug mode… Running Gradle task ‘assembleDebug’… 基础原因&#xff1a; 默认存放Gradle插件包的Maven仓库是国外(需VPN) 我的原因&#xff1a; 缺少JDK和缺少Androi…

【Oracle】Oracle导入导出dmp文件

文章目录 前言一、什么是dmp&#xff1f;二、imp/impdp、exp/expdp对比及示例1.区别2.imp/impdp对比及示例a. impb. impbp 3.exp/expdp对比及示例a. expb.expdp 3.其他事项 三、执行导入导出前置条件1.创建角色并授权2.创建目录映射 前言 在工作中&#xff0c;经常会遇到需要备…

React中的 Scheduler

为什么需要调度 在 React 中&#xff0c;组件最终体现为 Fiber&#xff0c;并形成 FiberTree&#xff0c;Fiber 的目的是提高渲染性能&#xff0c;将原先的 React 渲染任务拆分为多个小的微任务&#xff0c;这样做的目的是可以灵活的让出主线程&#xff0c;可以随时打断渲染&a…

定个小目标之刷LeetCode热题(10)

这道题属于一道中等题&#xff0c;看来又得背题了&#xff0c;直接看题解吧&#xff0c;有两种解法 第一种动态规划法 状态&#xff1a;dp[i][j] 表示字符串s在[i,j]区间的子串是否是一个回文串 状态转移方程&#xff1a;当s[i] s[j] && (j - i < 2 || dp[i 1]…

讨论C++类与对象

讨论C类与对象 C语言结构体和C类的对比类的实例化类对象的大小猜想一猜想二针对上述猜想的实践 this指针不同对象调用成员函数 类的6个默认成员函数构造函数析构函数拷贝构造函数浅拷贝和深拷贝 赋值运算符重载 初始化列表初始化顺序 C语言结构体和C类的对比 在C语言中&#x…

对猫毛过敏?怎么有效的缓解过敏症状,宠物空气净化器有用吗?

猫过敏是一种常见的过敏反应&#xff0c;由猫的皮屑、唾液或尿液中的蛋白质引起。这些蛋白质被称为过敏原&#xff0c;它们可以通过空气传播&#xff0c;被人体吸入后&#xff0c;会触发免疫系统的过度反应。猫过敏是宠物过敏中最常见的类型之一&#xff0c;对许多人来说&#…

xilinx的Aurora8B10B的IP仿真及上板测试(高速收发器十七)

前文讲解了Aurora8B10B协议原理及xilinx相关IP&#xff0c;本文讲解如何设置该IP&#xff0c;并且通过示例工程完成该IP的仿真和上板。 1、生成Aurora8B10B IP 如下图所示&#xff0c;首先在vivado的IP catalog中输入Aurora 8B10B&#xff0c;双击该IP。 图1 查找Aurora 8B10…

基于STM32开发的智能农业监控系统

目录 引言环境准备智能农业监控系统基础代码实现&#xff1a;实现智能农业监控系统 4.1 土壤湿度传感器数据读取4.2 温湿度传感器数据读取4.3 水泵与风扇控制4.4 用户界面与数据可视化应用场景&#xff1a;农业环境监测与管理问题解决方案与优化收尾与总结 1. 引言 随着智能…

Map深度学习

Map Map是一个键值对的集合&#xff0c;和object类似&#xff0c;Map作为构造函数&#xff0c;可以通过全局对象获取到。需要通过new操作创建实例对象&#xff0c;直接调用会报错。Map构造函数接受一个iterable类型的函数&#xff0c;用来初始化Map。 var m new Map([[1, &qu…