流媒体之HLS协议(其三)

news2024/9/20 10:37:37

在这里插入图片描述

欢迎诸位来阅读在下的博文~
在这里,在下会不定期发表一些浅薄的知识和经验,望诸位能与在下多多交流,共同努力!

江山如画,客心如若,欢迎到访,一展风采

文章目录

  • 前期博客
  • 参考书籍
  • 一、HLS协议简介
  • 二、RTMP vs HLS 对比
  • 三、HLS网络框架结构
  • 四、HLS的索引文件嵌套
  • 五、HLS协议详细讲解
      • m3u8简介
      • TS文件
      • 有关ES、PES、PS、TS的详细解析
      • PS/TS编码的基本流程
  • 六、TS码流详细讲解
      • TS包格式
      • 帧数据、PES包、TS包的对应关系
      • PAT及PMT表的格式

前期博客

流媒体与直播的基础理论(其一)
流媒体协议RTSP(其二)

参考书籍

《FFmpeg入门详解——流媒体直播原理及应用》——梅会东

一、HLS协议简介

HLS与RTMP都是流媒体协议,RTMP由Adobe开发,广泛应用于低延时直播,也是编码器和服务器对接的实际标准协议,在PC(Flash)上有最佳的观感体验;HLS由苹果公司开发,可以支持Live(直播),也可以支持VoD(点播)。HLS是苹果平台的标准流媒体协议,和RTMP在PC上一样支持得非常完善。
HLS全称HTTP Live Streaming,是一种基于HTTP的流媒体网络传输协议。它的基本工作原理是把整个流分成一个一个小的基于HTTP的文件来下载,每次只下载一些。

二、RTMP vs HLS 对比

特性RTMPHLS
延迟1-3 秒,低延迟10-30 秒,延迟较高
实时性适合实时互动场景(如直播、游戏等)主要用于点播,直播场景中实时性较差
兼容性Flash 支持逐渐减少,现代浏览器不支持浏览器、移动端、智能电视等设备广泛支持
传输协议基于 TCP,保持长连接基于 HTTP,使用短连接
自适应码率不支持支持自动码率切换(ABR)
扩展性不适合大规模分发支持 CDN,大规模分发良好
复杂性需要保持持续连接,推流方式复杂基于 HTTP,结构简单,易于实现
主要用途实时直播,互动性较强的场景视频点播、大规模直播流媒体

  • 值得注意的是,苹果公司在WWDC2019发布了新的解决方案来优化延迟性能,使得延迟从8s减低到1~2s,可以预知,HLS是充满活力与可能性的。
  • 不过,RTMP在推流方面依旧十分好用。

三、HLS网络框架结构

在这里插入图片描述

  • 需要留意的是:
    (1)服务器将媒体文件转换为m3u8文件及TS分片;对于直播源,服务器需要实时动态更新。
    (2)客户端请求m3u8文件,根据索引获取TS分片;点播与直播服务器不同的地方是,直播的m3u8文件动态更新,点播只需要请求依次m3u8文件。

四、HLS的索引文件嵌套

在这里插入图片描述

  • 注意:媒体流封装的分片格式只支持MPEG-2传输流(TS)、WebVTT文件 或 Packed Audio文件。

五、HLS协议详细讲解

HLS协议规定了四部分的内容:分别是 视频的封装格式、视频的编码格式、声频的编码格式、m3u8文件
如下:
在这里插入图片描述

m3u8简介

HLS协议中的m3u8,是一个包含TS列表的文本文件,目的是告诉客户端或浏览器可以播放这些TS文件。m3u8的一些主要标签解释如下:
(1)# EXTM3U:每个m3u8文件的第一行必须是这个tag,提供标识作用。
(2)# EXT-X-VERSION:用以标识协议版本。此标签m3u8文本中只能使用一次。例如版本3.
(3)# EXT-X-TARGETDURATION:所有切片的最大时长,如果不设置这个参数,则有些苹果设备就会无法播放。
(4)# EXT-X-MEDIA-SEQUENCE:切片的开始序号。每个切片都有唯一的序号,相邻序号+1。这个编号会持续增长,保证流的连续性。
(5)# EXTINF:TS切片的实际时长。
(6)# EXT-X-PLAYLIST-TYPE:类型,VoD表示点播,Live表示直播。
(7)# EXT-X-ENDLIST:文件结束符号,表示不再向播放列表文件添加媒体文件。

一个典型的例子:

//chapter5/hls.sample1.m3u8
#EXTM3U			//开始标志
#EXT-X-VERSION:3		//版本为3
#EXT-X-ALLOW-CACHE:YES 	//允许缓存
#EXT-X-TARGETDURATION:13	//切片最大时长
#EXT-X-MEDIA-SEQUENCE:430	//切片的起始序列号
#EXT-X-PLAYLIST-TYPE:VOD	//VOD表示点播
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
http://example.con/low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000
http://example.con/mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000
http://example.con/hi.m3u8
#EXTINF:11.800
news-430.ts
#EXTINF:10.120
news-431.ts
#EXT-X-DISCONTINUITY
#EXTINF:11.952
news-430.ts
#EXTINF:12.640
news-431.ts
#EXTINF:11.160
news-432.ts
#EXT-X-DISCONTINUITY
#EXTINF:11.751
news-430.ts
#EXTINF:2.040
news-431.ts
#EXT-X-ENDLIST		//结束标志
  • 注意:BANDWIDTH用于指定视频流的比特率。
  • #EXT-X-STREAM-INF的下一行是二级index文件的路径,可以用相对、绝对路径。二级文件负责给出TS文件的下载地址。

在这里插入图片描述

TS文件

TS文件的结构如下:
在这里插入图片描述

在这里插入图片描述

有关ES、PES、PS、TS的详细解析

  • 包和流的关系是,流含有多个包,比如ES流含有多个ES包。
  • PES包是ES流/包经过PES打包器处理后形成的,包含有分组、打包、加入包头信息等操作。
  • 一个PES包含有一个或多个ES包,一个ES包含有多个存取单元(AU)。
  • 一个AU是编码了的1幅视频图像帧或一个声频帧。
  • PS 是节目流,由PS包组成,和TS属于同一层次但不同类别的流。一个PS包由若干个PES包组成。PS包的包头中包含了同步信息与时钟恢复信息。
  • 一个PS包最多包含具有同一时钟基准的16个视频PES包和32和声频的PES包。PS包不定长。
  • TS 是传输流,由定长的TS包组成(188B)。由于定长,所以某个包即使损坏后,也不影响其余的TS包的正常解码,PS则不然。
  • PS主要用于误码率相对较低的演播室和数字存储(DVD)中。
  • 传输流主要用于传输中,不怕数据包损坏。

PS/TS编码的基本流程

在这里插入图片描述

六、TS码流详细讲解

TS流将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成单一数据流。
在这里插入图片描述

  • 注意:TS是位流格式,TS流是可以按位读取的。

TS包格式

TS包是基于Packet的位流格式,每个包是188B(或204B,后面有16B的CRC校验数据)。如图:
在这里插入图片描述

TS流包头字段结构:

字段名称字节位置描述
同步字节(Sync Byte)第1个字节固定为0x47,用于标识TS包的开始。
传输错误指示(TEI)第2个字节,第1位当设置为1时,表示当前包中至少有一个不可纠正的错误位。
有效负载单元起始指示(PUSI)第2个字节,第2位当设置为1时,表示当前包的有效负载部分以一个PES包或PSI/SI表的第一个字节开始。
传输优先级(Priority)第2个字节,第3位用于传输机制,但在解码时通常不被使用。
包标识符(PID)占13位用于标识TS包属于哪个特定的流或服务。
传输加扰控制(Scrambling)第4个字节,第1位和第2位用于指示TS包是否被加扰以及加扰的方式。
自适应字段控制(Adaptation)第4个字节,第3位和第4位用于指示TS包是否包含自适应字段,以及自适应字段的位置和长度。
连续计数器(Counter)第4个字节,第5位到第8位用于检测包的丢失或重复,范围从0到15,每发送一个TS包,计数器加1。
  • 一些TS表的PID值是固定的,如下:
PID值
PAT0x0000
CAT0x0001
TSDT0x0002
EIT/ST0x0012
RST/ST0x0013
TDT/TOT/ST0x0014

在TS流中,TS包有可能是音视频数据,也有可能是表格(PAT/PMT/…)。举例说明,TS流的包顺序如下:

PAT,PMT,DATA,DATA,,,,,,,,PAT,PMT,DATA,DATA,,,,,,

每隔一段时间,发送一张PAT表,紧接着发送DATA数据。PAT表格里面包含所有PMT表格的信息,一个PMT表格对应一个频道,例如中央电视台综合频道,而一个PMT里面包含所有节目的信息,例如CCTV-1到CCTV-14。在实际情况中有很多频道,所以PMT表格不止一张,有可能是以下:

PAT、PMT、PMT、PMT,,,DATA,DATA,DATA,,,,

除了这个设定外,每个频道或节目都有自己的标识符(PID),这样当得到一个DATA,解释出里面的PID,就知道是什么节目了,也知道节目所属频道是什么。在看电视时,会收到所有节目的DATA,当选择某个节目时,机顶盒会把这个节目的DATA单独过滤出来,其他舍弃。

TS包的长度是188B,分为TS Header和TS Body。其中TS Header里买你会有个PID字段,标识当前DATA的类型。其实,DATA其实是PES包,而PES包是对ES流的封装,等价“ PES Header + ES ”。这里的ES是原始流,是指经过压缩后的H.264、AAC等格式的音视频数据。

帧数据、PES包、TS包的对应关系

一帧数据封装成一个PES包,如果 PES包 <= 184字节,则一个TS包就可以放下,如果还有空余的地方,就填充无实际用处的字节,使其满足184字节。如果 PES包 > 184字节,则需要多个TS包封装,这里需要注意的是,PES Header只需要首个TS包封装即可。伪代码如下:

第一个TS包 : TS Header + PES头 + 部分ES
第二个TS包 : TS Header + 部分ES
第三个TS包 : TS Header + 部分ES
....
第n个TS包 : TS Header + 填充字节 + 部分ES

PAT及PMT表的格式

1、PAT
TS流中包含一个或者多个PAT。PAT由PID为0x0000的TS包传送,其作用是为复用的每一条传送流提供所包含的节目和节目编号,以及对应节目的PMT的位置,既PMT的TS包的PID值,同时还提供NIT的位置,即NIT的TS包的PID值。TS码流解析从PAT开始。PAT定义了当前TS流中所有的节目,其PID为0x0000,他是PSI的根节点。下面是PAT的字段,代码如下:

typedef struct TS_PAT_Program  
{  
    unsigned program_number :	16;	//节目号  
    unsigned program_map_PID:	13;	// 节目映射表(PMT)的PID,每个节目对应一个   
}TS_PAT_Program;
program_association_section()
{  
    unsigned table_id 		: 8; 	//固定为0x00 ,标志是该表是PAT表  
    unsigned section_syntax_indicator    : 1; //段语法标志位,固定为1  
    unsigned0: 1; //0  
    unsigned reserved_1	: 2; // 保留位  
    unsigned section_length	: 12; //段长度字节  

    
    unsigned transport_stream_id: 16;   //该传输流的ID,区别于一个网络中其它多路复用的流
    unsigned reserved_2	: 2;// 保留位
    unsigned version_number	: 5; //范围0-31,表示PAT的版本号  
	unsigned current_next_indicator: 1; //发送的当前PAT是否有效,还是下一个有效

//PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
// 给出section号,在sub_table中,第一个section其section_number为"0x00"
//每增加一个section,section_number加1
    unsigned section_number	: 8; //分段的号码。
    							//PAT可能分多段传输,第一段为0,以后每个分段加1,最多可能有256个分段。
    
    //最后一个分段的号码 ,sub_table中最后一个section的section_number
    unsigned last_section_number    : 8;  

    /*循环部分 4个Byte*/
    for(i=0;i<N;i++{
    	program_number     		:16;  //节目号
    	Reserved				:3;   //保留位
    	//网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID;
		//其余情况是program_map_PID(PMT的PID)
    	network_id 或 program_map_PID	:13; 
    }
    CRC_32                                	:32; //校验码     
}
  • 这两个结构体的关系是,program_association_section 结构体包含了多个 TS_PAT_Program 结构体,每个 TS_PAT_Program 结构体代表PAT中的一个节目条目。在解析PAT时,首先解析 program_association_section 结构体以获取PAT的基本信息,然后根据其中的循环部分,解析每个 TS_PAT_Program 结构体以获取每个节目的具体信息。

PAT的解析函数代码如下:

int adjust_PAT_table( TS_PAT * packet, unsigned char * buffer)  
{  
    packet->table_id		= buffer[0];  
    packet->section_syntax_indicator	= buffer[1] >> 7;  
    packet->zero		= buffer[1] >> 6 & 0x1;  
    packet->reserved_1		= buffer[1] >> 4 & 0x3;  
    packet->section_length	= (buffer[1] & 0x0F) << 8 | buffer[2];   
   
    packet->transport_stream_id	= buffer[3] << 8 | buffer[4];  
   
    packet->reserved_2			= buffer[5] >> 6;  
    packet->version_number		= buffer[5] >> 1 &  0x1F;  
    packet->current_next_indicator	= (buffer[5] << 7) >> 7;  
    packet->section_number		= buffer[6];  
    packet->last_section_number	= buffer[7];   
  
    int len = 0;  
    len = 3 + packet->section_length;  
    packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24  
  | (buffer[len-3] & 0x000000FF) << 16  
  | (buffer[len-2] & 0x000000FF) << 8   
  | (buffer[len-1] & 0x000000FF);   
   
	int n = 0;  
	///循环次数
    for ( n = 0; n < packet->section_length - 12; n += 4 )  
    {  
        unsigned  program_num = buffer[8 + n ] << 8 | buffer[9 + n ];    
        packet->reserved_3 		= buffer[10 + n ] >> 5;   
    
        packet->network_PID = 0x00;  
        if ( program_num == 0x00)  
        {    
            packet->network_PID = (buffer[10+n ] & 0x1F) << 8 | buffer[11+n ];  
  
            TS_network_Pid = packet->network_PID; //记录该TS流的网络PID  
  
            TRACE(" packet->network_PID %0x /n/n", packet->network_PID );  
        }  
        else  
        {  
           TS_PAT_Program PAT_program;  //队列
           PAT_program.program_map_PID = 
		(buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];  
           PAT_program.program_number = program_num;  
           packet->program.push_back( PAT_program );  
           //向全局PAT节目数组中添加PAT节目信息
           TS_program.push_back( PAT_program );       
        }           
    }  
    return 0;  
}
  • 注:在上述代码中,从for循环开始,描述了当前流中的频道数目和每个频道对应的PMT的PID值。解复用程序需要接收所有的频道号码和对应的PMT的PID,并把这些信息在缓冲区中保存起来。在后续处理中,需要PMT的PID。

2、PMT
PMT在传输流中用于指示组成某一套节目的视频、声频和数据在传送流中的位置,即对应的TS包的PID值,以及每路节目的节目时钟参考(PCR)字段的位置。PMT流结构,代码如下:

typedef struct TS_PMT_Stream    
{    
 unsigned stream_type	: 8; //指示特定PID的节目元素包的类型。
 unsigned elementary_PID: 13; //该域指示TS包的PID值,包含有相关的节目元素    
 unsigned ES_info_length: 12; //前两位是00,指示跟随其后的相关节目元素的字节数    
 unsigned descriptor;    
}TS_PMT_Stream;
TS_program_map_section() {
    table_id                    :8; //固定为0x02 标识PMT表
    section_syntax_indicator :1; //固定为0x01
    '0'                          :1; //
    reserved                    :2; // 保留位
    section_length             :12 //该字段的头两bit必为‘00’,剩余10bit指定该分段的字节数,紧随section_length 字段开始,并包括CRC。此字段中的值应不超过1021(0x3FD)。
    program_number            :16 //指出TS流中Program map section的版本号
    reserved                   :2  // 保留位
    version_number            :5  //指出TS流中Program map section的版本号 
    current_next_indicator  :1  //当该位置1时,当前传送的Program map section可用;当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效
    section_number            :8  //固定为0x00
    last_section_number      :8  //固定为0x00
    reserved                   :3  //保留
    PCR_PID                    :13 //指明TS包的PID值,该TS包含有PCR域,  
    //该PCR值对应于由节目号指定的对应节目。  
    //如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
    reserved                  :4  //保留位
    program_info_length     :12 //节目信息长度。该字段的头两比特必为‘00’,剩余10 比特指定紧随program_info_length 字段的描述符的字节数 ,
//(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes)紧接着就是频道内部包含的节目类型和对应的PID号码

    for (i = 0; i < N; i++) {
        descriptor()
    }
    for (i = 0; i < N1; i++) {
        stream_type             :8 //流类型,标志是Video还是Audio还是其他数据。 
        reserved                 :3 //保留位
        elementary_PID          :13 //该节目的音频或视频PID
        reserved                 :4 //保留位
        ES_info_length          :12 //该字段的头两比特必为‘00’,剩余10比特指示紧随ES_info_length字段的相关节目元描述符的字节数。
        for (i = 0; i < N2; i++) {
        descriptor()
    }
    }
    CRC_32                       :32 
}

两个结构体的关系是,TS_program_map_section 结构体包含了多个 TS_PMT_Stream 结构体。在解析PMT时,首先解析 TS_program_map_section 结构体以获取PMT的基本信息,然后根据循环部分,解析每个 TS_PMT_Stream 结构体以获取关于每个流的具体信息。每个 TS_PMT_Stream 结构体代表PMT中的一个流条目,提供了流类型、PID和描述符等信息。

PMT的解析函数,代码如下:

int adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer )  
{   
    //读取各个字段
    packet->table_id			= buffer[0];  
    packet->section_syntax_indicator	= buffer[1] >> 7;  
    packet->zero			= buffer[1] >> 6 & 0x01;   
    packet->reserved_1			= buffer[1] >> 4 & 0x03;  
    packet->section_length 	= (buffer[1] & 0x0F) << 8 | buffer[2];      
    packet->program_number	= buffer[3] << 8 | buffer[4];  
    packet->reserved_2		= buffer[5] >> 6;  
    packet->version_number	= buffer[5] >> 1 & 0x1F;  
    packet->current_next_indicator	= (buffer[5] << 7) >> 7;  
    packet->section_number		= buffer[6];  
    packet->last_section_number	= buffer[7];  
    packet->reserved_3			= buffer[8] >> 5;  
    packet->PCR_PID	= ((buffer[8] << 8) | buffer[9]) & 0x1FFF;  
  
    PCRID = packet->PCR_PID;  
  
    packet->reserved_4			= buffer[10] >> 4;  
    packet->program_info_length= (buffer[10] & 0x0F) << 8 | buffer[11];   
    // Get CRC_32  
    int len = 0;  
    len = packet->section_length + 3;      
    packet->CRC_32                = (buffer[len-4] & 0x000000FF) << 24  
  | (buffer[len-3] & 0x000000FF) << 16  
  | (buffer[len-2] & 0x000000FF) << 8  
  | (buffer[len-1] & 0x000000FF);   
  
    int pos = 12;  
    // program info descriptor  //节目信息描述符
    if ( packet->program_info_length != 0 )  
        pos += packet->program_info_length;      
    // Get stream type and PID      
    for ( ; pos <= (packet->section_length + 2 ) -  4; )  
    {  
  	TS_PMT_Stream pmt_stream;  //流信息
  	pmt_stream.stream_type =  buffer[pos];  
 	packet->reserved_5  =   buffer[pos+1] >> 5;  
pmt_stream.elementary_PID =  ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;  
	packet->reserved_6     =   buffer[pos+3] >> 4;  
	pmt_stream.ES_info_length =   (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];  
    
  pmt_stream.descriptor = 0x00;  //描述符
  if (pmt_stream.ES_info_length != 0)  
  {  
   pmt_stream.descriptor = buffer[pos + 5];  
     
   for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )  
   {  
    pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];  
   }  
   pos += pmt_stream.ES_info_length;  
  }  
  pos += 5;  
  packet->PMT_Stream.push_back( pmt_stream );  //存储下来
  TS_Stream_type.push_back( pmt_stream );  
    }  
 return 0;  
}

至此,结束~
在这里插入图片描述
望诸位不忘三连支持一下~

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

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

相关文章

TwinCAT3 实时核中ADS实现C++ server、clinet数据传输

一、基本概念 ADS &#xff1a;Automation Device Specification&#xff0c;ADS设备间进行通信的协议规范。协议定义了ADS device之间如何寻址对方、ADS device之间可以执行哪些操作、执行这些操作需要哪些参数&#xff0c;以及操作完成后如何返回结果等。从编程角度看&#…

SVM 监督学习

一、分类问题 利用一条直线分类存在很多问题 二、SVM 支持向量机 其核心思想是通过在特征空间中找到一个最优的超平面来进行分类&#xff0c;并且间隔最大。分类面尽可能远离样本点&#xff0c;宽度越大越好。 适用于中小型复杂数据集的分类。 三、硬间隔和软间隔 硬&#x…

Android Studio -> Android Studio 获取release模式和debug模式的APK

Android Studio上鼠标修改构建类型 Release版本 激活路径&#xff1a;More tool windows->Build Variants->Active Build Variant->releaseAPK路径&#xff1a;Project\app\build\intermediates\apk\app-release.apk Debug版本 激活路径&#xff1a;More tool w…

linux上使用rpm的方式安装mysql

1.从mysql官网上下载需要的版本&#xff0c;根据操作系统版本&#xff0c;CPU架构&#xff0c;下载让rpm bundle,这个版本是个完整版&#xff0c;包含其他所有版本 上传到服务器的一个目录&#xff0c;进行解压 执行tar -xvf mysql*.tar tar -xvf mysql*.tar 2.卸载老版本m…

【Canvas与电脑桌面】用六角回旋镖铺满一个平面(1920*1080)

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>六角回旋镖桌面1920x1080</title><style type"text/cs…

动手学深度学习(pytorch)学习记录27-深度卷积神经网络(AlexNet)[学习记录]

目录 创建模型读取数据集训练AlexNet AlexNet 是由 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 在 2012 年提出的深度卷积神经网络&#xff0c;它在当年的 ImageNet 大规模视觉识别挑战赛&#xff08;ILSVRC&#xff09;中取得了显著的成绩&#xff0c;从而引起了深度…

动手学深度学习(pytorch土堆)-02TensorBoard的使用

1.可视化 代码使用了 torch.utils.tensorboard 将数据记录到 TensorBoard 以便可视化。具体来说&#xff0c;它将标量数据记录到目录 logs 中&#xff0c;使用的是 SummaryWriter 类。 代码分解如下&#xff1a; SummaryWriter("logs")&#xff1a;初始化一个 Ten…

常用的 git命令的使用

一. 简介 本文简单学习一下&#xff0c;在从远程仓库中拉取代码&#xff0c;或者向远程仓库提交代码时&#xff0c;经常用到的一些 git命令。 二. git的其他命令的使用 1. 重新提交代码的命令 当已经提交过一笔代码&#xff0c;并经过了 CI自动化编译通过。这时可能发现…

WebAPI(二)、DOM事件监听、事件对象event、事件流、事件委托、页面加载与滚动事件、client,offset

文章目录 一、 DOM事件1. 事件监听2. 事件类型(1)、鼠标事件(2)、焦点事件(3)、键盘事件(4)、文本事件 3. 事件对象(1)、获取事件对象(2)、事件对象常用属性 4. 环境对象 this5. 回调函数 二、 DOM事件进阶1. 事件流(1)、 捕获阶段(2)、 冒泡阶段(3)、 阻止冒泡(4) 、阻止元素默…

python绘制3D瀑布图

成品&#xff1a; 代码&#xff1a; def line_3d(x, y, z, x_label_indexs):"""在y轴的每个点&#xff0c;向x轴的方向延伸出一个折线面&#xff1a;展示每个变量的时序变化。x: x轴&#xff0c;时间维&#xff0c;右边。y: y轴&#xff0c;变量维&#xff0c;…

前端:JavaScript 实现类

文章目录 1. Es6-类-class2. Es6-class 实现继承3. Es6-class 静态属性和私有属性4. Es5-寄生组合式继承 1. Es6-类-class 类是创建对象的模板&#xff0c;用代码封装数据以处理该数据&#xff0c;js中的类建立在原型上。 如何定义类&#xff0c;首先需要关键字 class&#x…

C++之打造my vector篇

目录 前言 1.参照官版&#xff0c;打造vector的基本框架 2.丰富框架&#xff0c;实现接口方法 基本的迭代器实现 数据的[]访问 容量和数据空间的改变 vector空间大小的返回与判空 数据的增删 数据打印 拷贝构造和赋值重载 3.扩展延伸&#xff0c;深度理解代码 迭代器…

iText2KG:显著降低LLM构建知识图谱时的幻觉现象

1. 当前知识图谱构建存在的问题 知识图谱通过捕捉实体之间的关系来构建知识的结构化表示&#xff0c;在分析文本数据集和从结构化异构数据中推断知识方面具有显著优势。比如&#xff0c;知识图谱能够融合来自多个来源的不同数据&#xff0c;提供一个具有凝聚力的信息视角。还能…

【Python进阶】学习Python从入门到进阶,详细步骤,就看这一篇。文末附带项目演练!!!

详细的Python学习路线 1. Python基础 Python安装和环境配置&#xff1a;学习如何在你的操作系统上安装Python&#xff0c;并配置开发环境。变量和数据类型&#xff1a;学习如何定义变量&#xff0c;以及Python中的基本数据类型&#xff0c;如整数、浮点数、字符串等。 Pytho…

【人工智能学习笔记】4_3 深度学习基础之循环神经网络

循环神经网络(Recurrent Neural Network, RNN) 是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络(recursive neural network),循环神经网络具有短期记忆能力 RNN核心思想 RNN的结构 一个典型…

基于CNN-BiLSTM-Attention的流量预测 完整数据代码可直接运行

直接看视频: 基于CNN-BiLSTM-Attention的流量预测 完整数据代码可直接运行_哔哩哔哩_bilibili 模型: 有效提取径流时间序列的信息特征,提高径流预测模型的高维非线性拟合能力和预测性能的稳定性,将卷积神经网络(CNN),双向长短期记忆网络(BiLSTM)和注意力机制(attention)相…

tomcat端口被占用解决方法

在安装目录的conf下修改server.xml文件&#xff0c;修改后保存重启即可

十四、MySQL高级— 分库分表(7)

&#x1f33b;&#x1f33b; 目录 一、分库1.1 修改配置 schema.xml1.2 如何选择分库表1.3 SQLyog 连接 mycat 二、水平分表2.1 schema.xml2.2 rule.xml2.3 跨库join2.3.1 ER表2.3.2 全局表 2.4 全局序列2.4.1 本地文件2.4.2 数据库方式(一般都用这个)2.4.3 时间戳方式2.4.4 自…

时间序列预测学习方向总概括

推荐资源&#xff1a; 1.MA、AR、ARIMA 算法小陈-CSDN博客 2.informer论文讲解 【2024最火的两个模型&#xff1a;InformerLSTM两大时间序列预测模型&#xff0c;论文精读代码复现&#xff0c;究极通俗易懂&#xff01;——人工智能|AI|机器学习|深度学习-哔哩哔哩】 https…

微波无源器件 4 基于高阶定向耦合器的双极化波束形成网络

摘要&#xff1a; 一种Ka频段的双极化3dB定向耦合器被设计用于波束形成网络应用。所提出的解决方案对于紧凑Nolen网络。Nolen结构优于器平面和无损特别具有吸引力。两个平行方波导通过口径阵列耦合&#xff0c;设计用于获得两个正交极化之间的所需耦合和高隔离度。 索引词&…