HLS(m3u8) 草案 译 (待修订)

1. 介绍HLS(Introduction to HTTP Live Streaming)

HLS (HTTP Live Streaming),Apple的动态码率自适应技术。主要用于PC和Apple终端的音视频服务。包括一个m3u(8)的索引文件,TS媒体分片文件和key加密串文件。

2. 概览(Overview)

3. TS片(Media Segments)

4. 播放列表(Playlists)

4.1. 播放列表的定义(Definition of a Playlist)

播放列表文件的格式是从M3U[M3U]演变而来,并继承了早期版本的文件两个标签格式:EXTM3U和EXTINF。

每个播放列表文件必须通过URI或HTTP内容类型验证。

在第一种情况下,该路径必须使用.m3u8或m3u格式结束。在第二种情况下,HTTP内容类型必须是 “application/vnd.apple.mpegurl” 或 “audio/mpegurl”。

客户端应该拒绝解析未通过验证的播放列表。

播放列表文件必须使用UTF-8进行编码。

以字符’#’开头的行要么是注释或标签。

标签以#EXT开始,它们是区分大小写的。剩下的以’#’开始是注释,应该被忽略。

一个URI表示是TS片或播放列表文件。每个TS片由URI和指的定标签所指定的。

如果一个播放列表是一个Media播放列表,在播放列表中所有URI行都标识其是一个TS片。如果一个播放列表是一个Master播放列表,在其播放列表中所有URI行都标识其是一个Media播放列表。

播放列表必须是Media播放列表或Master播放列表。

媒体播放列表文件的持续时间是其中所有的TS片的持续时间的总和。

4.2. 属性列表(Attribute Lists)

一个属性/值对的语法如:AttributeName=AttributeValue

AttributeNames只包含大写字母。AttributeName会和’=’字符之间以及AttributeValue之间,不能有任何空白。

AttributeValue是下列之一:

  1. 十进制整数字符
  2. 十六进制序列
  3. 十进制浮点数
  4. 引用的字符串,其中不能出现换行和回车
    等等

在属性列表中不能出现多次相同的属性名。客户端应该拒绝解析此类播放列表。

4.3. 播放列表标记(Playlist Tags)

4.3.1. 基础标签(Basic Tags)

允许在Media播放列表和Master播放列表中出现。

4.3.1.1. EXTM3U

该EXTM3U标签表示该文件是一个扩展的M3U播放列表文件。它必须是每个Media播放列表的第一行。

其格式为: #EXTM3U

4.3.1.2. EXT-X-VERSION

该EXT-X-VERSION标记表示播放列表文件/关联的媒体/服务器的兼容版本。

该EXT-X-VERSION标记适用于在整个播放列表文件。

其格式是:#EXT-X-VERSION:<n>

n是一个整数表示协议兼容的版本号。

所有的播放列表必须包含这个标记。

播放列表文件不能包含多个EXT-X-VERSION标记。

如果一个客户端遇到多个EXT-X-VERSION标签的播放列表,必须拒绝它。

4.3.2. TS片标签(Media Segment Tags)

每个TS片是由一系列其后跟着URL的TS片标签所指定的,一些媒体段标签仅应用于下一个TS片,一些适用于后续所有的TS片,直到另一个相同的标记实例。

一个TS片不能出现于Master播放列表。

4.3.2.1. EXTINF

该EXTINF标记指定媒体段的持续时间。

它适用到下一个媒体段。必须使用这个标签分隔每个媒体段。

其格式为:#EXTINF:<duration>,[<title>]

4.3.2.2. EXT-X-BYTERANGE

该EXT-X-BYTERANGE标签指定TS片是URI所指定资源的一个子范围。

它仅适用于下一行的URI,其格式为:#EXT-X-BYTERANGE:<n>[@<o>]

其中n是一整数,表示子范围的字节长度。如果o存在,其也是个整数,表示开始的子范围,如果o不存在,则开始于上一个媒体段的下一个字节,并且上一个媒体段必须存在,并且必须是相同的媒体资源的子范围。

在使用EXT-X-BYTERANGE标签时,需要4以上的兼容版本为。

4.3.2.3. EXT-X-DISCONTINUITY

该EXT-X-DISCONTINUITY表示它后面的TS片和它之前的TS片是不连续的。

其格式为:#EXT-X-DISCONTINUITY

如有一下几个特性,EXT-X-DISCONTINUITY标签必须存在:

  1. 文件格式
  2. 编号,类型和标识
  3. 时间戳序列

如有以下几个特性,EXT-X-DISCONTINUITY标签应该存在:

  1. 编码参数
  2. 编码序列
4.3.2.4. EXT-X-KEY

媒体片段可以被加密。该EXT-X-KEY标记指定如何解密。

它适用于碰到下一个EXT-X-KEY标签间的所有TS片(使用相同的KEYFORMAT)。

多个EXT-X-KEY标签使用不同的KEYFORMAT属性,如果他们最终产生相同的解密密钥,则可以应用于同一个媒体段。

格式为:#EXT-X-KEY:<attribute-list>

属性定义如下:

  1. METHOD:
    该值是一个枚举字符串指定加密方法。此属性是必需的。定义的加密方法有:NONE,AES-128,SAMPLE-AES三种。
    NONE的加密方法是指TS片不加密。如果加密方法是NONE,其他属性不能出现。
    AES-128的加密方法,该方法的TS片是使用AES-128(高级加密标准)完全加密。此方法需要URI属性。
    SAMPLE-AES加密方法意味着TS片内含有一些样本,可能是视频或音频,其使用AES加密标准。

  2. URI:其值是如何获得密钥的URL引用字符串。除非METHOD为NONE,否则此属性是必需的。

  3. IV:是一个十六进制序列,指定一个128位的无符号整数初始化向量,和解密KEY一起使用。这个属性需要2以上的兼容版本。

  4. KEYFORMAT:其值是一个字符串,指定解密KEY是如何由URI所指定的。

这个属性需要5以上的兼容版本。

如果Media播放列表没有包含EXT-X-KEY标签,说名媒体段没有被加密。

4.3.2.5. EXT-X-MAP

该EXT-X-MAP标记指定如何获取媒体初始化的部分,来解析使用的TS片。

它适用于每一个TS片,直到碰到下一个EXT-X-MAP标签,或直到文件结尾。

其格式如下:#EXT-X-MAP:<attribute-list>

下面是属性定义:

  1. URI:该值是是一个包含URI的字符串,此属性是必须的。

  2. BYTERANGE:该值是一个指定URI资源的字节范围的字符串,此范围只包括初始化部分。如果不存在,表示整个URI只是的资源。

4.3.2.6. EXT-X-PROGRAM-DATE-TIME

4.3.3. Media播放列表标签(Media Playlist Tags)

每种类型的Media播放列表标签不能出现多次。

Media播放列表标签不能出现在Master播放列表中。

4.3.3.1. EXT-X-TARGETDURATION

该EXT-X-TARGETDURATION标记指定最大的TS片的持续时间。

每个TS片的持续时间,必须小于等于最大的持续时间。

其格式是:#EXT-X-TARGETDURATION:<s>

其中s是一个整数,代表的是秒时间。该EXT-X-TARGETDURATION标签是必须的。

4.3.3.2. EXT-X-MEDIA-SEQUENCE

该EXT-X-MEDIA-SEQUENCE表示TS片的开始序号。

#EXT-X-MEDIA-SEQUENCE:<number>

如果EXT-X-MEDIA-SEQUENCE不存在,则媒体段的第一个序号应该为0.

如果EXT-XMEDIA-SEQUENCE存在,则必须在第一个媒体段之前。

4.3.3.3. EXT-X-DISCONTINUITY-SEQUENCE
4.3.3.4. EXT-X-ENDLIST

该EXT-X-END-LIST标签表示没有更多的TS片被添加到播放列表中。

它可能发生在任何地方Media播放列表文件。

其格式为:#EXT-X-ENDLIST

4.3.3.5. EXT-X-PLAYLIST-TYPE

该EXT-X-PLAYLIST标签提供了关于Media播放列表可变性的相关信息。

它适用于整个Media播放列表文件。它是可选的。

其格式为:#EXT-X-PLAYLIST-TYPE:<EVENT|VOD>

6.2.1节定义了EXT-X-PLAYLIST-TYPE标签的影响。

如果EXT-X-PLAYLIST-TYPE的值是EVENT时,TS片只能被添加到Media播放列表的末尾。

当EXT-X-PLAYLIST-TYPE的值是VOD时,播放列表不能被改变。

如果EXT-X-PLAYLIST-TYPE标签没有出现在Media播放列表中,那么可以按照6.2.1的规则更新列表

4.3.3.6. EXT-X-I-FRAMES-ONLY

该EXT-X-I-FRAMES-ONLY标签表示每个TS片是一个I-Frame,I-Frames(or Intra Frames)视频的编码不依赖于其他的Frame。

在 EXT-X-I-FRAMES-ONLY标签适用于整个播放列表。

其格式为:EXT-X-I-FRAMES-ONLY

4.3.4. Master Playlist Tags

Master播放列表标签的定义了多流,多译和一些其他的全局参数。

Master播放列表标签不能在出现在Media播放列表中。

客户端必须拒绝同时包含Master播放列表标签和Media播放列表标签或媒体段标签的播放列表。

4.3.4.1. EXT-X-MEDIA

该EXT-X-MEDIA标签用于Media播放列表的翻译功能,相同内容的不同翻译语言。

例如,三个EXT-X-MEDIA标签能够标识出英语,法语,中文等翻译语言。或者,两个EXT-X-MEDIA标签来标识出对于同一内容的,不同播放角度。

其格式为:#EXT-X-MEDIA:<attribute-list>

例子:

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/eng/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="en",URI="subtitles/eng_forced/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="fr",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/fra/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="fr",URI="subtitles/fra_forced/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Español",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="es",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/spa/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Español (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="es",URI="subtitles/spa_forced/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="日本語",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="ja",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/jpn/prog_index.m3u8"

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="日本語 (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="ja",URI="subtitles/jpn_forced/prog_index.m3u8"

属性定义如下:

  1. TYPE:该值是一个枚举字符串,合法的字符串有:AUDIO,VIDEO,SUBTITLES,CLOSED-CAPTIONS[http://baike.baidu.com/link?url=fVkQIBnGTiLMZ2GtkBD4VcQ-pP7-q98pvfzqoHeAHRcxXM9gJLSCHXQZvbgMd-VwRmxiEDZuFDrZHIPQz1g25_]. 这个属性是必须的。

  2. URI:该值是一个Media播放文件的URL引用字符串。此属性是可选的。如果TYPE是CLOSED-CAPTIONS,则URI一定不能存在。

  3. GROUP-ID: 用于标识翻译属于那个组。此属性是必须的。

  4. LANGUAGE: 翻译语言,此属性是可选的。

  5. ASSOC-LANGUAGE:

  6. NAME: 翻译语言名,此属性是必须的。

  7. DEFAULT: 该值是一个枚举字符串,合法的值有:YES,NO。如果值是YES,那代表客户端应该使用这个翻译内容,在用户没有其他选择时。此属性是可选的。如果没有此属性,则是NO

  8. AUTOSELECT: 该值是一个枚举字符串,合法的值有:YES,NO。此属性是可选的。如果没有此属性,则表示其值是NO,如果该值是YES,当没有用户偏好时,匹配当前的用户环境,如选择系统的语言,来使用此翻译。

  9. FORCED:该值是一个枚举字符串,合法的值有:YES,NO。此属性是可选的。如果没有此属性,则表示其值是NO。只有TYPE为SUBTITLES,FORCE属性才能存在。

4.3.4.1.1. 翻译组(Rendition Groups)

一组中的一个或多个EXT-X-MEDIA标签具有相同的GROUP-ID值,与同类型的值定义了一个翻译集合。

每个成员必须是同一内容的另一种翻译,否则可能会出现播放错误。

在播放列表中的所有EXT-X-MEDIA标签必须满足一下约束:

  • 在同一组中的所有EXT-X-MEDIA标签必须有不同的名称属性。
  • 一组中不允许有多个成员的默认属性是YES
  • 组内成员的AUTOSELECT的属性如果为YES,那么LANGUAGE属性必须有唯一值

一个播放列表可能包含相同TYPE的多个组,以提供媒体类型的多种编码。如果这么做,相同TYPE的多个组,组内必须有相同的成员,相同的成员必须具有除URI之外的相同属性

4.3.4.2. EXT-X-STREAM-INF

该EXT-X-STREAM-INF标签用于指定多码率流的信息。

URI行跟在EXT-X-STREAM-INF标签行的后面,用于指定Media播放列表。该URI是必须存在的。

格式如下:

#EXT-X-STREAM-INF:<attribute-list>
<URI>

属性定义如下:

  1. BANDWIDTH:该值是每秒比特的十进制整数,它代表了流高峰段的比特率。每个EXT-X-STREAM-INF标签必须包含BANDWIDTH属性

  2. AVERAGE-BANDWIDTH:该值是每秒比特的十进制整数,它代表了流平均的比特率。AVERAGE-BANDWIDTH标签是可选的。

  3. CODECS:该值是使用逗号(,)分隔的引用字符串,其中每一个指定了媒体采样类型,每个EXT-X-STREAM-INF标签应该包含CODECS属性。

  4. RESOLUTION(分辨率):该值是一个整数的用于描述视频的最佳像素分辨率,该RESOLUTION是可选的,如果流包含视频,推荐有这个属性。

  5. FRAME-RATE:该值是一个浮点数用于描述视频的最大帧。该FRAME-RATE是可选的,如果流包含视频,推荐有这个属性。

  6. AUDIO:该值是一个引用字符串,该值是可选的。

  7. VIDEO:该值是一个引用字符串,该值是可选的。

  8. SUBTITLES:该值是一个引用字符串,该值是可选的。

  9. CLOSED-CAPTIONS:该值是一个引用字符串,该值是可选的。

4.3.4.2.1. Alternative Renditions
4.3.4.3. EXT-X-I-FRAME-STREAM-INF
4.3.4.4. EXT-X-SESSION-DATA
4.3.4.5. EXT-X-SESSION-KEY

4.3.5. Media or Master Playlist Tags

4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS
4.3.5.2. EXT-X-START

5. (解密文件)Key files

5.1. Structure of Key files

EXT-X-KEY标签的URI属性用于标识秘钥文件。用于解密TS片。

[AES128]加密使用16字节的密钥。

5.2. IV for [AES_128]

加密和解密时,[AES_128]要求提供16字节的初始化向量(IV)。IV增加了密码的强度。

6. (客户端/服务端职责)Client/Server Responsibilities

6.1. Introduction

6.2. Server Responsibilities

6.2.1. General Server Responsibilities

6.2.2. Live Playlists

6.2.3. Encrypting Media Segments

6.2.4. Providing Variant Streams

6.3. Client Responsibilities

6.3.1. General Client Responsibilities

6.3.2. Loading the Media Playlist file

6.3.3. Playing the Media Playlist file

6.3.4. Reloading the Media Playlist file

6.3.5. Determining the next segment to load

6.3.6. Decrypting encrypted Media Segments

7. 协议版本的兼容性(Protocol version compatibility)

使用EXT-X-VERSION标签指定协议的兼容性。如果不存在EXT-X-VERSION标签,客户端应该拒绝播放。

EXT-X-VERSION:2(以上),支持EXT-X-KEY的IV属性

EXT-X-VERSION:3(以上),支持EXTINF浮点数值

EXT-X-VERSION:4(以上),支持EXT-X-BYTERANGE标签,支持EXT-X-I-FRAMES-ONLY标签

EXT-X-VERSION:5(以上),支持EXT-X-KEY的KEYFORMAT和KEYFORMATVERSIONS属性,支持EXT-X-MAP标签。

EXT-X-VERSION:6(以上),支持EXT-X-MAP标签内不支持EXT-X-I-FRAMES-ONLY。

EXT-X-VERSION:7(以上),支持EXT-X-MEDIA标签的INSTREAM-ID有SERVICE值

EXT-X-STREAM-INF和EXT-X-I-FRAME-STREAM-INF标签的PROGRAM-ID属性在6版本被移除。

EXT-X-ALLOW-CACHE标签在7版本被移除。

8. 播放列表的例子(Playlist Examples)

8.1. 简单的Media播放列表 Simple Media Playlist

#EXTM3U
#EXT-X-TARGETDURATION:10

#EXTINF:9.009,
http://media.example.com/first.ts

#EXTINF:9.009,
http://media.example.com/second.ts

#EXTINF:3.003,
http://media.example.com/third.ts

#EXT-X-ENDLIST

8.2. Https的视频直播流(Live Media Playlist, using HTTPS)

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:2680

#EXTINF:7.975,
https://priv.example.com/fileSequence2680.ts

#EXTINF:7.941,
https://priv.example.com/fileSequence2681.ts

#EXTINF:7.975,
https://priv.example.com/fileSequence2682.ts

8.3. 加密媒体段的播放列表(Playlist with encrypted Media Segments)

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:7794
#EXT-X-TARGETDURATION:15
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=52"

#EXTINF:2.833,
http://media.example.com/fileSequence52-A.ts

#EXTINF:15.0,
http://media.example.com/fileSequence52-B.ts
#EXTINF:13.333,

http://media.example.com/fileSequence52-C.ts
#EXT-X-KEY:METHOD=AES-128,URI="https://priv.example.com/key.php?r=53"

#EXTINF:15.0,
http://media.example.com/fileSequence53-A.ts

8.4. Master播放列表(Master Playlist)

#EXTM3U

#EXT-X-STREAM-INF:BANDWIDTH=1280000,AVERAGE-BANDWIDTH=1000000
http://example.com/low.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=2560000,AVERAGE-BANDWIDTH=2000000
http://example.com/mid.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=7680000,AVERAGE-BANDWIDTH=6000000
http://example.com/hi.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8

8.5. Master Playlist with I-Frames

#EXTM3U

#EXT-X-STREAM-INF:BANDWIDTH=1280000
low/audio-video.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=86000,URI="low/iframe.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=2560000
mid/audio-video.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=150000,URI="mid/iframe.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=7680000
hi/audio-video.m3u8
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=550000,URI="hi/iframe.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5"
audio-only.m3u8

8.6. Master Playlist with Alternative audio

#EXTM3U

#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="English", DEFAULT=YES,AUTOSELECT=YES,LANGUAGE="en", URI="main/english-audio.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Deutsch", DEFAULT=NO,AUTOSELECT=YES,LANGUAGE="de", URI="main/german-audio.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",NAME="Commentary", DEFAULT=NO,AUTOSELECT=NO,LANGUAGE="en", URI="commentary/audio-only.m3u8"

#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",AUDIO="aac"
low/video-only.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",AUDIO="aac"
mid/video-only.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",AUDIO="aac"
hi/video-only.m3u8

#EXT-X-STREAM-INF:BANDWIDTH=65000,CODECS="mp4a.40.5",AUDIO="aac"
main/english-audio.m3u8

8.7. Master Playlist with Alternative video

#EXTM3U

#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Main", DEFAULT=YES,URI="low/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Centerfield", DEFAULT=NO,URI="low/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="low",NAME="Dugout", DEFAULT=NO,URI="low/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS="...",VIDEO="low"
low/main/audio-video.m3u8

#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Main", DEFAULT=YES,URI="mid/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Centerfield", DEFAULT=NO,URI="mid/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="mid",NAME="Dugout", DEFAULT=NO,URI="mid/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=2560000,CODECS="...",VIDEO="mid"
mid/main/audio-video.m3u8

#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Main", DEFAULT=YES,URI="hi/main/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Centerfield", DEFAULT=NO,URI="hi/centerfield/audio-video.m3u8"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="hi",NAME="Dugout", DEFAULT=NO,URI="hi/dugout/audio-video.m3u8"
#EXT-X-STREAM-INF:BANDWIDTH=7680000,CODECS="...",VIDEO="hi"
hi/main/audio-video.m3u8
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值