0%

1、FFmpeg命令行基础

FFmpeg官网:

1
http://ffmpeg.org/

FFmpeg程序下载:包括Windows、MacOS、Linux等操作系统,包括FFmpeg、FFprobe、FFplay、FFserver等程序。

1
http://ffmpeg.org/download.html

FFmpeg命令行文档:

1
http://ffmpeg.org/ffmpeg.html

FFplay命令行文档:

1
http://ffmpeg.org/ffplay.html

2、配置FFmpeg环境变量

MacOS上配置FFmpeg环境变量:

1
vi ~/.bash_profile

按I键编辑文本:

1
export PATH=${PATH}:/Users/zhangchaozhou/Documents/Software/ffmpeg-4.4

按ESC停止编辑,并按:wq键保存退出编辑。
更新配置:

1
source ~/.bash_profile

如果环境变量PATH被修改,需在命令行执行:

1
export PATH=/bin:/usr/bin:$PATH

执行FFmpeg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
TeochewZhang:video zhangchaozhou$ ffmpeg -hide_banner -help
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...

Getting help:
-h -- print basic options
-h long -- print more options
-h full -- print all options (including all format and codec specific options, very long)
-h type=name -- print all options for the named decoder/encoder/demuxer/muxer/filter/bsf/protocol
See man ffmpeg for detailed description of the options.

Print help / information / capabilities:
-L show license
-h topic show help
-? topic show help
-help topic show help
--help topic show help
-version show version
-buildconf show build configuration
-formats show available formats
-muxers show available muxers
-demuxers show available demuxers
-devices show available devices
-codecs show available codecs
-decoders show available decoders
-encoders show available encoders
-bsfs show available bit stream filters
-protocols show available protocols
-filters show available filters
-pix_fmts show available pixel formats
-layouts show standard channel layouts
-sample_fmts show available audio sample formats
-colors show available color names
-sources device list sources of the input device
-sinks device list sinks of the output device
-hwaccels show available HW acceleration methods

Global options (affect whole program instead of just one file):
-loglevel loglevel set logging level
-v loglevel set logging level
-report generate a report
-max_alloc bytes set maximum size of a single allocated block
-y overwrite output files
-n never overwrite output files
-ignore_unknown Ignore unknown stream types
-filter_threads number of non-complex filter threads
-filter_complex_threads number of threads for -filter_complex
-stats print progress report during encoding
-max_error_rate maximum error rate ratio of decoding errors (0.0: no errors, 1.0: 100% errors) above which ffmpeg returns an error instead of success.
-bits_per_raw_sample number set the number of bits per raw sample
-vol volume change audio volume (256=normal)

Per-file main options:
-f fmt force format
-c codec codec name
-codec codec codec name
-pre preset preset name
-map_metadata outfile[,metadata]:infile[,metadata] set metadata information of outfile from infile
-t duration record or transcode "duration" seconds of audio/video
-to time_stop record or transcode stop time
-fs limit_size set the limit file size in bytes
-ss time_off set the start time offset
-sseof time_off set the start time offset relative to EOF
-seek_timestamp enable/disable seeking by timestamp with -ss
-timestamp time set the recording timestamp ('now' to set the current time)
-metadata string=string add metadata
-program title=string:st=number... add program with specified streams
-target type specify target file type ("vcd", "svcd", "dvd", "dv" or "dv50" with optional prefixes "pal-", "ntsc-" or "film-")
-apad audio pad
-frames number set the number of frames to output
-filter filter_graph set stream filtergraph
-filter_script filename read stream filtergraph description from a file
-reinit_filter reinit filtergraph on input parameter changes
-discard discard
-disposition disposition

Video options:
-vframes number set the number of video frames to output
-r rate set frame rate (Hz value, fraction or abbreviation)
-fpsmax rate set max frame rate (Hz value, fraction or abbreviation)
-s size set frame size (WxH or abbreviation)
-aspect aspect set aspect ratio (4:3, 16:9 or 1.3333, 1.7777)
-bits_per_raw_sample number set the number of bits per raw sample
-vn disable video
-vcodec codec force video codec ('copy' to copy stream)
-timecode hh:mm:ss[:;.]ff set initial TimeCode value.
-pass n select the pass number (1 to 3)
-vf filter_graph set video filters
-ab bitrate audio bitrate (please use -b:a)
-b bitrate video bitrate (please use -b:v)
-dn disable data

Audio options:
-aframes number set the number of audio frames to output
-aq quality set audio quality (codec-specific)
-ar rate set audio sampling rate (in Hz)
-ac channels set number of audio channels
-an disable audio
-acodec codec force audio codec ('copy' to copy stream)
-vol volume change audio volume (256=normal)
-af filter_graph set audio filters

Subtitle options:
-s size set frame size (WxH or abbreviation)
-sn disable subtitle
-scodec codec force subtitle codec ('copy' to copy stream)
-stag fourcc/tag force subtitle tag/fourcc
-fix_sub_duration fix subtitles duration
-canvas_size size set canvas size (WxH or abbreviation)
-spre preset set the subtitle options to the indicated preset

3、常用命令

3.1、获取文件信息

使用-i参数。

1
$ ffmpeg -i input.mp4

加上-hide_banner参数,可以只显示元信息,去除冗余信息。

1
$ ffmpeg -i input.mp4 -hide_banner

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
TeochewZhang:video zhangchaozhou$ ffmpeg -i 那些年,我们一起追的女孩.mp4 -hide_banner
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '那些年,我们一起追的女孩.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf56.19.100
Duration: 00:00:30.32, start: 0.000000, bitrate: 397 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 480x208 [SAR 16:15 DAR 32:13], 261 kb/s, 25 fps, 25 tbr, 12800 tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]

3.2、转换编码格式/transcoding

将视频文件从一种编码转成另一种编码。比如转成 H.264 编码,一般使用编码器libx264,所以只需指定输出文件的视频编码器即可。

1
$ ffmpeg -i [input.file] -c:v libx264 output.mp4

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
TeochewZhang:video zhangchaozhou$ ffmpeg -i Forrest_Gump_IMAX.mp4 -c:v libx264 output.mp4 -hide_banner
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Forrest_Gump_IMAX.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf56.19.100
Duration: 00:00:31.21, start: 0.000000, bitrate: 878 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 640x352, 748 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Stream mapping:
Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
Stream #0:1 -> #0:1 (aac (native) -> aac (native))
Press [q] to stop, [?] for help
[libx264 @ 0x7fcc4e803e00] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
[libx264 @ 0x7fcc4e803e00] profile High, level 3.0, 4:2:0, 8-bit
[libx264 @ 0x7fcc4e803e00] 264 - core 161 r3048 b86ae3c - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=11 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=23 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to 'output.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf58.76.100
Stream #0:0(und): Video: h264 (avc1 / 0x31637661), yuv420p(progressive), 640x352, q=2-31, 23.98 fps, 24k tbn (default)
Metadata:
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : Lavc58.134.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
Metadata:
handler_name : SoundHandler
vendor_id : [0][0][0][0]
encoder : Lavc58.134.100 aac
frame= 744 fps=297 q=-1.0 Lsize= 3080kB time=00:00:31.18 bitrate= 809.1kbits/s speed=12.4x
video:2595kB audio:460kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.800888%
[libx264 @ 0x7fcc4e803e00] frame I:20 Avg QP:17.34 size: 24048
[libx264 @ 0x7fcc4e803e00] frame P:245 Avg QP:22.46 size: 6089
[libx264 @ 0x7fcc4e803e00] frame B:479 Avg QP:24.79 size: 1428
[libx264 @ 0x7fcc4e803e00] consecutive B-frames: 7.0% 18.8% 8.1% 66.1%
[libx264 @ 0x7fcc4e803e00] mb I I16..4: 34.0% 22.0% 44.0%
[libx264 @ 0x7fcc4e803e00] mb P I16..4: 5.4% 8.7% 3.1% P16..4: 30.5% 12.4% 7.2% 0.0% 0.0% skip:32.7%
[libx264 @ 0x7fcc4e803e00] mb B I16..4: 0.7% 0.6% 0.4% B16..8: 30.4% 4.7% 1.6% direct: 2.1% skip:59.5% L0:45.2% L1:43.1% BI:11.7%
[libx264 @ 0x7fcc4e803e00] 8x8 transform intra:40.6% inter:46.7%
[libx264 @ 0x7fcc4e803e00] coded y,uvDC,uvAC intra: 45.5% 49.2% 16.2% inter: 12.0% 10.6% 0.5%
[libx264 @ 0x7fcc4e803e00] i16 v,h,dc,p: 39% 35% 13% 13%
[libx264 @ 0x7fcc4e803e00] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 15% 34% 20% 3% 5% 5% 6% 4% 6%
[libx264 @ 0x7fcc4e803e00] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 15% 30% 13% 5% 7% 7% 8% 6% 8%
[libx264 @ 0x7fcc4e803e00] i8c dc,h,v,p: 53% 31% 11% 5%
[libx264 @ 0x7fcc4e803e00] Weighted P-Frames: Y:24.9% UV:20.4%
[libx264 @ 0x7fcc4e803e00] ref P L0: 62.2% 20.9% 13.6% 3.0% 0.3%
[libx264 @ 0x7fcc4e803e00] ref B L0: 89.2% 9.0% 1.7%
[libx264 @ 0x7fcc4e803e00] ref B L1: 98.3% 1.7%
[libx264 @ 0x7fcc4e803e00] kb/s:684.96
[aac @ 0x7fcc4e805600] Qavg: 4577.948

3.3、转换容器格式/transmuxing

将视频文件从一种容器转到另一种容器。
内部的编码格式不变,使用-c copy指定直接拷贝,不经过转码。

1
$ ffmpeg -i input.mp4 -c copy output.webm

3.4、调整码率/transrating

改变编码的比特率,一般用来将视频文件的体积变小。
指定码率最小为964K,最大为3856K,缓冲区大小为 2000K。

1
2
3
4
$ ffmpeg \
-i input.mp4 \
-minrate 964K -maxrate 3856K -bufsize 2000K \
output.mp4

3.5、改变分辨率/transsizing

从 1080p 转为 480p 。

1
2
3
4
$ ffmpeg \
-i input.mp4 \
-vf scale=480:-1 \
output.mp4

3.6、提取音频/demuxing

从视频里面提取音频,-vn表示去掉视频,-c:a copy表示不改变音频编码,直接拷贝。

1
2
3
4
$ ffmpeg \
-i input.mp4 \
-vn -c:a copy \
output.aac

3.7、添加音轨/muxing

将外部音频加入视频,比如添加背景音乐或旁白。

1
2
3
$ ffmpeg \
-i input.aac -i input.mp4 \
output.mp4

3.8、截图

从指定时间开始,连续对1秒钟的视频进行截图。

1
2
3
4
5
$ ffmpeg \
-y \
-i input.mp4 \
-ss 00:01:24 -t 00:00:01 \
output_%3d.jpg

指定只截取一,-vframes 1指定只截取一帧,-q:v 2表示输出的图片质量,一般是1到5之间(1 为质量最高)。

1
2
3
4
5
$ ffmpeg \
-ss 01:23:45 \
-i input \
-vframes 1 -q:v 2 \
output.jpg

3.9、裁剪/cutting

截取原始视频里面的一个片段,输出为一个新视频。可以指定开始时间(start)和持续时间(duration),也可以指定结束时间(end)。
-c copy表示不改变音频和视频的编码格式,直接拷贝。

1
2
3

$ ffmpeg -ss [start] -i [input] -t [duration] -c copy [output]
$ ffmpeg -ss [start] -i [input] -to [end] -c copy [output]
1
2
$ ffmpeg -ss 00:01:50 -i [input] -t 10.5 -c copy [output]
$ ffmpeg -ss 2.5 -i [input] -to 10 -c copy [output]

3.10、为音频添加封面

有些视频网站只允许上传视频文件。如果要上传音频文件,必须为音频添加封面,将其转为视频,然后上传。
将音频文件,转为带封面的视频文件。

-loop 1参数表示图片无限循环,-shortest参数表示音频文件结束,输出视频就结束。

1
2
3
4
5
6

$ ffmpeg \
-loop 1 \
-i cover.jpg -i input.mp3 \
-c:v libx264 -c:a aac -b:a 192k -shortest \
output.mp4

1、YUV视频像素数据

1.1、YUV设计

YUV 是一种彩色编码系统,主要用在视频、图形处理流水线中。相对于 RGB 颜色空间,设计 YUV 的目的就是为了编码、传输的方便,减少带宽占用和信息出错。

Y’UV、YUV、YCbCr、YPbPr 几个概念其实是一回事儿。由于历史关系,Y’UV、YUV 主要是用在彩色电视中,用于模拟信号表示。YCbCr 是用在数字视频、图像的压缩和传输,如 MPEG、JPEG。通常所讲的 YUV 其实就是指 YCbCr,其中Y 表示亮度(luma),CbCr 表示色度(chroma)。

Y’UV 设计的初衷是为了使彩色电视能够兼容黑白电视。对于黑白电视信号,没有色度信息也就是(UV),那么在彩色电视显示的时候指显示亮度信息。Y’UV 不是 Absolute Color Space,只是一种 RGB 的信息编码,实际的显示还是通过 RGB 来显示。Y’,U,V 叫做不同的 component 。

人眼的视觉特点是对亮度更敏感,对位置、色彩相对来说不敏感。在视频编码系统中为了降低带宽,可以保存更多的亮度信息(luma),保存较少的色差信息(chroma)。这叫做 chrominance subsamping, 色度二次采样。原则:在数字图像中,(1) 每一个图形像素都要包含 luma(亮度)值;(2)几个图形像素共用一个 Cb + Cr 值,一般是 2、4、8 个像素。

1.2、图形像素与亮度、色度的关系

图形像素:
对于一个 w 宽、h 高的像素图,在水平方向,一行有 w 个像素;在垂直方向,一列有 h 个像素,整个图形有 w * h 个像素。

如果用 YCbCr 像素格式来表示像素图,需搞清楚亮度和图形像素的关系,色度和图形像素的关系。

YUV/YCbCr 解释
Y 明亮度
UV/CbCr 色度

yuv 各种格式后面数字的含义:

1
2
3
4
左侧一列,每一个小矩形是图形像素表示,黑框矩形是色度像素表示,小黑点是表示色度像素值(Cb+Cr),表示图形像素和色度像素在水平和垂直方向的比例关系。
4:4:0 水平方向是1/1,垂直方向是1/2,表示一个色度像素对应了两个图形像素。
4:2:2 水平方向是1/2,垂直方向是1/1,表示一个色度像素对应了两个图形像素。
4:2:0 水平方向是1/2,垂直方向是1/2,表示一个色度像素对应了四个图形像素。
1
2
3
4
右侧一列是二次采样模式记号表示,是 J:a:b 模式,实心黑色圆圈表示包含色度像素(Cb+Cr),空心圆圈表示不包含色度像素。对于 J:a:b 模式,主要是围绕参考块的概念定义的,这个参考块是一个 J x 2 的矩形,J 通常是 4。这样,此参考块就是宽度有 4 个像素、高度有 2 个像素的矩形。a 表示参考块的第一行包含的色度像素样本数,b 表示在参考块的第二行包含的色度像素样本数。
4:4:0 参考块第一行包含四个色度样本,第二行没有包含色度样本。
4:2:2 参考块第一行包含两个色度样本,第二行也包含两个色度样本,他们是交替出现。
4:2:0 参考块第一行包含两个色度样本,第二行没有包含色度样本。

yuv444,yuv422,yuv420 yuv 等像素格式的本质是:每个图形像素都会包含亮度值,但是某几个图形像素会共用一个色度值,这个比例关系就是通过 4 x 2 的矩形参考块来定的。

1.3、YUV格式

YUV格式 解释
平面格式planar 用三个不同的数组来表示 YCbCr 的三个 Component,每一个 Component 都是通过不同的平面表示。
压缩格式packed 用一个数组表示 YCbCr,每一个 component 是交替出现的。

1.3.1、平面格式

yuv420 planar/yuv420p/i420的位深度
Y、U、V三个平面,每个平面8比特bit二进制数字表示
1
2
3
4
5
6
如果用 yuv420p 来表示分辨率为 1280 * 720 的图片,需要占用多少存储空间呢?

每一个像素都需要一个 luma 值,即 y。那么总共需要 1280 * 720 = 921600 bytes。
每四个像素需要一个 chroma u 值,那么总共需要 1280 * 720 / 4 = 230400 bytes。
每四个像素需要一个 chroma v 值,那么总共需要 1280 * 720 / 4 = 230400 bytes。
把 y、u、v 三个 plane 加起来就是:921600 + 230400 + 230400 = 1382400 bytes。

通过ffmpeg将jpeg转成yuv,生成图形的分辨率是1280*72。

1
ffmpeg -i test.jpeg -s 1280x720 -pix_fmt yuv420p test.yuv

1.3.2、压缩格式

ffmpeg 中对 yuv420p 像素格式大小计算。

1、RGB视频像素数据

RGB色彩模式,是通过对红绿蓝三个颜色通道的变化,以及相互之间的叠加得到各种颜色。
每种颜色值范围为0-255,所以每个颜色值使用1个字节byte也就是8个比特bit表示。

1.1、色深

色深即色彩深度,色彩深度是计算机图形学领域表示在位图或者视频帧缓冲区中储存1像素的颜色所用的位数,它也称为位/像素(bpp)。色彩深度越高,可用的颜色就越多。

1.2、RGB格式

1.2.1、索引形式

索引形式表示每个像素使用1、4、8个比特bit表示,存储的是对应像素在调色板的索引,而非像素的RGB分量值,调色板是通过编号映射到颜色的一张二维表。

索引形式 颜色
RGB1 黑白两种颜色
RGB4 16种颜色
RGB8 256种颜色

1.2.2、像素形式

像素形式表示每个像素的RGB值使用N个比特bit表示。

像素形式 颜色
RGB565 R、G、B分别使用5、6、5个比特bit表示,共16个比特/位bit,2个字节byte,1个字Word
RGB55 RGB分别使用5个比特表示,剩余一位高字节不用,共16个比特
RGB24/RGB888 RGB分别使用8个比特表示,共24个比特
RGB32 RGB分别使用8个比特表示,剩余8位低字节不用,共32个比特

假设计算机中存储某一个像素点的变量为color,数据类型为short。
RGB565:
RGB565

1
2
3
R = color & 0xF800, (获取高字节的 5 个 bit)
G = color & 0x07E0, (获取中间 6 个 bit)
B = color & 0x001F, (获取低字节 5 个 bit)

RGB55:
RGB55

1
2
3
R = color & 0x7C00, (获取高字节的 5 个 bit)
G = color & 0x03E0, (获取中间 5 个 bit)
B = color & 0x001F, (获取低字节 5 个 bit)

RGB24:
RGB24

1
2
3
R = color & 0x000000FF, 
G = color & 0x0000FF00,
B = color & 0x00FF0000,

RGB32:
RGB32

1
2
3
R = color & 0x0000FF00
G = color & 0x00FF0000,
B = color & 0xFF000000,

1.2.3、RGB格式转换

RGB格式转换,由于精度不同,需要量化补偿和量化压缩。

1.2.3.1、量化补偿:

  1. 将原数据填充至高位
  2. 对于低位,用原始数据的低位进行补偿
  3. 如果仍然有未填充的位,继续使用原始数据的低位进行循环补偿
1
2
3
4
例子: 16 bit RGB565 -> 24 bit RGB888 的转换
16 bit RGB656: R4 R3 R2 R1 R0 G5 G4 G3 G2 G1 G0 B4 B3 B2 B1 B0
24 bit RGB888: R4 R3 R2 R1 R0 0 0 0 G5 G4 G3 G2 G1 G0 0 0 B4 B3 B2 B1 B0 0 0 0
24 bit RGB888: R4 R3 R2 R1 R0 R2 R1 R0 G5 G4 G3 G2 G1 G0 G1 G0 B4 B3 B2 B1 B0 B2 B1 B0
1
2
3
4
5
6
7
8
9
10
11
12
13
void rgb565_to_rgb888(unsigned char *image, unsigned char *image888) 
{
unsigned char R, G, B;

R = *(image + 1) & 0xF8; // 补齐成:RRRRR000
G = (*(image + 1) << 5) | (*image & 0xe0 >> 3); // 补齐成:GGGGGG00
B = *image << 3 ; // 补齐成: BBBBB000

// 补偿
*(image888) = B | ((B & 0x38) >> 3);
*(image888 + 1) = G | ((G & 0x0c) >> 2);
*(image888 + 2) = R | ((R & 0x38) >> 3);
}

1.2.3.2、量化压缩:

三个字取高位

1
2
3
例子: 24 bit RGB888 -> 16 bit RGB565 的转换
24 bit RGB888:R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0
16 bit RGB656: R7 R6 R5 R4 R3 G7 G6 G5 G4 G3 G2 B7 B6 B5 B4 B3
1
2
3
4
unsigned short rgb_24_to_565(unsigned short r, unsigned short g, unsigned short b)
{
return ((r << 8) & 0xF800) | ((g << 3) & 0x07E0) | ((b >> 3) & 0x001f);
}

1、视频播放原理

视频播放原理

1.1、播放器

视音频技术包括流媒体协议技术、封装技术、视音频压缩编码技术。

播放器
Media Player Classic - HC
MPlayer
FFPlay
XBMC

1.2、流媒体协议

流媒体协议是服务器与客户端之间通信遵循的规定,视音频采用各种流媒体协议,在网络上传输视音频数据的同时,也会传输信令数据,包括对播放的控制或者对网络状态的描述等。

名称 推出机构 传输层协议 客户端 目前使用领域
RTSP+RTP IETF TCP+UDP VLC,WMP IPTV
RTMP Adobe Inc. TCP Flash 互联网直播
RTMFP Adobe Inc. UDP Flash 互联网直播
MMS Microsoft Inc. TCP/UDP WMP 互联网直播+点播
HTTP WWW+IETF TCP Flash 互联网点播
名称 概览
RTSP+RTP 采用UDP传输视音频,支持组播,效率较高,常用于IPTV领域。
但其缺点是网络不好的情况下可能会丢包,影响视频观看质量,因而围绕IPTV视频质量的研究较多。
因为互联网网络环境的不稳定性,RTSP+RTP较少用于互联网视音频传输。
RTMP,MMS,HTTP 互联网视频服务通常采用TCP作其流媒体的传输层协议,因而RTMP,MMS,HTTP这类协议广泛用于互联网视音频服务之中。
这类协议不会发生丢包,因而保证了视频的质量,但是传输的效率会相对低一些。
RTMFP 较新的流媒体协议,特点是支持P2P。

1.3、解协议

将流媒体协议的数据,解析为标准的相应的封装格式数据。
解协议的过程中会去除掉信令数据而只保留视音频数据。
例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。

1.4、封装格式

将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。
有些封装格式支持的视音频编码标准十分广泛,算比较优秀的封装格式,比如MKV;
而有些封装格式支持的视音频编码标准很少,属于落后的封装格式,比如RMVB。

名称 推出机构 流媒体 支持的视频编码 支持的音频编码 目前的使用领域
AVI Microsoft Inc. 不支持 几乎所有格式 几乎所有格式 BT下载影视
MP4 MPEG 支持 MPEG-2, MPEG-4, H.264, H.263等 AAC, MPEG-1 Layers I, II, III, AC-3等 互联网视频网站
TS MPEG 支持 MPEG-1, MPEG-2, MPEG-4, H.264 MPEG-1 Layers I, II, III, AAC IPTV,数字电视
FLV Adobe Inc. 支持 Sorenson, VP6, H.264 MP3, ADPCM, Linear PCM, AAC等 互联网视频网站
MKV CoreCodec Inc. 支持 几乎所有格式 几乎所有格式 互联网视频网站
RMVB Real Networks Inc. 支持 RealVideo 8, 9, 10 AAC, Cook Codec, RealAudio Lossless BT下载影视
其他封装格式
MOV
WMV

1.5、解封装

将输入的封装格式数据,分离成音频流压缩编码数据和视频流压缩编码数据。
例如,FLV格式的数据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。

1.6、编码

1.6.1、视频压缩编码标准

视频编码的主要作用是将视频像素数据RGB,YUV等压缩成为【视频码流】,从而降低视频的数据量。
高效率的视频编码在同等的码率下,可以获得更高的视频质量。
当前使用最多的视频编码方案就是H.264。
在码率一定的情况下,编码标准的比较:

1
HEVC > VP9 > H.264> VP8 > MPEG4 > H.263 > MPEG2
名称 推出机构 推出时间 目前使用领域
HEVC(H.265) MPEG/ITU-T 2013 研发中心
H.264 MPEG/ITU-T 2003 各个领域
MPEG4 MPEG 2001 不温不火
MPEG2 MPEG 1994 数字电视
VP9 Google 2013 研发中
VP8 Google 2008 不普及
VC-1 Microsoft Inc. 2006 微软平台

视频压缩编码器

编码器 编码标准
JM H.264
x264 H.264
HM H.265/HEVC
x265 H.265/HEVC

1.6.2、音频压缩编码标准

音频编码的主要作用是将音频采样数据PCM等压缩成为【音频码流】,从而降低音频的数据量。
高效率的音频编码在同等的码率下,可以获得更高的音质。

名称 推出机构 推出时间 目前使用领域
AAC MPEG 1997 各个领域
AC-3 Dolby Inc. 1992 电影
MP3 MPEG 1993 各个领域
WMA Microsoft Inc. 1999 微软平台

1.7、解码

将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。
通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P、RGB等等;
压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。

视频原始数据
YUV
RGB
音频原始数据
PCM

1.8、视音频同步

根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。

1.9、网络视音频

1.9.1、直播

1
直播服务普遍采用了RTMP作为流媒体协议,FLV作为封装格式,H.264作为视频编码格式,AAC作为音频编码格式。

FLV是RTMP使用的封装格式,H.264是当今实际应用中编码效率最高的视频编码标准,AAC则是当今实际应用中编码效率最高的音频编码标准。

1.9.2、点播

1
点播服务普遍采用了HTTP作为流媒体协议,H.264作为视频编码格式,AAC作为音频编码格式。

采用HTTP作为点播协议有以下两点优势:
一方面,HTTP是基于TCP协议的应用层协议,媒体传输过程中不会出现丢包等现象,从而保证了视频的质量;另一方面,HTTP被绝大部分的Web服务器支持,因而流媒体服务机构不必投资购买额外的流媒体服务器,从而节约了开支。
点播服务采用的封装格式有多种:MP4,FLV,F4V等,它们之间的区别不是很大。视频编码标准和音频编码标准是H.264和AAC。这两种标准分别是当今实际应用中编码效率最高的视频标准和音频标准。

1.10、视频参数对比

名称 对比
流媒体系统 https://en.wikipedia.org/wiki/Comparison_of_streaming_media_software
封装格式 https://en.wikipedia.org/wiki/Comparison_of_video_container_formats
视频编码器 https://en.wikipedia.org/wiki/Comparison_of_video_codecs
音频编码器 https://en.wikipedia.org/wiki/Comparison_of_audio_coding_formats
视频播放器 https://en.wikipedia.org/wiki/Comparison_of_video_player_software

2、视音频数据处理

2.1、RGB、YUV视频像素数据处理

RGB/YUV视频像素数据在视频播放器的解码流程中的位置:

视频像素数据

进程

线程

继承Thread类创建线程类

1、定义Thread子类,重写线程执行体也就是run方法。
2、创建Thread子类的实例,也就是线程对象。
3、调用线程对象的start方法来启动该线程,也就是run方法中的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class StackThread extends Thread {
private int mNum = 0;

@Override
public void run() {
super.run();//有无均可,此时Thread中target肯定是空的。
mNum++;
System.out.println(mNum);
}

public static void main(String[] args) {
StackThread stackThread = new StackThread();
stackThread.start();
StackThread stackThread1 = new StackThread();
stackThread1.start();
}
}

运行结果:

1
2
1
1

使用继承Thread类的方法创建线程类时,多个线程之间无法共享线程类的实例变量。

实现Runnable接口创建线程类

1、定义Runnable接口的实现类,重写该接口的run方法。
2、创建Runnable实现类的实例,将实例作为Thread的target属性来创建线程对象。
3、调用线程对象的start方法来启动线程。

1
2
3
4
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class StackRunnable implements Runnable{
private int mNum=0;
@Override
public void run() {
mNum++;
System.out.println(mNum);
}

public static void main(String[] args) {
StackRunnable stackRunnable = new StackRunnable();
Thread thread = new Thread(stackRunnable);//此处的stackRunnable就是Thread中的target
thread.start();
Thread thread1 = new Thread(stackRunnable);
thread1.start();
}
}

运行结果:

1
2
1
2

使用Runnable接口的方式,创建多个使用共同target的线程时,多个线程共享Runnable实现类的变量。

Thread与Runnable的关系

1
2
3
4
5
6
7
8
9
10
public class Thread implements Runnable {

@Override
public void run() {
if (target != null) {
target.run();
}
}

}

Thread类实现了Runnable接口,并且拥有Runnable类型的属性变量target。
所以若是以Thread子类的方式实现,则执行的是Thread子类的run方法;
若是以实现Runnable接口的方式实现,则执行的是Thread类中run方法,继而执行的是Runnable实现类中的run方法。
不存在既有Thread子类实现、又有Runnable接口实现的方式,Thread子类不能传入Runnable实现类实例作为构造参数来创建线程。

1
2
3
4
5
6
7
public class StackThreadRunnable {
public static void main(String[] args) {
StackRunnable stackRunnable = new StackRunnable();
StackThread thread = new StackThread(stackRunnable);//无法编译成功
thread.start();
}
}

使用Callable和Future创建线程

Callable接口提供了一个call方法作为线程执行体。
特性:call方法有返回值,且可以抛出异常。
因为call方法有返回值,所以Callable接口是一个泛型接口,实现Callable接口的时候需要传入泛型类型。

1
2
3
4
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}

Future接口,是异步计算结果的容器接口。
提供了在等待异步计算完成时检查计算是否完成的状态,并在异步计算完成后获取计算结果而且只能通过 get 方法获取结果,如果异步计算没有完成则阻塞。
可以在异步计算完成前通过 cancel 方法取消,如果异步计算被取消则标记一个取消的状态。
如果希望异步计算可以被取消而且不提供可用的计算结果,则可以声明 Future<?> 形式类型,并返回 null 作为底层任务的结果。

1
2
3
4
5
6
7
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);//试图取消Future也就是FutureTask里关联的Callable任务。
boolean isCancelled();//如果在Callable任务正常完成前被取消,返回true。
boolean isDone();//任务已经结束,在任务完成、任务取消、任务异常的情况下都返回true。
V get() throws InterruptedException, ExecutionException;//返回Callable任务里call方法的返回值,此方法会导致程序阻塞,必须等到子线程结束后才会得到返回值。
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;////如果在指定单位时间内没有返回值,将会抛出异常。
}

RunnableFuture接口,继承了Future接口和Runnable接口。

1
2
3
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}

FutureTask类,实现了RunnableFuture接口。

Callable构造函数:

1
2
3
4
5
6
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}

Runnable和返回值构造函数:

1
2
3
4
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}

通过Executors的callable方法将Runnable和结果值生成Callable对象:

1
2
3
4
5
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}

Runnable适配器实现了Callable接口,并包含Runnable属性,实现的call方法中调用Runnable的run方法:

1
2
3
4
5
6
7
8
9
10
11
12
private static final class RunnableAdapter<T> implements Callable<T> {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}

状态:

1
2
3
4
5
6
7
private static final int NEW          = 0;//新建
private static final int COMPLETING = 1;//计算中
private static final int NORMAL = 2;//正常
private static final int EXCEPTIONAL = 3;//异常
private static final int CANCELLED = 4;//已取消
private static final int INTERRUPTING = 5;//中断中
private static final int INTERRUPTED = 6;//已中断

属性:

1
2
3
4
private Callable<V> callable;//执行任务
private Object outcome;//执行结果,或者异常
private volatile Thread runner;//执行线程
private volatile WaitNode waiters;//等待的线程栈

FutureTask实现了Runnable接口,所以可以作为Thread的target,所以Thread执行的是FutureTask的run方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}

而FutureTask的run方法中执行的是Callable实例的call方法。
FutureTask的方法是阻塞方法,直到获取到到结果才返回值,结束阻塞:

1
2
3
4
5
6
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}

使用:
FutureTask与Callable:
1、创建Callable接口的实现类,并实现call方法作为线程执行体,并返回值,再创建Callable实现类的实例。
2、使用FutureTask类来包装Callable对象,FutureTask对象封装了Callable对象中call方法的返回值。
3、使用FutureTask对象作为Thread对象的target,创建并启动新线程。
4、调用FutureTask对象的get方法,来获取子线程执行结束后的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static Integer callable() {
Integer result=0;
FutureTask futureTask = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 100;
}
});
Thread thread = new Thread(futureTask);
thread.start();
try {
result = (Integer) futureTask.get();
System.out.println(result);
} catch (Exception exception) {
System.out.println(exception.getMessage());
}
return result;
}

结果:

1
100

FutureTask与Runnable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    FutureTask futureTask = new FutureTask<Integer>(new Runnable() {
@Override
public void run() {
System.out.println();
}
}, 0);
Thread thread = new Thread(futureTask);
thread.start();
try {
Integer result = (Integer) futureTask.get();
System.out.println(result);
} catch (Exception exception) {
System.out.println(exception.getMessage());
}
}

结果:

1
0

线程的生命周期

1、新建状态。
新建一个线程,Java虚拟机为其分配内存,并初始化其成员变量的值。
2、就绪状态。
当线程对象调用了start方法,线程处于就绪状态,Java虚拟机会为该线程创建方法调用栈和程序计数器,表示可以运行,什么时候运行取决于Java虚拟机里线程调度器的调度。
只能对处于新建状态的线程调用start方法,否则会抛出IllegalThreadStateException异常。
3、运行状态。
如果处于就绪状态的线程获得了CPU的时间,开始执行run方法的线程执行体,则该线程处于运行状态,多处理器的机器上将会有多个线程并行parallel执行。
4、阻塞状态。
除非线程的执行体够短,瞬间被执行结束,否则线程不会一直处于运行状态,线程在运行过程中会被中断,剥夺该线程所占用的资源,目的是使其他线程获得执行的机会。
抢占式调度。系统给每个线程一个时间片段执行,用完后执行其他线程,且会考虑线程的优先级。
协作式调度。线程主动调用了sleep或yeild方法后才会放弃所占用的资源。

线程将会进入阻塞状态:
线程调用sleep方法主动放弃所占用的处理器资源。
线程调用了一个阻塞式IO方法,在方法返回之前线程会被阻塞。
线程视图获取同步监视器,但该同步监视器正在被其他线程所持有。
线程在等待某个通知notify。
线程被调用了suspend方法被挂起。但suspend方法容易发生死锁,应该尽量避免使用。

当前正在执行的线程被阻塞后,其他就绪的线程将会获得执行的机会,之后在合适的时候被阻塞的线程将会重新进入就绪状态,等待系统调度器再次调度该线程。

解除阻塞,进入就绪状态:
调用sleep方法的线程进过了指定时间。
线程调用的阻塞式IO方法已经返回。
线程成功地获取了试图获取的同步监视器。
线程正在等待某个通知时,其他线程发送了一个通知。
处于挂起状态的线程被调用了resume恢复方法。

线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。
就绪状态线程获取处理器资源后进入运行状态,运行状态线程失去处理器资源后进入就绪状态,由系统线程调度决定。
调用yield方法可以让运行状态线程进入就绪状态。

5、死亡状态。
run方法或call方法执行完成,线程正常结束。
线程抛出一个未捕获的异常exception或错误error。
直接调用线程的stop方法结束线程,容易导致死锁,不建议使用。
线程的isAlive方法,判断线程是否存活,线程处于就绪、运行、阻塞状态返回true,线程处于新建、死亡状态返回false。

控制线程

join线程:
在当前线程调用某线程的join方法,当前线程将被阻塞,直到某线程执行完成为止。

后台/守护/精灵/Daemon线程:
Java垃圾回收线程就是典型的后台线程。
如果所有的前台线程都死亡,那么后台线程会自动死亡。
调用线程的setDaemon(true)方法可以将线程设置为后台线程,但必须在start方法之前设置,否则会抛出IllegalThreadStateException异常。
线程的isDaemon方法判断方法是否是后台线程。

线程睡眠sleep:
调用线程的sleep方法,让正在执行的线程暂停一段时间,并进入阻塞状态,在其睡眠时间内,该线程不会获得执行的机会。

线程的yield方法只是让线程暂停一下,但不会让线程进入阻塞状态,而是让线程进入就绪状态,并让系统的线程调度重新调度,有可能又重新将该线程调度出来执行,具体哪个线程被调度到,需要看线程的优先级。

sleep方法声明了InterruptedException异常,yield方法没有,不建议使用yield方法控制并发线程的执行。

线程优先级

线程默认优先级与创建它的父线程优先级相同。
线程通过setPriority(int newPriority)、getPriority设置和获取线程的优先级。
MAX_PRIORITY 10
MIN__PRIORITY 1
NORMAL_PRIORITY 5

线程同步

同步代码块。
线程的run方法不具备同步安全性,为了解决这个问题,Java的多线程引入了同步监视器,使用同步监视器的通用方法就是同步代码块。

1
2
3
synchronized(obj){
//同步代码块
}

obj就是同步监视器,线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。
任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码执行完成后,该线程会释放对同步监视器的锁定。
同步监视器的目的:
阻止两个线程对同一个共享资源的并发访问,推荐使用可能被并发访问的共享资源充当同步监视器。

同步方法。
synchronized修饰的非静态实例方法,同步监视器是this,也就是调用该方法的对象。

线程安全的类:
该类的对象可以被多个线程安全地访问。
每个线程调用该对象的任意方法之后都将得到正确的结果。
每个线程调用该对象的任意方法之后,该对象的状态依然保持合理状态。

可变类的线程安全是以降低程序的运行效率作为代价的,所以只对会改变竞争资源的方法同步,且提供单线程和多线程两种版本,例如单线程使用StringBuilder保证性能,多线程使用StringBuffer保证多线程安全。

线程释放对同步监视器的锁定:
线程的同步方法、同步代码快执行结束,释放同步监视器。
线程的同步代码块、同步方法中遇到未处理的error和exception结束,释放同步监视器。
线程的同步代码块、同步方法中遇到break、return终止,释放同步监视器。
线程的同步代码块、同步方法执行的同时,程序执行了同步监视器对象的wait方法,当前线程暂停,释放同步监视器。而调用线程sleep方法和yield方法,其他线程调用该线程的suspend方法都不会释放同步监视器。

同步锁

通过显式定义同步锁Lock对象来实现同步,同步锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应该先获得Lock对象。

读写锁:ReadWriteLock。
可重入锁:ReentrantLock,显式加锁、释放锁。
可重入性:线程可以对已被加锁的ReentrantLock锁对象再次加锁,ReentrantLock对象会维持一个计数器追踪lock方法的嵌套调用,线程每次调用lock加锁后,必须显式地调用unlock来释放锁,所以一段被锁保护的代码可以调用另一个被锁保护的代码。
可重入读写锁:ReentrantReadWriteLock。
StampedLock:是为了优化可重入读写锁性能的一个锁实现工具。
Lock显式地使用Lock对象作为同步锁,同步方法/块是系统隐式地使用当前对象作为同步监视器。
使用finally块,确保在必要时释放锁。

死锁

线程互相等待对方释放同步监视器,就会发生死锁,所有线程处于阻塞状态,无法继续。
Thread类的suspend方法容易产生死锁,不建议使用。

线程通信

Object类的wait、notify、notifyAll方法,由同步监视器对象来调用进行线程通信。

为什么wait,notify,notifyAll这些跟线程有关的方法是封装在Object中而不线程的Thread类呢?

1
因为可以把任意的对象作为锁资源进行竞争,所以任意对象都可以调用wait()和notify(),而Object是所有类的父类,所以wait和notify属于Object。

notify:唤醒在此对象监视器上等待的单个线程。
notifyAll:唤醒在此对象监视器上等待的所有线程。
wait:导致当前的线程等待,直到其他线程调用此对象的notify方法或notifyAll方法。
wait(long timeout):导致当前的线程等待,直到其他线程调用此对象的notify方法或notifyAll方法,或者超过指定的时间量。
wait(long timeout, int nanos):导致当前的线程等待,直到其他线程调用此对象的notify方法或notifyAll方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

第一章、简介

0、OpenGL ES

OpenGL ES,Open Graphics Library for Embedded Systems,嵌入式系统的开放图形库。
OpenGL ES支持的平台有iOS、Android、BlackBerry、Linux、Windows,且是基于浏览器的3D图形Web标准WebGL的基础。
OpenGL ES规范有OpenGL ES 1.0、OpenGL ES 1.1、OpenGL ES2.0、OpenGL ES 3.0。
OpenGL ES 1.0和1.1采用固定功能管线,2.0采用可编程图形管线,3.0采用阴影贴图、体渲染、基于GPU的粒子动画、几何形状实例化、纹理压缩和伽马校正等技术。

1.1、OpenGL ES 3.0

OpenGL ES 3.0实现了具有可编程着色功能的图形管线,由OpenGL ES3.0API规范和OpenGL ES着色语言3.0规范也就是OpenGL ES SL组成。

在OpenGL ES3.0图形管线的各个阶段中,顶点着色器和片段着色器是管线的可编程阶段。

OpenGL ES3.0图形管线的各个阶段

1.1.1、顶点着色器

顶点着色器实现了顶点操作的通用可编程方法。

1、变量声明

var用于声明值从不更改的变量。
val用于声明值可以更改的变量。

2、类型推断

为变量赋予初始值后,Kotlin编译器可以根据所赋值的类型来推断其变量类型。

3、null安全

在变量类型后面加上 ? 后缀,可将变量指定为 null。

4、条件语句

Kotlin 的条件语句彰显了智能类型转换功能。
if-else 表达式。
when 表达式。

5、函数

fun关键字声明函数。
函数返回单个表达式的结果时,可以通过直接返回函数中包含的 if-else 表达式的结果来跳过声明局部变量。

6、匿名函数

匿名函数不需要名称,通过输入和输出更直接地进行标识。
可以保留对某个匿名函数的引用,以便日后使用此引用来调用该匿名函数。
与其他引用类型一样,也可以在应用中传递引用
变量:

1
(参数类型)->返回值类型
1
2
3
4
val stringLengthFunc: (String) -> Int = { input ->
    input.length
}
val stringLength: Int = stringLengthFunc("Android")

7、高阶函数

将其他函数用作参数的函数称为高阶函数。此模式对组件之间的通信(其方式与在 Java 中使用回调接口相同)很有用。
参数:

1
(参数类型)->返回值类型
1
2
3
fun stringMapper(str: String, mapper: (String) -> Int): Int {
    return mapper(str)
}

如果匿名函数是在某个函数上定义的最后一个参数,则可以在用于调用该函数的圆括号之外传递它。

1
2
3
stringMapper("Android") { input ->
    input.length
}

8、类

class关键字定义类。

9、属性

类使用属性来表示状态。

10、类函数和封装

类使用函数对行为建模。
函数可以修改状态,从而只公开希望公开的数据。
这种访问控制机制属于一个面向对象的封装。

11、互操作性

由于 Kotlin 代码可编译为 JVM 字节码,因此 Kotlin 代码可直接调用 Java 代码,反之亦然。