当前位置: 首页 > article >正文

FFMPEG利用H264+AAC合成TS文件

        本次的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流。

相关文章:

FFMPEG利用H264+AAC合成TS文件

本次的DEMO是利用FFMPEG框架把H264文件和AAC文件合并成一个TS文件。这个DEMO很重要&#xff0c;因为在后面的推流项目中用到了这方面的技术。所以&#xff0c;大家最好把这个项目好好了解。 下面这个是流程图 从这个图我们能看出来&#xff0c;在main函数中我们主要做了这几步&…...

Linux搭建个人大模型RAG-(ollama+deepseek+anythingLLM)

本文是远程安装ollama deepseek&#xff0c;本地笔记本电脑安装anythingLLM&#xff0c;并上传本地文件作为知识库。 1.安装ollama 安装可以非常简单&#xff0c;一行命令完事。&#xff08;有没有GPU&#xff0c;都没有关系&#xff0c;自动下载合适的版本&#xff09; cd 到…...

Docker 学习(二)——基于Registry、Harbor搭建私有仓库

Docker仓库是集中存储和管理Docker镜像的平台&#xff0c;支持镜像的上传、下载、版本管理等功能。 一、Docker仓库分类 1.公有仓库 Docker Hub&#xff1a;官方默认公共仓库&#xff0c;提供超过10万镜像&#xff0c;支持用户上传和管理镜像。 第三方平台&#xff1a;如阿里…...

PHP之变量

在你有别的编程语言的基础下&#xff0c;你想学习PHP&#xff0c;可能要了解的一些关于变量的信息。 PHP中的变量不用指定数据类型&#xff0c;同时必须用$开头。 全局变量 可以在除函数外任意地方访问&#xff0c;如果需要在函数中访问要先获取 $x 111; function tt() {gl…...

centos和ubuntu下安装redis

1&#xff0c;判断环境是否有gcc gcc --version 如果未安装则执行 yum install -y gcc tcl 2&#xff0c;安装包下载,编译安装 cd /usr/local mkdir redis wget https://download.redis.io/releases/redis-4.0.11.tar.gz tar -xvf redis-4.0.11.tar.gz cd redis-4.0.11 编译 m…...

韩国互联网巨头 NAVER 如何借助 StarRocks 实现实时数据洞察

作者&#xff1a; Youngjin Kim Team Leader, NAVER Moweon Lee Data Engineer, NAVER 导读&#xff1a;开源无国界&#xff0c;在“StarRocks 全球用户精选案例”专栏中&#xff0c;我们将介绍韩国互联网巨头 NAVER 的 StarRocks 实践案例。 NAVER 成立于 1999 年&#xff0…...

K8s 1.27.1 实战系列(二)安装集群并初始化

一、安装 kubeadm、kubelet 和 kubectl(所有节点) 1、配置k8s的yum源地址 cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgchec…...

生命周期总结(uni-app、vue2、vue3生命周期讲解)

一、vue2生命周期 Vue2 的生命周期钩子函数分为 4 个阶段&#xff1a;创建、挂载、更新、销毁。 1. 创建阶段 beforeCreate&#xff1a;实例初始化之后&#xff0c;数据观测和事件配置之前。 created&#xff1a;实例创建完成&#xff0c;数据观测和事件配置已完成&#xff0c…...

十一、Redis Sentinel(哨兵)—— 高可用架构与配置指南

Redis Sentinel(哨兵)—— 高可用架构与配置指南 在分布式应用中,Redis 主从复制(Master-Slave)虽然能提供读写分离的能力,但它 无法自动故障转移(failover)。如果主节点(Master)发生故障,系统管理员需要手动将某个从节点(Slave)提升为主节点,并重新配置所有从节…...

java8中young gc的垃圾回收器选型,您了解嘛

在 Java 8 的 Young GC&#xff08;新生代垃圾回收&#xff09;场景中&#xff0c;对于 ToC的场景&#xff0c;即需要尽可能减少垃圾回收停顿时间以满足业务响应要求的场景&#xff0c;以下几种收集器各有特点&#xff0c;通常 Parnew和 G1 young表现较为出色&#xff0c;下面详…...

C语言学习笔记-初阶(30)深入理解指针2

1. 数组名的理解 在上一个章节我们在使用指针访问数组的内容时&#xff0c;有这样的代码&#xff1a; int arr[10] {1,2,3,4,5,6,7,8,9,10}; int *p &arr[0]; 这里我们使用 &arr[0] 的方式拿到了数组第⼀个元素的地址&#xff0c;但是其实数组名本来就是地址&…...

【Wireshark 02】抓包过滤方法

一、官方教程 Wireshark 官网文档 &#xff1a; Wireshark User’s Guide 二、显示过滤器 2.1、 “数据包列表”窗格的弹出过滤菜单 例如&#xff0c;源ip地址作为过滤选项&#xff0c;右击源ip->prepare as filter-> 选中 点击选中完&#xff0c;显示过滤器&#…...

MySQL基础四(JDBC)

JDBC(重点) 数据库驱动 程序会通过数据库驱动&#xff0c;和数据库打交道。 sun公司为了简化开发人员对数据库的统一操作&#xff0c;提供了一个Java操作数据库的规范。这个规范由具体的厂商去完成。对应开发人员来说&#xff0c;只需要掌握JDBC接口。 熟悉java.sql与javax.s…...

基于CURL命令封装的JAVA通用HTTP工具

文章目录 一、简要概述二、封装过程1. 引入依赖2. 定义脚本执行类 三、单元测试四、其他资源 一、简要概述 在Linux中curl是一个利用URL规则在命令行下工作的文件传输工具&#xff0c;可以说是一款很强大的http命令行工具。它支持文件的上传和下载&#xff0c;是综合传输工具&…...

cenos7网络安全检查

很多网络爱好者都知道&#xff0c;在Windows 2000和Windows 9x的命令提示符下可使用Windows系统自带的多种命令行网络故障检测工具&#xff0c;比如说我们最常用的ping。但大家在具体应用时&#xff0c;可能对这些命令行工具的具体含义&#xff0c;以及命令行后面可以使用的种…...

FastGPT 引申:混合检索完整实例

文章目录 FastGPT 引申&#xff1a;混合检索完整实例1. 各检索方式的初始结果2. RRF合并过程3. 合并后的结果4. Rerank重排序后5. 最终RRF合并6. 内容总结 FastGPT 引申&#xff1a;混合检索完整实例 下边通过一个简单的例子说明不同检索方式的分值变化过程&#xff0c;假设我…...

一、Prometheus架构

Prometheus 云原生十二要素是一套最佳实践和规范,旨在帮助开发人员更好地构建云原生应用 这十二个要素分别是: 单一职责独立部署无状态声明式API服务发现容错处理自适应算法自动化运维响应式编程通信协议服务注册与发现数据持久化一、Prometheus 是什么 Prometheus 是一个…...

蓝桥杯C组真题——巧克力

题目如下 思路 代码及解析如下 谢谢观看...

【大模型】大模型分类

大模型&#xff08;Large Models&#xff09;通常指参数量巨大、计算能力强大的机器学习模型&#xff0c;尤其在自然语言处理&#xff08;NLP&#xff09;、计算机视觉&#xff08;CV&#xff09;等领域表现突出。以下是大模型的常见分类方式&#xff1a; 1. 按应用领域分类 …...

WebUSB的常用API及案例

WebUSB API 允许网页与 USB 设备进行交互&#xff0c;但出于安全考虑&#xff0c;浏览器要求在调用 requestDevice 方法&#xff08;用于请求用户选择一个 USB 设备并授予网页访问权限&#xff09;时&#xff0c;必须是在处理用户手势&#xff08;例如点击按钮&#xff09;的过…...

在线研讨会 | 加速游戏和AI应用,全面认识Imagination DXTP GPU

近日&#xff0c;Imagination宣布推出 Imagination DXTP GPU IP&#xff0c;该产品重新定义了智能手机和其他功耗受限设备的图形和计算加速。它专为高效的效率而设计&#xff0c;能够提供运行AI、游戏和用户界面体验所需的性能&#xff0c;确保这些体验可以全天候流畅且持续地运…...

The Rust Programming Language 学习 (三)

所有权 所有权&#xff08;系统&#xff09;是 Rust 最为与众不同的特性&#xff0c;它让 Rust 无需垃圾回收器&#xff08;garbage collector&#xff09;即可保证内存安全。因此&#xff0c;理解 Rust 中所有权的运作方式非常重要。 这里是非常重非常重的一个知识点,这里一…...

【一个月备战蓝桥算法】递归与递推

字典序 在刷题和计算机科学领域&#xff0c;字典序&#xff08;Lexicographical order&#xff09;也称为词典序、字典顺序、字母序&#xff0c;是一种对序列元素进行排序的方式&#xff0c;它模仿了字典中单词的排序规则。下面从不同的数据类型来详细解释字典序&#xff1a; …...

【零基础到精通Java合集】第二十九集:SQL常用优化手段

课程标题:SQL常用优化手段——15分钟快速提升数据库性能 目标:掌握10+核心SQL优化技巧,解决慢查询、高负载等生产问题 0-1分钟:优化核心原则——减少数据扫描量 本质逻辑:通过索引、分页、过滤条件等手段,最小化磁盘I/O和内存计算。 反例:SELECT * FROM orders(全表扫…...

ArcGIS操作:07 绘制矢量shp面

1、点击目录 2、右侧显示目录 3、选择要存储的文件夹&#xff0c;新建shp 4、定义名称、要素类型、坐标系 5、点击开始编辑 6、点击创建要素 7、右侧选择图层、创建面 8、开始绘制&#xff0c;双击任意位置结束绘制...

如何远程访问svn中的URL

简介&#xff1a; 主要opencascade相关知识学习 格言&#xff1a; 万丈高楼平地起 要远程访问 SVN&#xff08;Subversion&#xff09;仓库中的 URL&#xff0c;通常需要以下步骤和注意事项&#xff1a; 1. 确认远程 SVN 服务器的访问协议 SVN 支持多种协议访问远程仓库&…...

归并排序:分治哲学的完美演绎与时空平衡的艺术

引言&#xff1a;跨越世纪的算法明珠 在计算机科学的璀璨星河中&#xff0c;归并排序犹如一颗恒久闪耀的明星。1945年&#xff0c;现代计算机之父冯诺伊曼在EDVAC计算机的研发过程中首次系统性地提出了这一算法&#xff0c;其精妙的分治思想不仅奠定了现代排序算法的理论基础&…...

【电控笔记z69】电机选型-机械特性

转矩特性 启动转矩 定义:指电机在启动瞬间所能提供的转矩。对于一些需要快速启动负载的设备,如起重机起升机构、电动汽车起步等,较大的启动转矩至关重要。影响因素:电机的类型、绕组参数、电源电压等都会影响启动转矩。例如,直流电机通过调节电枢电压和励磁电流可以在较大…...

Axure原型模板与元件库APP交互设计素材(附资料)

为了高效地进行APP和小程序的设计与开发&#xff0c;原型设计工具Axure凭借其强大的功能和灵活性&#xff0c;成为了众多产品经理和设计师的首选。本文将详细介绍Axure原型模板APP常用界面组件元件库、交互设计素材&#xff0c;以及多套涵盖电商、社区服务、娱乐休闲、农业农村…...

<网络> TCP协议

目录 TCP协议 与系统相关联 文件与套接字的关系 C语言的多态 谈谈可靠性 TCP协议格式 目的端口号 4位首部长度 16位窗口大小 序号与确认序号 32位序号 32位确认序号 标志位 TCP连接 三次握手 四次挥手 三次握手状态变化 四次挥手状态变化 流量控制 滑动窗口 拥塞控制 延迟应…...