【音视频 | H.264】H.264视频编码及NALU详解

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍H.264视频编码🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭

本文未经允许,不得转发!!!

目录

  • 🎄一、概述
    • ✨1.1 为什么需要对视频进行编码?
    • ✨1.2 视频编码本质
    • ✨1.3 视频压缩
  • 🎄二、H.264相关概念
    • ✨2.1 序列(GOP,Group of picture)
    • ✨2.2 图像(I帧、P帧、B帧)
    • ✨2.3 片(Slice)
    • ✨2.4 宏块(Macroblock)
    • ✨2.5 子宏块(subblock)
  • 🎄三、网络提取层(NAL,Network Abstraction Layer)——解码必读
    • ✨3.1 H.264原始码流
    • ✨3.2 NALU(NAL Unit)
      • 🎇3.2.1 NAL头信息(NAL header)
      • 🎇3.2.2 原始字节序列负荷(RBSP,Raw Byte Sequence Payload)
  • 🎄四、总结


在这里插入图片描述

🎄一、概述

H.264,同时也是MPEG-4第十部分,是由ITU-T视频编码专家组(VCEG)和ISO/IEC动态图像专家组(MPEG)联合组成的联合视频组(JVT,Joint Video Team)提出的高度压缩数字视频编解码器标准。这个标准通常被称之为H.264/AVC(或AVC/H.264,或H.264/MPEG-4 AVC,或MPEG-4/H.264 AVC)。

✨1.1 为什么需要对视频进行编码?

因为视频数据太大,一帧YUV420图像的大小为:width * height * 3 / 2 ,假设分辨率是1920x1080的,则一帧视频数据就是1920 * 1080 * 3/2 = 3,110,400。一个小时的1920x1080@30的视频数据就是:(1920 * 1080 * 3 / 2)* 30 * 60 * 60 = 335,923,200,000字节,约等于312G字节。这显然不利于存储和传输。

✨1.2 视频编码本质

视频编码本质就是去除冗余信息。视频中存在很多冗余信息,比如图像相邻像素之间有较强的相关性,视频序列的相邻图像之间内容相似,人的视觉系统对某些细节不敏感等,对这部分冗余信息进行处理的过程就是视频编码。

✨1.3 视频压缩

H.264 采用了16 * 16的分块大小对视频帧图像进行相似比较和压缩编码。如下图所示:

在这里插入图片描述

帧内压缩

帧内压缩,指的是一帧图片的内部压缩。当H.264对图片进行 16 * 16 分块后,会对每个小块内的图像进行分析,如果2个小块图像比较相近,那么住需要存储一张即可,无需存储重复图块。这样可以有效压缩图片的存储大小。

帧内压缩是生成I帧的算法 。

帧内(Intraframe)压缩的原理是:当压缩一帧图像时,仅考虑本帧的数据而不考虑相邻帧之间的冗余信息,一般采用有损压缩算法,由于帧内压缩是编码一个完整的图像,所以可以独立的解码、显示。帧内压缩率一般不高。

帧间压缩

帧间压缩 , 指的是图片间的图像压缩。在每帧图片划分成16 * 16 小块的图像进行分析基础上,比图片间的数据,如果两张图片比较相近,对相同的图像模块只需存储一份,对不同的部分再做存储。避免了重复数据的存储,极大改善了图片压缩空间。

帧间压缩是生成B帧和P帧的算法 。

帧间(Interframe)压缩的原理是:相邻几帧的数据有很大的相关性,或者说前后两帧信息变化很小的特点。连续的视频其相邻帧之间具有冗余信息,根据这一特性,压缩相邻帧之间的冗余量就可以进一步提高压缩量,减小压缩比。

帧间压缩也称为时间压缩(Temporalcompression),它通过比较时间轴上不同帧之间的数据进行压缩。帧间压缩是无损的,它通过比较本帧与相邻帧之间的差异,仅记录本帧与其相邻帧的差值,这样可以大大减少数据量。


在这里插入图片描述

🎄二、H.264相关概念

H.264 的功能分为两层,即视频编码层( VCL, Video Coding Layer)和网络提取层( NAL, Network Abstraction Layer)。 VCL 数据即编码处理的输出,它表示被压缩编码后的视频数据序列。在 VCL 数据传输或存储之前,这些编码的 VCL 数据,先被映射或封装进 NAL 单元中。

本节介绍的概念大部分都是视频编码层( VCL, Video Coding Layer)的。

在H.264 中,句法元素共被组织成 序列(GOP,Group of picture)、图像(picture)、片(Slice)、宏块(Macroblock)、子块(subblock) 五个层次。

在这里插入图片描述


✨2.1 序列(GOP,Group of picture)

序列,有时也被称为画面组( Group Of Picture ),在H264中图像以序列为单位进行组织,一个序列是一组图像编码后的数据流。一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像(I 帧不一定是IDR图像)。

GOP(图像组)常用作形容一个IDR帧 到下一个IDR帧之间的间隔了多少个帧。 例如:1080P@60 的视频的GOP是120的话,说明这段数据是2秒一个IDR帧。

在这里插入图片描述


✨2.2 图像(I帧、P帧、B帧)

一个序列由连续的图像(帧)组成。在H.264的协议中,定义了三类帧,分别是I帧、B帧和P帧。

  • I帧:完整编码的帧叫I帧,是一个图像经过压缩后的产物,自身可以通过视频解压算法解压成一张单独的完整的图片;

    注意:I帧又分为普通I帧和IDR帧,IDR帧可以认为是序列的首个I帧(但是很多场景I帧都是IDR帧),这样区分视为了方便控制编码和解码的流程。 IDR帧一定是I帧,但是I帧不一定是IDR帧。

    IDR帧因为附带SPS、PPS等信息,解码器在收到 IDR 帧时,需要做的工作就是:把所有的 PPS 和 SPS 参数进行更新。 将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现错误,在这里可以获得重新同步的机会。IDR帧之后的帧永远不会使用IDR帧之前的数据来解码。

  • P帧:参考之前的I帧生成的只包含差异部分编码的帧叫P帧, 需要参考其前面的一个I 帧或者P 帧来生成一张完整的图片 ;

  • B帧:参考前后的帧编码的帧叫B帧 , 要参考其前一个I或者P帧及其后面的一个P帧来生成一张完整的图片 。

在这里插入图片描述


✨2.3 片(Slice)

在这里插入图片描述

一个视频图像可编码成一个或更多个片,每片包含整数个宏块( MB,Macroblock),即每片至少一个宏块 ,最多时每片包含整个图像的宏块。总之,一幅图像中每片的宏块数不一定固定。

设片的目的是为了限制误码的扩散和传输,应使编码片相互间是独立的。某片的预测不能以其它片中的宏块为参考图像,这样某一片中的预测误差才不会传播到其它片中去。

对于切片(slice)来讲,分为以下几种类型:

  • I片:只包 I宏块。I 宏块利用从当前片中已解码的像素作为参考进行帧内预测(不能取其它片中的已解码像素作为参考进行帧内预测);

  • P片:可包 P和I宏块。P 宏块利用前面已编码图象作为参考图象进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割:即 16×16、16×8、8×16 或 8×8 亮度像素块(以及附带的彩色像素);如果选了 8×8 的子宏块,则可再分成各种子宏块的分割,其尺寸为 8×8、8×4、4×8 或 4×4 亮度像素块(以及附带的彩色像素);

  • B片:可包 B和I宏块。B 宏块则利用双向的参考图象(当前和未来的已编码图象帧)进行帧内预测;

  • SP片(切换P):用于不同编码流之间的切换,包含 P 和/或 I 宏块;

  • SI片:扩展档次中必须具有的切换,它包 了一种特殊类型的编码宏块,叫做 SI 宏块,SI 也是扩展档次中的必备功能。

在这里插入图片描述


✨2.4 宏块(Macroblock)

宏块是视频信息的主要承载者,因为它包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中的像素阵列。

一个编码图像通常划分成若干宏块组成,一个宏块由一个 16×16 亮度像素和附加的一个 8×8 Cb 和一个 8×8 Cr 彩色像素块组成。每个图像中,若干宏块被排列成片的形式。

在这里插入图片描述


✨2.5 子宏块(subblock)

子宏块就是宏块分割之后的结果。每个宏块( 16×16 像素)可以 4 种方式分割:一个 16×16,两个 16×8,两个 8×16,四个 8×8。其运动补偿也相应有四种。而 8×8 模式的每个子宏块还可以四种方式分割:一个 8×8,两个 4×8 或两个 8×4 及 4 个 4×4。这些分割和子宏块大大提高了各宏块之间的关联性。这种分割下的运动补偿则称为树状结构运动补偿。

在这里插入图片描述


在这里插入图片描述

🎄三、网络提取层(NAL,Network Abstraction Layer)——解码必读

无论是存储还是网络传输,H264 原始码流是由一个接一个 NALU(NAL Unit) 组成,它的功能分为两层,VCL(Video Coding Layer)视频编码层和 NAL(Network Abstraction Layer)网络提取层。

VCL:包括核心压缩引擎和块、宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码;
NAL:负责将 VCL 产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片级以上的语法级别;

✨3.1 H.264原始码流

H.264原始码流(裸流)是由一个接一个NALU组成, 每个NALU之间都使用start code(起始码)分隔,NALU单元通常由[StartCode] [NALU Header] [NALU Payload] 三部分组成,其中 Start Code 用于标示这是一个NALU 单元的开始,必须是00 00 00 0100 00 01。每个 NALU包括一个头部信息(NAL header)和一个原始字节序列负荷(RBSP,Raw Byte Sequence Payload)。

在这里插入图片描述

关于 start code(起始码):

为了节省码流, H.264 没有另外在 NAL 的头部设立表示起始的句法元素 。 但是如果编码数据时,由于 NAL 是依次紧密排列,解码器将无法在数据流中分辨每个 NAL的起始和终止,所以必须要有另外的机制来解决这个问题 。

所以,H.264 提出方案,在存储或传输时,在每个NALU前加上00 00 00 0100 00 01,同时提出”防止竞争“的机制(感兴趣可以去h264文档了解),来避免 NALU 内部出现了00 00 00 0100 00 01的情况。

什么时候使用00 00 00 01 ,什么时候使用 00 00 01 ?一个说法是为了4字节对齐, 4字节类型的开始码通常只用于标识流中的随机访问点,如SPS PPS AUD和IDR,然后其他地方都用3字节类型的开始码以减少数据量 。另一个说法是, 一个完整的帧被编为多个slice(片)的时候StartCode是 00 00 01, 否则都是00 00 00 01


✨3.2 NALU(NAL Unit)

NALU(NAL Unit),也就是NAL 单元。每个NALU包含了一个字节大小的NALU头信息(NAL header),以及一个原始字节序列负荷(RBSP,Raw Byte Sequence Payload)。下图展示了多个NALU排列:

在这里插入图片描述

接下来看下面这个NALU示意图:
在这里插入图片描述
注意:

  • 从上图我们可以知道,一张图片可以有多个NALU;
  • 对解码器来说,需要先收到SPS和PPS进行初始化,否则解码器无法解出正常的帧数据。
  • 发I帧之前,至少要发送一次SPS和PPS,因此如果在实际应用中遇到H264无法解码的时候,检查SPS和PPS是否有接收到并正常初始化。

🎇3.2.1 NAL头信息(NAL header)

在这里插入图片描述

  • forbidden_zero_bit (F,占1bit)
    在 H.264 规范中规定了这⼀位必须为 0 。

  • nal_ref_idc (NRI,占2bit)
    NAL重要性,值越大,越重要,解码器在解码处理不过来的时候,可以丢掉重要性为0的NALU,而不影响图像的回放 。 如果当前NALU是属于参考帧的片,或是序列参数集,或是图像参数集这些重要的单位时,本句法元素必需大于0。

  • nal_unit_type(Type,占5bit):

    这个NALU单元的类型,1~12由H.264使用,24~31由H.264以外的应用使用。

    NALU类型的详细解析表 :

    nal_unit_typeNAL单元和RBSP语法结构的内容
    0未指定
    1一个非 IDR 图像的编码条带(p帧/b帧)
    2编码条带数据分割块 A
    3编码条带数据分割块 B
    4编码条带数据分割块 C
    5IDR 图像的编码条带(IDR帧)
    6辅助增强信息 (SEI)
    7序列参数集
    8图像参数集
    9访问单元分隔符
    10序列结尾
    11流结尾
    12填充数据
    13序列参数集扩展
    14-18保留
    19未分割的辅助编码图像的编码条带
    20-23保留
    24-31未指定

    下表是常见的NALU类型:

    十六进制、二进制类型重要性类型值
    0x67 (0 11 00111)SPS非常重要type = 7
    0x68 (0 11 01000)PPS非常重要type = 8
    0x65 (0 11 00101)IDR帧关键帧 非常重要type = 5
    0x61 (0 11 00001)I帧非常重要type=1非IDR的I帧不大常见
    0x41 (0 10 00001)P帧重要type = 1
    0x01 (0 00 00001)B帧不重要type = 1
    0x06 (0 00 00110)SEI不重要type = 6

🎇3.2.2 原始字节序列负荷(RBSP,Raw Byte Sequence Payload)

RBSP 指原始字节序列载荷,它是 NAL 单元的数据部分的封装格式,封装的数据来自 SODB(String Of Data Bits,原始数据比特流),SODB 是编码后的原始数据, SODB 经封装为 RBSP 后放入 NAL 的数据部分 。最后,加上 RBSP Trailing Bits(RBSP尾部补齐字节,一个 bit 1 若干比特 0)做8位字节补齐。

  • EBSP为扩展字节序列载荷(Encapsulated Byte Sequence Payload)

EBSP = RBSP插入防竞争字节(0x03)

  • RBSP为原始字节序列载荷(Raw Byte Sequence Payload)

RBSP = SODB + RBSP Trailing Bits(RBSP尾部补齐字节);引入RBSP Trailing Bits做8位字节补齐

  • SODB为原始数据比特流 (String Of Data Bits)

就是最原始的编码/压缩得到的数据。

NAL头信息的类型决定了RBSP是一个什么数据,下面是一些常见的类型及其解析:

  • SPS:全称Sequence Paramater Set,翻译为序列参数集。SPS中保存了一组编码视频序列(Coded Video Sequence)的全局参数。因此该类型保存的是和编码序列相关的参数。
  • PPS:全称Picture Paramater Set,翻译为图像参数集。对应的是一个序列中某一幅图像或者某几幅图像的参数。
  • I帧:帧内编码帧,可独立解码生成完整的图片。
  • P帧:前向预测编码帧,需要参考其前面的一个I 或者B 来生成一张完整的图片。
  • B帧:双向预测内插编码帧,则要参考其前面个I或者P帧及其后面的一个P帧来生成一张完整的图片。
  • SEI:英文全称Supplemental Enhancement Information,翻译为“补充增强信息”,提供了向视频码流中加入额外信息的方法。
  • AU分隔:AU全称Access Unit,它是一个或者多个NALU的集合,代表了一个完整的帧。一帧数据可能分成几个NALU,AU分隔表示一帧数据的结束。

在这里插入图片描述

🎄四、总结

本文介绍H.264编码的基础概念:序列、图像、片、宏块、子块。然后介绍H.264的NAL层,可以了解H.264码流是由一个个NALU组成,每个NALU又是由00 00 00 0100 00 01来分隔开,NALU都有一个NAL头和一个RBSP。

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考资料:
H264码流分析和打包RTP过程
H264编码原理及NALU介绍
深入浅出理解视频编码H264结构(内涵福利)
H264编码基础概念+格式分析
H264 编解码协议详解
《新一代视频压缩编码标准H.264/AVC》——毕厚杰主编

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

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

相关文章

spark介绍及简单使用

简介 Spark是由加州大学伯克利分校AMPLab(AMP实验室)开发的开源大数据处理框架。起初,Hadoop MapReduce是大数据处理的主流框架,但其存在一些限制,如不适合迭代算法、高延迟等。为了解决这些问题,Spark在20…

【数据结构】复习题(一)

一、选择题 1.组成数据的基本单位是()。 A. 数据项 B.数据类型 C.数据元素 D.数据变量 2.设数据结构A{D,R},其中D&#xff5b;1,2,3,4},R{r},r{<1,2>,<2,3>,< 3,4>,<4,1>}&#xff0c;则数据结构A是()。 A.线性结构 B.树型结构 C.图型结构 D.集合 3.…

鸿蒙HarmonyOS开发用什么语言

1.网上流行一句有中国底蕴的话&#xff1a;鸿蒙系统方舟框架盘古大模型。都方舟框架了肯定主推的是ArkUI框架。其实还能使用C、Java和Js开发。 2.从API8开始&#xff0c;Java语言已经从鸿蒙开发剔除了&#xff0c;而官方推荐的是ArkTs.下图是ArkTS与TS、JS的关系。 ArkTs 是TS的…

Programming Abstractions in C阅读笔记:p235-p241

《Programming Abstractions in C》学习第66天&#xff0c;p235-p241总结。 一、技术总结 1.backtracking algorithm(回溯算法) (1)定义 p236, For many real-world problem, the solution process consits of working your way through a sequence of decision points in…

统信UOS|DNS server|02-部署DNS服务器

原文链接&#xff1a;统信UOS&#xff5c;DNS server&#xff5c;02-部署DNS服务器 hello&#xff0c;大家好啊&#xff01;继上次我们介绍了如何在统信UOS操作系统1060上搭建一个测试用的HTTP服务器之后&#xff0c;今天我们将继续我们的DNS服务器部署系列。这是第二篇文章&am…

Ubuntu18.04 上通过 jihu 镜像完成 ESP-IDF 编译环境搭建流程

为了解决国内开发者从 github 克隆 esp 相关仓库慢的问题&#xff0c;已将 esp-idf 和部分重要仓库及其关联的子模块镜像到了 jihu&#xff0c;这些仓库将自动从原始仓库进行同步。此篇博客用来阐述 Ubuntu18.04 上通过 jihu 镜像完成 ESP-IDF 编译环境搭建流程。 注&#xff1…

IDEA shorten command line介绍和JAR manifest 导致mybatis找不到接口类处理

如果类路径太长&#xff0c;或者有许多VM参数&#xff0c;程序就无法启动。原因是大多数操作系统都有命令行长度限制。在这种情况下&#xff0c;IntelliJIDEA将试图缩短类路径。最好选中 classpath file模式。 shorten command line 选项提供三种选项缩短类路径。 none&#x…

HCIP —— BGP 基础实验

实验拓扑&#xff1a; 实验要求&#xff1a; 1.所有设备上均有环回接口 2.R1属于AS 100 &#xff0c;R2-R4 属于AS 200 &#xff0c;R5 属于AS 300 3.R2 - R4 属于同一个area &#xff0c;运行OSPF。 4.全网通过运行BGP实现网络互通。 实验步骤&#xff1a; 1.配置 IP地址…

时序预测 | Python实现LSTM-Attention电力需求预测

时序预测 | Python实现LSTM-Attention电力需求预测 目录 时序预测 | Python实现LSTM-Attention电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前最先进的行…

UCloud + 宝塔 + PHP = 个人网站

UCloud 宝塔 PHP 个人网站 文章目录 1.概要2.UCloud使用教程&#xff08;租用云端服务器&#xff09;3.宝塔使用教程&#xff08;免费服务器运维面板&#xff09;4.总结 1.概要 今天主要是想教大家如何将在网络上白嫖到源码&#xff08;特指PHP源码!!!&#xff09;搭建运行…

大创项目推荐 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

嵌入式中串口输入

学习目标 掌握串口初始化流程掌握串口接收逻辑了解中断接收逻辑熟练掌握串口开发流程学习内容 需求 串口接收PC机发送的数据。 串口数据接收 串口初始化 static void USART_config() {uint32_t usartx_tx_rcu = RCU_GPIOA;uint32_t usartx_tx_port = GPIOA;uint32_t usartx…

RabbitMQ入门指南(一):初识与安装

专栏导航 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、消息队列介绍 1.同步调用和异步调用 2.常见消息队列介绍 二、RabbitMQ简介及其安装步骤 1.RabbitMQ简介 2.RabbitMQ安装步骤&#xff08;使用Docker&#xff09; (1) 创建网络 (2) 使用Docker来…

Apache RocketMQ 5.0 腾讯云落地实践

Apache RocketMQ 发展历程回顾 RocketMQ 最早诞生于淘宝的在线电商交易场景&#xff0c;经过了历年双十一大促流量洪峰的打磨&#xff0c;2016年捐献给 Apache 社区&#xff0c;成为 Apache 社区的顶级项目&#xff0c;并在国内外电商&#xff0c;金融&#xff0c;互联网等各行…

【每日OJ—有效的括号(栈)】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 1、有效的括号题目&#xff1a; 1.1方法讲解&#xff1a; 1.2代码实现&#xff1a; 总结 前言 世上有两种耀眼的光芒&#xff0c;一种是正在升起的太阳&#…

本地运行大语言模型并可视化(Ollama+big-AGI方案)

目前有两种方案支持本地部署&#xff0c;两种方案都是基于llamacpp。其中 Ollama 目前只支持 Mac&#xff0c;LM Studio目前支持 Mac 和 Windows。 LM Studio&#xff1a;https://lmstudio.ai/ Ollama&#xff1a;https://ollama.ai/download 本文以 Ollama 为例 step1 首先下…

九牧:科技卫浴,长期主义

“没有做错什么&#xff0c;但却输给了时代”&#xff0c;这是人们给当年手机巨头诺基亚的注解。 谁也没有想到&#xff0c;曾在手机行业称雄的诺基亚&#xff0c;最终败给了时代。当年&#xff0c;在2G向3G、4G跨越的时候&#xff0c;苹果、微软的iOS和安卓系统将手机从简单的…

MIT18.06线性代数 笔记1

文章目录 方程组的几何解释矩阵消元乘法和逆矩阵A的LU分解转置-置换-向量空间R列空间和零空间求解Ax0主变量 特解求解Axb可解性和解的结构线性相关性、基、维数四个基本子空间矩阵空间、秩1矩阵和小世界图图和网络复习一 方程组的几何解释 线性组合&#xff1a; 找到合适的x和…

Unity 通过代码将一张大图切成多个小图的方法

在Unity 中要通过代码将一张贴图切割成多张小图&#xff0c;可以使用以下方法&#xff1a; /// <summary>/// 把一张图片切割成多张使用/// </summary>/// <param name"texture">原图</param>/// <param name"rows">切割的行…

Python实验项目9 :网络爬虫与自动化

实验 1&#xff1a;爬取网页中的数据。 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 # 要求&#xff1a;使用 urllib 库和 requests 库分别爬取 http://www.sohu.com 首页的前 360 个字节的数据。 import urllib.r…