H.264帧结构和RTSP协议源码框架

目录

 

1、H264编码原理和基本概念

1.1、h.264编码原理

1.2、h.264编码相关的一些概念

2、H264的NAL单元详解

2.1、VCL和NAL的关系

2.2、H.264视频流分析工具

2.3、h264视频流总体分析

2.4、相关概念

3、H264的NAL单元---sps和pps

3.1、sps和pps详解

3.2、H264的profile和level

3.3、sequence

4、rtsp协议和源码框架

4.1、源码框架函数调用关系

4.2、直接发送与环状buffer发送

4.3、RTP发送一帧数据的两种不同发送格式->整发送和分包发送


 

1、H264编码原理和基本概念

1.1、h.264编码原理

(1)图像冗余信息:空间冗余、时间冗余

(2)视频编码关键点:压缩比高、算法复杂度小、还原度高

(3)H.264的2大组成部分:VCL和NAL

        VCL关心如何进行视频压缩

        NAL关心压缩后的视频流如何被网络传输和解码播放

1.2、h.264编码相关的一些概念

(1)宏块 MB(macroblock):多个像素组成的一块,宏块是视频压缩算法的最基本单位

由于图像本身在局部,颜色具有相似性,所以可以把一幅图像分成若干个宏块

(2)片 slice:构成帧的一部分

        一帧图像=若干个slice(可以是一个slice)

        一个slice=若干个MB

        一个MB=多个像素

(3)帧 frame:一整幅完整的图像

(4)I帧、B帧、P帧:帧有好几种类型

I帧:非参考帧,这一帧图像的内容只和本身有关,和前一帧后一帧图像的内容无关,一般作为起始帧,因为这一帧没有任何参考,所以只能对这一帧进行帧内压缩(空间冗余上的优化)

B帧:参考帧,这一帧图像的内容不光和本身有关,还和前一帧或后一帧图像的内容有关(空间冗余+时间冗余的优化)

P帧:参考帧,这一帧图像的内容不光和本身有关,还和前一帧图像的内容有关(空间冗余+时间冗余的优化)

(5)帧率 fps:一秒中有多少帧,帧率高(慢动作),帧率低(快动作)

(6)像素->宏块->片->帧->序列->码流

2、H264的NAL单元详解

2.1、VCL和NAL的关系

(1)VCL只关心编码部分,重点在于编码算法以及在特定硬件平台的实现,VCL输出的是编码后的纯视频流信息,没有任何头信息

(2)NAL关心的是VCL的输出纯视频流如何被表达和封包以利于网络传输

(3)SODB:String Of Data Bits(VCL输出的纯视频流)

(4)RBSP:Raw Byte Sequence Payload

(5)NALU:Network Abstraction Layer Units        NALU是H264文件的基本组成单元

(6)关系:

SODB + RBSP trailing bits = RBSP

NALU header(1 byte) + RBSP = NALU

H264文件由若干个序列组成 -> 序列由若干个帧/slice组成 -> 帧/slice由分隔符和NALU单元组成 -> 去掉NALU header得到RBSP -> 去掉RBSP trailing得到SODB-> VLC播放器解码播放SODB

(7)总结:做编码器的人关心的是VCL部分;做视频传输和解码播放的人关心的是NAL部分

2.2、H.264视频流分析工具

(1)雷神作品:SpecialVH264.exe

(2)国外工具:Elecard StreamEye Tools

(3)二进制工具:winhex

(4)网络抓包工具:wireshark

(5)播放器:vlc

2.3、h264视频流总体分析

(1)h264标准有多个版本,可能会有差异,具体差异不详

(2)网上看的资料有时讲法会有冲突,或者无法验证的差异

(3)这里以海思平台为主、为准、为案例,不能保证其他平台也完全一样

(4)海思平台编码出来的H.264码流(就是一个二进制文件)都是一个一个序列组成,序列包含:1sps+1pps+1sei+1I帧+若干p帧

3a52b471bc984eb98d57f907f9d2e64d.png

2.4、相关概念

(1)序列 sequence,一般一秒发一个sequence,也就是说sequence(除去sps、pps、sei)和帧率相等

(2)分隔符:00 00 00 01的四字节组合,是用来做识别的,不是有效数据,表示一个slice的开始,表示我们有一个新的片到来。

d8fcfe76a46d42b884f6c5575dcaa3a8.png

帧的有效数据部分是不会出现(00 00 00 01)的,h264的标准规定有效数据不允许出现连续的3个00,会在第二个00后面,第三个00前面添加一个03进去,用(00 00 03 00)来表示(00 00 00)。

(3)sps

(4)pps

(5)sei

(6)NALU header:分隔符后的第一个字节

914df4ea696f4168ab46dd2c24380990.png

NALU header有8个位

bit7(forbidden_zero_bit):禁止位,默认位0,值为1表示语法出错

bit6~5(nal_ref_idc):重要性,代表这一帧的内容在视频流中的重要程度,重要程度由大到小11>10>01>00

bit4~1(nal_unit_type):NAL单元类型

f4574dd7343640338fde646a0dbde61b.png

1:表示 非I帧,具体是P还是B帧不知道

5:表示 I帧

6:表示 SEI

7:表示 SPS

8:表示 PPS

参考博文:

H264(NAL简介与I帧判断)_h264 判断i帧_jefry_xdz的博客-CSDN博客

 

3、H264的NAL单元---sps和pps

参考博文:

https://www.cnblogs.com/wainiwann/p/7477794.html

3.1、sps和pps详解

在H.264标准协议中规定了多种不同的NAL Unit类型,其中类型7表示该NAL Unit内保存的数据为Sequence Paramater Set(序列参数集),描述这个序列的参数信息都在这里。在H.264的各种语法元素中,SPS中的这些参数信息至关重要。如果其中的数据丢失或出现错误,那么解码过程很可能会失败。SPS和PPS中的信息会用于播放器的初始化使用。

SPS中保存了一组视频序列的全局参数。所谓的视频序列即原始视频的一帧一帧的图像数据经过编码之后组成的序列。而每一帧的编码后数据所依赖的参数保存于图像参数集(PPS)中,例如P帧需要参考前面的帧进行编码,那么是怎么参考的,这个参考数据就保存在PPS中。

一般情况SPS和PPS位于整个码流的起始位置。但在某些特殊情况下,在码流中间也可能出现这两种结构,主要原因可能为:

  • 解码器需要在码流中间开始解码;
  • 编码器在编码的过程中改变了码流的参数(如图像分辨率等);

视频播放器为了让后续的解码过程可以使用SPS中包含的参数,必须对SPS其中的数据进行解析。其中H.264标准协议中规定的SPS格式位于文档的7.3.2.1.1部分,如下图所示:

2c6060b688da4e33b1b62e52c1f37853.png

(1) profile_idc:

标识当前H.264码流的profile。我们知道,H.264中定义了三种常用的档次profile:

基准档次:baseline profile;

主要档次:main profile;

扩展档次:extended profile;

在H.264的SPS中,第一个字节表示profile_idc,根据profile_idc的值可以确定码流符合哪一种档次。判断规律为:

profile_idc = 66(0x42) → baseline profile;

profile_idc = 77 → main profile;

profile_idc = 88 → extended profile;

在新版的标准中,还包括了High、High 10、High 4:2:2、High 4:4:4、High 10 Intra、High 4:2:2 Intra、High 4:4:4 Intra、CAVLC 4:4:4 Intra等,每一种都由不同的profile_idc表示。

当前码流中,profile_idc = 0x42 = 66 ,因此profile档次为 baseline profile;

8eb9eae6d74d47e98d2ac45987d27d6e.png

(2) level_idc

标识当前码流的Level。编码的Level定义了某种条件下的最大视频分辨率、最大视频帧率等参数,码流所遵从的level由level_idc指定。

455fbb5138304c76a06574a73ec7fb0b.png

当前码流中,level_idc = 0x1F = 31,因此码流的级别为3.1。

Level为3.1的特性:

c99f45c9897d4b54b2e145f722a362e6.png

3.2、H264的profile和level

参考博文:

h264中profile和level的含义_xiaojun11-的博客-CSDN博客

Profile是对视频压缩特性的描述(CABAC呀、颜色采样数等等),简而言之就是压缩算法的档次。Level是对视频本身特性的描述(码率、分辨率、fps)。

简单来说,Profile越高,就说明采用了越高级的压缩特性(越高级的压缩算法)。Level越高,视频的码率、分辨率、fps越高。

一些移动设备(手机、游戏机、PMP)由于性能有限,不支持全部高级视频压缩特性和高分辨率图像,只支持基础压缩特性和分辨率低一些的图像。为了让这个限制更加清晰明了,H264从低到高划分了很多Profile和Level。

profile主要是定义了编码工具(压缩算法)的集合,不同的profile,包含了不同的编码技术;

h.264的profile有三种Baseline Profile(基本的)、Main Profile(主线的)、High Profile(高等级的)

3.3、sequence

(1)一段h.264的码流其实就是多个sequence组成的

(2)每个sequence均有固定结构:1sps+1pps+1sei+1I帧+若干p帧

(3)sps和pps和sei描述该sequence的图像信息,这些信息有利于网络传输或解码

(4)I帧是关键,丢了I帧整个sequence就废了,因为I帧是自参考的,其它的帧都参考于它,每个sequence有且只有1个I帧

(5)p帧的个数等于fps-1

(6)I帧越大则P帧可以越小,反之I帧越小则P帧会越大

I帧越大,说明I帧本身压缩比例不高,参考信息比较多,比较详细,所以P帧就越好参考,就可以越小

(7)I帧的大小取决于图像本身内容,和压缩算法的空间压缩部分

如果图像本身色彩丰富、画面复制,那么I帧就会很大

(8)P帧的大小取决于图像变化的剧烈程度

(9)CBR和VBR下P帧的大小策略会不同,CBR时P帧大小基本恒定,VBR时变化会比较剧烈

CBR:固定码率

VBR:可变码率

4、rtsp协议和源码框架

rtsp协议参考:

链接:https://pan.baidu.com/s/1IHU8vaFShMq3mOUz3c2iDw?pwd=0000 
提取码:0000

4.1、源码框架函数调用关系

mainRtspServer_initRtspServerListen								    //线程函数---达到PLAY状态socket                            		        //创建TCP套接字setsockopt                                 	    //设置端口复用bind                                      	    //绑定服务器IP和端口listen								            //监听、并告诉linux系统这是个服务器while(accept)							        //不断等待客户端连接setsockoptfor                                         //实际只执行一次RtspClientMsg                           //此函数是重点,创建一个线程去处理这此连接pthread_detach                      //分离线程,子线程结束后自动回收资源while                               //如果当前不是未连接状态{recv                            //读1024个字节if(没读到){退出线程}ParseRequestString              //解析客户端发来的Requst请求并填充字符串数组    if(是OPTIONS请求)OptionAnswer                //返回服务器提供的可用方法sprintf                 //组包strcat                  //连接RTSP头和sdp信息send                    //发送if(是DESCRIBE请求)DescribeAnswer              //返回sdp包给播放器client去配置解码GetLocalIP              //获取本地IPioctlsprintf                 //组包send                    //发送if(是SETUP请求)SetupAnswerParseTransportHeader    //解析客户端发来的SETUP Request,然后填充到变量中GetLocalIP              //获取本地IPsprintf                 //组包send                    //发送if(是PLAY请求)PlayAnswersprintf                 //组包send                    //发送socket                  //创建一个UDPsocket//由此可见,之前的TCPsocket是一个命令通道,这里的UDPsocket是一个传输数据的通道if(是PAUSE请求)                 //暂停请求PauseAnswer                 //源码分析得知,这里并没有实现暂停码流的传输sprintf                 //组包send                    //发送if(是TEARDOWN请求)TeardownAnswersprintf                 //组包send                    //发送close(udp)              //关闭数据传输通道UDPsocketclose(tcp)                  //关闭命令传输通道TCPsocket}   vdRTPSendThread                                     //线程函数---发送流媒体的数据while(1)                                        //每5ms判断当前是否有数据要发送{if(!list_empty)                             //链表为空,则表明当前没有数据要发送{get_first_item                          //取出链表中第一个非空节点VENC_Sent//发送码流数据sendto                              //UDP发送list_del                                //从链表中把节点去掉}usleep(5000);                               //延时5ms}SAMPLE_VENC_720P_CLASSICSAMPLE_COMM_VENC_StartGetStreamSAMPLE_COMM_VENC_GetVencStreamProc              //线程函数HI_MPI_VENC_GetChnAttrSAMPLE_COMM_VENC_GetFilePostfixHI_MPI_VENC_GetFdwhile{HI_MPI_VENC_QuerymallocHI_MPI_VENC_GetStream                   //获取一帧编码完的码流//以下两种方式二选一SAMPLE_COMM_VENC_Sentjin                //此函数是重点---编码完后直接发送VENC_Sent                           //直接发送    saveStream                              //此函数是重点---编码完后保存到环状buf中malloc                              //生成一个链表节点INIT_LIST_HEAD                      //初始化这个节点的next指针和prev指针都指向自己填充节点内容list_add_tail                       //将节点加入链表//当我将编码完的数据保存到环形链表后,此时链表就有节点数据了//然后就可以在 vdRTPSendThread 线程中发送了HI_MPI_VENC_ReleaseStreamfree}

4.2、直接发送与环状buffer发送

直接发送:编码完直接发送,编多少发多少

代码体现在:

SAMPLE_COMM_VENC_GetVencStreamProc线程函数:HI_MPI_VENC_GetStream            //获取编码完的码流SAMPLE_COMM_VENC_Sentjin         //发送函数

缺陷:视频采集和编码速度 与 网络传输速度 都是变动的,速度不一定一致,会导致系统进入漫长的等待,不能更好的实时视频监控

 

环状buffer发送:编码完放到环状buffer中,待发送

环状buffer即环形链表,当生产者(获取码流的线程)编码完一帧数据就将这帧数据作为节点加入链表,消费者(发送线程)只管从中取。

对比代码:SAMPLE_COMM_VENC_GetVencStreamProc线程函数中,调用HI_MPI_VENC_GetStream获取编码完的码流,调用saveStream将码流数据节点加入链表,vdRTPSendThread线程从链表中取走节点,然后调用VENC_Sent发送。

当链表中没有节点,则消费者要阻塞,等待生产者编码完将数据节点加入链表。环形链表相比数组的优点是使用内存可以足够大,直到内存撑爆。

代码体现在:

SAMPLE_COMM_VENC_GetVencStreamProc线程函数:HI_MPI_VENC_GetStream         //获取编码完的码流saveStream                    //编码完后保存到环状buf中
vdRTPSendThread线程函数:get_first_item                //取出链表中第一个非空节点VENC_Sent                     //发送码流数据list_del                      //从链表中把节点去掉

接下来就只用关注VENC_Sent函数内是怎么组包,然后通过UDP传输码流数据了。。

4.3、RTP发送一帧数据的两种不同发送格式->整发送和分包发送

关于rtp头的timestamp位:
RTP timestamp与帧率及时钟频率的关系_帧率 时钟频率_jasonhwang的博客-CSDN博客

关于RTP发送一帧数据的两种不同发送格式:整包发送和分包发送

https://www.cnblogs.com/yjg2014/p/6144977.html

VENC_Sent函数全解:

HI_S32 VENC_Sent(char *buffer,int buflen)
{HI_S32 i;int is=0;int nChanNum=0;for(is=0;is<MAX_RTSP_CLIENT;is++){if(g_rtspClients[is].status!=RTSP_SENDING)//判断是否是 可发送 状态{continue;}int heart = g_rtspClients[is].seqnum % 10000;//seqnum:序列号,对10000取余char* nalu_payload;int nAvFrmLen = 0;int nIsIFrm = 0;int nNaluType = 0;char sendbuf[500*1024+32];nAvFrmLen = buflen;//音频或视频数据一帧的长度struct sockaddr_in server;server.sin_family=AF_INET;server.sin_port=htons(g_rtspClients[is].rtpport[0]);   //对方的端口       server.sin_addr.s_addr=inet_addr(g_rtspClients[is].IP);//对方的IPint    bytes=0;unsigned int timestamp_increse=0;timestamp_increse=(unsigned int)(90000.0 / 25);    //  两帧之间RTP timestamp的增量 = 时钟频率 / 帧率rtp_hdr =(RTP_FIXED_HEADER*)&sendbuf[0];         //填充RTP包头rtp_hdr->payload     = RTP_H264;   rtp_hdr->version     = 2;                          //RTP版本号rtp_hdr->marker    = 0;           rtp_hdr->ssrc      = htonl(10);                   //信源标记if(nAvFrmLen<=nalu_sent_len)    //如果这一帧数据的长度<=1400{rtp_hdr->marker=1;                            //标记一帧的结束rtp_hdr->seq_no     = htons(g_rtspClients[is].seqnum++);     //下一帧的序列号nalu_hdr =(NALU_HEADER*)&sendbuf[12]; nalu_hdr->F=0; nalu_hdr->NRI=  nIsIFrm; nalu_hdr->TYPE=  nNaluType;nalu_payload=&sendbuf[13];memcpy(nalu_payload,buffer,nAvFrmLen);g_rtspClients[is].tsvid = g_rtspClients[is].tsvid+timestamp_increse;            rtp_hdr->timestamp=htonl(g_rtspClients[is].tsvid);bytes=nAvFrmLen+ 13 ;                sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));}else if(nAvFrmLen>nalu_sent_len)    //如果这一帧数据的长度>1400{int k=0,l=0;k=nAvFrmLen/nalu_sent_len;                    //这一帧数据的长度/1400l=nAvFrmLen%nalu_sent_len;                    //这一帧数据的长度对1400取余int t=0;        g_rtspClients[is].tsvid = g_rtspClients[is].tsvid+timestamp_increse;    //计算timestamprtp_hdr->timestamp=htonl(g_rtspClients[is].tsvid);                 //把timestamp放入RTP包头       while(t<=k)            //循环k+1次,也就是发k+1个包,代表这一帧发完{rtp_hdr->seq_no = htons(g_rtspClients[is].seqnum++);if(t==0)        //第一包{rtp_hdr->marker=0;            //填充完RTP_header的12个字节后,接下来填充的是NALU的分包形式fu_ind =(FU_INDICATOR*)&sendbuf[12];//fu_indicato是NALU头的分包形式的第一部分fu_ind->F= 0; fu_ind->NRI= nIsIFrm;fu_ind->TYPE=28;                    //Type为FU-Afu_hdr =(FU_HEADER*)&sendbuf[13];    //fu_header     是NALU头的分包形式的第二部分fu_hdr->E=0;fu_hdr->R=0;fu_hdr->S=1;                        //表示帧开始fu_hdr->TYPE=nNaluType;                //NALU typenalu_payload=&sendbuf[14];    //填充完NALU的分包形式后,接下来开始填充有效数据memcpy(nalu_payload,buffer,nalu_sent_len);bytes=nalu_sent_len+14;        //发送的长度是有效数据长度+14(14就是前面添加的这些信息)                sendto( udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));t++;}else if(k==t)    //最后一包{rtp_hdr->marker=1;                    //标记一帧的结束fu_ind =(FU_INDICATOR*)&sendbuf[12]; fu_ind->F= 0 ;fu_ind->NRI= nIsIFrm ;fu_ind->TYPE=28;fu_hdr =(FU_HEADER*)&sendbuf[13];fu_hdr->R=0;fu_hdr->S=0;fu_hdr->TYPE= nNaluType;fu_hdr->E=1;                        //表示帧结束,和marker一样nalu_payload=&sendbuf[14];memcpy(nalu_payload,buffer+t*nalu_sent_len,l);bytes=l+14;        sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));t++;}else if(t<k && t!=0)    //中间的包{rtp_hdr->marker=0;fu_ind =(FU_INDICATOR*)&sendbuf[12]; fu_ind->F=0; fu_ind->NRI=nIsIFrm;fu_ind->TYPE=28;fu_hdr =(FU_HEADER*)&sendbuf[13];//fu_hdr->E=0;fu_hdr->R=0;fu_hdr->S=0;fu_hdr->E=0;                        //S=0,E=0表示既不是开头也不是结尾fu_hdr->TYPE=nNaluType;nalu_payload=&sendbuf[14];memcpy(nalu_payload,buffer+t*nalu_sent_len,nalu_sent_len);bytes=nalu_sent_len+14;    sendto(udpfd, sendbuf, bytes, 0, (struct sockaddr *)&server,sizeof(server));t++;}}}}//------------------------------------------------------------
}

 

 

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

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

相关文章

【从删库到跑路】详细讲解MySQL的函数和约束作用

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; 文章目录 &#x1f354;函数⭐字符串函数&#x1f388;字符串拼接函数&…

途乐证券|人工智能概念再度下挫 海天瑞声、寒武纪等跌超10%

人工智能概念26日盘中大幅回调&#xff0c;截至发稿&#xff0c;当虹科技跌近18%&#xff0c;美亚柏科、昆仑万维跌约13%&#xff0c;博睿数据、光云科技、海天瑞声、寒武纪等跌超10%&#xff0c;焦点科技跌停&#xff0c;云从科技、朗玛信息、三六零等跌超9%。 香港途乐证券有…

如何在编程中中实现负载均衡和容错处理

什么是容错 容错是指系统&#xff08;计算机、网络、云集群等&#xff09;在其一个或多个组件发生故障时继续运行而不会中断的能力。 创建容错系统的目的是防止由单点故障引起的中断&#xff0c;确保任务关键型应用程序或系统的高可用性和业务连续性。 容错系统使用备份组件…

走进人工智能|深度学习 算法的创世纪

前言&#xff1a; 深度学习通过训练深层神经网络模型&#xff0c;可以自动学习和提取数据的特征&#xff0c;包括更准确的图像识别、自然语言处理、医学诊断等方面的应用。 文章目录 序言背景算法的创世纪技术支持应用领域程序员如何学总结 序言 深度学习是一种机器学习方法&a…

【2023年江西省研究生数学建模竞赛】题目一 蒸汽发生器倒U型管内液体流动 建模方案及参考文献

代码与结果如下&#xff1a;完整文档见文末 完整思路”请点击这里“到原文章获取 题目&#xff1a; PACTEL压水堆整体测试设备在2009年建造&#xff0c;用于带有垂直倒U型管蒸汽发生器的压水堆热液压相关的安全性研究,参见图1。 PACTEL压水堆设施包括一个反应堆压力容器模型…

2.ThreadLocalRandom

Random类及其局限性 一般情况下&#xff0c;我们都会使用java.util.Random来生成随机数&#xff08;Math.random()也是使用Random实例生成随机数&#xff09;。 示例 public static void main(String[] args) {Random random new Random();for (int i 0; i < 10; i) {S…

LED显示屏结构

LED显示屏通常由以下几个主要组成部分构成&#xff1a; LED模块&#xff1a;LED模块是构成LED显示屏的基本单元。它包含多个LED点阵&#xff0c;每个点阵包含红色、绿色和蓝色LED灯珠&#xff0c;通过不同的亮度和颜色组合来呈现图像和视频。LED模块的尺寸和像素密度可以根据需…

训练自己的ChatGPT 语言模型(一).md

0x00 Background 为什么研究这个&#xff1f; ChatGPT在国内外都受到了广泛关注&#xff0c;很多高校、研究机构和企业都计划推出类似的模型。然而&#xff0c;ChatGPT并没有开源&#xff0c;且复现难度非常大&#xff0c;即使到现在&#xff0c;没有任何单位或企业能够完全复…

利用低代码平台实现协同办公,助力企业提升效益

概要&#xff1a;本文介绍了协同办公的作用&#xff0c;以及利用低代码平台实现协同办公的优势。同时也分享了天翎为华晨汽车打造的低代码协同工具&#xff0c;帮助企业提高管理效率&#xff0c;改善运营模式&#xff0c;提升产品质量及生产精益化。展示了咨询库、原料质量录入…

实习工作之定时任务

需求&#xff1a;在当天晚上12点定时将过期数据的状态置为冻结状态 版本一代码实现[相当于是一个死代码&#xff0c;因为不能自动调度] public void updateStatus() throws CommonException, ParseException {String date_str "2023-07-01 00:00:00";Date expireT…

阿里云盘如何实现 大文件 秒上传?

文章目录 Intro极速上传的原因隐私保护 Intro 今天把几个软件上传到阿里云盘进行分享&#xff0c;文件大小将近1GB&#xff0c;按理说上传需要个2~3分钟吧。 之前上传一个压缩包看到上传速度大概是4~5MB/s。 但是我刚到别的软件看了一圈&#xff0c;回来发现文件居然已经上传…

华为云专家出品《从零到一•Python图像处理入门》电子书

《华为云云享.书库》系列电子书来啦&#xff01; 本系列电子书旨在帮助开发者成长&#xff0c;汇聚华为云内外部专家技术精华制作而成。 本书《从零到一•Python图像处理》是该系列电子书第3部。 我们在华为开发者即将到来之际&#xff0c;开放电子书免费下载。 点击下方链接…