基于ImGui+FFmpeg实现播放器
基于ImGui+FFmpeg实现播放器
演示:
ImGui播放器
继续研究FFmpeg,之前做了一个SDL的播放器,发现SDL的可视化UI界面的功能稍微差了点,所以今天我们换了一个新的工具,也就是ImGui。
ImGui官方文档:https://github.com/ocornut/imgui
接下来讲解一下播放器的主要功能,以及实现的方案
播放页面
播放页面是基于OpenGL渲染,ImGui实现UI界面完成的。
实现流程如下:
- FFmpeg解析视频文件,将每一帧写入数组中
- 根据帧率计算每一帧的播放时间
- 循环渲染每一帧
frame = frame_vector.at(current_index).frame;
render_image(frame);
ImGui::Image((ImTextureID)(intptr_t)texture_ids[0],ImVec2(codec_ctx->width, codec_ctx->height));
暂停/快进功能
暂停和快进功能相对比较简单
定义一个 is_pause变量控制视频播放
定义一个 delay变量控制播放速度
/**播放线程*/
int play_thread(void* arg) {for (;;) {while (!is_pause && current_index < frame_vector.size()) {SDL_Delay((Uint32) delay);current_index ++;// 从头播放if (current_index == frame_vector.size() -1) {current_index = 0;}}}return 1;
}
帧解析
帧解析就是将数组中的每一帧展示出来,程序对帧的类型进行了区分,使用不同的颜色来区分 I帧,P帧,B帧。
ImGui::BeginGroup();
ImGui::BeginChild("ScrollArea", ImVec2(0, codec_ctx->height), true, ImGuiWindowFlags_HorizontalScrollbar);
for(int i=0;i<frame_vector.size();i++) {if (frame_vector.at(i).frame->pict_type == AV_PICTURE_TYPE_I) {ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.0f, 0.0f, 1.0f));}else if (frame_vector.at(i).frame->pict_type == AV_PICTURE_TYPE_P) {ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.5f, 0.0f, 1.0f));}else {ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.5f, 1.0f));}if (ImGui::Button(get_frame_title(i))) {current_index = i;}// 恢复样式ImGui::PopStyleColor();
}
ImGui::EndChild();
ImGui::EndGroup();
宏块解析
根据鼠标的位置,找到对应的宏块 16*16,必将对应的字节打印出来,方便我们进行逐个宏块的分析。
源代码
//
// main.cpp
// analyser
//
// Created by chenhuaiyi on 2025/4/3.
//#include <SDL.h>
#include "imgui.h" // ImGui核心
#include "imgui_impl_sdl2.h" // SDL2后端
#include "imgui_impl_opengl3.h" // OpenGL渲染器
#include "iostream"
#include "glew.h"
#include <vector>
#include "string.h"
#include <iomanip> // 包含格式控制函数
#include <cstdint>// ffmpeg
extern "C" {
#include "libavcodec/avcodec.h"
#include "libswresample/swresample.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/time.h"
#include "libavutil/channel_layout.h"
#include "libavutil/log.h"
}#define Y_SIZE 256
#define U_SIZE 64
#define V_SIZE 64typedef struct Frame {AVFrame* frame;int64_t pts; // pts 播放相关int64_t duration; // durationAVPictureType pict_type; // 帧类型
}Frame;/**单个宏块的定义 16*16 个字节y 256 byteu 64 bytev 64 byte*/
typedef struct MircoBlock{uint8_t y[Y_SIZE];uint8_t u[U_SIZE];uint8_t v[V_SIZE];
} MircoBlock;// 窗口尺寸
const int WIDTH = 1600;
const int HEIGHT = 1200;GLuint texture_ids[2];std::vector<Frame> frame_vector;
int current_index = 0;const char* file = "//Users/chenhuaiyi/workspace/ffmpeg/files/恋爱_重编码.mp4";
AVFormatContext* format_ctx = avformat_alloc_context();
int video_stream = -1;
const AVCodec* codec = NULL;
AVCodecContext* codec_ctx;
double delay; // 延迟单位ms
int is_pause = 0;char* get_frame_title(int num) {// 为最大整数长度(含符号和结束符)分配空间char buffer[32];// 将整数写入缓冲区snprintf(buffer, sizeof(buffer), "frame:%d", num);// 复制到动态内存char* str = strdup(buffer);return str;
}/**获取当前帧的时间戳*/
double getCurrentTimeStamp() {AVFrame* frame = frame_vector.at(current_index).frame;double tag = av_q2d(format_ctx->streams[video_stream]->time_base);return frame->pts * tag;
}/**播放线程*/
int play_thread(void* arg) {for (;;) {while (!is_pause && current_index < frame_vector.size()) {SDL_Delay((Uint32) delay);current_index ++;// 从头播放if (current_index == frame_vector.size() -1) {current_index = 0;}}}return 1;
}/**宏块渲染*/
void render_block(float x, float y, AVFrame* frame, uint8_t* src[]) {src[0] = static_cast<uint8_t*>(av_malloc(Y_SIZE));src[1] = static_cast<uint8_t*>(av_malloc(U_SIZE));src[2] = static_cast<uint8_t*>(av_malloc(V_SIZE));int x_int = static_cast<int>(x);int y_int = static_cast<int>(y);// 计算当前坐标是哪个宏块int block_x = x / 16;int block_y = y / 16;int cb_block_x = block_x / 2;int cb_block_y = block_y / 2;// yuint8_t* y_start = frame->data[0] +(block_y * 16) * frame->linesize[0] + // 行偏移(block_x * 16); // 列偏移for (int row = 0; row < 16; row++) {const uint8_t* src_row = y_start + row * frame->linesize[0];std::memcpy(src[0] + (row * 16), src_row, 16);}// === 提取 Cb 分量 ===uint8_t* cb_start = frame->data[1] +(cb_block_y * 8) * frame->linesize[1] + // 行偏移(cb_block_x * 8); // 列偏移for (int row = 0; row < 8; row++) {const uint8_t* src_row = cb_start + row * frame->linesize[1];std::memcpy(src[1] + (row * 8), src_row, 8);}// === 提取 Cr 分量 ===uint8_t* cr_start = frame->data[2] +(cb_block_y * 8) * frame->linesize[2] + // 行偏移(cb_block_x * 8); // 列偏移for (int row = 0; row < 8; row++) {const uint8_t* src_row = cr_start + row * frame->linesize[2];std::memcpy(src[2] + (row * 8), src_row, 8);}// 渲染当前宏块到图像struct SwsContext* sws_ctx;sws_ctx = sws_getContext(16,16,AV_PIX_FMT_YUV420P,16,16,AV_PIX_FMT_RGBA,SWS_BILINEAR,NULL, NULL, NULL);/**初始数据分配*/int linesize[3] = {16, 8, 8};AVFrame* frame2 = av_frame_alloc();frame2->width = 16;frame2->height = 16;frame2->format = AV_PIX_FMT_RGBA;av_frame_get_buffer(frame2, 0);sws_scale(sws_ctx,(uint8_t const **)src,linesize,0,frame2->height,frame2->data,frame2->linesize);sws_freeContext(sws_ctx);glBindTexture(GL_TEXTURE_2D, texture_ids[1]);glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,16,16,0,GL_RGBA,GL_UNSIGNED_BYTE,frame2->data[0]);av_free(frame2);
}/**帧渲染*/
void render_image(AVFrame* frame) {AVFrame* frame2 = av_frame_alloc();struct SwsContext* sws_ctx;sws_ctx = sws_getContext(frame->width,frame->height,(AVPixelFormat)frame->format,frame->width,frame->height,AV_PIX_FMT_RGBA,SWS_BILINEAR,NULL, NULL, NULL);if (sws_ctx == NULL) {av_log(NULL, AV_LOG_ERROR, "sws context init error\n");return;}frame2->width = frame->width;frame2->height = frame->height;frame2->format = AV_PIX_FMT_RGBA;av_frame_get_buffer(frame2, 0);sws_scale(sws_ctx,(uint8_t const **)frame->data,frame->linesize,0,frame2->height,frame2->data,frame2->linesize);sws_freeContext(sws_ctx);glBindTexture(GL_TEXTURE_2D, texture_ids[0]);glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,frame2->width,frame2->height,0,GL_RGBA,GL_UNSIGNED_BYTE,frame2->data[0]);av_free(frame2);}int main(int argc, char* argv[]) {int ret = -1;ret = avformat_open_input(&format_ctx, file, NULL, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "Format open error: %s\n", av_err2str(ret));return -1;}if (avformat_find_stream_info(format_ctx, NULL) < 0) {printf("文件探测流信息失败");}video_stream = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);if (video_stream == -1) {av_log(NULL, AV_LOG_ERROR, "do not find video stream\n");return -1;}codec = avcodec_find_decoder(format_ctx->streams[video_stream]->codecpar->codec_id);if (codec == NULL) {av_log(NULL, AV_LOG_ERROR, "do not find encoder\n");return -1;}codec_ctx = avcodec_alloc_context3(codec);ret = avcodec_parameters_to_context(codec_ctx, format_ctx->streams[video_stream]->codecpar);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "codec context init error: %s\n", av_err2str(ret));return -1;}ret = avcodec_open2(codec_ctx, codec, NULL);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "encoder open error: %s\n", av_err2str(ret));return -1;}AVFrame* frame = av_frame_alloc();AVPacket packet;while (!av_read_frame(format_ctx, &packet)) {if (packet.stream_index == video_stream) {ret = avcodec_send_packet(codec_ctx, &packet);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "packet send error: %s\n", av_err2str(ret));}while (!avcodec_receive_frame(codec_ctx, frame)) {struct Frame frame_node;AVFrame* cpy_frame = av_frame_alloc();cpy_frame->width = codec_ctx->width;cpy_frame->height = codec_ctx->height;cpy_frame->format = codec_ctx->pix_fmt;av_frame_get_buffer(cpy_frame, 0);ret = av_frame_copy_props(cpy_frame, frame);ret = av_frame_copy(cpy_frame, frame);if (ret < 0) {av_log(NULL, AV_LOG_ERROR, "frame copy error:%s\n", av_err2str(ret));}frame_node.frame = cpy_frame;frame_node.duration = frame->duration;frame_node.pts = frame->pts;frame_node.pict_type = frame->pict_type;frame_vector.push_back(frame_node);}}av_packet_unref(&packet);}// 1. 初始化SDLif (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) {SDL_Log("SDL Initialization Failed! %s", SDL_GetError());return -1;}// 2. 创建窗口SDL_Window* window = SDL_CreateWindow("ImGui + SDL2 Example",SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,WIDTH, HEIGHT,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!window) {SDL_Log("Window Creation Failed! %s", SDL_GetError());return -1;}// 3. 创建OpenGL上下文SDL_GLContext gl_context = SDL_GL_CreateContext(window);if (!gl_context) {SDL_Log("OpenGL Context Creation Failed! %s", SDL_GetError());return -1;}// 4. 初始化ImGuiIMGUI_CHECKVERSION();ImGui::CreateContext();ImGuiIO& io = ImGui::GetIO();io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;// 5. 设置ImGui风格和主题ImGui::StyleColorsDark();// 6. 初始化 ImGui的SDL和OpenGL后端ImGui_ImplSDL2_InitForOpenGL(window, gl_context);ImGui_ImplOpenGL3_Init("#version 120"); // 根据OpenGL版本调整// 初始化纹理glGenTextures(1, texture_ids);for (int i=0; i<2; i++) {glBindTexture(GL_TEXTURE_2D, texture_ids[i]);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);}// 计算每一帧的延迟delay = 1/av_q2d(format_ctx->streams[video_stream]->avg_frame_rate)*1000;SDL_CreateThread(play_thread, "play_thread", NULL);// 7. 主循环bool is_running = true;while (is_running) {SDL_Event event;while (SDL_PollEvent(&event)) {ImGui_ImplSDL2_ProcessEvent(&event);if (event.type == SDL_QUIT)is_running = false;if (event.type == SDL_KEYDOWN) {}}// 开始 ImGui帧ImGui_ImplOpenGL3_NewFrame();ImGui_ImplSDL2_NewFrame();ImGui::NewFrame();// ---------------// 在此处编写UI逻辑:ImGui::Begin("Hello, World!"); // 开始窗口ImGui::BeginGroup();frame = frame_vector.at(current_index).frame;render_image(frame);ImGui::Image((ImTextureID)(intptr_t)texture_ids[0],ImVec2(codec_ctx->width, codec_ctx->height));// 获取 Image 的矩形区域ImVec2 img_min = ImGui::GetItemRectMin(); // 左上角坐标(全局坐标)ImVec2 img_max = ImGui::GetItemRectMax(); // 右下角坐标(全局坐标)ImVec2 mouse_global = ImGui::GetMousePos();if (ImGui::IsItemHovered()) {// 计算局部坐标ImVec2 local_mouse = ImVec2(mouse_global.x - img_min.x,mouse_global.y - img_min.y);// 显示坐标(归一化到纹理比例)float normalized_x = local_mouse.x / codec_ctx->width;float normalized_y = local_mouse.y / codec_ctx->height;uint8_t* src[3];render_block(local_mouse.x, local_mouse.y, frame, src);// 打印YUV数据ImGui::SameLine();ImGui::BeginGroup();std::string y_str;for (int i=0; i<16; i++) {for (int j=0; j<16; j++) {char buffer[3];snprintf(buffer, sizeof(buffer), "%02X", *(src[0] + i*16+j));y_str += buffer;}y_str.append("\n");}ImGui::Text(y_str.c_str());std::string u_str;for (int i=0; i<8; i++) {for (int j=0; j<8; j++) {char buffer[3];snprintf(buffer, sizeof(buffer), "%02X", *(src[1] + i*8+j));u_str += buffer;}u_str.append("\n");}ImGui::Text(u_str.c_str());std::string v_str;for (int i=0; i<8; i++) {for (int j=0; j<8; j++) {char buffer[3];snprintf(buffer, sizeof(buffer), "%02X", *(src[2] + i*8+j));v_str += buffer;}v_str.append("\n");}ImGui::Text(v_str.c_str());for (int i=0; i<3; i++) {av_free(src[i]);}ImGui::EndGroup();ImGui::Text("Relative Coordinates: %.2f, %.2f", local_mouse.x, local_mouse.y);ImGui::Text("Normalized: %.2f, %.2f", normalized_x, normalized_y);ImGui::Image((ImTextureID)(intptr_t)texture_ids[1],ImVec2(16, 16));// 鼠标宏块显示int block_x = local_mouse.x / 16;int block_y = local_mouse.y / 16;ImVec2 rect_min(img_min.x + block_x * 16, img_min.y + block_y * 16);ImVec2 rect_max(rect_min.x + 16, rect_min.y + 16);ImGui::GetWindowDrawList()->AddRect(rect_min,rect_max,ImGui::GetColorU32(ImGuiCol_Header), // 红色0.0f,0,4.0f // 线宽);}ImGui::BeginGroup();if (ImGui::Button("<<")) {delay*=2;}ImGui::SameLine();if (ImGui::Button("stop")) {is_pause = !is_pause;}ImGui::SameLine();if (ImGui::Button(">>")) {delay/=2;}ImGui::EndGroup();ImGui::BeginGroup();ImGui::Text("decoder&encoder");ImGui::Text("decoder: %s", codec->name);ImGui::Text("time_base: %f", av_q2d(format_ctx->streams[video_stream]->time_base));ImGui::Text("frame_rate: %.2f", av_q2d(format_ctx->streams[video_stream]->avg_frame_rate));ImGui::Text("frame_size: %lld", format_ctx->streams[video_stream]->nb_frames);ImGui::Text("bit_rate: %lld", codec_ctx->bit_rate);ImGui::Text("duration: %lld", format_ctx->duration);ImGui::EndGroup();ImGui::SameLine();ImGui::BeginGroup();ImGui::Text("SPS&PPS");ImGui::Text("profile_idc: %d", codec_ctx->profile);ImGui::Text("profile_name: %s", av_get_profile_name(codec, codec_ctx->profile));ImGui::Text("level_idc: %d", codec_ctx->level);ImGui::Text("frame_cropping_flag: %d", codec_ctx->apply_cropping);ImGui::Text("gop_size: %d", codec_ctx->gop_size);ImGui::EndGroup();ImGui::SameLine();ImGui::BeginGroup();ImGui::Text("frame detail");ImGui::Text("pts: %lld", frame->pts);ImGui::Text("duration: %lld", frame->duration);ImGui::Text("pict_type: %c", av_get_picture_type_char(frame->pict_type));ImGui::Text("format: %d", frame->format);ImGui::Text("pkt_size: %d", frame->pkt_size);ImGui::Text("pkt_pos: %d", frame->pkt_pos);ImGui::Text("pkt_dts: %lld", frame->pkt_dts);ImGui::Text("play_timestamp: %.2f", getCurrentTimeStamp());ImGui::EndGroup();ImGui::SameLine();ImGui::EndGroup();ImGui::SameLine();ImGui::BeginGroup();ImGui::BeginChild("ScrollArea", ImVec2(0, codec_ctx->height), true, ImGuiWindowFlags_HorizontalScrollbar);for(int i=0;i<frame_vector.size();i++) {if (frame_vector.at(i).frame->pict_type == AV_PICTURE_TYPE_I) {ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.0f, 0.0f, 1.0f));}else if (frame_vector.at(i).frame->pict_type == AV_PICTURE_TYPE_P) {ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.5f, 0.0f, 1.0f));}else {ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.0f, 0.0f, 0.5f, 1.0f));}if (ImGui::Button(get_frame_title(i))) {current_index = i;}// 恢复样式ImGui::PopStyleColor();}ImGui::EndChild();ImGui::EndGroup();ImGui::End();// ---------------// 渲染ImGui::Render();glViewport(0, 0, WIDTH, HEIGHT);glClearColor(0.2f, 0.2f, 0.2f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());// 切换缓冲区SDL_GL_SwapWindow(window);}// 8. 清理和退出av_packet_unref(&packet);for(Frame f : frame_vector) {av_free(f.frame);}avcodec_free_context(&codec_ctx);avformat_free_context(format_ctx);ImGui_ImplOpenGL3_Shutdown();ImGui_ImplSDL2_Shutdown();ImGui::DestroyContext();SDL_GL_DeleteContext(gl_context);SDL_DestroyWindow(window);SDL_Quit();return 0;
}
相关文章:
基于ImGui+FFmpeg实现播放器
基于ImGuiFFmpeg实现播放器 演示: ImGui播放器 继续研究FFmpeg,之前做了一个SDL的播放器,发现SDL的可视化UI界面的功能稍微差了点,所以今天我们换了一个新的工具,也就是ImGui。 ImGui官方文档:https://g…...
【Docker】快速部署 Certbot 并为 Nginx 服务器配置 SSL/TLS 证书
【Docker】快速部署 Certbot 并为 Nginx 服务器配置 SSL/TLS 证书 引言 Certbot 是一个免费的开源工具,用于自动化管理和获取 SSL/TLS 证书,主要用于与 Let’s Encrypt 证书颁发机构交互。 步骤 Nginx 挂载 certbot 文件夹。 docker run -d \--name…...
MATLAB编写的机械臂控制仿真程序,它主要实现了对一个二连杆机械臂的运动控制仿真,比较了PID控制和非线性模型预测控制两种方法在机械臂轨迹跟踪任务中的性能
clc; clear; close all;%% 机械臂参数 l1 0.5; l2 0.4; Ts 0.02; sim_time 60; t 0:Ts:sim_time;%% 物理参数 m1 1.0; m2 0.8; g 9.81;%% 直线轨迹参数 start_point [0.3; 0.1]; end_point [0.7; 0.3]; progress t/sim_time; xd start_point(1) (end_point(1) - s…...
python办公自动化------word文件的操作
一、 word文件的创建 需要安装包:python-docx 例1:创建word文件 from docx import Document# 创建doc对象 doc Document()# 指定路径下创建一个docx文件 doc.save(./dataFile/test1_doc.docx) 运行结果: 例2:word中添加内容 …...
Python小程序 - 文件处理3:正则表达式
正则表达式:文本年鉴表。遗留的问题很多。。。用AI再想想 需求:读入txt文件,过滤文件有关年记录 0)读入txt文件 1)以“。”,中文句号,为界区分一句,最小统计单位 2)年格…...
JAVA中正则表达式的入门与使用
JAVA中正则表达式的入门与使用 一,基础概念 正则表达式(Regex) 用于匹配字符串中的特定模式,Java 中通过 java.util.regex 包实现,核心类为: Pattern:编译后的正则表达式对象。 Matcher&#…...
智慧能源管理平台:驱动电网数字化转型,引领绿色能源新时代
安科瑞 华楠18706163979 一、引言 在全球能源转型和"双碳"目标的推动下,微电网作为分布式能源系统的重要组成部分,正迎来快速发展期。安科瑞电气股份有限公司推出的微电网智慧能源管理平台(EMS 3.0),通过整…...
OpenCV 图形API(30)图像滤波-----腐蚀操作函数erode()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 使用特定的结构元素腐蚀图像。 cv::gapi::erode 是 OpenCV 的 G-API 模块中用于执行图像腐蚀操作的函数。腐蚀是一种基本的形态学操作࿰…...
02-MySQL 面试题-mk
1.如何定位慢查询? 什么是慢查询? 页面加载过慢、接口压测响应时间过长(超过1s) 慢查询出现的情况有哪些? 聚合查询多表查询表数据量过大查询深度分页查询如何定位慢查询? 方案一:开源工具 调试工具:Arthas运维工具:Prometheus、Skywalking**Arthas:**可以使用命令的…...
利用安固软件实现电脑屏幕录像:四种实用方法分享
在日常工作中,有时我们需要录制电脑屏幕以进行教学演示、软件操作记录或重要会议的存档。安固软件终端安全管理系统提供了强大的屏幕录像功能,可以满足这些需求。 接下来,本文将介绍如何使用安固软件设置电脑实时画面录像,并分享…...
Gitee DevSecOps 以 CBB 驱动军工研发范式革新:平台化管理构件实践
随着军工软件向智能化、标准化与集约化发展,传统研发模式在效率、质量及协同方面面临显著瓶颈。项目规模扩大与系统复杂度的提升,亟需一种创新研发范式。Gitee DevSecOps 平台基于 CBB(通用构件库)理念,通过模块化、标…...
spring:xml方式调用构造方法创建Bean,调用set方法配置字段
如题: 要创建的Bean类UserServiceImpl02 : package com.itheima.service.impl;import com.itheima.dao.interfaces.InterfaceUserDao; import com.itheima.service.interfaces.InterfaceUserService;/*** copyright 2003-2024* author qiao wei* da…...
PM2 完全指南:Node.js 应用后台启动、关闭与重启详解
文章目录 **PM2 完全指南:Node.js 应用后台启动、关闭与重启详解****1. 什么是 PM2?****2. 安装 PM2****全局安装****验证安装** **3. 使用 PM2 启动 Node.js 应用****基本启动****指定应用名称****集群模式(多进程负载均衡)****监…...
LeetCode算法题(Go语言实现)_40
题目 给你一个二叉树的根节点 root。设根节点位于二叉树的第 1 层,而根节点的子节点位于第 2 层,依此类推。 请返回层内元素之和 最大 的那几层(可能只有一层)的层号,并返回其中 最小 的那个。 一、代码实现 import &…...
网关与路由器知识点
1.自治系统是由同构型的网关连接的因特网。自治系统内部的网关之间执行内部网关协议IGP,半天自治系统之间用外部网关协议EGP交换路由信息。 2.BGP4是一种动态路由发现协议,支持无类别域间路由CIDR,应用于不同ISP的网络之间,成为事…...
【Hadoop入门】Hadoop生态之Pig简介
1 什么是Pig? 在大数据分析领域,Apache Pig是一个不可忽视的重要工具。Pig是Apache Hadoop生态系统中的一个高级数据分析平台,它提供了一种称为Pig Latin的脚本语言,用于简化大规模数据集的并行处理。Pig的核心思想是将复杂的数据…...
TPS入门DAY04 服务器篇
1.创建委托并绑定回调,实现解耦 /* 创建会话完成 */ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiPlayerOnCreateSessionCompleted, bool, bWasSuccessful); /* 开始会话完成 */ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FMultiPlayerOnStartSessionC…...
YOLO学习笔记 | YOLOv8环境搭建全流程指南(2025.4)
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== YOLOv8环境搭建 一、环境准备与工具配置1. Conda虚拟环境搭建2. CUDA与…...
Java 设计模式:适配器模式详解
Java 设计模式:适配器模式详解 适配器模式(Adapter Pattern)是一种结构型设计模式,它通过将一个类的接口转换为客户端期望的另一个接口,使原本不兼容的类能够协同工作。适配器模式就像现实生活中的电源适配器…...
Java的基本语法(1)
一、运算符和表达式 举例说明什么是运算符,什么是表达式: int a 1; int b 2; int c a b; 在这个例子当中,是运算符,并且是算术运算符 ab是表达式,因为是运算符,所以ab是算术表达式 1.1算术运算符 …...
Spark-Core编程二
23) sortByKey import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} object Cww { def main(args: Array[String]): Unit { // 创建 SparkConf 对象,设置应用名称和运行模式 val conf new SparkConf().setAppName("SortBy…...
【antd + vue】Modal 对话框:修改弹窗标题样式、Modal.confirm自定义使用
一、标题样式 1、目标样式:修改弹窗标题样式 2、问题: 直接在对应css文件中修改样式不生效。 3、原因分析: 可能原因: 选择器权重不够,把在控制台找到的选择器直接复制下来,如果还不够就再加ÿ…...
JavaWeb 课堂笔记 —— 08 请求响应
本系列为笔者学习JavaWeb的课堂笔记,视频资源为B站黑马程序员出品的《黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖SpringMyBatisSpringMVCSpringBoot等)》,章节分布参考视频教程,为同样学习…...
DataWorks智能体Agent发布!基于MCP实现数据开发与治理自动化运行
在传统的数据开发工作中,企业用户或者开发者常常需要进行繁琐的配置、复杂的代码撰写、反复的性能调优和大量重复性的操作,才能实现数据开发、数据集成和数据治理等工作,效率十分低下。 近日,阿里云大数据开发治理平台DataWorks基…...
VitePress 项目部署 cloudflare page 提示 Node 构建错误
提示的构建错误信息为: 09:35:29.838 Error: Exit with error code: 1 09:35:29.839 at ChildProcess.<anonymous> (/snapshot/dist/run-build.js) 09:35:29.839 at Object.onceWrapper (node:events:652:26) 09:35:29.839 at ChildProcess.emit …...
VUE的node包缓存很严重,问题及解决办法
以下是一些可能导致缓存问题的地方以及如何检查和解决这些问题的建议: 1. 检查依赖包的版本是否过时 某些依赖包可能有已知的缓存问题或性能问题。以下是需要特别注意的几个包: Vue CLI 相关: vue/cli-service 和 vue/cli-plugin-babel 的版…...
<C#>在 .NET 开发中,依赖注入, 注册一个接口的多个实现
在 .NET 开发里,有时一个接口会有多个实现类,此时就需要向依赖注入容器注册多个实现。下面会详细介绍不同场景下如何注册多个实现,以及怎样从容器中解析这些实现。 1. 注册多个实现 在 .NET 中,依赖注入容器可以通过不同方式注册…...
计算机视觉色彩空间全解析:RGB、HSV与Lab的实战对比
计算机视觉色彩空间全解析:RGB、HSV与Lab的实战对比 一、前言二、RGB 色彩空间2.1 RGB 色彩空间原理2.1.1 基本概念2.1.2 颜色混合机制 2.2 RGB 在计算机视觉中的应用2.2.1 图像读取与显示2.2.2 颜色识别2.2.3 RGB 色彩空间的局限性 三、HSV 色彩空…...
美食推荐小程序
用户端 1. **定位与筛选** - **精准定位**:根据用户所在位置,推荐周边美食餐厅。 - **筛选条件**:支持按菜系(如川菜、粤菜)、价格区间、评分、距离远近等筛选餐厅。 2. **餐厅展示** - **基本信息**:…...
使用django实现windows任务调度管理
在 Django 中实现 Windows 任务调度管理,你可以使用几种不同的方法。最常见的方法是使用 Django 自带的 celery 或者 django-background-tasks 库,或者使用 Windows 自带的任务计划程序。下面我会分别介绍这几种方法: 方法 1:使用…...
【Android】Android 打包 Release 崩溃问题全解析:Lint 错误、混淆类丢失及解决方法大全
摘要: 在 Android 项目的 Release 打包过程中,经常遇到诸如 Lint 校验失败、程序闪退、类找不到等问题。本文将详细分析 Android 打包时常见的崩溃原因,特别是如何应对 Lint 报错、混淆引发的类丢失(NoClassDefFoundError…...
【Python 开源】你的 Windows 关机助手——PyQt5 版定时关机工具
🖥️ 你的 Windows 关机助手——PyQt5 版定时关机工具 相关资源文件已经打包成EXE文件,可双击直接运行程序,且文章末尾已附上相关源码,以供大家学习交流,博主主页还有更多Python相关程序案例,秉着开源精神的…...
布局决定终局:基于开源AI大模型、AI智能名片与S2B2C商城小程序的战略反推思维
摘要:在商业竞争日益激烈的当下,布局与终局预判成为企业成功的关键要素。本文探讨了布局与终局预判的智慧性,强调其虽无法做到百分之百准确,但能显著提升思考能力。终局思维作为重要战略工具,并非一步到位的战略部署&a…...
vue3循环表单【以el-form组件为例】,如何校验所有表单,所有表单校验通过后提交
1.循环时,在组件标签上使用ref绑定组件实例 3.如何校验所有表单都通过后方可提交?利用promise.all()判断 代码如下: <template><div><!-- 循环渲染表单 --><el-formv-for"(formItem, index) in formList":…...
量子代理签名:量子时代的数字授权革命
1. 量子代理签名的定义与核心原理 量子代理签名(Quantum Proxy Signature, QPS)是经典代理签名在量子信息领域的延伸,允许原始签名者(Original Signer)授权给代理签名者(Proxy Signer)代为签署文…...
HTTPS代理的实际优势,如何选择HTTPS代理服务?
在数字化时代,网络请求的安全性和效率成为企业和个人用户关注的焦点。HTTPS代理作为一种强大的工具,不仅在数据安全方面表现出色,还在安全访问、突破地域限制以及支持复杂任务等方面展现出多样化的优势。本文将深入探讨HTTPS代理的实际优势&a…...
Java 中常见的数据结构
目录 1. List (列表) 2)ArrayList 2)LinkedList 2. Set (集合) 1)HashSet 2)TreeSet 3. Map (映射) 1)HashMap 2)TreeMap 4. Queue (队列) 1)LinkedList (也实现了Queue接口) 2&…...
Transformer多卡训练初始化分布式环境:(backend=‘nccl‘)
Transformer多卡训练初始化分布式环境:(backend=‘nccl’) dist.init_process_group(backend=nccl)在多卡环境下初始化分布式训练环境,并为每个进程分配对应的 GPU 设备。下面为你逐行解释代码的含义: 1. 初始化分布式进程组 try:dist.init_process_group(backend=nccl) e…...
云曦月末断网考核复现
Web 先看一个BUUCTF中的文件一个上传题 [BUUCTF] 2020新生赛 Upload 打开后是一个文件上传页面 随便上传一个txt一句话木马后出现js弹窗,提示只能上传图片格式文件 说明有前端验证。我的做法是把一句话改为.jpg格式, 然后上传 访问发现虽然上传成功了…...
SQL Server AlwaysOn (SQL 查询数据详解及监控用途)
修正后的完整查询 SELECT ar.replica_server_name AS [副本名称],ar.availability_mode_desc AS [同步模式],DB_NAME(dbr.database_id) AS [数据库名称],dbr.database_state_desc AS [数据库状态],dbr.synchronization_state_desc AS [同步状态],dbr.synchronization_health_d…...
使用 Q - learning 算法解决迷宫路径规划问题
整体功能概述 这段 Python 代码实现了一个使用 Q - learning 算法解决迷宫路径规划问题的程序。智能体在给定的迷宫环境中学习如何找到从起点到终点的最优路径,以获得最大奖励。 模块导入 python import numpy as np import randomnumpy:用于处理数组…...
SqlYog无限试用方法
1、WinR ,输入 :regedit 回车 2、进入注册表,在 \HEYK_CURRENT_USER\Software\{*********-D8CE-4637-9BC7-93E************}下的【InD100】保存着SQLyog的使用天数 3、在【InD】上右键,点击删除该项,在重启SQLyog后注册表中会重…...
14 nginx 的 dns 缓存的流程
前言 这个是 2020年11月 记录的这个关于 nginx 的 dns 缓存的问题 docker 环境下面 前端A连到后端B 前端B连到后端A 最近从草稿箱发布这个问题的时候, 重新看了一下 发现该问题的记录中仅仅是 定位到了 nginx 这边的 dns 缓存的问题, 但是 并没有到细节, 没有到 具体的 n种…...
Spring Cloud Gateway 具体的实现案例
文章目录 前言✅ 1. **创建 Spring Boot 项目**Maven 依赖:Gradle 依赖: ✅ 2. **配置 application.yml 路由和过滤器**解释: ✅ 3. **创建自定义过滤器**3.1 **前置过滤器(Pre Filter)**3.2 **后置过滤器(…...
css易混淆的知识点
子选择器 (>) vs 后代选择器 (空格) 子选择器 (>) 只匹配直接子元素。后代选择器 (空格) 匹配所有后代元素(无论嵌套多深)。 绝对定位vs相对定位 布局: justify-content 的作用 控制子元素在主轴上的分布方式。常见值包括 flex-start、…...
OpenCV 图形API(27)图像滤波-----膨胀函数dilate()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 使用特定的结构元素膨胀图像。 cv::gapi::dilate 是 OpenCV G-API 模块中的一个函数,用于对图像执行膨胀操作。膨胀是一种形态学操作…...
13. Git 远程仓库配置
基本步骤 以Gitee为例子,GitHub的步骤也基本一致 1.注册/登录 Gitee 账号 https://gitee.com/ 2.新建仓库 3.配置仓库 根据自己的喜好,配置即可。 4.生成SSH密钥 ssh-keygen -t ed25519 -C "你的邮箱example.com"-t ed25519:使…...
语音识别——根据声波能量、VAD 和 频谱分析实时输出文字
ASR(语音识别)是将音频信息转化为文字的技术。在实时语音识别中,一个关键问题是:如何决定将采集的音频数据输入大模型的最佳时机?固定时间间隔显然不够灵活,太短可能导致频繁调用模型,太长则会延迟文字输出。有没有更智能的方式?答案是肯定的。 一种常见的解决方案是使…...
netty中的ChannelHandler详解
Netty中的ChannelHandler是网络事件和数据处理的核心执行单元,负责处理I/O事件(如连接建立、数据读写)以及业务逻辑的实现。它通过ChannelPipeline形成责任链,实现事件的动态编排与传播。以下从功能分类、核心机制到实际应用进行详细解析: 1. ChannelHandler的核心功能与分…...
介绍一下freertos
FreeRTOS 是一款开源的实时操作系统(RTOS),专为嵌入式系统和微控制器设计,以轻量级、高可靠性、低延迟著称。它广泛应用于物联网(IoT)、工业自动化、消费电子等领域。以下是详细介绍: …...