读取YUV数据到AVFrame并用多线程控制帧率

 文件树:

 1.xvideo_view.h

class XVideoView
{
public:// 像素格式枚举enum Format { RGBA = 0, ARGB, YUV420P };// 渲染类型枚举enum RenderType { SDL = 0 };// 创建渲染对象的静态方法static XVideoView* Create(RenderType type = SDL);// 绘制帧的方法bool DrawFrame(AVFrame* frame);// 纯虚函数,需在派生类中实现virtual bool Init(int w, int h, Format fmt = RGBA, void* win_id = nullptr) = 0;virtual void Close() = 0;virtual bool IsExit() = 0;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);// 获取显示帧率的方法int render_fps();protected:// 成员变量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;            // 统计显示次数
};

2. xsdl.h

#pragma once#include "xvideo_view.h"// 前向声明 SDL 结构体
struct SDL_Window;
struct SDL_Renderer;
struct SDL_Texture;// 定义继承自 XVideoView 的 XSDL 类
class XSDL : public XVideoView
{
public:// 关闭渲染窗口,覆盖基类的纯虚函数void Close() override;/// 初始化渲染窗口,线程安全/// @param w 窗口宽度/// @param h 窗口高度/// @param fmt 绘制的像素格式/// @param win_id 窗口句柄,如果为空,创建新窗口/// @return 是否创建成功bool Init(int w, int h,Format fmt = RGBA,void* win_id = nullptr) override;///// 渲染图像,线程安全/// @param data 渲染的二进制数据/// @param linesize 一行数据的字节数,对于 YUV420P 就是 Y 一行字节数/// @param linesize <= 0 就根据宽度和像素格式自动算出大小/// @return 渲染是否成功bool Draw(const unsigned char* data,int linesize = 0) override;// 渲染 YUV420P 图像,线程安全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 相关成员变量,用于管理窗口、渲染器和纹理SDL_Window* win_ = nullptr;SDL_Renderer* render_ = nullptr;SDL_Texture* texture_ = nullptr;
};

3.sdlqtrgb.h

#pragma once#include <QtWidgets/QWidget>
#include "ui_sdlqtrgb.h"
#include <thread>// 定义继承自 QWidget 的 SdlQtRGB 类
class SdlQtRGB : public QWidget
{Q_OBJECTpublic:// 构造函数SdlQtRGB(QWidget* parent = Q_NULLPTR);// 析构函数~SdlQtRGB(){is_exit_ = true;// 等待渲染线程退出th_.join();// 当前线程(主线程)将等待,直到 th 线程完成}// 定时器事件处理void timerEvent(QTimerEvent* ev) override;// 窗口大小调整事件处理void resizeEvent(QResizeEvent* ev) override;// 线程函数,用于刷新视频void Main();signals:// 信号函数,将任务放入列表void ViewS();public slots:// 显示的槽函数void View();private:std::thread th_;           // 渲染线程bool is_exit_ = false;     // 处理线程退出Ui::SdlQtRGBClass ui;      // UI 组件
};

4.xvideo_view.cpp


#include "xsdl.h"
#include <thread>
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;}
}
//MSleep 函数实现了一个基于忙等待和定时器的睡眠功能。它将当前线程暂停执行一段时间(以毫秒为单位)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();}//假如一秒钟调用了20次DrawFrame,count=20,表示一秒钟渲染了20次图像,即FPS=20,count置于零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:return Draw(frame->data[0], frame->linesize[0]);default:break;}return false;
}
  • else if ((clock() - beg_ms_) / (CLOCKS_PER_SEC / 1000) >= 1000)
    • clock() - beg_ms_:计算从 beg_ms_ 到当前时间经过的时钟周期数。
    • CLOCKS_PER_SEC:宏定义,表示每秒的时钟周期数。通常值是 1000000 或 1000,取决于系统。
    • (clock() - beg_ms_) / (CLOCKS_PER_SEC / 1000):将经过的时钟周期数转换为毫秒,再检查是否已经过了 1000 毫秒(即 1 秒)。
  • render_fps_ = count_;:将当前帧计数 count_ 赋值给 render_fps_,表示过去一秒内显示的帧数,即 FPS。
  • count_ = 0;:重置帧计数器,为下一秒重新计数。
  • beg_ms_ = clock();:重置开始时间,记录当前时间,开始新的计时周期。

 5.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;
}
该函数通过调用 SDL 库提供的函数等待事件,如果接收到退出事件,则返回 true,否则返回 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;}
}
该 Close 函数用于关闭 SDL 窗口和相关资源。在关闭窗口之前,它使用互斥量确保线程安全性。然后,依次销毁 SDL 窗口、渲染器和纹理对象,并将相应的指针置为空,以防止内存泄漏和悬空指针。通过这样的实现,可以安全地关闭 SDL 窗口和释放相关资源,确保程序运行的稳定性和正确性。bool XSDL::Init(int w, int h, Format fmt, void* win_id)
{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: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_);
}
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;
}

draw函数解析:

  • 首先进行了参数检查。检查输入的 YUV 数据指针是否为非空,如果有任何一个为空,则返回 false
  • 接着使用独占锁 sdl_lock 对 SDL 窗口相关资源进行保护,确保在绘制过程中不会被其他线程干扰。
  • 进一步检查 SDL 相关资源是否已经初始化,并且窗口的宽度和高度是否大于零,如果存在任何不满足条件的情况,则返回 false
  • 使用 SDL_UpdateYUVTexture 函数将 YUV 数据复制到显存中的纹理对象中。这个函数会更新已经存在的 YUV 纹理,以便后续渲染到屏幕上。
  • 使用 SDL_RenderClear 函数清空渲染器的渲染目标,即清空屏幕。
  • 根据用户是否手动设置缩放参数,设置渲染区域的大小。
  • 使用 SDL_RenderCopy 函数将纹理对象复制到渲染器中,并在屏幕上渲染出来。
  • 最后,使用 SDL_RenderPresent 函数将渲染器中的内容呈现到屏幕上,完成一帧的绘制。
  • Draw 函数用于在 SDL 窗口中绘制 YUV 格式的图像。它首先将 YUV 数据复制到纹理对象中,然后清空屏幕并将纹理对象渲染到屏幕上。通过这种方式,可以实现基于 SDL 的视频播放功能。

对比两个draw函数

第二个draw函数处理 YUV 数据的方式相对来说更简单,因为它只需要处理单个分量的数据(一个数组),而不需要分别处理 Y、U、V 三个分量(三个数组)。这种处理方式可能在一些情况下效率更高,特别是当只需要显示图像的亮度信息时,而对色度信息的准确性要求不是很高时,使用单个分量的方法会更加简洁和高效。

第一个draw函数处理 YUV 数据的优点主要体现在以下几个方面:

  1. 精确控制每个分量:第一个函数能够分别处理 Y、U、V 三个分量的数据,可以对每个分量进行精确的控制和处理,适用于需要对图像的亮度和色度信息进行精细调节的场景。

  2. 灵活性:通过分别处理每个分量,可以实现更多样化的图像处理操作,如亮度调整、对比度调整、色调转换等。这种灵活性使得第一个函数在一些特定的应用场景中更加适用。

  3. 兼容性:在某些情况下,需要对 YUV 数据进行特定格式的处理,比如将 YUV 数据转换为其他格式或者进行编解码操作。通过分别处理 Y、U、V 三个分量,可以更容易地满足这些需求,提高代码的兼容性和通用性。

总的来说,第一个函数适用于对图像进行复杂处理和转换的场景,能够提供更多的灵活性和控制能力。而第二个函数则更适用于简单的图像显示场景,能够提供更高的处理效率和性能。选择哪个函数取决于具体的需求和应用场景。

6.sdlqtrgb.cpp


#include "sdlqtrgb.h"
#include <fstream>
#include <iostream>
#include <QMessageBox>
#include <thread>
#include <sstream>
#include <QSpinBox>
#include "xvideo_view.h"
extern "C"
{
#include <libavcodec/avcodec.h>
}using namespace std;static int sdl_width = 0;
static int sdl_height = 0;
static int pix_size = 2;
static ifstream yuv_file;
static XVideoView* view = nullptr;
static AVFrame* frame = nullptr;
static long long file_size = 0;
static QLabel* view_fps = nullptr; //显示fps控件
static QSpinBox* set_fps = nullptr;//设置fps控件
int fps = 25; //播放帧率
void SdlQtRGB::timerEvent(QTimerEvent* ev)
{//yuv_file.read((char*)yuv, sdl_width * sdl_height * 1.5);// yuv420p// 4*2// yyyy yyyy // u    u// v    vyuv_file.read((char*)frame->data[0], sdl_width * sdl_height);//Yyuv_file.read((char*)frame->data[1], sdl_width * sdl_height / 4);//Uyuv_file.read((char*)frame->data[2], sdl_width * sdl_height / 4);//Vif (view->IsExit()){view->Close();exit(0);}view->DrawFrame(frame);//view->Draw(yuv);
}void SdlQtRGB::View()
{yuv_file.read((char*)frame->data[0], sdl_width * sdl_height);//Yyuv_file.read((char*)frame->data[1], sdl_width * sdl_height / 4);//Uyuv_file.read((char*)frame->data[2], sdl_width * sdl_height / 4);//Vif (yuv_file.tellg() == file_size) //读取到文件结尾{yuv_file.seekg(0, ios::beg);}//yuv_file.gcount()//yuv_file.seekg() 结尾处seekg无效if (view->IsExit()){view->Close();exit(0);}view->DrawFrame(frame);stringstream ss;ss << "fps:" << view->render_fps();//只能在槽函数中调用view_fps->setText(ss.str().c_str());fps = set_fps->value(); //拿到播放帧率
}void SdlQtRGB::Main()
{while (!is_exit_){ViewS();if (fps > 0){MSleep(1000 / fps);}elseMSleep(10);}
}
SdlQtRGB::SdlQtRGB(QWidget* parent): QWidget(parent)
{//打开yuv文件yuv_file.open("400_300_25.yuv", ios::binary);if (!yuv_file){QMessageBox::information(this, "", "open yuv failed!");return;}yuv_file.seekg(0, ios::end);   //移到文件结尾file_size = yuv_file.tellg();   //文件指针位置yuv_file.seekg(0, ios::beg);ui.setupUi(this);//绑定渲染信号槽connect(this, SIGNAL(ViewS()), this, SLOT(View()));//显示fps的控件view_fps = new QLabel(this);view_fps->setText("fps:100");//设置fpsset_fps = new QSpinBox(this);set_fps->move(200, 0);set_fps->setValue(25);set_fps->setRange(1, 300);sdl_width = 400;sdl_height = 300;ui.label->resize(sdl_width, sdl_height);view = XVideoView::Create();//view->Init(sdl_width, sdl_height,//    XVideoView::YUV420P);//view->Close();view->Close();view->Init(sdl_width, sdl_height,XVideoView::YUV420P, (void*)ui.label->winId());//生成frame对象空间frame = av_frame_alloc();frame->width = sdl_width;frame->height = sdl_height;frame->format = AV_PIX_FMT_YUV420P;//  Y Y//   UV//  Y Yframe->linesize[0] = sdl_width;     //Yframe->linesize[1] = sdl_width / 2;   //Uframe->linesize[2] = sdl_width / 2;   //V//生成图像空间 默认32字节对齐auto re = av_frame_get_buffer(frame, 0);if (re != 0){char buf[1024] = { 0 };av_strerror(re, buf, sizeof(buf));cerr << buf << endl;}//startTimer(10);th_ = std::thread(&SdlQtRGB::Main, this);
}void SdlQtRGB::resizeEvent(QResizeEvent* ev)
{ui.label->resize(size());ui.label->move(0, 0);//view->Scale(width(), height());
}

timeEvent函数解析:

  •   从文件中依次读取 YUV420P 格式的视频帧数据,分别存储到 frame->data[0](Y 分量)、frame->data[1](U 分量)和 frame->data[2](V 分量)中。根据 YUV420P 格式的特点,U 和 V 分量的大小是 Y 分量的四分之一。
  • 调用 XVideoView 类的 DrawFrame 函数来渲染读取到的视频帧数据。这个函数会将 YUV 数据传递给渲染器进行显示。

view函数解析:

  •   如果读取到文件结尾,就将文件指针移到文件开头,实现视频循环播放。
  • view->DrawFrame(frame);: 这行代码调用了 XVideoView 类的 DrawFrame 函数,将从视频文件中读取的帧数据 frame 渲染到屏幕上。具体的渲染逻辑在 DrawFrame 函数中实现。

  • stringstream ss;: 创建了一个 stringstream 对象 ss,用于构建帧率信息的字符串。

  • ss << "fps:" << view->render_fps();: 将帧率信息拼接到 ss 中。view->render_fps() 会调用 XVideoView 对象的 render_fps() 方法来获取当前的渲染帧率,然后将其拼接到字符串后面。

  • view_fps->setText(ss.str().c_str());: 将构建好的帧率信息字符串设置到界面上用于显示帧率的文本框 view_fps 中。ss.str()stringstream 对象转换为 std::string 类型,然后通过 setText 函数将其设置到界面上。

  • fps = set_fps->value();: 获取用户设置的播放帧率。这里假设 set_fps 是一个用户用于设置播放帧率的控件(如滑块、输入框等),通过 value 属性获取用户设置的播放帧率,并将其保存在变量 fps 中。

Main函数解析:

SdlQtRGB::Main 方法是一个视频播放的主循环,不断地显示视频帧,控制播放帧率,直到退出条件满足为止。

sdlQtRGB构造函数解析:

  • std::thread(&SdlQtRGB::Main, this) 表示创建了一个新的线程,线程的入口函数是 SdlQtRGB 类的 Main 方法,当前对象的指针作为参数传递给线程。
  • yuv_file.seekg(0, ios::end);: 将文件指针移动到文件的末尾。通过将文件指针移动到文件末尾,然后调用 tellg() 函数获取文件指针的位置,就可以得到文件的大小。

  • file_size = yuv_file.tellg();: 获取文件指针的位置,即文件的大小,并将其赋值给变量 file_size。这样,file_size 变量就存储了 YUV 文件的大小。

  • yuv_file.seekg(0, ios::beg);: 将文件指针重新移动到文件的开头。这是为了在后续操作中重新使用文件时将文件指针定位到文件的起始位置。

7.运行过程:

  • 程序初始化:

    • 包括全局变量的初始化、配置文件的加载等操作。
  • 创建 SdlQtRGB 对象:

    • 在主函数中,会创建一个 SdlQtRGB 对象,这将触发 SdlQtRGB 类的构造函数执行。
  • 初始化界面和文件:

    • SdlQtRGB 类的构造函数中,会初始化界面、打开 YUV 文件,并获取文件大小等操作。
  • 创建视频渲染器和帧对象:

    • 在构造函数中会创建视频渲染器对象 view,并初始化它。
    • 创建 AVFrame 对象 frame,分配内存空间并设置帧的宽度、高度和像素格式为 YUV420P。
  • 启动视频播放主循环线程:

    • 在构造函数中,通过创建线程的方式启动视频播放主循环,即调用 SdlQtRGB::Main 方法。
  • 主循环运行:

    • SdlQtRGB::Main 方法中,程序会进入主循环,不断地执行视频播放的相关操作。
    • 主循环中会不断地读取 YUV 文件中的数据,并将数据传递给渲染器进行渲染。
  • 渲染帧和更新界面:

    • View 方法中,会不断地读取 YUV 数据,然后将其传递给渲染器进行渲染。
    • 同时,程序会更新界面上显示的帧率信息。
  • 用户交互和定时操作:

    • 程序会监听用户输入,响应键盘、鼠标等事件。
    • 如果设置了播放帧率,程序会根据帧率控制视频播放的速度。
  • 退出和清理:

    • 当用户关闭程序或触发退出条件时,程序会退出主循环。
    • 程序会执行必要的清理操作,包括释放资源、关闭文件等。

8.特别注意对帧率的调整过程:

在view函数中,最后两行,

view_fps->setText(ss.str().c_str());
//左上角显示帧率
fps = set_fps->value(); 

   通过调整 QSpinBox控件来拿到要播放帧率,若调整到40,则fps=40,在Main函数中通过fps参数将视频渲染的fps调整到40帧率
比如 fps = 40,MSleep(1000/40)即MSleep(25),即休眠25ms,即两幅图像渲染的时间间隔为25ms,1000ms 共有40个25ms,即40副图像,一秒渲染40副图像 ,即fps = 40 

 

9.运行结果:

这里我们可以通过QspinBox控件来调整视频播放的帧率,帧率越高播放速度越快。

 

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

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

相关文章

影响生产RAG流水线5大瓶颈

检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;已成为基于大型语言模型的生成式人工智能应用的关键组成部分。其主要目标是通过将通用语言模型与外部信息检索系统集成&#xff0c;增强通用语言模型的能力。这种混合方法旨在解决传统语言模…

无法删除dll文件

碰到xxxxxx.dll文件无法删除不要慌&#xff01; 通过Tasklist /m dll文件名称 去查看它和哪个系统文件绑定运行&#xff0c;发现是explorer.exe。 我们如果直接通过del命令【当然需要在该dll文件所在的路径中】。发现拒绝访问 我们需要在任务管理器中&#xff0c;将资源管理器…

如何处理网安发出的网络安全监督检查限期整改通知

近期&#xff0c;很多客户都收到了网安发出的限期整改通知。大家都比较关心的问题是&#xff0c;如何应对处理这些限期整改通知。后续是否有其他的影响&#xff0c;需要如何做进一步的优化整改和调整。今天就这些问题给大家做一些分享。 一. 为什么会有网安的网络安全检查 主…

大多数JAVA程序员都干不到35岁吗?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「 Java的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;不少人认为的程序员吃青春饭…

项目VS运营

一、项目与运营的定义与区别 项目与运营是企业管理中的两个重要概念&#xff0c;尽管在实际运作中它们常被视为同义词&#xff0c;但它们之间存在明显的区别。 项目&#xff0c;指的是为达到特定目标&#xff0c;通过临时性、系统性、有计划的组织、协调、控制等系列活动&…

基于深度学习的端到端语音识别时代

随着深度学习的发展&#xff0c;语音识别由DNN-HMM时代发展到基于深度学习的“端到端”时代&#xff0c;这个时代的主要特征是代价函数发生了变化&#xff0c;但基本的模型结构并没有太大变化。总体来说&#xff0c;端到端技术解决了输入序列长度远大于输出序列长度的问题。 采…

Visual Studio中调试信息格式参数:/Z7、/Zi、/ZI参数

一般的调试信息都保存在pdb文件中。 Z7参数表示这些调试信息保存到OBJ目标文件中&#xff0c;这样的好处是不需要单独分发PDB文件给下游。Zi就是把所有的调试信息都保存在pdb文件中&#xff0c;以缩小发布文件的大小。ZI和Zi类似&#xff0c;但是增加了热重载的能力&#xff1…

Django admin后台创建密文密码

Django admin后台创建密文密码 如题现在有一张用户表User # user/models.py from django.db import models from django.contrib.auth.models import AbstractUserclass User(AbstractUser):SEX_CHOICES [(0, 男),(1, 女),]sex models.IntegerField(choicesSEX_CHOICES, de…

数据结构:详解二叉树(树,二叉树顺序结构,堆的实现与应用,二叉树链式结构,链式二叉树的4种遍历方式)

目录 1.树的概念和结构 1.1树的概念 1.2树的相关概念 1.3树的代码表示 2.二叉树的概念及结构 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的存储结构 2.3.1顺序存储 2.3.2链式存储 3.二叉树的顺序结构和实现 3.1二叉树的顺序结构 3.2堆的概念和结构 3.3堆的特点 3…

MySQL统计字符长度:CHAR_LENGTH(str)

对于SQL表&#xff0c;用于计算字符串中字符数的最佳函数是 CHAR_LENGTH(str)&#xff0c;它返回字符串 str 的长度。 另一个常用的函数 LENGTH(str) 在这个问题中也适用&#xff0c;因为列 content 只包含英文字符&#xff0c;没有特殊字符。否则&#xff0c;LENGTH() 可能会返…

django使用fetch上传文件

在上一篇文章中&#xff0c;我包装了fetch方法&#xff0c;使其携带cookie。但是之前fetch传递的是json数据&#xff0c;现在有了一个上传文件的需求&#xff0c;因此需要进行修改&#xff1a; const sendRequest (url, method, data) > {const csrftoken Cookies.get(cs…

discuz如何添加主导航

大家好&#xff0c;今天教大家怎么样给discuz添加主导航。方法其实很简单&#xff0c;大家跟着我操作既可。一个网站的导航栏是非常重要的&#xff0c;一般用户进入网站的第一印象就是看网站的导航栏。如果大家想看效果的话可以搜索下网创有方&#xff0c;或者直接点击查看效果…

精选免费在线工具与资源推荐20240531

精选免费在线工具与资源推荐 引言 在互联网高速发展的今天&#xff0c;我们身处一个信息爆炸的时代。为了更好地应对工作和学习中的挑战&#xff0c;我们时常需要借助各种工具和资源来提高效率。幸运的是&#xff0c;网络上存在着大量免费且高效的在线工具和资源&#xff0c;…

标准发布 | 反渗透和纳滤水处理膜修复再利用技术要求

本文件由浙江大学、中华环保联合会水环境治理专业委员会提出。 本文件由中华环保联合会归口。 本文件主编单位&#xff1a;浙江大学、河南一膜环保技术有限公司、安徽精高水处理有限公司、国能龙源环保有限公司、湖南沁森高科新材料有限公司。 本文件参编单位&#xff1a;深…

rtl8723DU移植 android4.4 4418

一、 linux 的移植。 首先编译一遍确保没有问题。 将驱动拷贝到 driver/net/wireless 目录下。 使用的是&#xff1a; 改写 makefile Kconfig 去改写 8723 的makefile 设置menuconfig 使能固有的 库。 使能USB部分 ieee 部分 编译一遍 有报错。 解决&#xff1a; …

Java对sqlserver表的image字段图片读取和输出本地

Java代码实现对sqlserver数据库表的image字段图片的读取&#xff0c;和输出存储到本地 由于表image字段图片存的内容是二进制值&#xff0c;如何输出保存到本地&#xff1a; 代码示例&#xff1a;&#xff08;注&#xff1a;连接sqlserver数据库需配置其驱动文件&#xff09; …

Spring MVC 应⽤分层

什么是应用分层 引用分层是一种软件开发思想 将应用程序分为N个层次每个层次负责各个职责 其中MVC是常见的设计模式这就是应用分层的具体体现 目前主流的开发方式是前后段分离后端开发工程师不再需要关注前端的实现,对此就需要分为表现层&#xff0c;数据层&#xff0c;业务逻…

FPGA DMA IP核使用指南

摘要 本文旨在介绍FPGA中DMA(Direct Memory Access)IP核的使用,包括其基本框架、测试代码编写以及仿真波形的分析。DMA是一种允许外围设备直接与内存进行数据交换的技术,无需CPU的介入,从而提高了数据传输的效率。 1. 引言 在现代FPGA设计中,DMA IP核因其…

Leetcode刷题笔记6

34. 在排序数组中查找元素的第一个和最后一个位置 34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣&#xff08;LeetCode&#xff09; 解法一&#xff1a;暴力查找 [1, 2, 3, 3, 3, 4, 5] t 3 从前往后扫描暴力查找&#xff0c;最坏情况下O(N) 优化 利用数组有序的…

【LLM多模态】综述Visual Instruction Tuning towards General-Purpose Multimodal Model

note 文章目录 note论文1. 论文试图解决什么问题2. 这是否是一个新的问题3. 这篇文章要验证一个什么科学假设4. 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f;5. 论文中提到的解决方案之关键是什么&#xff1f;6. 论文中的…