音视频开发 sdl库

介绍

SDL (Simple DirectMedia Layer) 是一个跨平台的开源多媒体库,它提供了底层访问多种硬件的接口,如音频、视频、输入设备等。它主要用于游戏开发,但也可用于其他类型的多媒体应用程序。下面是 SDL 的一些主要特点:

  1. 跨平台性: SDL 支持多种操作系统,包括 Windows、macOS、Linux、iOS、Android 等。开发者可以编写一套代码,就能在不同平台上运行。

  2. 高性能: SDL 采用底层硬件访问的方式,可以提供出色的性能表现。

  3. 丰富的功能: SDL 包含了大量的功能模块,如窗口管理、图形渲染、音频处理、输入事件处理等。

  4. 易用性: SDL 提供了简单易用的 API,开发者可以快速上手并实现各种多媒体功能。

  5. 开源和免费: SDL 是一个开源项目,开发者可以免费使用并修改源代码。

SDL 广泛应用于游戏开发、模拟器、媒体播放器等领域。它拥有活跃的开发者社区,提供丰富的文档和示例代码,非常适合初学者和专业开发者使用。

下载安装

https://github.com/libsdl-org/SDL/releases/tag/release-2.30.6
下载编译好的 SDL2-devel-2.30.6-VC.zipqt .por引入,并把SDL2.lib动态库文件放到build文件目录
win32 {
INCLUDEPATH += $$PWD/SDL2-2.30.6/include
LIBS  += $$PWD/SDL2-2.30.6/lib/x86/SDL2.lib
}

系统架构以及使用流程 

1.SDL子系统

SDL将功能分成下列数个子系统(subsystem):
◼ SDL_INIT_TIMER:定时器
◼ SDL_INIT_AUDIO:音频
◼ SDL_INIT_VIDEO:视频
◼ SDL_INIT_JOYSTICK:摇杆
◼ SDL_INIT_HAPTIC:触摸屏
◼ SDL_INIT_GAMECONTROLLER:游戏控制器
◼ SDL_INIT_EVENTS:事件
◼ SDL_INIT_EVERYTHING:包含上述所有选项

2.SDL显示视频,数据结构和函数

◼ SDL_Init():初始化SDL系统
◼ SDL_CreateWindow():创建窗口SDL_Window
◼ SDL_CreateRenderer():创建渲染器SDL_Renderer
◼ SDL_CreateTexture():创建纹理SDL_Texture
◼ SDL_UpdateTexture():设置纹理的数据
◼ SDL_RenderCopy():将纹理的数据拷贝给渲染器
◼ SDL_RenderPresent():显示
◼ SDL_Delay():工具函数,用于延时
◼ SDL_Quit():退出SDL系统纹理 依赖 渲染器 依赖 窗口
SDL数据结构简介
◼ SDL_Window 代表了一个“窗口”
◼ SDL_Renderer 代表了一个“渲染器”
◼ SDL_Texture 代表了一个“纹理”
◼ SDL_Rect 一个简单的矩形结构存储RGB和存储纹理的区别:
比如一个从左到右由红色渐变到蓝色的矩形,用存储RGB的话就需要把矩形中每个点的具体颜色值存储下来;而纹理只是一些描述信息,比如记录了矩形的大小、起始颜色、终止颜色等信息,显卡可以通过这些信息推算出矩形块的详细信息。所以相对于存储RGB而已,存储纹理占用的内存要少的多理解:以一条美食街举例 
rgb方式详细记录这条美食街,比如1.炸鸡 (大鸡腿,小鸡腿10元2个) 2.炸串... 3.烧烤.... 4.小龙虾...
纹理方式记录基本信息  比如1-2家是油炸店  3-4是宵夜店

实战

视频显示 初学乍到

#include <stdio.h>
#include<SDL.h>//屏蔽main因为sdl存在main
#undef main
int main()
{//创建窗口 渲染器 纹理器SDL_Window* window =NULL;SDL_Renderer* renderer =NULL;SDL_Texture* texture =NULL;SDL_Init(SDL_INIT_VIDEO);//初始化函数,可以确定希望激活的子系统//SDL_WINDOWPOS_UNDEFINED xy设置中心window =SDL_CreateWindow("测试sdl"    ,SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,640,480,SDL_WINDOW_OPENGL);if(!window){printf("create windows error\n");return 1;}renderer =SDL_CreateRenderer(window,-1,0);if(!renderer) return 1;texture =SDL_CreateTexture(renderer,SDL_PIXELFORMAT_RGBA8888,SDL_TEXTUREACCESS_TARGET,640,480);if(!texture) return -1;int count=0,run=1;SDL_Rect rect;rect.w=50;rect.h=50;while(run){rect.x =rand()%600;rect.y=rand()%400;SDL_SetRenderTarget(renderer,texture);// 设置渲染目标纹理对象SDL_SetRenderDrawColor(renderer,0,0,0,255); // 纹理背景为黑色SDL_RenderClear(renderer);//清除渲染目标上的所有内容SDL_RenderDrawRect(renderer,&rect);//在渲染目标上绘制一个矩形的轮廓和填充SDL_SetRenderDrawColor(renderer,0,255,255,255);SDL_RenderFillRect(renderer,&rect);SDL_SetRenderTarget(renderer,NULL);//将渲染目标设置为默认的渲染窗口SDL_RenderCopy(renderer,texture,NULL,NULL);//将之前绘制的矩形纹理拷贝到渲染窗口上SDL_RenderPresent(renderer);//将渲染的内容显示到屏幕上。SDL_Delay(300);if(count++ > 10) run =0;}SDL_DestroyTexture(texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();return 0;
}

 

视频显示 小试牛刀

SDL事件系统

SDL事件
◼ 函数
• SDL_WaitEvent():等待一个事件
• SDL_PushEvent():发送一个事件
• SDL_PumpEvents():将硬件设备产生的事件放入事件队列,用于读取事件,在调用该函数之前,必须调用SDL_PumpEvents搜集键盘等事件
• SDL_PeepEvents():从事件队列提取一个事件
◼ 数据结构
• SDL_Event:代表一个事件为什么需要? 
比如播放器进度条拖动则表示快进或者方向键移动15s,需要实现这些功能就必须要通过键盘事件映射
好处有特别多。
#include <SDL.h>
#include <stdio.h>
#define FF_QUIT_EVENT    (SDL_USEREVENT + 2) // 用户自定义事件
#undef main
int main(int argc, char* argv[])
{SDL_Init(SDL_INIT_VIDEO);               // Initialize SDL2SDL_Window * window = SDL_CreateWindow("An SDL2 window",                  // window titleSDL_WINDOWPOS_UNDEFINED,           // initial x position500,           // initial y position640,                               // width, in pixels480,                               // height, in pixelsSDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS// flags - see below);SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0);SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); //红色画布SDL_RenderClear(renderer);SDL_RenderPresent(renderer);//将渲染的内容显示到屏幕上。SDL_Event event;int b_exit = 0;for (;;){SDL_WaitEvent(&event);  //等待事件switch (event.type){case SDL_KEYDOWN:	/* 键盘事件 */switch (event.key.keysym.sym){case SDLK_a:printf("key down a\n");break;case SDLK_s:printf("key down s\n");break;case SDLK_d:printf("key down d\n");break;case SDLK_q:printf("key down q and push quit event\n");SDL_Event event_q;  //自定义事件event_q.type = FF_QUIT_EVENT;SDL_PushEvent(&event_q);break;default:printf("key down 0x%x\n", event.key.keysym.sym);break;}break;case SDL_MOUSEBUTTONDOWN:			/* 鼠标按下事件 */if (event.button.button == SDL_BUTTON_LEFT)         printf("mouse down left\n");else if(event.button.button == SDL_BUTTON_RIGHT)    printf("mouse down right\n");else     printf("mouse down %d\n", event.button.button);break;case SDL_MOUSEMOTION:		/* 鼠标移动事件 */printf("mouse movie (%d,%d)\n", event.button.x, event.button.y);break;case FF_QUIT_EVENT:  //自定义事件处理printf("receive quit event\n");b_exit = 1;break;}if(b_exit)break;}if (renderer)SDL_DestroyRenderer(renderer);if (window)SDL_DestroyWindow(window);SDL_Quit();return 0;
}

 小试牛刀2

SDL线程

SDL多线程
◼ SDL线程创建:SDL_CreateThread
◼ SDL线程等待:SDL_WaitThead
◼ SDL互斥锁:SDL_CreateMutex/SDL_DestroyMutex
◼ SDL锁定互斥:SDL_LockMutex/SDL_UnlockMutex
◼ SDL条件变量(信号量):SDL_CreateCond/SDL_DestoryCond
◼ SDL条件变量(信号量)等待/通知:SDL_CondWait/SDL_CondSingal 注意点坑点: SDL_CondSingal 只会通知等待在这个信号量上的线程,不会释放当前线程锁
cpp新特性notify会通知并释放当前持有的互斥锁。为什么需要?
虽然cpp新特性提供了,但是sdl的互斥锁条件变量,粒度更小,能够更好的适用与视频编解码
#include <SDL.h>
#include <stdio.h>SDL_mutex *s_lock = NULL;
SDL_cond *s_cond = NULL;int thread_work(void *arg)
{SDL_LockMutex(s_lock);printf("                <============thread_work sleep\n");sleep(10);      // 用来测试获取锁printf("                <============thread_work wait\n");SDL_CondWait(s_cond, s_lock); //另一个线程(1)发送signal和(2)释放lock后,这个函数退出printf("                <===========thread_work receive signal, continue to do ~_~!!!\n");SDL_UnlockMutex(s_lock);printf("                <===========thread_work end\n");return 0;
}#undef main
int main()
{s_lock = SDL_CreateMutex();s_cond = SDL_CreateCond();SDL_Thread * t = SDL_CreateThread(thread_work,"thread_work",NULL);if(!t){printf("  %s",SDL_GetError);return -1;}for(int i = 0;i< 2;i++){sleep(2);printf("main execute =====>\n");}printf("main SDL_LockMutex(s_lock) before ====================>\n");SDL_LockMutex(s_lock);  // 获取锁,但是子线程还拿着锁printf("main ready send signal====================>\n");printf("main SDL_CondSignal(s_cond) before ====================>\n");SDL_CondSignal(s_cond); // 发送信号,唤醒等待的线程printf("main SDL_CondSignal(s_cond) after ====================>\n");sleep(10);SDL_UnlockMutex(s_lock);// 释放锁,让其他线程可以拿到锁printf("main SDL_UnlockMutex(s_lock) after ====================>\n");SDL_WaitThread(t, NULL);SDL_DestroyMutex(s_lock);SDL_DestroyCond(s_cond);return 0;
}

略有小成 播放yuv视频

代码思路:
1.开辟一个线程,不停投递自定义事件,实现自定义事件循环,并做一些做退出处理。2.准备一个yuv420p_320x240文件,进行初始化工作 窗口->渲染器->纹理 记录一些基本信息
进入死循环,通过自定义事件编写读取yuv420p数据,每次读取一帧的yuv分量图像。
将数据通过渲染器展示在窗口。3. 还定义了窗口拖拽,保持320x240比例。比如横拉 不会造成比例失调
#include <stdio.h>
#include <string.h>#include <SDL.h>//自定义消息类型
#define REFRESH_EVENT   (SDL_USEREVENT + 1)     // 请求画面刷新事件
#define QUIT_EVENT      (SDL_USEREVENT + 2)     // 退出事件//定义分辨率
// YUV像素分辨率
#define YUV_WIDTH   320
#define YUV_HEIGHT  240
//定义YUV格式
#define YUV_FORMAT  SDL_PIXELFORMAT_IYUVint s_thread_exit = 0;  // 退出标志 = 1则退出int refresh_video_timer(void *data)
{while (!s_thread_exit){SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event);SDL_Delay(40);}s_thread_exit = 0;//push quit eventSDL_Event event;event.type = QUIT_EVENT;SDL_PushEvent(&event);return 0;
}
#undef main
int main(int argc, char* argv[])
{//初始化 SDLif(SDL_Init(SDL_INIT_VIDEO)){fprintf( stderr, "Could not initialize SDL - %s\n", SDL_GetError());return -1;}// SDLSDL_Event event;                            // 事件SDL_Rect rect;                              // 矩形SDL_Window *window = NULL;                  // 窗口SDL_Renderer *renderer = NULL;              // 渲染SDL_Texture *texture = NULL;                // 纹理SDL_Thread *timer_thread = NULL;            // 请求刷新线程uint32_t pixformat = YUV_FORMAT;            // YUV420P,即是SDL_PIXELFORMAT_IYUV// 分辨率// 1. YUV的分辨率int video_width = YUV_WIDTH;int video_height = YUV_HEIGHT;// 2.显示窗口的分辨率int win_width = YUV_WIDTH;int win_height = YUV_WIDTH;// YUV文件句柄FILE *video_fd = NULL;const char *yuv_path = "yuv420p_320x240.yuv";size_t video_buff_len = 0;uint8_t *video_buf = NULL; //读取数据后先把放到buffer里面// 我们测试的文件是YUV420P格式uint32_t y_frame_len = video_width * video_height;uint32_t u_frame_len = video_width * video_height / 4;uint32_t v_frame_len = video_width * video_height / 4;uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;//创建窗口window = SDL_CreateWindow("Simplest YUV Player",SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,video_width, video_height,SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE);if(!window){fprintf(stderr, "SDL: could not create window, err:%s\n",SDL_GetError());goto _FAIL;}// 基于窗口创建渲染器renderer = SDL_CreateRenderer(window, -1, 0);// 基于渲染器创建纹理texture = SDL_CreateTexture(renderer,pixformat,SDL_TEXTUREACCESS_STREAMING,video_width,video_height);// 分配空间video_buf = (uint8_t*)malloc(yuv_frame_len);if(!video_buf){fprintf(stderr, "Failed to alloce yuv frame space!\n");goto _FAIL;}// 打开YUV文件video_fd = fopen(yuv_path, "rb");if( !video_fd ){fprintf(stderr, "Failed to open yuv file\n");goto _FAIL;}// 创建请求刷新线程timer_thread = SDL_CreateThread(refresh_video_timer,NULL,NULL);float video_ratio = (float)video_width / (float)video_height;float video_ratio2 =0;while (1){// 收取SDL系统里面的事件SDL_WaitEvent(&event);if(event.type == REFRESH_EVENT) // 画面刷新事件{video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);if(video_buff_len <= 0){fprintf(stderr, "Failed to read data from yuv file!\n");goto _FAIL;}// 设置纹理的数据 video_width = 320, planeSDL_UpdateTexture(texture, NULL, video_buf, video_width);// 显示区域,可以通过修改w和h进行缩放rect.x = 0;rect.y = 0;// 320x240 怎么保持原视频的宽高比例rect.w = win_width;rect.h = win_height;
//            rect.w = video_width * 0.5;
//            rect.h = video_height * 0.5;// 清除当前显示SDL_RenderClear(renderer);// 将纹理的数据拷贝给渲染器SDL_RenderCopy(renderer, texture, NULL, &rect);// 显示SDL_RenderPresent(renderer);}else if(event.type == SDL_WINDOWEVENT){printf(" win_width:%d, win_height:%d newratio:%d \n", win_width, win_height);//If ResizeSDL_GetWindowSize(window, &win_width, &win_height);float new_ratio = (float)win_width / (float)win_height;// 保持原有的宽高比if (new_ratio > video_ratio){// 新窗口更宽, 以高度为基准计算宽度win_height = win_width / video_ratio;}else{// 新窗口更高, 以宽度为基准计算高度win_width =  win_height * video_ratio;}printf("SDL_WINDOWEVENT win_width:%d, win_height:%d newratio:%d \n", win_width, win_height,new_ratio);}else if(event.type == SDL_QUIT) //退出事件{s_thread_exit = 1;}else if(event.type == QUIT_EVENT){break;}}_FAIL:s_thread_exit = 1;      // 保证线程能够退出// 释放资源if(timer_thread)SDL_WaitThread(timer_thread, NULL); // 等待线程退出if(video_buf)free(video_buf);if(video_fd)fclose(video_fd);if(texture)SDL_DestroyTexture(texture);if(renderer)SDL_DestroyRenderer(renderer);if(window)SDL_DestroyWindow(window);SDL_Quit();return 0;}

 略有小成 播放pcm声音

测试的PCM数据采用采样率44.1k, 采用精度S16SYS, 通道数2
1.提取PCM文件
ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm2.打开音频设备desired:期望的参数。 obtained:实际音频设备的参数,一般情况下设置为NULL即可。
int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained);typedef struct SDL_AudioSpec {int freq; // 音频采样率SDL_AudioFormat format; // 音频数据格式Uint8 channels; // 声道数: 1 单声道, 2 立体声Uint8 silence; // 设置静音的值,因为声音采样是有符号的,所以0当然就是这个值Uint16 samples; // 音频缓冲区中的采样个数, 要求必须是2的n次Uint16 padding; // 考虑到兼容性的一个参数Uint32 size; // 音频缓冲区的大小,以字节为单位SDL_AudioCallback callback; // 填充音频缓冲区的回调函数  44100 / 1024 = 43次/s void *userdata; // 用户自定义的数据
} SDL_AudioSpec;帧率计算公示 :ar * ac * nbit采样点
以1024个采样点一帧 2通道 16bit采样点为例 (1024*2*2*2) 4096字节  20s 3528004 
计算帧率:
总采样点数 = 3,528,004
每帧 4096 byte
所以总帧数 = 3,528,004 / 4096 = 862
视频时长 20s
帧率 = 862 / 20 = 43.1 Hz 差不多对应上了44hz2.1音频缓冲区的回调函数
void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);
userdata:SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用。
stream:该指针指向需要填充的音频缓冲区。
len:音频缓冲区的大小(以字节为单位)1024*2*2。3.播放音频数据
当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值。
void SDLCALL SDL_PauseAudio(int pause_on)4.销毁动作,避开原则。具体看代码

#include <stdio.h>
#include <SDL.h>// 每次读取2帧数据, 以1024个采样点一帧 2通道 16bit采样点为例
#define PCM_BUFFER_SIZE (1024*2*2*2)// 音频PCM数据缓存
static Uint8 *s_audio_buf = NULL;
// 目前读取的位置
static Uint8 *s_audio_pos = NULL;
// 缓存结束位置
static Uint8 *s_audio_end = NULL;//音频设备回调函数
void fill_audio_pcm(void *udata, Uint8 *stream, int len)
{SDL_memset(stream, 0, len);if(s_audio_pos >= s_audio_end) // 数据读取完毕{return;}// 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少)int remain_buffer_len = s_audio_end - s_audio_pos;len = (len < remain_buffer_len) ? len : remain_buffer_len;// 拷贝数据到stream并调整音量SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME/8);printf("len = %d\n", len);s_audio_pos += len;  // 移动缓存指针
}// 提取PCM文件
// ffmpeg -i input.mp4 -t 20 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
// 测试PCM文件
// ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm
#undef main
int main(int argc, char *argv[])
{int ret = -1;const char *path = "44100_16bit_2ch.pcm";// 每次缓存的长度size_t read_buffer_len = 0;//SDL initializeif(SDL_Init(SDL_INIT_AUDIO))    // 支持AUDIO{fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());return ret;}FILE *audio_fd = NULL;//打开PCM文件audio_fd = fopen(path, "rb");if(!audio_fd){fprintf(stderr, "Failed to open pcm file!\n");goto _FAIL;}SDL_AudioSpec spec;s_audio_buf = (uint8_t *)malloc(PCM_BUFFER_SIZE);// 音频参数设置SDL_AudioSpecspec.freq = 44100;          // 采样频率spec.format = AUDIO_S16SYS; // 采样点格式spec.channels = 2;          // 2通道spec.silence = 0;spec.samples = 1024;       // 44100 / 1024 = 43次/s  每次读取的采样数量,多久产生一次回调和 samplesspec.callback = fill_audio_pcm; // 回调函数spec.userdata = NULL;//打开音频设备if(SDL_OpenAudio(&spec, NULL)){fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());goto _FAIL;}//play audioSDL_PauseAudio(0);int data_count = 0;while(1){// 从文件读取PCM数据read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd);if(read_buffer_len == 0){break;}data_count += read_buffer_len; // 统计读取的数据总字节数printf("now playing %10d bytes data.\n",data_count);s_audio_end = s_audio_buf + read_buffer_len;    // 更新buffer的结束位置s_audio_pos = s_audio_buf;  // 更新buffer的起始位置//the main thread wait for a momentwhile(s_audio_pos < s_audio_end){SDL_Delay(10);  // 等待PCM数据消耗}}printf("play PCM finish\n");// 关闭音频设备SDL_CloseAudio();_FAIL://release some resourcesif(s_audio_buf)free(s_audio_buf);if(audio_fd)fclose(audio_fd);//quit SDLSDL_Quit();return 0;
}

 总结

 SDL 提供了丰富的多媒体开发功能,是学习音视频开发的一个很好的辅助工具。

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

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

相关文章

HashMap中 put()方法的流程、扩容的思路(源码分析~)

文章目录 put() 方法的流程扩容流程为什么它会按照2的幂次方进行扩容呢&#xff1f; put() 方法的流程 下面我们通过分析源码来总结一下 put() 方法的流程 扩容流程 根据上图的分析&#xff0c;就可以总结出 HashMap 的扩容流程&#xff1a; 在插入元素时&#xff0c;会先…

C# 设计模式之原型模式

总目录 前言 在软件系统中&#xff0c;当创建一个类的实例的过程很昂贵或很复杂&#xff0c;并且我们需要创建多个这样类的实例时&#xff0c;如果我们用new操作符去创建这样的类实例&#xff0c;这未免会增加创建类的复杂度和耗费更多的内存空间&#xff0c;因为这样在内存中…

复现一下最近学习的漏洞(sqlab 1-10)

第一个问题&#xff1a;为什么不能用#来闭合单引号呢&#xff1f; 在进行URL地址栏传参的时候&#xff0c;是有一套编码规范的。他不会编码英文、数字和某些符号。但是#它会进行编码。也就是%23。&#xff08;先转ascii码&#xff0c;然后再转十六进制&#xff0c;之后加上%就是…

软甲测试定义和分类

软件测试定义 使用人工和自动手段来运行或测试某个系统的过程&#xff0c;其目的在于检验他是否满足规定的需求或弄清预期结果与实际结果之间的差别 软件测试目的 为了发现程序存在的代码或业务逻辑错误 – 第一优先级发现错误为了检验产品是否符合用户需求 – 跟用户要求实…

函数实例讲解(四)

文章目录 提取不重复值&#xff08;INDEX、MATCH、COUNTIF&#xff09;1、INDEX2、MATCH3、COUNTIF 提取不重复的值的经典套路&#xff08;LARGE、SMALL、ROW&#xff09;1、ROW2、LARGE3、SMALL&#xff09; 制作Excel动态查询表四舍五入函数(ROUND、ROUNDUP、ROUNDDOWN&#…

20240806 每日AI必读资讯

英伟达最强AI芯片曝重大设计缺陷&#xff0c;中国特供版意外曝光&#xff01; - 由于Blackwell GPU的设计缺陷&#xff0c;英伟达发货时间不得不推迟3个月 - GB200包含了2个Blackwell GPU和1个Grace CPU。问题出在连接2个Blackwell GPU的关键电路 - 意味着对于Meta、谷歌、微…

AI在医学领域:医学成像中针对深度神经网络(DNN)的对抗性攻击及其防御策略

关键词&#xff1a;对抗性攻击、医学图像、深度神经网络、模型安全、鲁棒性 机器学习&#xff08;ML&#xff09;是医学领域快速发展的一个分支&#xff0c;它利用计算机科学和统计学的方法来解决医学问题。众所周知&#xff0c;攻击者可能通过故意为机器学习分类器创建输入来…

VoNR网络架构与网元 IMS终端号码结构(VoLTE和VoNR适用)

目录 1. VoNR网络架构与网元 1.1 VoNR架构 vs VoLTE架构 1.2 回顾语音网络的演进与“分离” 1.3 TS23.228给出的5G的VoNR国际漫游 Home Routed 方案架构图 1.4 VoNR 网络架构图&#xff08;2022版&#xff09; 1.5 IMS 网元分类&#xff08;VoNR VoLTE 适用&#xff09…

LlamaIndex 实现 React Agent

React Agent 是指 LLM 对问题自行推理并调用外部工具解决问题&#xff0c;如下图所示&#xff0c;通过一些推理步骤最终找到想要的答案。 LlamaIndex 提供了实现 React Agent 的框架&#xff0c;通过框架可以轻松的实现上图中的步骤。那么&#xff0c;如果不用 LlamaIndex 应该…

【精选】6款一键生成论文的软件3000字论文网站

千笔-AIPassPaPer是一款功能强大且全面的AI论文写作工具&#xff0c;特别适合学术研究者和学生使用。它不仅能够一键生成高质量的论文初稿&#xff0c;还涵盖了700多个学科专业方向&#xff0c;满足各种学术需求。 一、千笔-AIPassPaPer 传送门&#xff1a;https://www.aipape…

汇昌联信数字做拼多多运营怎么入行?

拼多多作为中国领先的电商平台之一&#xff0c;近年来在数字运营领域展现出了强大的生命力和创新能力。汇昌联信数字作为一个潜在的新入行者&#xff0c;如何进入拼多多的运营领域&#xff0c;成为业界关注的焦点。本文旨在探讨汇昌联信数字如何通过有效的策略和方法&#xff0…

AttributeError: ‘ChatGLMTokenizer‘ object has no attribute ‘sp_tokenizer‘. 已解决

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 ☁️运维工程师的职责&#xff1a;监…

3GPP入门

官网地址 3GPP – The Mobile Broadband Standard 协议下载链接 Directory Listing /ftp/specs/archive 总纲 重点series Signalling protocols ("stage 3") - user equipment to network24 series信令Radio aspects25 series3G 基础LTE (Evolved UTRA), LTE-Adva…

锂电池生产工艺数字化的业务架构.pptx

搜索《方案驿站》公众号进行下载。

【AI落地应用实战】Amazon Bedrock + Amazon DynamoDB 数据设计与建模

一、Amazon DynamoDB简介 在当今数字化转型的浪潮中&#xff0c;企业对数据处理能力的需求日益增长&#xff0c;为了应对大规模数据和高并发访问的挑战&#xff0c;选择一款合适的数据库解决方案变得尤为重要。 Amazon DynamoDB&#xff0c;作为亚马逊云科技提供的一种完全托…

3.表的操作

目录 创建表 创建表案例&#xff1a; 查看表结构 修改表 1.增加新列 2.修改列的属性 3.删除列 4.修改表名 5.修改列 删除表 创建表 语法&#xff1a; CREATE TABLE [IF NOT EXISTS] table_name(field1 datatype1 [COMMENT 注释信息],field2 datatype2 [COMMENT 注释…

k8s(六)---pod

六、pod&#xff08;k8s中最小的调度单元&#xff09; pod中可以有一个或多个容器 1、官网 2、简介 Pod是k8s中最小的调度单元、Pod具有命名空间隔离性 3、如何创建一个Pod资源&#xff08;主要两种方式&#xff09; 1&#xff09;kubctl run ①kubectl run nginx–imagereg…

【vulnhub】DC-2靶机

信息收集 靶机扫描 nmap 192.168.93.1/24 端口扫描 网页访问 发现访问不到&#xff0c;根据显示考虑IP未遵循重定向到域名 在本机的C:\Windows\System32\drivers\etc 修改hosts⽂件&#xff0c;添加192.168.93.136 dc-2 再次进行访问&#xff0c;可以访问到 点击flag&#x…

测试GPT4o分析巴黎奥运会奖牌数据

使用GPT4o快速调用python代码&#xff0c;生成数据图表 测试GPT4o分析巴黎奥运会奖牌数据 测试GPT4o分析巴黎奥运会奖牌数据 1.首先我们让他给我们生成下当前奥运奖牌数 2.然后我们直接让GPT帮我们运行python代码&#xff0c;并生成奥运会奖牌图表 3.我们还可以让他帮我们…

数组——对数组进行更加全面的理解

1.数组的概念 数组是一组相同类型元素的集合。数组可分为一维数组和多维数组&#xff0c;多维数组常见的是二维数组。 2.一维数组的创建和初始化 2.1 数组的创建 一维数组的创建的基本语法是&#xff1a; type arr_name[常量值] 例如&#xff0c;我们现在想要存储某个班级…