live555源码学习(1)

1 基础组件

live项目主要包含了四个基础库、程序入口类(mediaServer)和测试程序(testProgs)。四个基础库是UsageEnvironment、BasicUsageEnvironment、groupsock和liveMedia

UsageEnvironment

抽象了两个类UsageEnvironment和TaskScheduler,UsageEnvironment表示整个运行环境,提供错误记录和输出的功能。TaskScheduler表示任务调度中心,用于异步事件的读取和处理。该库中还有一个抽象类HashTable,这是一个通用的HashTable,在整个项目中都可以使用它。

BasicUsageEnvironment

UsageEnvironment和TaskScheduler的具体实现类

groupsock

网络接口的封装,用于数据包的接收和发送,同时支持多播和单播。groupsock库中包括了GroupEId、Groupsock、GroupsockHelper、NetAddress、NetInterface等类。GroupsockHelper类主要用于读写Socket。

liveMedia

基于基类Medium,实现各种流媒体和编解码类型结构,定义了source(生产者)和sink(消费者)操作。

mediaServer

mediaServer下的live555MediaServer提供了main函数,DynamicRTSPServer继承了RTSPServer并重写了虚函数lookupServerMediaSession

2 主程序

2.1 基本概念和实体

MediaServer是服务器的抽象。

  • 它创建用于接受客户端连接的socket。它还是其他实体的容器。

ClientConnection是与客户端的数据连接的抽象。

  • 当客户端连接服务器时,MediaServer创建ClientConnection的实例,保存在成员fClientConnections[]中。

MediaSession是媒体的抽象。

  • 当用户请求建立媒体连接时,创建MediaSession实例,保存在MediaServer的成员fServerMediaSessions[]中。这是一个Hash表,key值是媒体的名字。

媒体中可以有多个通道,MediaSubsession是媒体通道的抽象。

  • 创建MediaSession时,同时为其中的通道创建MediaSubsession实例,保存在成员fSubsessionHead指向的链表中。

ClientSession是与客户端的对话的抽象,承载在ClientConnection上。

  • 当客户端请求开始播放时,创建ClientSession实例,保存在成员fClientSessions[]中。这是一个Hash表,key值是全局唯一的session id。

StreamState是ClientSession用于挂接到MediaSubsession的中介。

  • ClientSession要挂接到MediaSession上,获得媒体源。它的成员fStreamStates[]引用MediaSession中的MediaSubsession实例。
  • fStreamStates[]是一个数组,这里的trackNum指它的索引。

2.2 main函数

main函数创建任务调度器,创建RTSPServer实例,将它的socket置于调度器的监听下,最后运行调度器,处理socket事件。

  • 调用BasicTaskScheduler::createNew()创建任务调度器,这是一个BasicTaskScheduler实例。
  • 引用调度器实例,创建BasicUsageEnvironment实例。
  • 调用DynamicRTSPServer::createNew(),创建RTSPServer实例。
    • 调用GenericMediaServer::setUpOurSocket()创建TCP socket。createNew()的参数指定本地端口号,这里先指定端口号554。但绑定可能失败,如果失败了,main()会再次调用createNew(),用端口号8554再重试一次。
    • 创建的socket保存在GenericMediaServer的成员fServerSocket中。
    • 调用TaskSheduler::turnOnBackgroundReadHandling()将socket置于监听状态,回调函数为GenericMediaServer::incomingConnectionHandler()。
GenericMediaServer
::GenericMediaServer(UsageEnvironment& env, int ourSocketIPv4, int ourSocketIPv6, Port ourPort,
		     unsigned reclamationSeconds)
  : Medium(env),
    fServerSocketIPv4(ourSocketIPv4), fServerSocketIPv6(ourSocketIPv6),
    fServerPort(ourPort), fReclamationSeconds(reclamationSeconds),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
    fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
    fClientSessions(HashTable::create(STRING_HASH_KEYS)),
    fPreviousClientSessionId(0),
    fTLSCertificateFileName(NULL), fTLSPrivateKeyFileName(NULL) {
  ignoreSigPipeOnSocket(fServerSocketIPv4); // so that clients on the same host that are killed don't also kill us
  ignoreSigPipeOnSocket(fServerSocketIPv6); // ditto
  
  // Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocketIPv4, incomingConnectionHandlerIPv4, this);
  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocketIPv6, incomingConnectionHandlerIPv6, this);
}
  void turnOnBackgroundReadHandling(int socketNum, BackgroundHandlerProc* handlerProc, void* clientData) {
    setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);
  }
void BasicTaskScheduler
  ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
  if (socketNum < 0) return;
#if !defined(__WIN32__) && !defined(_WIN32) && defined(FD_SETSIZE)
  if (socketNum >= (int)(FD_SETSIZE)) return;
#endif
  FD_CLR((unsigned)socketNum, &fReadSet);
  FD_CLR((unsigned)socketNum, &fWriteSet);
  FD_CLR((unsigned)socketNum, &fExceptionSet);
  if (conditionSet == 0) {
    fHandlers->clearHandler(socketNum);
    if (socketNum+1 == fMaxNumSockets) {
      --fMaxNumSockets;
    }
  } else {
    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
    if (socketNum+1 > fMaxNumSockets) {
      fMaxNumSockets = socketNum+1;
    }
    if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
    if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
    if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
  }
}
  • 调用RTSPServer::rtspURLPrefix()得到URL信息,其中调用getourIPAddress()得到本地地址。
  • 调用BasicTaskScheduler0::doEventLoop()调度任务。

GenericMediaServer::setUpOurSocket()创建TCP socket。

  • 调用setupStreamSocket()创建TCP socket。
  • 调用increaseSendBufferTo()重新设置增加缓存大小。
  • 调用listen()开始监听。

BasicTaskScheduler0::doEventLoop()消息循环。

  • 循环调用BasicTaskScheduler::SingleStep()
    • 为所有需要操作的socket执行select。
    • 找到第一个socket handler执行。
    • 找到第一个响应事件执行。
    • 找到第一个延迟任务执行。

2.3 GenericMediaServer::incomingConnectionHandler()

当有新的数据连接请求时,GenericMediaServer::incomingConnectionHandler()被调用。其中调用incomingConnectionHandlerOnSocket(),参数是成员fServerSocket。

  • 调用accept(),返回新数据连接的socket,同时得到客户端的地址。
  • 调用makeSocketNonBlocking()设置socket为非阻塞模式。
  • 调用increaseSendBufferTo()增加发送缓存大小。
  • 用新连接的socket和客户端地址,调用RTSPServer::createNewClientConnection()。

在createNewClientConnection()中

  • 创建RTSPClientConnection实例。
    • ClientConnection的成员fOurSocket保存新连接的socket值,成员fClientAddr保存客户端地址。
    • GenericMediaServer的成员fClientConnections->Add保存ClientConnection的实例。
    • 调用TaskScheduler::setBackgroundHandling(),将新连接的socket置于监听状态,回调函数为ClientConnection::incomingRequestHandler()。
  • 调用RTSPClientConnection::resetRequestBuffer()复位与接收缓存有关的位置指示变量。

2.4 ClientConnection::incomingRequestHandler()

当socket有数据到达时,ClientConnection::incomingRequestHandler()被调用。

  • 调用readSocket()读取数据,这是RTSP请求字符串。接收缓存是成员fRequestBuffer[]。
  • 调用RTSPClientConnection::handleRequestBytes解析RTSP请求并处理。
    • 调用parseRTSPRequestString()解析请求字符串,得到RTSP命令字及参数,包括:cmdName请求命令字(如:OPTIONS),urlSuffix是文件名,cseq是请求序列号。
    • 后面根据cmdName,调用不同的函数处理。如handleCmd_OPTIONS()处理OPTIONS请求等。
    • 对于SETUP请求,调用GenericMediaServer::createNewClientSessionWithId创建ClientSession实例。
    • 对于SETUP之后的请求,parseRTSPRequestString()得到有效的sessionIdStr,调用GenericMediaServer::lookupClientSession()得到对应的ClientSession实例。然后调用RTSPClientSession::handleCmd_withinSession(),再调用相应的处理函数。
	  if (authenticationOK("SETUP", urlTotalSuffix, (char const*)fRequestBuffer)) {
	    clientSession
	      = (RTSPServer::RTSPClientSession*)fOurRTSPServer.createNewClientSessionWithId();
	  } else {
	    areAuthenticated = False;
	  }
      if (requestIncludedSessionId) {
	clientSession
	  = (RTSPServer::RTSPClientSession*)(fOurRTSPServer.lookupClientSession(sessionIdStr));
	if (clientSession != NULL) clientSession->noteLiveness();
      }
else if (strcmp(cmdName, "TEARDOWN") == 0
		 || strcmp(cmdName, "PLAY") == 0
		 || strcmp(cmdName, "PAUSE") == 0
		 || strcmp(cmdName, "GET_PARAMETER") == 0
		 || strcmp(cmdName, "SET_PARAMETER") == 0) {
	if (clientSession != NULL) {
	  clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
	}

2.5 RTSPClientConnection::handleCmd_OPTIONS

填充回复字符串,告诉客户端支持哪些请求

void RTSPServer::RTSPClientConnection::handleCmd_OPTIONS() {
  snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,
	   "RTSP/1.0 200 OK\r\nCSeq: %s\r\n%sPublic: %s\r\n\r\n",
	   fCurrentCSeq, dateHeader(), fOurRTSPServer.allowedCommandNames());
}

2.6 RTSPClientConnection::handleCmd_DESCRIBE

  • 调用DynamicRTSPServer::lookupServerMediaSession()查找ServerMediaSession实例,因为这时实例还不存在,所以这里会先创建一个。执行回调RTSPClientConnection::handleCmd_DESCRIBE_afterLookup
  • 用这个ServerMediaSeesin实例调用generateSDPDescription()。
    • 调用ourIPAddress()得到本地地址。
    • 构建会话级sdp字符串,本地地址是它的一个组成部分。
    • 遍历ServerMediaSession的成员fSubsessionHead,对其中的subsession,调用OnDemandServerMediaSubsession::sdpLines()得到媒体级sdp字符串。
    • 连接会话级sdp字符串和媒体级sdp字符串。
  • 调用RTSPServer::rtspURL()得到URL信息。这是头部字符串的一部分。

DynamicRTSPServer::lookupServerMediaSession()根据key值,在GenericMediaServer的成员fSerMediaSessions中查找对应的实例。fServerMediaSessesions是一个Hash表。lookupServerMediaSession()实现:

  • fopen打开文件,如test.264
  • 实例不存在调用全局函数createNewSMS()创建新的MediaSession实例。并调用GenericMediaServer::addServerMediaSession()将它加入成员fServerMediaSessions。

在全局函数createSMS()中

  • 从文件名test.264中,解析出扩展名“.264”。根据扩展名做不同处理。 对于扩展名“.264”,NEW_SMS("H.264 Video"),创建ServerMediaSession实例。
  • 调用H264VideoFileServerMediaSubsession::createNew(),它创建ServerMediaSubsession实例,文件名保存在成员fFileName中。后面创建FileSource实例时将用到它。
  • 调用ServerMediaSession::addSubsession把这个ServerMediaSubsession加入ServerMediaSession的子session表。ServerMediaSession的成员fSubsesisionHead和fSubsessionTail指向这个表的首部和尾部。

OnDemandServerMediaSubsession::sdpLines()得到子session的sdp字符串。sdpLines实现:

  • 调用createNewStreamSource()创建FramedSource实例
  • 调用createGroupsock()创建Groupsock实例
  • 基于FramedSource和Groupsock实例,调用createNewRTPSink()创建RTPSink实例。
  • 调用setSDPLinesFromRTPSink()。其中调用RTPSink的接口构建sdp字符串。
  • 清理掉以上创建的FramedSource、Groupsock、和RTPSink实例。这时只有ServerMediaSession和ServerMediaSubsession的实例保留下来。

2.7 RTSPClientSession::handleCmd_SETUP

先调用createNewClientSessionWithId()创建ClientSession实例,再调用handleCmd_SETUP()处理。注意handleCmd_SETUP是RTSPClientSession的成员函数,handleCmd_DESCRIBE是RTSPClientConnection的成员函数。

createNewClientSessionWithId()实现:

  • 调用our_random32()生成sessionid。
  • 用sessionid调用RTSPServer::createNewClientSession()创建RTSPClientSession实例。
    • ClientConnection的构造函数调用TaskScheduler::resheduleDelayedTask()设置延时任务,回调函数是ClientSession::LivenessTimeoutTask()。这个函数的作用是在这个Client session实例长时间不工作时,删除它。
  • 将这个实例加入GenericMediaServer的成员fClientSessions,这是一个ClientSession实例的Hash表。

RTSPClientSession::handleCmd_SETUP()实现:

  • 调用lookupServerMediaSession(),找到对应的ServerMediaSession实例。这个实例在handleCmd_DESCRIBE()中已经创建好了。
  • 调用numSubsessions()得到ServerMediaSession中的subsession数量。
  • 新建streamState数组fStreamStates[],长度为subsession数量。fStreamStates[]引用ServerMediaSubsession对象,这些对象保存在fSubsessionsHead链表中
  • 比对subsession的trackId成员和请求串中的trackId(如"track1"),相同则纪录trackNum。
  • 调用parseTransportHeader()从请求串中解析得到传输头中包括的数据流配置信息。如请求串"RTP/AVP;unicast;client_port=65512-65513",代表单播UDP,RTP连接的客户端端口为65512,RTCP连接的客户端端口为65513。
  • 调用parseRangeHeader()解析得到点播范围。如果失败,则再调用parsePlayNowHeader()看能不能找到。没有设置点播范围两个函数都返回失败,则从头开始播到尾。
  • 用前面找到的trackNum对应的subsession,调用OnDemandServerMediaSubsession::getStreamParameters()创建数据流通道。
    • 与subsesssion对应的streamState,它的成员streamToken,是这个函数的一个参数。OnDemandServerMediaSubsession实例将它当做辅助数据结构,它被定义为类StreamState,与streamState只是首字母不同。
  • 构造回复字符串。

getStreamParameters()实现:

  • 它的参数clientAddress指定了目标地址。这里因为请求中没有包括“destination=xxx”,所以这个地址是0。这时将目标地址指定ClientConnection的客户端地址。
  • 调用createNewStreamSource()创建FramedSource实例。
  • 创建用于RTP连接和RTCP连接的Groupsock实例。
    • 从成员fInitialPortNum指定的起始端口开始,尝试可用的本地端口,作为RTP连接的本地端口。fInitialPortNum在OnDemandServerMediaSubsession的构造函数中,指定缺省值为6970。
    • 调用createGroupsock()创建RTP连接的Groupsock实例。
    • 如果不复用RTCP连接和RTP连接,则将RTCP连接的端口加1,创建RTCP连接的Groupsock实例。
    • 为RTP连接,调用createNewRTPSink()创建RTPSink实例。如:.264格式文件调用H264VideoFileServerMediaSubsession::createNewRTPSink()。
    • 基于上述Groupsock和RTPSink实例,创建StreamState实例。
    • 创建Destinations实例,保存客户端地址和端口,将Destination实例加入成员fDestinationHashTable中。

2.8 RTSPClientSession::handleCmd_PLAY

实现:

  • 调用parseScaleHeader()和parseRangeHeader(),得到播放的起点和终点。
  • 遍历成员fNumStreamStates,streamState的成员subsession,调用OnDemandServerMediaSubsession::seekStream(),调整播放位置。
  • 遍历成员fNumStreamStates,streamState的成员subsession,调用OnDemandServerMediaSubsession::startStream(),开始播放。
  • 构造回复字符串

OnDemandServrMediaSubsession::startStream()实现:

  • 根据clientSessionId在成员fDestinationHashTable中查找对应的Destinations实例,其中保存了目标地址。
  • 调用StreamState::startPlaying()开始播放。
    • 这个StreamState实例是在处理SETUP请求时创建的, streamState结构体的成员streamToken保存了这个实例。
    • 调用OnDemandServerMediaSubsession::createRTCP()创建RTCPInstance实例。RTPSink实例和RTCP连接的Groupsock实例作为参数。并调用setAppHandler()和setSpecificRRHandler()设置回调函数。
    • 调用MediaSink::startPlaying(),开始解析媒体帧并通过RTP连接发送,同时调用RTCPInstance::sendReport()发送RTCP信息。

接收RTCP信息实现:

  • 调用RTCPInstance::setByeHandler设置一个回调函数,当收到bye消息时,该回调函数被调用。回调函数保存在成员fByeHandlerTask。
  • 在RTCPInstance构造函数中,调用RTCPInterface::startNetworkReading(),将RTCPInstance::incomingReportHandler()设置为数据可读时的回调函数。

在RTCPInstance::incomingReportHandler()中

  • 调用RTCPInterface::handleRead()读取数据到本地缓存。
  • 调用processIncomingReport()处理缓存。如果是bye消息,调用fByeHandlerTask()进行处理。

MediaSink::startPlaying()实现:

  • 调用MultiFramedRTPSink::continuePlaying(),其实是调用MultiFramedRTPSink::buildAndSendPacket()。
    • buildAndSendPacket封装RTP头,调用packFrame()打包帧数据
      • packFrame()从source文件读取一帧数据,通过回调afterGettingFrame()返回给sink,其实是调用afterGettingFrame1()。
        • afterGettingFrame1()向包中打帧数据,包完成则调用sendPacketIfNecessary()发送包,反之则调用packFrame()从source获取帧数据打包。

MultiFramedRTPSink中的帧数据和包缓冲区共用一个,只是用一些额外的变量指明缓冲区中属于包的部分以及属于帧数据的部分(包以外的数据叫做overflow data)。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的钢材表面缺陷检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发钢材表面缺陷检测系统对于保障制造质量和提高生产效率具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个钢材表面缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#…

Grid-Based Continuous Normal Representation for Anomaly Detection 论文阅读

Grid-Based Continuous Normal Representation for Anomaly Detection 论文阅读 摘要简介方法3.1 Normal Representation3.2 Feature Refinement3.3 Training and Inference 4 实验结果5 总结 文章信息&#xff1a; 原文链接&#xff1a;https://arxiv.org/abs/2402.18293 源码…

应用层DDoS防护:理解、必要性与实现策略

一、应用层简介 应用层&#xff0c;也称作第七层&#xff0c;是OSI&#xff08;开放系统互联&#xff09;模型中的最高层。在这一层&#xff0c;数据以特定的应用程序协议格式进行传输&#xff0c;如HTTP、FTP、SMTP等。应用层的主要职责是为用户提供网络服务&#xff0c;如文…

Android Gradle开发与应用 (四) : Gradle构建与生命周期

1. 前言 前几篇文章&#xff0c;我们对Gradle中的基本知识&#xff0c;包括Gradle项目结构、Gradle Wrapper、GradleUserHome、Groovy基础语法、Groovy语法概念、Groovy闭包等知识点&#xff0c;这篇文章我们接着来介绍Gradle构建过程中的知识点。 2. Project : Gradle中构建…

python61-Python的循环之for-in循环遍历列表和元组

在使用 for-in 循环遍历列表和元组时&#xff0c;列表或元组有几个元素&#xff0c;for-in 循环的循环体就执行几次&#xff0c;针对每个元素执行一次&#xff0c;循环计数器会依次被赋值为元素的值&#xff0c;如下代码使用 for-in 循环遍历元组。 # !/usr/bin/env python# -…

C# Socket通信从入门到精通(21)——TCP发送文件与接收文件 C#代码实现

1、前言 我们在开发上位机软件的过程中经常需要发送文件,本文就是介绍如何利用tcp客户端发送文件、tcp服务器端接收文件,而且本文介绍的方法可以自动发送一个文件夹下的所有子目录以及所有文件,经验来自于实际项目,具备非常有价值的参考意义! 2、发送文件以及C#代码 被发…

基于React俄罗斯方块h5小游戏源码响应式支持PC+手机

俄罗斯方块是一款广受欢迎的经典游戏&#xff0c;许多编程语言都热衷于实现它。在JavaScript中&#xff0c;也有许多版本。 我的目标是使用React框架来实现这个游戏。 地 址 &#xff1a; runruncode.com/vue/19701.html 游戏的架构采用了React和Redux&#xff0c;为了提高性…

php源码 单色bmp图片取模工具 按任意方式取模 生成字节数组 自由编辑点阵

http://2.wjsou.com/BMP/index.html 想试试chatGPT4生成&#xff0c;还是要手工改 php 写一个网页界面上可以选择一张bmp图片&#xff0c;界面上就显示这张bmp图片&#xff0c; 点生成取模按钮&#xff0c;在图片下方会显示这张bmp图片的取模数据。 取模规则是按界面设置的&a…

Pegasus智能家居套件样例开发--软定时器

样例简介 此样例将演示如何在Pegasus Wi-Fi IoT智能家居套件上使用cmsis 2.0 接口进行定时器开发。 工程版本 系统版本/API版本&#xff1a;OpenHarmony 3.0 releaseIDE版本&#xff1a;DevEco Device Tool Release 3.0.0.401 快速上手 准备硬件环境 预装windows系统的PC…

指针与malloc动态内存申请,堆和栈的差异

定义了两个函数print_stack()和print_malloc()&#xff0c;分别演示了两种不同的内存分配方式&#xff1a;栈内存和堆内存。然后在main()函数中调用这两个函数&#xff0c;并将它们返回的指针打印出来。 由于print_stack()中的数组c是在栈上分配的&#xff0c;当函数返回后&…

Matlab|考虑源-荷-储协同互动的主动配电网优化调度研究

目录 主要内容 部分代码 结果一览 主要内容 该程序以33节点系统为例实现了考虑源-荷-储协同互动的主动配电网优化调度模型&#xff0c;程序采用配电网二阶锥约束、储能约束、分布式电源约束、可平移、可削减负荷约束等&#xff0c;以负荷调用成本、储能调用成本、…

USB4之ASM2464PD与ASM2464PDX兼容与运用

首先在NVMe上运用: 一&#xff1a;ASM2464PD&#xff08;现在可以做带PD的方案&#xff09; 二&#xff1a;ASM2464PDX 1&#xff1a; Application Guide- CFX card reader NVMe SSD 2&#xff1a;ASM2464PDX Application Guide- NVMe SSD x4 with data clone 三&#xff…

并查集(Disjoint Set)

目录 1.定义 2.初始化 3.查找 4.合并 4.1.按秩合并&#xff08;启发式合并&#xff09; 5.例题 题目描述 输入格式 输出格式 输入输出样例 说明/提示 1.定义 并查集&#xff0c;也称为不相交集合数据结构&#xff0c;是一种用于管理元素分组以及查找元素所属组的数…

回溯 Leetcode 47 全排列II

全排列II 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 Leetcode 47 学习记录自代码随想录 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2], [1,2,1], [2,1,1]] 示例 2&#xff1a; 输入&#xff1…

pd sink取电协议芯片介绍

前言&#xff1a; 在如今快节奏生活不断蔓延的背景下&#xff0c;人们对各种事情的处理也渐渐地开始要求在保证质量的情况下&#xff0c;不断加快。手机快充就是一个典型的例子&#xff0c;从开始的18W&#xff0c;30W快充&#xff0c;到现在已经有240W的超级快充出现。在这其…

ICLR 2024|ReLU激活函数的反击,稀疏性仍然是提升LLM效率的利器

论文题目&#xff1a; ReLU Strikes Back: Exploiting Activation Sparsity in Large Language Models 论文链接&#xff1a; https://arxiv.org/abs/2310.04564 参数规模超过十亿&#xff08;1B&#xff09;的大型语言模型&#xff08;LLM&#xff09;已经彻底改变了现阶段人工…

tritonserver学习之八:redis_caches实践

tritonserver学习之一&#xff1a;triton使用流程 tritonserver学习之二&#xff1a;tritonserver编译 tritonserver学习之三&#xff1a;tritonserver运行流程 tritonserver学习之四&#xff1a;命令行解析 tritonserver学习之五&#xff1a;backend实现机制 tritonserv…

【解决】虚幻导入FBX模型不是一个整体

问题&#xff1a; 现在有一个汽车的fbx模型&#xff0c;导入虚幻引擎&#xff0c;导入后变成了很多汽车零件模型。 解决&#xff1a; 把“合并网格体”勾选上&#xff0c;解决问题。

SpringBoot整合JdbcTemplate

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:SpringBoot整合JdbcTemplate 📚个人知识库: Leo知识库,欢迎大家访问 目录 …

MySQL 数据库表设计和优化

一、数据结构设计 正确的数据结构设计对数据库的性能是非常重要的。 在设计数据表时&#xff0c;尽量遵循一下几点&#xff1a; 将数据分解为合适的表&#xff0c;每个表都应该有清晰定义的目的&#xff0c;避免将过多的数据存储在单个表中。使用适当的数据类型来存储数据&…
最新文章