ffmpeg与sdl的个人笔记

说明

这里的ffmpeg基础知识和sdl基础知识仅提及与示例代码相关的知识点, 进阶可学习雷神的博客。
https://blog.csdn.net/leixiaohua1020
当然,如代码写的有问题或有更好的见解,欢迎指正!

音视频基础知识

在学习音视频理论知识时,可能会有一些乏味,笔者也是如此,但对于基本原理至少得留个印象

音视频录制原理

在这里插入图片描述

音视频播放原理

在这里插入图片描述

图像表示
  • RGB: red/green/blue,每个像素由8个bit组成
  • YUV: Y:亮度 U/V: 色度
  • YUV格式:有两大类:planar和packed。
    • 对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
    • 对于packed的YUV格式,每个像素点的Y,U,V是连续交叉存储的。
视频基本概念
  • 视频码率:kb/s,是指视频文件在单位时间内使用的数据流量,也叫码流率。码率越大,说明单位时间内取样率越大,数据流精度就越高。
  • 视频帧率:fps,通常说一个视频的25帧,指的就是这个视频帧率,即1秒中会显示25帧。帧率越高,给人的视觉就越流畅。
  • 视频分辨率:分辨率就是我们常说的640x480分辨率、1920x1080分辨率,分辨率影响视频图像的大小。
视频重要概念(I/P/B帧)

I 帧(Intra coded frames):I帧不需要参考其他画面而生成,解码时仅靠自己就重构完整图像;
I帧图像采用帧内编码方式;
I帧所占数据的信息量比较大;
I帧图像是周期性出现在图像序列中的,出现频率可由编码器选择;
I帧是P帧和B帧的参考帧(其质量直接影响到同组中以后各帧的质量);
I帧是帧组GOP的基础帧(第一帧),在一组中只有一个I帧;
I帧不需要考虑运动矢量;

P 帧(Predicted frames):根据本帧与相邻的前一帧(I帧或P帧)的不同点来压缩本帧数据,同时利用了空间和时间上的相关性。
P帧属于前向预测的帧间编码。它需要参考前面最靠近它的I帧或P帧来解码。

B 帧(Bi-directional predicted frames):B 帧图像采用双向时间预测,可以大大提高压缩倍数。

音频常见名词
  • 采样频率:每秒钟采样的点的个数。常用的采样频率有:
    22000(22kHz): 无线广播。
    44100(44.1kHz):CD音质。
    48000(48kHz): 数字电视,DVD。
    96000(96kHz): 蓝光,高清DVD。
    192000(192kHz): 蓝光,高清DVD。

  • 采样精度(采样深度):每个“样本点”的大小,
    常用的大小为8bit, 16bit,24bit。

  • 通道数:单声道,双声道,四声道,5.1声道。

  • 比特率:每秒传输的bit数,单位为:bps(Bit Per Second)
    间接衡量声音质量的一个标准。

  • 没有压缩的音频数据的比特率 = 采样频率 * 采样精度 * 通道数。

  • 码率: 压缩后的音频数据的比特率。常见的码率:
    96kbps: FM质量
    128-160kbps:一般质量音频。
    192kbps: CD质量。
    256-320Kbps:高质量音频
    码率越大,压缩效率越低,音质越好,压缩后数据越大。
    码率 = 音频文件大小/时长。

  • 帧:每次编码的采样单元数,比如MP3通常是1152个采样点作为一个编码单元,AAC通常是1024个采样点作为一个编码单元。

  • 帧长:可以指每帧播放持续的时间:每帧持续时间(秒) = 每帧采样点数 / 采样频率(HZ)
    比如:MP3 48k, 1152个采样点,每帧则为 24毫秒
    1152/48000= 0.024 秒 = 24毫秒;
    也可以指压缩后每帧的数据长度。

  • 交错模式:数字音频信号存储的方式。数据以连续帧的方式存放,即首先记录帧1的左声道样本和右声道样本,再开始帧2的记录…

  • 非交错模式:首先记录的是一个周期内所有帧的左声道样本,再记录所有右声道样本

常见的视频封装格式

AVI、MKV、MPE、MPG、MPEG
MP4、WMV、MOV、3GP
M2V、M1V、M4V、OGM
RM、RMS、RMM、RMVB、IFO
SWF、FLV、F4V、
ASF、PMF、XMB、DIVX、PART
DAT、VOB、M2TS、TS、PS

音视频同步

基本概念

  • DTS(Decoding Time Stamp):即解码时间戳,这个时间戳的意义在于告诉播放器该在什么时候解码这一帧的数据。
  • PTS(Presentation Time Stamp):即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。

同步方式

  • Audio Master:同步视频到音频
  • Video Master:同步音频到视频
  • External Clock Master:同步音频和视频到外部时钟

ffmpeg 基础知识

ffmpeg封装格式相关函数

◼ avformat_alloc_context();负责申请一个AVFormatContext 结构的内存,并进行简单初始化
◼ avformat_free_context();释放该结构里的所有东西以及该结构本身
◼ avformat_close_input();关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
◼ avformat_open_input();打开输入视频文件
◼ avformat_find_stream_info():获取视频文件信息
◼ av_read_frame(); 读取音视频包
◼ avformat_seek_file(); 定位文件
◼ av_seek_frame():定位文件

解码器相关函数

• avcodec_alloc_context3(): 分配解码器上下文
• avcodec_find_decoder():根据ID查找解码器
• avcodec_find_decoder_by_name():根据解码器名字
• avcodec_open2(): 打开编解码器
• avcodec_decode_video2():解码一帧视频数据
• avcodec_decode_audio4():解码一帧音频数据
• avcodec_send_packet(): 发送编码数据包
• avcodec_receive_frame(): 接收解码后数据
• avcodec_free_context():释放解码器上下文,包含了avcodec_close()
• avcodec_close():关闭解码器

ffmpeg数据结构简介

AVFormatContext: 封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息。
AVInputFormat demuxer每种封装格式(例如FLV, MKV, MP4, AVI)对应一个该结构体。
AVOutputFormat muxer
AVStream 视频文件中每个视频(音频)流对应一个该结构体。
AVCodecContext 编解码器上下文结构体,保存了视频(音频)编解码相关信息。
AVCodec 每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。
AVPacket 存储一帧压缩编码数据。
AVFrame 存储一帧解码后像素(采样)数据。

AVPacket和AVFrame的关系

在这里插入图片描述

ffmpeg数据结构分析
  • AVFormatContext
    • iformat:输入媒体的AVInputFormat,比如指向AVInputFormat 中 ff_flv_demuxer
    • nb_streams:输入媒体的AVStream 个数
    • streams:输入媒体的AVStream []数组
    • duration:输入媒体的时长(以微秒为单位),计算方式可以参考 av_dump_format()函数。
    • bit_rate:输入媒体的码率
  • AVInputFormat
    • name:封装格式名称
    • extensions:封装格式的扩展名
    • id:封装格式ID
    • 一些封装格式处理的接口函数,比如read_packet()
  • AVStream
    • index:标识该视频/音频流
    • time_base:该流的时基,PTS*time_base=真正的时间(秒)
    • avg_frame_rate: 该流的帧率
    • duration:该视频/音频流长度
    • codecpar:编解码器参数属性
  • AVCodecParameters
    • codec_type:媒体类型AVMEDIA_TYPE_VIDEO/AVMEDIA_TYPE_AUDIO等
    • codec_id:编解码器类型, AV_CODEC_ID_H264/AV_CODEC_ID_AAC等。
  • AVCodecContext
    • codec:编解码器的AVCodec,比如指向AVCodec 中 ff_aac_latm_decoder
    • width, height:图像的宽高(只针对视频)
    • pix_fmt:像素格式(只针对视频)
    • sample_rate:采样率(只针对音频)
    • channels:声道数(只针对音频)
    • sample_fmt:采样格式(只针对音频)
  • AVCodec
    • name:编解码器名称
    • type:编解码器类型
    • id:编解码器ID
    • 一些编解码的接口函数,比如int (*decode)()

下载ffmpeg

  • 官网: https://ffmpeg.org/

ffmpeg 解码 ts 视频文件得到 yuv 视频文件 程序

环境配置
  1. 创建空项目
    在这里插入图片描述

  2. 填写项目名(大家随意)

  3. 新建一个main.cpp文件

  4. 拷贝ffmpeg到项目路径下
    在这里插入图片描述

  5. 将 ffmpeg-4.2/bin 下的 dll 文件拷贝到项目路径下(即源代码所在目录)
    在这里插入图片描述

  6. 选中项目名,右键选择属性,依次进行如下配置
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

测试代码

#include <stdio.h>extern "C"      //因为ffmpeg是C语言写的,而我们建的是cpp文件.
{
#include "libavformat/avformat.h"
}int main() {const char *p = av_version_info();  //获取ffmpeg版本信息printf("FFmpeg Version : %s ", p);  //打印输出return 0;
}
ffmpeg 解码 ts 获取 yuv
#pragma warning(disable:4996)#include <stdio.h>extern "C" 
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}int main(int argc, char* argv[]) 
{/* 初始化 */AVFormatContext* pFormatContext = NULL; //格式上下文const char* fileName = "believe.ts";    //文件地址int videoIndex = -1;    //视频流索引号int i = 0;  //循环变量AVCodecContext *pCodecContext = NULL;   //编解码上下文AVCodec* pCodec = NULL;     //编解码器AVPacket* pkt = NULL;   //解码前的一帧数据AVFrame* frame = NULL;  //解码后的一帧数据int ret = 0;    //存放avcodec_decode_video2的返回值int gotPicture = 0; //作为avcodec_decode_video2的一个参数av_register_all();      //注册所有组件pFormatContext = avformat_alloc_context();  //分配格式上下文空间/* avformat_open_input返回0表示成功 */if (avformat_open_input(&pFormatContext, fileName, NULL, NULL) != 0){printf("Can't open input %s", fileName);return -1;}/* avformat_find_stream_info返回值 >= 0 表示成功 */if (avformat_find_stream_info(pFormatContext, NULL) < 0){printf("Can't find stream info of %s", fileName);return -1;}/* 寻找视频流 */for (i = 0; i < pFormatContext->nb_streams; i++){/* 判断是否为视频流 */if (pFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){videoIndex = i;break;}}/* 判断是否找到视频流 */if (videoIndex == -1){printf("Can't find video stream !");return -1;}pCodecContext = pFormatContext->streams[i]->codec;      //获取编解码上下文pCodec = avcodec_find_decoder(pCodecContext->codec_id); //寻找解码器,未找到时返回NULL/* 判断pCodec是否为NULL */if (pCodec == NULL){printf("Can't find decoder !");return -1;}/* 打开解码器,avcodec_open2返回 0 表示成功 */if (avcodec_open2(pCodecContext, pCodec, NULL) != 0){printf("Can't open decoder !");return -1;}/* 分配空间并初始化 */pkt = av_packet_alloc();    av_new_packet(pkt, pCodecContext->width * pCodecContext->height);frame = av_frame_alloc();/* 将ts文件改写为h264文件 */FILE* fp_h264 = fopen("test.h264", "wb");/* 将ts文件解码得到yuv文件 */FILE* fp_yuv = fopen("test.yuv", "wb");/* 循环读帧解码,av_read_frame返回0表示读取成功 */while (av_read_frame(pFormatContext, pkt) == 0){/* 判断是否为视频流(除了视频流可能还有音频流,字幕流) */if (pkt->stream_index == videoIndex){/* 写入h264文件 */fwrite(pkt->data, 1, pkt->size, fp_h264);/* avcodec_decode_video2返回值 < 0 表示解码失败 */ret = avcodec_decode_video2(pCodecContext, frame, &gotPicture, pkt);/* 判断是否解码失败 */if (ret < 0){printf("Can't decode video !");return -1;}/* 写入yuv文件,frame->data[0]为Y分量 frame->data[1]为U分量 frame->data[2]为V分量*/fwrite(frame->data[0], 1, pCodecContext->width * pCodecContext->height, fp_yuv);fwrite(frame->data[1], 1, pCodecContext->width * pCodecContext->height / 4, fp_yuv);fwrite(frame->data[2], 1, pCodecContext->width * pCodecContext->height / 4, fp_yuv);}av_free_packet(pkt);}/* 关闭释放相关资源 */fclose(fp_h264);fclose(fp_yuv);avcodec_close(pCodecContext);avformat_close_input(&pFormatContext);return 0;
}
  • 使用ffplay命令播放yuv文件: ffplay -pixel_format yuv420p -video_size 1920x1080 your_yuv_file.yuv
  • 或者使用yuv播放器

sdl 基础知识

sdl 子系统

◼ SDL_INIT_TIMER:定时器
◼ SDL_INIT_AUDIO:音频
◼ SDL_INIT_VIDEO:视频
◼ SDL_INIT_JOYSTICK:摇杆
◼ SDL_INIT_HAPTIC:触摸屏
◼ SDL_INIT_GAMECONTROLLER:游戏控制器
◼ SDL_INIT_EVENTS:事件
◼ SDL_INIT_EVERYTHING:包含上述所有选项

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 一个简单的矩形结构

SDL事件

◼ 函数

  • SDL_WaitEvent():等待一个事件
  • SDL_PushEvent():发送一个事件
  • SDL_PumpEvents():将硬件设备产生的事件放入事件队列,用于
    读取事件,在调用该函数之前,必须调用SDL_PumpEvents搜集
    键盘等事件
  • SDL_PeepEvents():从事件队列提取一个事件

◼ 数据结构

  • SDL_Event:代表一个事件
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 yuv 数据显示流程

这里借用雷神的sdl流程图
在这里插入图片描述

我们的代码就是围绕这个流程图编写的。

下载sdl

  • 下载地址: http://www.libsdl.org/

sdl 显示 yuv 数据

环境配置
  1. 创建空项目

  2. 新建一个main.cpp文件

  3. 拷贝sdl到项目路径下
    在这里插入图片描述

  4. 将./SDL2-2.0.10/lib/x64/SDL2.dll拷贝到项目路径下(即源代码所在目录)

  5. 选中项目名,右键选择属性,依次进行如下配置

在这里插入图片描述
在这里插入图片描述

测试代码

#include <stdio.h>// 引入SDL头文件
extern "C"
{
#include <SDL.h>
}#undef mainint main() {// 初始化SDLif (SDL_Init(SDL_INIT_VIDEO) < 0) {printf("SDL初始化失败: %s\n", SDL_GetError());return 1;}// 创建窗口SDL_Window* sdlWindow = SDL_CreateWindow("SDL_Test", 100, 100, 800, 600, SDL_WINDOW_SHOWN);if (sdlWindow == nullptr) {printf("窗口创建失败: %s\n", SDL_GetError());return 1;}// 主循环bool quit = false;SDL_Event event;while (!quit) {while (SDL_PollEvent(&event)) {if (event.type == SDL_QUIT) {quit = true;}}}// 销毁窗口SDL_DestroyWindow(sdlWindow);// 退出SDLSDL_Quit();return 0;
}
sdl 显示 yuv 数据 代码
#pragma warning(disable:4996)#include <stdio.h>extern "C"	//cpp文件引用sdl头文件
{
#include "SDL.h"
};const int bpp = 12;	//Y: 8 + U: 2 + V: 2int screen_w = 800, screen_h = 600;	//屏幕的宽和高(可以自由设置)
const int pixel_w = 1920, pixel_h = 1080;	//画面展示的宽和高(根据视频窗口大小设定)unsigned char buffer[pixel_w * pixel_h * bpp / 8];	//一帧画面的缓冲//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)//Break Event
#define BREAK_EVENT  (SDL_USEREVENT + 2)int thread_exit = 0;	//状态控制变量int refresh_video(void* opaque) 
{thread_exit = 0;/* 循环读帧事件 */while (!thread_exit) {SDL_Event event;event.type = REFRESH_EVENT;SDL_PushEvent(&event);	//SDL_PushEvent函数用于将事件推送到事件队列中SDL_Delay(40);	//延时,不要读的太快了}thread_exit = 0;//BreakSDL_Event event;event.type = BREAK_EVENT;SDL_PushEvent(&event);return 0;
}int main(int argc, char* argv[])
{/* 初始化 */if (SDL_Init(SDL_INIT_VIDEO)) {printf("Could not initialize SDL - %s\n", SDL_GetError());return -1;}SDL_Window* screen;/* * SDL_CreateWindow* SDL_WINDOWPOS_UNDEFINED是SDL库中定义的一个常量,用于指定窗口的位置。* 它表示将窗口的位置设置为未定义,即由操作系统决定窗口的位置。* SDL_WINDOW_RESIZABLE: 表示窗口大小可变* SDL_WINDOW_OPENGL: 表示支持opengl* @Parma title: 窗口的标题* @Parma x: 运行窗口距电脑桌面左侧的距离* @Parma y: 运行创建距电脑桌面上方的距离* @Parma w: 窗口的宽度* @Parma h: 窗口的高度* @Parma flags: 一些支持设置* @Return: 创建成功返回窗口,失败返回NULL*/screen = SDL_CreateWindow("My YUV Player", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);/* 判断是否成功创建窗口 */if (screen == NULL) {printf("SDL: could not create window - exiting:%s\n", SDL_GetError());return -1;}SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);/* 判断是否成功创建渲染器 */if (sdlRenderer == NULL){printf("SDL: could not create renderer - exiting:%s\n", SDL_GetError());return -1;}Uint32 pixformat = 0;/*IYUV: Y + U + V(3 planes)* YV12: Y + V + U  (3 planes)* SDL_PIXELFORMAT_IYUV: SDL中用于表示IYUV格式的像素格式常量。IYUV是一种YUV格式,其中Y表示亮度分量,U和V表示色度分量。* 在IYUV格式中,亮度分量Y是按照完整的图像大小进行存储的,而色度分量U和V则是按照图像大小的四分之一进行存储的。*/pixformat = SDL_PIXELFORMAT_IYUV;/** SDL_CreateTexture	创建纹理* SDL_TEXTUREACCESS_STREAMING是SDL2中的一个纹理访问标志,用于指定纹理的访问方式。* 具体来说,SDL_TEXTUREACCESS_STREAMING表示纹理可以通过内存访问进行更新,即可以直接访问纹理的像素数据进行修改。*/SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);/* 判断是否创建成功 */if (sdlTexture == NULL){printf("SDL: no rending context is active");return -1;}/* 打开yuv文件,文件路径自行设置 */FILE* fp = fopen("test.yuv", "rb+");/* 判断是否打开成功 */if (fp == NULL) {printf("can't open this file\n");return -1;}/** SDL_Rect: SDL库中定义的一个矩形结构体,用于表示矩形的位置和大小。* 它包含了四个整型成员变量x、y、w和h,分别表示矩形的左上角顶点的x坐标、y坐标,以及矩形的宽度和高度。*/SDL_Rect sdlRect;/** SDL_CreateThread: SDL库中用于创建线程的函数* 该函数接受五个参数:* fn:线程函数指针,指向要在新线程中执行的函数。* name:线程的名称,用于调试目的。* data:传递给线程函数的数据指针。* pfnBeginThread:指向线程启动函数的指针。* pfnEndThread:指向线程结束函数的指针*/SDL_Thread* refresh_thread = SDL_CreateThread(refresh_video, NULL, NULL);/** SDL_Event: SDL中所有事件处理的核心,它是一个联合体,包含了SDL中使用的所有事件结构的并集。* SDL的所有事件都存储在一个队列中,而SDL_Event的常规操作就是从这个队列中读取事件或者写入事件。*/SDL_Event event;while (1) {/* 等待事件 */SDL_WaitEvent(&event);/* 判断事件类型 */if (event.type == REFRESH_EVENT) {/* 读取一帧yuv数据到buffer中 */while (fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp) != pixel_w * pixel_h * bpp / 8) {// Loopfseek(fp, 0, SEEK_SET);fread(buffer, 1, pixel_w * pixel_h * bpp / 8, fp);}/* SDL_UpdateTexture: SDL库中用于更新纹理数据的函数 */SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_w);//FIX: If window is resizesdlRect.x = 0;sdlRect.y = 0;sdlRect.w = screen_w;sdlRect.h = screen_h;/* SDL_RenderClear函数用于清空渲染器的颜缓冲区,将其填充为指定的颜色 */SDL_RenderClear(sdlRenderer);/* * SDL_RenderCopy: SDL库中用于将纹理数据复制给渲染目标的函数* 该函数接受四个参数:* renderer:渲染器,用于指定渲染目标。* texture:纹理,包含要复制的图像数据。* srcrect:源矩形,指定要复制的纹理区域。* dstrect:目标矩形,指定要将纹理复制到的位置和大小。*/SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);/* SDL_RenderPresent: SDL库中用于显示画面的函数 */SDL_RenderPresent(sdlRenderer);}/* SDL_WINDOWEVENT: SDL中的一个事件类型,用于处理窗口相关的事件 */else if (event.type == SDL_WINDOWEVENT) {//If ResizeSDL_GetWindowSize(screen, &screen_w, &screen_h);}/* 退出事件 */else if (event.type == SDL_QUIT) {thread_exit = 1;	//退出子线程中的循环}/* 当窗口关闭时,退出循环 */else if (event.type == BREAK_EVENT) {break;}}/* SDL_Quit是SDL库中的一个函数,用于退出SDL子系统并释放相关资源。* 调用SDL_Quit函数后,SDL库将关闭所有已打开的子系统,并释放分配的内存。 */SDL_Quit();return 0;
}
定要将纹理复制到的位置和大小。*/SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect);/* SDL_RenderPresent: SDL库中用于显示画面的函数 */SDL_RenderPresent(sdlRenderer);}/* SDL_WINDOWEVENT: SDL中的一个事件类型,用于处理窗口相关的事件 */else if (event.type == SDL_WINDOWEVENT) {//If ResizeSDL_GetWindowSize(screen, &screen_w, &screen_h);}/* 退出事件 */else if (event.type == SDL_QUIT) {thread_exit = 1;	//退出子线程中的循环}/* 当窗口关闭时,退出循环 */else if (event.type == BREAK_EVENT) {break;}}/* SDL_Quit是SDL库中的一个函数,用于退出SDL子系统并释放相关资源。* 调用SDL_Quit函数后,SDL库将关闭所有已打开的子系统,并释放分配的内存。 */SDL_Quit();return 0;
}

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

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

相关文章

德思特车载天线方案:打造智能互联的公共安全交通网络

作者介绍 一、方案介绍 随着自动驾驶与智慧汽车概念的逐步推进&#xff0c;人们对汽车的交互性、智能性、互联性有了更高的要求。今天&#xff0c;大多数汽车制造商和供应商普遍将GNSS定位功能与其他信号如广播、电视、蓝牙、Wifi一起集成到汽车中&#xff0c;包括博世、大陆、…

LabVIEW学习记录2 - MySQL数据库连接与操作

LabVIEW学习记录2 - MySQL数据库连接与操作 一、前期准备1.1 windows下安装MySQL的ODBC驱动 二、LabVIEW创建MySQL 的UDL文件三、LabVIEW使用UDL文件进行MySQL数据库操作3.1 建立与数据库的连接&#xff1a;DB Tools Open Connection.vi3.2 断开与数据库的连接&#xff1a;DB T…

【C语言】贪吃蛇详解(附源码)

一、贪吃蛇实现效果 【C语言】贪吃蛇&#xff08;控制台&#xff09; 二、源码 &#x1f388;&#x1f388;&#x1f388;Snake 残风也想永存/C语言项目 - 码云 - 开源中国 (gitee.com)&#x1f388;&#x1f388;&#x1f388; 三、如何使用C语言去实现一个贪吃蛇&#xff1f…

【04-提升模型性能:集成学习与超参数优化】

文章目录 前言集成学习BaggingBoosting超参数优化随机搜索贝叶斯优化总结前言 在前几篇博文中,我们已经介绍了一些机器学习的基础知识、Scikit-learn的核心工具与技巧,以及如何评估模型性能。本篇博文将重点讲解两个可以显著提升机器学习模型性能的高级策略:集成学习和超参数…

【机器学习】集成学习---投票法(Voting)

一、引言 集成学习&#xff08;Ensemble Learning&#xff09;是机器学习领域中的一种重要策略&#xff0c;它通过结合多个模型的预测结果来提高整体性能。在单个模型容易过拟合或欠拟合的情况下&#xff0c;集成学习能够通过综合多个模型的优点来减少这种风险&#xff0c;从而…

【北京迅为】《iTOP龙芯2K1000开发指南》-第三部分 迅为龙芯开发板快速体验

龙芯2K1000处理器集成2个64位GS264处理器核&#xff0c;主频1GHz&#xff0c;以及各种系统IO接口&#xff0c;集高性能与高配置于一身。支持4G模块、GPS模块、千兆以太网、16GB固态硬盘、双路UART、四路USB、WIFI蓝牙二合一模块、MiniPCIE等接口、双路CAN总线、RS485总线&#…

使用navicate演示在 PostgreSQL 中使用 for 循环语句

1、简单循环示例 do $$ beginfor cnt in 1..10 loopraise notice cnt: %, cnt;end loop; end; $$ navicate中执行 2、循环查询 do $$ declare_record record; beginfor _record in (SELECT version,description FROM flyway_schema_history ORDER BY installed_rank desc li…

JavaScript:js实现在线五子棋人机(人人)对弈

在线五子棋人机对弈 全部使用前端技术,使用HTML,CSS以及JS进行实现. 棋盘在后端就是一个15*15的二维数组 页面设计 页面设计的比较粗糙 主要使用js自带的canvas画布进行绘画 HTML代码如下: <div class"outer"><canvas id"canvas" height&qu…

软件设计师-基础知识科目-计算题汇总1

1. 位运算&#xff1a; 略 2. 进制转换&#xff1a; 3. 计算内存存储容量&#xff1a; 前置概念&#xff1a;** 内存地址是16进制。 16进制后面的加上的字母H&#xff0c;表示数字为16进制。内存地址编址的单位是Byte&#xff0c;1K 1024B&#xff0c;1024用16进制表示为400H …

C语言 流程图与伪代码 缩减

本文 我们来说说流程图 伪代码和代码缩进 这些可以让我们在后面书写复杂逻辑时 不会感到 繁琐或逻辑混乱 流程图(Flowchart) 是用以算法、工作流或流程的一种框图表示&#xff0c;它以不同类型的框代表不同种类的步骤&#xff0c;每两个步骤之间则以箭头连接。 流程图是程序…

【UE C++】打印输出的两种方式

目录 一、UE_LOG 二、调试屏幕信息 一、UE_LOG 定义&#xff1a; UE_LOG 是一个将格式化消息记录到日志文件中的宏。 用法&#xff1a; UE_LOG(LogTemp, Warning, TEXT("Hello World")); 第一个输入参数 LogTemp 是提供给 DEFINE_LOG_CATEGORY 宏的类别名称。你…

OpenAIGPT-4.5提前曝光?

OpenAI GPT-4.5的神秘面纱&#xff1a;科技界的震撼新篇章 在人工智能的世界里&#xff0c;每一次技术的飞跃都不仅仅是一次更新&#xff0c;而是对未来无限可能的探索。近日&#xff0c;科技巨头OpenAI似乎再次站在了这场革命的前沿&#xff0c;其潜在的新产品——GPT-4.5 Tur…

使用gdb调试遇到No symbol table is loaded. Use the “file“ command.怎么办?

问题排查 出现下面问题&#xff0c;通常是没有处于调式模式环境下&#xff0c;所以我们需要在gcc指令后加 【-g】。 因为&#xff0c;我么的gcc编辑器默认是动态链接&#xff0c;而且是realese发布版本。 想要解决也很简单 主要思路就是在gcc -g。 在makefile文件如下进行修改即…

Unity | 集成 Protobuf(proto 转 cs 插件及序列化与反序列化)

1. 添加 dll 1. 下载 protobuf 源码 根据需要下载 protobuf 指定版本的源码&#xff0c;这里以 v3.21.12&#xff08;protobuf-csharp-3.21.12.zip&#xff09;为例&#xff1a; 下载地址&#xff1a;「https://github.com/protocolbuffers/protobuf/releases」 2. 下载 Vis…

常见内网代理工具及其应用

常见内网代理工具 1. 正向代理&#xff1a;Squid原理示例 2. 反向代理&#xff1a;Nginx原理示例 3. SOCKS代理&#xff1a;Shadowsocks原理示例 零基础入门学习路线视频配套资料&国内外网安书籍、文档网络安全面试题 常见的内网代理工具包括多种类型&#xff0c;如正向代理…

自从用上电路仿真软件后,我才领悟到的4大奥秘

在电子工程领域&#xff0c;电路仿真软件已成为设计师们不可或缺的得力助手。自从我开始使用这款软件&#xff0c;它不仅极大提升了我的工作效率&#xff0c;还让我领悟到了许多前所未有的知识。今天&#xff0c;就让我来分享自从用上电路仿真软件后&#xff0c;我才知道的4件事…

【分享】WinRAR软件如何压缩文件?

WinRAR是一款功能强大的压缩文件管理工具&#xff0c;支持多种压缩文件格式&#xff0c;那如何使用WinRAR来压缩文件呢&#xff1f;不清楚的小伙伴一起来看看吧&#xff01; 压缩方法&#xff1a; 首先&#xff0c;安装好WinRAR工具&#xff0c;然后选中需要压缩的文件或文件夹…

OSPF的学习笔记

1.OSPF &#xff08;1&#xff09;链路状态路由协议的路由信息并不是像距离矢量路由协议那样(邻居告诉的)&#xff0c;通过收集自身以及邻居发出的LSA(原材料)&#xff0c;并LSA放到指定仓库里面(LSDB)&#xff0c;通过SPF算法&#xff0c;以自己为根计算到达网络每个节点的最优…

手机图片制作动态gif怎么做?不能错过的好用网站!

现在手机可是人人不离手的&#xff0c;而且手机上的很多软件功能已经逐渐取代了电脑。有时候我们想要把手机中的照片变成有动态效果的gif动图还不想下载软件的时候&#xff0c;就可以用Gif在线制作网站&#xff0c;不用下载软件&#xff0c;手机电脑都能在线操作。非常的方便小…

小孩近视用白炽灯好吗?分享多款央视推荐的护眼台灯

很多家长关心&#xff0c;小孩近视用白炽灯好吗&#xff1f;首先肯定是不太建议的&#xff0c;虽说白炽灯价格便宜&#xff0c;显色较好&#xff0c;不过光线会比较集中刺眼&#xff0c;而且由于是发热发光&#xff0c;也存在一定的安全隐患&#xff0c;更重要的是光衰现象会比…