广州做礼物的网站/好用的种子搜索引擎

广州做礼物的网站,好用的种子搜索引擎,西安培训网站建设,房地产公司网站模板本次的DEMO是利用FFMPEG框架把H264文件和AAC文件合并成一个TS文件。这个DEMO很重要,因为在后面的推流项目中用到了这方面的技术。所以,大家最好把这个项目好好了解。 下面这个是流程图 从这个图我们能看出来,在main函数中我们主要做了这几步&…

        本次的DEMO是利用FFMPEG框架把H264文件和AAC文件合并成一个TS文件。这个DEMO很重要,因为在后面的推流项目中用到了这方面的技术。所以,大家最好把这个项目好好了解。

        下面这个是流程图

        从这个图我们能看出来,在main函数中我们主要做了这几步: 

        1.创建read_video_file_thread(读取视频文件线程)。

        2. 创建read_audio_file_thread(读取音频文件线程)。

        3.初始化音视频队列,初始化ffmpeg的 H264,aac合成模块的容器。

        4.创建muxer_ts_thread线程进行H264文件和aac文件的合成ts文件。

 每一个代码模块的讲解:

        初始化视频队列、音频队列、FFMPEG合成容器。

        在代码第一步就需要把所有东西给初始化,包括队列、FFMPEG输出容器。

int main
{// 为音频和视频队列对象分配内存video_queue = new VIDEO_QUEUE();audio_queue = new AUDIO_QUEUE();// 为ffmpeg_group结构体分配内存FFMPEG_CONFIG *ffmpeg_config = (FFMPEG_CONFIG *)malloc(sizeof(FFMPEG_CONFIG));// 判断是否分配成功,如果不成功就退出if (ffmpeg_config == NULL){printf("malloc ffmpeg_group failed\n");return -1;}// 初始化ffmpeg_group结构体相关成员ffmpeg_config->config_id = 0; // 流媒体idffmpeg_config->network_type = TS_FILE;ffmpeg_config->video_codec = AV_CODEC_ID_H264;                     // 视频编码格式为h264ffmpeg_config->audio_codec = AV_CODEC_ID_AAC;                      // 音频编码格式为AACmemcpy(ffmpeg_config->network_addr, "test.ts", strlen("test.ts")); // 进行流媒体地址字符赋值,这是写到本地文件// 初始化流媒体格式上下文init_ffmpeg_config_params(ffmpeg_config);}

         FFMPEG合成容器,这部分非常重要,只有做好这个一步,接下来的工作才有意义,因为里面配置了生产什么的复合流是ts还是flv等,要往哪里推流......。

 init_ffmpeg_config_params

        初始化推流器

int init_ffmpeg_config_params(FFMPEG_CONFIG *ffmpeg_config)
{AVOutputFormat *fmt = NULL; // 音视频输出格式AVCodec *audio_codec = NULL; // 音频编码器AVCodec *video_codec = NULL; // 视频编码器int ret = 0;//第二步:创建一个用于输出的容器对象/*** 为什么?* 推流需要将音视频数据封装成某种格式(比如 FLV 是 RTMP 的常用容器格式),* 这个上下文对象管理流的元数据、格式信息和输出目标。* 如果没有这个对象,FFmpeg 无法知道数据要以什么形式输出到哪里。* * 原理: * 输出格式上下文是 FFmpeg 的核心数据结构,负责协调编码、封装和写入操作。它相当于推流的“总指挥”。*/// 流媒体类型判断并分配输出上下文if (ffmpeg_config->network_type == FLV_FILE){// 分配FLV格式的AVFormatContextret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "flv", ffmpeg_config->network_addr);if (ret < 0){return -1;}}else if (ffmpeg_config->network_type == TS_FILE){// 分配TS格式的AVFormatContextret = avformat_alloc_output_context2(&ffmpeg_config->oc, NULL, "mpegts", ffmpeg_config->network_addr);if (ret < 0){return -1;}}// 音视频输出格式上下文句柄fmt = ffmpeg_config->oc->oformat;/* 指定编码器 */fmt->video_codec = ffmpeg_config->video_codec;fmt->audio_codec = ffmpeg_config->audio_codec;//第三步:创建输出流/*** 作用:* 在输出格式上下文中添加一个流(如视频流或音频流),并为其分配编码参数。* 为什么需要?* 推流通常包含视频和音频两种数据,每种数据需要独立的流来承载。* 创建流是为了定义这些数据的结构(如分辨率、帧率、采样率等),否则 FFmpeg 无法组织数据。* 原理:* 流(AVStream)是容器中的独立轨道,推流时服务器和客户端会根据流信息解码数据。*/// 初始化视频流if (fmt->video_codec != AV_CODEC_ID_NONE){// 创建视频输出流ret = add_stream(&ffmpeg_config->video_stream, ffmpeg_config->oc, &video_codec, fmt->video_codec);if (ret < 0){avcodec_free_context(&ffmpeg_config->video_stream.enc);close_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);avformat_free_context(ffmpeg_config->oc);return -1;}// 打开视频编码器ret = open_video(ffmpeg_config->oc, video_codec, &ffmpeg_config->video_stream, NULL);if (ret < 0){avformat_free_context(ffmpeg_config->oc);return -1;}}// 初始化音频流if (fmt->audio_codec != AV_CODEC_ID_NONE){// 创建音频输出流ret = add_stream(&ffmpeg_config->audio_stream, ffmpeg_config->oc, &audio_codec, fmt->audio_codec);if (ret < 0){avcodec_free_context(&ffmpeg_config->audio_stream.enc);close_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);avformat_free_context(ffmpeg_config->oc);return -1;}// 打开音频编码器ret = open_audio(ffmpeg_config->oc, audio_codec, &ffmpeg_config->audio_stream, NULL);if (ret < 0){avformat_free_context(ffmpeg_config->oc);return -1;}}// 打印输出格式信息av_dump_format(ffmpeg_config->oc, 0, ffmpeg_config->network_addr, 1);// 打开输出文件if (!(fmt->flags & AVFMT_NOFILE)){// 打开输出文件用于建立与rtmp服务器的连接ret = avio_open(&ffmpeg_config->oc->pb, ffmpeg_config->network_addr, AVIO_FLAG_WRITE);if (ret < 0){close_stream(ffmpeg_config->oc, &ffmpeg_config->video_stream);close_stream(ffmpeg_config->oc, &ffmpeg_config->audio_stream);avformat_free_context(ffmpeg_config->oc);return -1;}}// 写入文件头信息//这行代码会根据ffmpeg_config->oc中指定的输出格式(如FLV或TS),//生成并写入相应的头部信息到输出文件或流中。avformat_write_header(ffmpeg_config->oc, NULL);return 0;
}

        ts流合成的流程,是一样ffmpeg的初始化很重要,在合成工作都是根据这个ffmpeg的配置来做的,是和成ts流还是flv,是推动远端还是保存到本地, FFmpeg 的核心数据结构,负责协调编码、封装和写入操作。它相当于推流的“总指挥”。

 


        创建线程 

       初始化所有容器和队列之后,紧接着就需要创建三个线程:分别是read_video_file_thread线程、read_audio_file_thread线程、muxer_ts_thread线程。

int main
{//读取视频文件线程pthread_t pid;int ret = pthread_create(&pid, NULL, read_video_file_thread, NULL);if (ret != 0){printf("Create read_video_file_thread failed...\n");}//读取音频文件线程ret = pthread_create(&pid, NULL, read_audio_file_thread, NULL);if (ret != 0){printf("Create read_video_file_thread failed...\n");}//TS复用线程函数ret = pthread_create(&pid, NULL, muxer_ts_thread, (void *)ffmpeg_config);if (ret != 0){printf("Create muxer_ts_thread failed...\n");}
}

read_video_file_threa

// 读取视频线程
void *read_video_file_thread(void *args)
{const char *input_video_file_name = "test_output.h264";AVFormatContext *ifmt_ctx_v = NULL;int in_stream_index_v = -1;int ret;// 打开输入test_output.h264文件if ((ret = avformat_open_input(&ifmt_ctx_v, input_video_file_name, NULL, NULL)) < 0){av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");}// 这个函数的作用不仅会检索视频的一些信息(宽、高、帧率等),而且会持续的读取和解码一些视频帧和音频帧,读取到的帧会放到缓存中;如果你知道这个媒体流的信息,可以不用这个函数去查找if ((ret = avformat_find_stream_info(ifmt_ctx_v, NULL)) < 0){av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");}ret = av_find_best_stream(ifmt_ctx_v, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);if (ret < 0){av_log(NULL, AV_LOG_ERROR, "Cannot find video stream\n");}in_stream_index_v = ret;av_dump_format(ifmt_ctx_v, 0, input_video_file_name, 0);AVPacket *packet = av_packet_alloc();while (av_read_frame(ifmt_ctx_v, packet) >= 0){if (packet->stream_index == in_stream_index_v){printf("detect video index.....\n");// 自己封装的视频数据包结构体video_data_packet_t分配内存video_data_packet_t *video_data_packet = (video_data_packet_t *)malloc(sizeof(video_data_packet_t));// 把读取到的视频数据拷贝到结构体成员里面的buffer缓冲区里面区memcpy(video_data_packet->buffer, packet->data, packet->size);// 数据帧的大小赋值video_data_packet->video_frame_size = packet->size;// 往视频队列里面写入视频流数据video_queue->putVideoPacketQueue(video_data_packet);}}return NULL;
}

        read_video_file_thread线程主要的作用是,利用ffmpeg的api avformat_open_input打开我们需要的H264文件,然后用av_read_frame读取每一帧的视频数据。当成功读取一帧数据的时候,把这个数据存放到VIDEO_QUEUE里面(这里封装的函数是putVideoPacketQueue)。流程如下

read_audio_file_thread线程 

//读取音频文件
void *read_audio_file_thread(void *args)
{AVFormatContext *pFormatCtx = NULL;// 给pFormatCtx进行内存分配pFormatCtx = avformat_alloc_context();char *aac_ptr = "test_mic.aac";// 打开输入test_mic.aac文件if (avformat_open_input(&pFormatCtx, aac_ptr, NULL, NULL) != 0){printf("无法打开信息流");}// 这个函数的作用不仅会检索视频的一些信息(宽、高、帧率等),而且会持续的读取和解码一些视频帧和音频帧,读取到的帧会放到缓存中;如果你知道这个媒体流的信息,可以不用这个函数去查找if (avformat_find_stream_info(pFormatCtx, NULL) < 0){printf("无法查找到流信息");}int audioindex = -1;audioindex = -1;//遍历找到音频流,有可能多语言音轨,立体声/环绕声备用音轨for (int i = 0; i < pFormatCtx->nb_streams; i++){//找到第一个音频流,就可以了if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){//保存音频流索引audioindex = i;break;}}// 没有找到音频流if (audioindex < 0){printf("No Audio Stream...\n");}// 给AVPacket结构体分配堆空间,释放堆空间使用av_packet_freeAVPacket *packet = av_packet_alloc();while (av_read_frame(pFormatCtx, packet) >= 0){if (packet->stream_index == audioindex){printf("detect audio index.....\n");// 自己封装的视频数据包结构体audio_data_packet_t分配内存audio_data_packet_t *audio_data_packet = (audio_data_packet_t *)malloc(sizeof(audio_data_packet_t));// 把读取到的视频数据拷贝到结构体成员里面的buffer缓冲区里面区memcpy(audio_data_packet->buffer, packet->data, packet->size);// 数据帧的大小赋值audio_data_packet->audio_frame_size = packet->size;// 往音频队列里面写入视频流数据audio_queue->putAudioPacketQueue(audio_data_packet);}}return NULL;
}

        read_audio_file_thread线程主要的作用是利用ffmpeg的api avformat_open_input打开我们需要的音频aac文件,然后用av_read_frame读取每一帧的音频数据。当成功读取一帧数据的时候,把这个数据存放到AUDIO_QUEUE里面(这里封装的函数是putAudioPacketQueue)。处理流程大概和视频一样。

muxer_ts_thread线程 

/*** TS复用线程函数* 该函数负责根据ffmpeg配置,将音视频数据复用到一个TS流中* @param args 传递给线程的参数,这里是ffmpeg配置的指针* @return 线程的返回值,本函数中始终返回NULL*/
void *muxer_ts_thread(void *args)
{// 解析传入的ffmpeg配置,并释放相应的内存FFMPEG_CONFIG ffmpeg_config = *(FFMPEG_CONFIG *)args;free(args);// 初始化输出格式指针AVOutputFormat *fmt = NULL;// 初始化返回值变量int ret;// 无限循环,直到遇到错误或完成处理while (1){/*我们以转换到同一时基下的时间戳为例,假设上一时刻音、视频帧的保存时间戳都是0。当前任意保存一种视频帧,例如保存视频的时间戳为video_t1。接着比较时间戳,发现音频时间戳为0 < video_t1,保存一帧音频,时间戳为audio_t1。继续比较时间戳,发现audio_t1 < video_t1,选择保存一帧音频,时间戳为audio_t2。再一次比较时间戳video_t1 < audio_t2,选择保存一帧视频,时间戳为video_t2。//同过比较时间基处理时间戳,如果视频时间戳小于音频时间戳,则保存视频,否则保存音频。写进复合流里面*/// 比较视频和音频的时间戳,决定下一步处理哪个流ret = av_compare_ts(ffmpeg_config.video_stream.next_pts,ffmpeg_config.video_stream.enc->time_base,ffmpeg_config.audio_stream.next_pts,ffmpeg_config.audio_stream.enc->time_base);if (ret <= 0)  //== 0 一样快,<0视频快,处理视频{// 处理视频包ret = deal_video_packet(ffmpeg_config.oc, &ffmpeg_config.video_stream);// 如果处理失败,打印错误信息并退出循环if (ret == -1){printf("deal video packet error break");break;}}else //>1音频快{// 处理音频包ret = deal_audio_packet(ffmpeg_config.oc, &ffmpeg_config.audio_stream);// 如果处理失败,打印错误信息并退出循环if (ret == -1){printf("deal audio packet error break");break;}}}// 线程结束,返回NULLreturn NULL;
}

         muxer_ts_thread线程的作用是通过av_compare_ts(这个函数是重中之重)的api进行音视频PTS时间戳的对比。若判断此PTS是视频的pts,则调用deal_video_packet从视频队列取出每一个视频包然后发送到ts文件;若判断此PTS是视频的pts,则调用deal_audio_packet从音频队列取出每一个音频包然后发送到ts文件。

        为什么需要进行时间基比较?我们要搞明白一个问题就是一个是时间基,一个是时间戳,时间基是代表单位,时间戳是代表,一个时间里面的格子,比如钱,1,2,3,4,5,这样是没有任何意义的,但是配合起时间基就有意义了,时间基就是美元或者rmb。所以这两个东西是决定了我们音频和视频在什么时候写进去复合流里面。

时间基 (time_base) :时间基也称之为时间基准,它代表的是每个刻度是多少秒。比方说:视频
帧率是 30FPS ,那它的时间刻度是 {1,30} 。相当于 1s 内划分出 30 个等分,也就是每隔 1/30 秒后显
示一帧视频数据。
时间戳 (PTS DTS) :首先时间戳它指的是在时间轴里面占了多少个格子,时间戳的单位不是具体
的秒数,而是时间刻度。只有当时间基和时间戳结合在一起的时候,才能够真正表达出来时间是
多少。
        正确的复合流,不会是连续几个视频流或者音频流

        错误的写入。

deal_video_packet 视频快,处理视频


int deal_video_packet(AVFormatContext *oc, OutputStream *ost)
{int ret;// 从队列中获取视频数据包AVPacket *video_pkt = get_video_ffmpeg_packet_from_queue(ost->packet);if (video_pkt != NULL){// 更新数据包的呈现时间戳(PTS)和下一个预期的时间戳ost->packet->pts = ost->next_pts++;}// 将AV数据包写入到输出文件中ret = write_avffmpeg_packet(oc, &(ost->enc->time_base), ost->stream, ost->packet);if (ret != 0){// 写入操作失败,返回错误代码return -1;}// 成功处理视频数据包,返回0return 0;
}

        我们来看看deal_video_packet做了什么功能,首先调用get_video_packet_from_queue取出每一个视频数据包,若video_queue不为空,则让视频的pts自增1(视频的pts每次都是自增1)。然后再利用write_avffmpeg_packet发送到ts文件。 

write_avffmpeg_packet

int write_avffmpeg_packet(AVFormatContext *fmt_ctx, const AVRational *time_base, AVStream *st, AVPacket *pkt)
{/*将输出数据包时间戳值从编解码器重新调整为流时基 */av_packet_rescale_ts(pkt, *time_base, st->time_base);// 设置数据包的流索引pkt->stream_index = st->index;// 将编码后的音视频数据(AVPacket)写入输出流,发送到服务器/或本地文件中。/***为什么需要: 这是推流的核心步骤,所有的音视频内容都通过这一步传输到目标。*如果没有这一步,之前的准备工作就毫无意义,因为数据没有实际发送。*原理: FFmpeg 使用“交错写入”(interleaved)方式处理多路流(如视频和音频),*确保时间戳对齐,符合实时传输的要求。数据会被封装为 FLV 标签(Tag)并通过网络发送。*/return av_interleaved_write_frame(fmt_ctx, pkt);
}

        第一步就是时间转换,视频时间基转换复合流时间基 ,假设视频时间基是{1,30},ts 封装格式的 time_base 为 {1,90000},这个api自动转换 av_packet_rescale_ts但是我们要知道

视频h264时间基转换成MPEGTS时间基:

DST_VIDEO_PTS = VIDEO_PTS * VIDEO_TIME_BASE / DST_TIME_BASE

音频AAC时间基转换成MPEGTS时间基:

**DST_AUDIO_PTS = AUDIO_PTS * AUDIO_TIME_BASE / DST_TIME_BASE

        最后后面调用这个 av_interleaved_write_frame(fmt_ctx, pkt);交错写入 

deal_audio_packet

int deal_audio_packet(AVFormatContext *oc, OutputStream *ost)
{int ret;AVCodecContext *c = ost->enc;AVPacket *pkt = get_audio_ffmpeg_packet_from_queue(ost->packet);if (pkt != NULL){pkt->pts = av_rescale_q(ost->samples_count, (AVRational){1, c->sample_rate}, c->time_base);ost->samples_count += 1024;ost->next_pts = ost->samples_count;}ret = write_avffmpeg_packet(oc, &(ost->enc->time_base), ost->stream, pkt);if (ret != 0){printf("[FFMPEG] write video frame error");return -1;}return 0;
}

        我们来看看deal_audio_packet做了什么功能,首先调用get_audio_packet_from_queue取出每一个视频数据包,若audio_queue不为空,则让音频pts自增1024(音频aacpts每一帧都是1024个采样点)。然后再利用write_avffmpeg_packet发送到ts文件。 处理过程是和音频一样的。

        其实把aac文件和h264文件,改成直接从摄像头,麦克风采集,就变成了一个录制ts流。

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

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

相关文章

sqli-lab靶场学习(七)——Less23-25(关键字被过滤、二次注入)

前言 之前的每一关&#xff0c;我们都是在末尾加上注释符&#xff0c;屏蔽后面的语句&#xff0c;这样我们只要闭合了区间之后&#xff0c;差不多就是为所欲为的状态。但如果注释符不生效的情况下&#xff0c;又该如何呢&#xff1f; Less23&#xff08;注释符被过滤&#xff…

Self-Supervised Prompt Optimization

论文&#xff1a;[2502.06855] Self-Supervised Prompt Optimization 仓库&#xff1a;GitHub - Airmomo/SPO: SPO | Self-Supervised Prompt Optimization 自监督提示优化&#xff08;SPO&#xff09; 创新点 成对比较评估 输出指导优化 全文介绍 背景 随着大语言模型…

AI-Ollama本地大语言模型运行框架与Ollama javascript接入

1.Ollama Ollama 是一个开源的大型语言模型&#xff08;LLM&#xff09;平台&#xff0c;旨在让用户能够轻松地在本地运行、管理和与大型语言模型进行交互。 Ollama 提供了一个简单的方式来加载和使用各种预训练的语言模型&#xff0c;支持文本生成、翻译、代码编写、问答等多种…

分布式多卡训练(DDP)踩坑

多卡训练最近在跑yolov10版本的RT-DETR&#xff0c;用来进行目标检测。 单卡训练语句&#xff08;正常运行&#xff09;&#xff1a; python main.py多卡训练语句&#xff1a; 需要通过torch.distributed.launch来启动&#xff0c;一般是单节点&#xff0c;其中CUDA_VISIBLE…

LLM大型语言模型(一)

1. 什么是 LLM&#xff1f; LLM&#xff08;大型语言模型&#xff09;是一种神经网络&#xff0c;专门用于理解、生成并对人类文本作出响应。这些模型是深度神经网络&#xff0c;通常训练于海量文本数据上&#xff0c;有时甚至覆盖了整个互联网的公开文本。 LLM 中的 “大” …

蓝桥杯备考:动态规划dp入门题目之数字三角形

依然是按照动态规划dp的顺序来 step1&#xff1a;定义状态表示 f[i][j]表示的是到,j这个坐标的结点时的最大权值 step2: 定义状态方程 i,j坐标可能是从i-1 j-1 到i,j 也可能是从i-1 j到 i,j 所以状态方程应该是 f[i][j] max(f[i-1][j-1],f[i-1][j]) a[i][j] step3:初始化…

HarmonyOS NEXT开发进阶(十一):应用层架构介绍

文章目录 一、前言二、应用与应用程序包三、应用的多Module设计机制四、 Module类型五、Stage模型应用程序包结构六、拓展阅读 一、前言 在应用模型章节&#xff0c;可以看到主推的Stage模型中&#xff0c;多个应用组件共享同一个ArkTS引擎实例&#xff1b;应用组件之间可以方…

deepseek+mermaid【自动生成流程图】

成果&#xff1a; 第一步打开deepseek官网(或百度版&#xff08;更快一点&#xff09;)&#xff1a; 百度AI搜索 - 办公学习一站解决 第二步&#xff0c;生成对应的Mermaid流程图&#xff1a; 丢给deepseek代码&#xff0c;或题目要求 生成mermaid代码 第三步将代码复制到me…

Solon AI —— RAG

说明 当前大模型与外部打交道的方式有两种&#xff0c;一种是 Prompt&#xff0c;一种是 Fuction Call。在 Prompt 方面&#xff0c;应用系统可以通过 Prompt 模版和补充上下文的方式&#xff0c;调整用户输入的提示语&#xff0c;使得大模型生成的回答更加准确。 RAG RAG &…

STM32——USART—串口发送

目录 一&#xff1a;USART简介 二&#xff1a;初始化USART 1.开启时钟 2.代码 三&#xff1a;USART发送数据 1.USART发送数据函数 2.获取标志位的状态 3.代码 4.在main.c内调用 5.串口调试 1.串口选择要与设备管理器中的端口保持一致 2.波特率、停止位等要与前面…

基于SpringBoot的在线骑行网站的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

通义万相2.1:开启视频生成新时代

文章摘要&#xff1a;通义万相 2.1 是一款在人工智能视频生成领域具有里程碑意义的工具&#xff0c;它通过核心技术的升级和创新&#xff0c;为创作者提供了更强大、更智能的创作能力。本文详细介绍了通义万相 2.1 的背景、核心技术、功能特性、性能评测、用户反馈以及应用场景…

3.3.2 Proteus第一个仿真图

文章目录 文章介绍0 效果图1 新建“点灯”项目2 添加元器件3 元器件布局接线4 补充 文章介绍 本文介绍&#xff1a;使用Proteus仿真软件画第一个仿真图 0 效果图 1 新建“点灯”项目 修改项目名称和路径&#xff0c;之后一直点“下一步”直到完成 2 添加元器件 点击元…

极狐GitLab 正式发布安全版本17.9.1、17.8.4、17.7.6

本分分享极狐GitLab 补丁版本 17.9.1、17.8.4、17.7.6 的详细内容。这几个版本包含重要的缺陷和安全修复代码&#xff0c;我们强烈建议所有私有化部署用户应该立即升级到上述的某一个版本。对于极狐GitLab SaaS&#xff0c;技术团队已经进行了升级&#xff0c;无需用户采取任何…

QT——对象树

在上一篇博客我们已经学会了QT的坏境安装以及打印一个hello world&#xff0c;但是如果有细心的朋友看了代码&#xff0c;就会发现有一个严重的问题&#xff0c;从C语法看来存在内存泄漏。 上面的代码实际上并没有发送内存泄漏&#xff0c;是不是觉得有点奇怪&#xff1f;大家有…

深度学习之-“深入理解梯度下降”

梯度下降是机器学习和深度学习的核心优化算法&#xff0c;几乎所有的模型训练都离不开它。然而&#xff0c;梯度下降并不是一个单一的算法&#xff0c;而是一个庞大的家族&#xff0c;包含了许多变体和改进方法。本文将从最基础的梯度下降开始&#xff0c;逐步深入学习&#xf…

力扣-字符串

字符串不能被修改&#xff0c;所以如果有想修改字符串的行为&#xff0c;需要转换为StringBuilder StringBuilder里也有很多封装方法String没有&#xff0c;比如reverse() StringBuilder sb new StringBuilder();// 添加字符串 sb.append("Hello"); sb.append(&qu…

Ollama+Deepseek-R1+Continue本地集成VScode

一、OllamaDeepseek-R1Continue本地集成VScode 1&#xff09;安装前知识点 Continue 介绍 详情可参照官网&#xff1a; continue官网 Continue 是 Visual Studio Code 和 JetBrains 中领先的开源 AI 代码助手。 •在侧边栏中进行聊天以理解和迭代代码。 •自动补全&#…

风虎云龙R87与RH87八卡服务器震撼首发

在科技迅猛发展的今天&#xff0c;人工智能&#xff08;AI&#xff09;领域正以前所未有的速度改变着世界。从内容创作的智能化&#xff0c;到智能客服的广泛应用&#xff0c;AI技术已经深入到我们生活的方方面面。而这一切的背后&#xff0c;都离不开高性能算力设备的强大支撑…

18.分布式任务调度

固定的时间点去执行固定的任务&#xff0c;这就是任务调度。