BlueXIII's Blog

热爱技术,持续学习

0%

Android推流

RTSP Camera

https://play.google.com/store/apps/details?id=com.miv.rtspcamera&hl=zh

播流地址:

RTMP Camera

https://play.google.com/store/apps/details?id=com.miv.rtmpcamera&hl=en_US

RTMP推流:
rtmp://10.30.22.242:1935/hls/mystream

需要提前搭建nginx-with-rtmp

OBS推流

https://obsproject.com/

ffmpeg推流

推视频文件

1
2
ffmpeg -i /tmp/test.mp4 -f flv rtmp://10.30.22.242:1935/hls/mystream
ffmpeg -i /tmp/test2.flv -f flv rtmp://10.30.22.242:1935/hls/mystream

推USB摄像头

1
2
3
4
5
6
7
8
9
10
11
# Windows列出设备
ffplay -f dshow -i video="USB2.0 Camera"

# macOS列出设备
ffmpeg -f avfoundation -list_devices true -i ""

# 只推摄像头
ffmpeg -f avfoundation -framerate 30 -video_size 1280x720 -i "0" -vcodec libx264 -acodec libfaac -f flv rtmp://10.30.22.242:1935/hls/mystream

# 推摄像头+麦克风
ffmpeg -f avfoundation -framerate 30 -video_size 1280x720 -i "0:1" -vcodec libx264 -preset ultrafast -acodec libmp3lame -ar 44100 -ac 1 -f flv rtmp://10.30.22.242:1935/hls/mystream

推桌面录屏

1
2
ffmpeg -f avfoundation -pixel_format uyvy422 -i "1" -f flv rtmp://10.30.22.242:1935/hls/mystream
ffmpeg -f avfoundation -i "1" -vcodec libx264 -preset ultrafast -acodec libfaac -f flv rtmp://10.30.22.242:1935/hls/mystream

官网

编译

1
2
3
4
5
6
7
git clone https://gitee.com/winlinvip/srs.oschina.git srs
cd srs/trunk
git remote set-url origin https://github.com/ossrs/srs.git
git pull

./configure --osx
make

运行

1
2
3
4
5
6
7
8
9
./objs/srs -c conf/srs.conf
./objs/srs -c conf/hls.conf
./objs/srs -c conf/http.flv.live.conf
./objs/srs -c conf/dash.conf

[2020-08-19 08:35:47.917][Trace][4124][388] RTMP listen at tcp://0.0.0.0:1935, fd=7
[2020-08-19 08:35:47.917][Trace][4124][388] HTTP-Server listen at tcp://0.0.0.0:8080, fd=8
[2020-08-19 08:35:47.929][Trace][4124][388] signal installed, reload=1, reopen=30, fast_quit=15, grace_quit=3
[2020-08-19 08:35:47.929][Trace][4124][388] http: api mount /console to ./objs/nginx/html/console

模拟推流

1
2
3
4
5
6
for((;;)); do \
./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
-vcodec copy -acodec copy \
-f flv -y rtmp://127.0.0.1/live/livestream; \
sleep 1; \
done

API

  • versions /api/v1/versions 获取服务器版本信息
  • summaries /api/v1/summaries 获取服务器的摘要信息
  • rusages /api/v1/rusages 获取服务器资源使用信息
  • self_proc_stats /api/v1/self_proc_stats 获取服务器进程信息
  • system_proc_stats /api/v1/system_proc_stats 获取服务器所有进程情况
  • meminfos /api/v1/meminfos 获取服务器内存使用情况
  • authors /api/v1/authors 获取作者、版权和License信息
  • features /api/v1/features 获取系统支持的功能列表
  • requests /api/v1/requests 获取请求的信息,即当前发起的请求的详细信息
  • vhosts /api/v1/vhosts 获取服务器上的vhosts信息
  • streams /api/v1/streams 获取服务器的streams信息
  • clients /api/v1/clients 获取服务器的clients信息,默认获取前10个
  • configs /api/v1/configs CUID配置,RAW API

参考文档

Windows预编译版下载地址

http://nginx-win.ecsds.eu/download/
http://nginx-win.ecsds.eu/download/nginx%201.7.11.3%20Gryphon.zip

直接安装即可,不做赘述

yamdi

简介

yamdi用于为rtmp点播文件添加索引

下载

http://yamdi.sourceforge.net/
https://github.com/ioppermann/yamdi/releases

编译

1
2
gcc yamdi.c -o yamdi -O2 -Wall
cp yamdi /usr/local/bin

nginx-rtmp

编译安装

1
2
3
./configure --prefix=/opt/nginx-rtmp --with-openssl=../openssl-1.1.1g --add-module=../nginx-rtmp-module-1.2.1 --with-debug
make
make install

配置

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
worker_processes  1;

error_log logs/error.log debug;

events {
worker_connections 1024;
}

rtmp {
server {
listen 1935;

application hls {
live on; #启用rtmp直播
#地址为rtmp://[server]:[rtmp_port]/[app]/[stream]
hls on; #启用hls直播
#地址为http://[server]:[http_port]/[app]/[stream].m3u8
#需要配合下面http段设置使用
hls_path nginx-rtmp-module/tmp/app/;
hls_fragment 5s;
recorder rec { #启用录制
record all manual; #手动控制录制启停
record_suffix _rec.flv;
record_path nginx-rtmp-module/tmp/rec/; #录制保存地址
record_unique on;
}
}
application vod2{ #rtmp点播
play nginx-rtmp-module/tmp/rec/;
}
}
}

http {
server {
listen 18080;

location /stat { #服务器状态
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}

location /stat.xsl {
root nginx-rtmp-module/;
}

location /control { #控制器
rtmp_control all;
}

location /hls/ { #hls直播地址
#server hls fragments
types{
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
alias nginx-rtmp-module/tmp/app/;
expires -1;
}

location /vod/{ #hls点播地址
alias nginx-rtmp-module/tmp/rec/;
}

location / {
root nginx-rtmp-module/test/www/;
}
}
}

注意拷贝stat.xsl到相应目录下

测试

rtmp监控

http://localhost:18080/stat

rtmp推流

地址:
rtmp://10.30.22.242:1935/hls/mystream

rtmp直播

rtmp://127.0.0.1:1935/hls/mystream

hls直播

http://127.0.0.1:18080/hls/mystream.m3u8

录制视频

http://127.0.0.1:18080/control/record/start?app=hls&name=mystream&rec=rec
http://127.0.0.1:18080/control/record/stop?app=hls&name=mystream&rec=rec

为rtmp点播文件添加索引

1
2
cd /tmp/rec
yamdi -i mystream-1597303714_rec.flv -o mystream-1597303714_rec_idx.flv

rtmp点播

rtmp://127.0.0.1:1935/vod2/mystream-1597303714_rec_idx.flv

制作hls点播分片文件

1
2
3
4
5
cd nginx-rtmp/nginx-rtmp-module/tmp/rec

ffmpeg -i mystream-1597303714_rec.flv -acodec copy -bsf:a h264_mp4toannexb -g 105 -vcodec libx264 -vprofile baseline -bf 0 -bufsize 850k -bsf:v dump_extra -map 0 -f segment -segment_format mpegts -segment_list "mystream-1597303714_rec.m3u8" -segment_time 10 mystream-1597303714_rec-%d.ts

ffmpeg -i mystream-1597303714_rec.flv -acodec copy -bsf:a aac_adtstoasc -g 105 -vcodec libx264 -vprofile baseline -bf 0 -bufsize 850k -bsf:v dump_extra -map 0 -f segment -segment_format mpegts -segment_list "mystream-1597303714_rec.m3u8" -segment_time 10 mystream-1597303714_rec-%d.ts

hls点播

http://127.0.0.1:18080/vod/mystream-1597303714_rec.m3u8

github

https://github.com/winshining/nginx-http-flv-module/blob/master/README.CN.md

源码下载

编译

1
2
3
./configure --prefix=/opt/nginx-flv --with-openssl=../openssl-1.1.1g --add-module=../nginx-http-flv-module-1.2.7 --with-debug
make
make install

配置

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
111
112
113
worker_processes  1; #运行在Windows上时,设置为1,因为Windows不支持Unix domain socket

error_log logs/error.log error;

events {
worker_connections 4096;
}

http {
include mime.types;
default_type application/octet-stream;

keepalive_timeout 65;

server {
listen 80;

location / {
root /opt/nginx-flv/html;
index index.html index.htm;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

location /live {
flv_live on; #打开HTTP播放FLV直播流功能
chunked_transfer_encoding on; #支持'Transfer-Encoding: chunked'方式回复

add_header 'Access-Control-Allow-Origin' '*'; #添加额外的HTTP头
add_header 'Access-Control-Allow-Credentials' 'true'; #添加额外的HTTP头
}

location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}

root /opt/nginx-flv/tmp;
add_header 'Cache-Control' 'no-cache';
}

location /dash {
root /opt/nginx-flv/tmp;
add_header 'Cache-Control' 'no-cache';
}

location /stat {
#推流播放和录制统计数据的配置

rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}

location /stat.xsl {
root /opt/nginx-flv/html/rtmp; #指定stat.xsl的位置
}

location /control {
rtmp_control all; #rtmp控制模块的配置
}
}
}

rtmp_auto_push on;
rtmp_auto_push_reconnect 1s;
rtmp_socket_dir /opt/nginx-flv/tmp;

rtmp {
out_queue 4096;
out_cork 8;
max_streams 128;
timeout 15s;
drop_idle_publisher 15s;

log_interval 5s; #log模块在access.log中记录日志的间隔时间,对调试非常有用
log_size 1m; #log模块用来记录日志的缓冲区大小

server {
listen 1935;
# server_name www.test.*; #用于虚拟主机名后缀通配

application myapp {
live on;
gop_cache on; #打开GOP缓存,减少首屏等待时间
}

application hls {
gop_cache on;
live on;
hls on;
hls_path /opt/nginx-flv/tmp/hls;

hls_fragment 5s;
recorder rec { #启用录制
record all manual; #手动控制录制启停
record_suffix _rec.flv;
record_path /opt/nginx-flv/tmp/record; #录制保存地址
record_unique on;
}
}

application dash {
gop_cache on;
live on;
dash on;
dash_path /opt/nginx-flv/tmp/dash;
}
}
}

注意拷贝stat.xsl到相应目录下

测试

rtmp监控

http://127.0.0.1/stat

rtmp推流

rtmp://10.30.22.242:1935/hls/mystream
rtmp://10.30.22.242:1935/dash/mystream
rtmp://10.30.22.242:1935/myapp/mystream

rtmp直播

rtmp://127.0.0.1:1935/hls/mystream
rtmp://127.0.0.1:1935/myapp/mystream

hls直播

http://127.0.0.1/hls/mystream.m3u8

flv直播

http://127.0.0.1/live?port=1935&app=hls&stream=mystream

dash直播

http://127.0.0.1/dash/mystream.mpd

录制视频

http://127.0.0.1/control/record/start?app=hls&name=mystream&rec=rec
http://127.0.0.1/control/record/stop?app=hls&name=mystream&rec=rec

ffplay

ffplay -i rtmp://127.0.0.1:1935/hls/mystream -fflags nobuffer -analyzeduration 1000000
ffplay “http://127.0.0.1/live?port=1935&app=hls&stream=mystream"

参考文档

官网

二进制下载

启动

1
2
3
4
5
6
7
./livego

INFO[2020-08-18T16:55:13+08:00] HLS server enable....
INFO[2020-08-18T16:55:13+08:00] HTTP-FLV listen On :7001
INFO[2020-08-18T16:55:13+08:00] HTTP-API listen On :8090
INFO[2020-08-18T16:55:13+08:00] RTMP Listen On :1935
INFO[2020-08-18T16:55:13+08:00] HLS listen On :7002

获取channelkey

访问 http://localhost:8090/control/get?room=movie 获取一个房间的 channelkey(channelkey用于推流,movie用于播放)

1
2
curl http://localhost:8090/control/get?room=movie
rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk

推流

通过RTMP协议推送视频流到地址 rtmp://localhost:1935/{appname}/{channelkey} (appname默认是live)

1
ffmpeg -re -i test.mp4 -c copy -f flv rtmp://127.0.0.1:1935/live/rfBd56ti2SMtYvSgD5xAV0YU99zampta7Z7S575KLkIZ9PYk

播放

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
./livego  -h
Usage of ./livego:
--api_addr string HTTP管理访问监听地址 (default ":8090")
--config_file string 配置文件路径 (默认 "livego.yaml")
--flv_dir string 输出的 flv 文件路径 flvDir/APP/KEY_TIME.flv (默认 "tmp")
--gop_num int gop 数量 (default 1)
--hls_addr string HLS 服务监听地址 (默认 ":7002")
--hls_keep_after_end Maintains the HLS after the stream ends
--httpflv_addr string HTTP-FLV server listen address (默认 ":7001")
--level string 日志等级 (默认 "info")
--read_timeout int 读超时时间 (默认 10)
--rtmp_addr string RTMP 服务监听地址 (默认 ":1935")
--write_timeout int 写超时时间 (默认 10)

官网

ffprobe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看媒体文件信息
ffprobe test.mp4

# 输出文件的格式信息
ffprobe -show_format test.mp4

# 以json格式输出每个流最详细的信息
ffprobe -print_format json -show_streams test.mp4

# 显示帧信息
ffprobe -show_streams test.mp4

# 查看包信息
ffprobe -show_packets test.mp4

ffplay

常用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 播放音视频文件
ffplay test.mp4

# 循环播放
ffplay test.mp4 -loop 3

# 播放音频流
ffplay test.mp4 -ast 1

# 播放视频流
ffplay test.mp4 -vst 0

# 播放rtmp流
ffplay -i rtmp://127.0.0.1:1935/hls/mystream -fflags nobuffer -analyzeduration 1000000

# 播放裸数据音频
ffplay 原始文件(pcm文件) -f 格式 -channels 声道数 -ar 采样率
# 播放裸数据视频
ffplay 原始格式(例如-f rawvideo) 格式(例如-pixel_format yuv420p) 宽高(例如-s 480*480) 原始文件(yuv)

# 播放裸数据RGB
ffplay -f rawvideo -pixel_format rgb24 -s 480*480 texture.rgb

常用参数

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
# 通用参数
-f fmt 指定格式(音频或视频格式)
-i filename 指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头
-y 覆盖已有文件
-t duration 记录时长为
-fs limit_size 设置文件大小上限
-ss time_off 从指定的时间(s)开始, [-]hh:mm:ss[.xxx]的格式也支持
-re 代表按照帧率发送,作为推流工具一定要加入参数,否则ffmpeg会按照最高速率向流媒体服务器不停发送数据

# 视频参数
-b 指定比特率(bits/s),似乎ffmpeg是自动VBR的,指定了就大概是平均比特率
-bitexact 使用标准比特率
-vb 指定视频比特率(bits/s)
-vframes number 设置转换多少桢(frame)的视频
-r rate 帧速率(fps) (可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97)
-s size 指定分辨率 (320x240)
-aspect aspect 设置视频长宽比(4:3, 16:9 or 1.3333, 1.7777)
-croptop size 设置顶部切除尺寸(in pixels)
-cropbottom size 设置底部切除尺寸(in pixels)
-cropleft size 设置左切除尺寸 (in pixels)
-cropright size 设置右切除尺寸 (in pixels)
-padtop size 设置顶部补齐尺寸(in pixels)
-padbottom size 底补齐(in pixels)
-padleft size 左补齐(in pixels)
-padright size 右补齐(in pixels)
-padcolor color 补齐带颜色(000000-FFFFFF)
-vn 取消视频
-vcodec codec 强制使用codec编解码方式('copy' to copy stream)
-sameq 使用同样视频质量作为源(VBR)
-pass n 选择处理遍数(1或者2)。两遍编码非常有用。第一遍生成统计信息,第二遍生成精确的请求的码率
-passlogfile file 选择两遍的纪录文件名为file
-newvideo 在现在的视频流后面加入新的视频流

# 高级视频选项
-pix_fmt format set pixel format, 'list' as argument shows all the pixel formats supported
-intra 仅适用帧内编码
-qscale q 以<数值>质量为基础的VBR,取值0.01-255,约小质量越好
-loop_input 设置输入流的循环数(目前只对图像有效)
-loop_output 设置输出视频的循环数,比如输出gif时设为0表示无限循环
-g int 设置图像组大小
-cutoff int 设置截止频率
-qmin int 设定最小质量,与-qmax(设定最大质量)共用,比如-qmin 10 -qmax 31
-qmax int 设定最大质量
-qdiff int 量化标度间最大偏差 (VBR)
-bf int 使用frames B 帧,支持mpeg1,mpeg2,mpeg4

# 音频参数
-ab 设置比特率(单位:bit/s,也许老版是kb/s)前面-ac设为立体声时要以一半比特率来设置,比如192kbps的就设成96,转换 默认比特率都较小,要听到较高品质声音的话建议设到160kbps(80)以上。
-aframes number 设置转换多少桢(frame)的音频
-aq quality 设置音频质量 (指定编码)
-ar rate 设置音频采样率 (单位:Hz),PSP只认24000
-ac channels 设置声道数,1就是单声道,2就是立体声,转换单声道的TVrip可以用1(节省一半容量),高品质的DVDrip就可以用2
-an 取消音频
-acodec codec 指定音频编码('copy' to copy stream)
-vol volume 设置录制音量大小(默认为256) <百分比> ,某些DVDrip的AC3轨音量极小,转换时可以用这个提高音量,比如200就是原来的2倍
-newaudio 在现在的音频流后面加入新的音频流

# 字幕参数
-sn 取消字幕
-scodec codec 设置字幕编码('copy' to copy stream)
-newsubtitle 在当前字幕后新增
-slang code 设置字幕所用的ISO 639编码(3个字母)
Audio/Video 抓取选项:
-vc channel 设置视频捕获通道(只对DV1394)
-tvstd standard 设置电视标准 NTSC PAL(SECAM)

ffmpeg

参数格式

1
2
3
4
5
6
7
8
9
10
11
12
13
ffmpeg {1} {2} -i {3} {4} {5}
1. 全局参数
2. 输入文件参数
3. 输入文件
4. 输出文件参数
5. 输出文件

ffmpeg \
-y \ # 全局参数
-c:a libfdk_aac -c:v libx264 \ # 输入文件参数
-i input.mp4 \ # 输入文件
-c:v libvpx-vp9 -c:a libvorbis \ # 输出文件参数
output.webm # 输出文件

常用参数

1
2
3
4
5
6
7
8
9
-c:指定编码器
-c copy:直接复制,不经过重新编码(这样比较快)
-c:v:指定视频编码器
-c:a:指定音频编码器
-i:指定输入文件
-an:去除音频流
-vn: 去除视频流
-preset:指定输出的视频质量,会影响文件的生成速度,有以下几个可用的值 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow。
-y:不经过确认,输出时直接覆盖同名文件。

常用示例

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
# 转换编码格式
ffmpeg -i input.mp4 -c:v libx264 output.mp4
ffmpeg -i input.mp4 -c:v libx265 output.mp4

# 转换容器格式
ffmpeg -i input.mp4 -c copy output.webm

# 调整码率
ffmpeg \
-i input.mp4 \
-minrate 964K -maxrate 3856K -bufsize 2000K \
output.mp4

# 改变分辨率(transsizing)
ffmpeg \
-i input.mp4 \
-vf scale=480:-1 \
output.mp4

# 提取音频
ffmpeg \
-i input.mp4 \
-vn -c:a copy \
output.aac

# 添加音轨
ffmpeg \
-i input.aac -i input.mp4 \
output.mp4

# 连续截图1秒钟
ffmpeg \
-y \
-i input.mp4 \
-ss 00:01:24 -t 00:00:01 \
output_%3d.jpg

# 指定一帧截图
# -vframes 1指定只截取一帧,-q:v 2表示输出的图片质量
ffmpeg \
-ss 00:01:24 \
-i input.mp4 \
-vframes 1 -q:v 2 \
output.jpg

# 裁剪
ffmpeg -ss 00:01:50 -i input.mp4 -t 10.5 -c copy output.mp4

# 为音频添加封面
# -loop 1参数表示图片无限循环,-shortest参数表示音频文件结束,输出视频就结束。
ffmpeg \
-loop 1 \
-i cover.jpg -i input.mp3 \
-c:v libx264 -c:a aac -b:a 192k -shortest \
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

# 列出ffmpeg支持的所有格式
ffmpeg -formats

# 剪切一段媒体文件,将文件从50秒开始剪切20秒,输入到新文件,-ss是指定时间,-t是指定时长 提取一个视频中的音频文件

ffmpeg -i input.mp4 -ss 00:00:50.0 -codec copy -t 20 output,mp4

# 提取一个视频中的音频文件
ffmpeg -i input.mp4 -vn -acodec copy output.m4a

# 使一个视频中的音频静音
ffmpeg -i input.mp4 -an -vcodec copy output.mp4

# 从MP4文件中导出H264裸流
ffmpeg -i test.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264

# 使用aac和h264文件生成mp4文件
ffmpeg -i input.aac -i input.h264 -acodec copy -bsf:a aac_adtstoasc -vcodec copy -f mp4 output.mp4

# 对音频文件的编码做转换
ffmpeg -i input.wav -acodec libfdk_aac output.aac

# 从wav文件导出pcm裸数据
ffmpeg -i input.wav -acodec pcm_s16le -f s16le output.pcm

# 将mp4导出为gif,参数设置为宽度100,帧率10,只处理前五秒
ffmpeg -i test.mp4 -vf sacle=100:-1 -t 5 -r 10 output.gif

# 将视频画面生成图片,参数为每四秒截取一张,生成缩略图
ffmpeg -i test.mp4 -r 0.25 frames_%04d.png

# 将两路声音合并,例如添加背景音乐,输出时间是以较短的为准
ffmpeg -i input.wav -i bgm.wav -filter_complex amix=inputs=2:deration=shortest output.wav


# 视频添加水印,视频宽度为100,水印宽度为20
ffmpeg -i test.mp4 -i image.png -filter_complex '[0:v][1:v]overlay=100-20-10:10:1[out]' -map '[out]' output.mp4

# 将视频推送到流媒体服务器(-re表示实际速度)
ffmpeg -re -i test.mp4 -acodec copy -vcodec copy -f flv rtmp://10.30.22.242:1935/hls/mystream

# 将流媒体服务器的流dump到本地
ffmpeg -i rtmp://10.30.22.242:1935/hls/mystream -acodec copy -vcodec copy -f flv output.flv

参考文档

换源

1
2
3
4
5
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes

conda config --show channels

查看版本等信息

1
2
conda info
conda -V/--version

环境查看

1
2
conda env list
conda info -e

环境管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建新环境
conda create --name newenv python=3.6

# 复制新环境
conda create -n newenv --clone base

# 删除环境
conda remove --name newenv --all

# 激活环境
conda activate newenv

# 退出环境
conda deactivate

环境导入导出

1
2
3
4
5
# 导出
conda env export > environment.yaml

# 导入
conda env create -f .../environment.yaml

包管理

1
2
3
4
5
6
7
8
9
conda list # 查看已经安装的包
conda update conda # 升级conda自身
conda search requests # 查询某个包
conda install requests # 安装新包
conda install -n test requests # 将包安装到指定环境
conda update --all # 更新所有包
conda update requests # 升级包
conda remove requests # 移除包
conda remove -n test requests # 从指定环境中移除包

requirements.txt导入导出

1
2
3
4
5
6
7
8
9
10
11
# pip批量导出包含环境中所有组件的requirements.txt文件
pip freeze > requirements.txt

# pip批量安装requirements.txt文件中包含的组件依赖
pip install -r requirements.txt

# conda批量导出包含环境中所有组件的requirements.txt文件
conda list -e > requirements.txt

# conda批量安装requirements.txt文件中包含的组件依赖
conda install --yes --file requirements.txt

参考文档

参考文档

tslint

命名

  • 类型名使用 PascalCase
  • 接口名前不要加 I
  • 枚举值使用 PascalCase
  • 函数名使用 camelCase
  • 属性和局部变量名使用 camelCase
  • 使用UPPER_SNAKE_CASE来命名常量
  • 私有属性名不要使用 _ 前缀
  • 命名时尽可能地使用全名(而非缩写)

风格

  • 使用单引号括起字符串
  • 总是使用大括号括起循环体和条件体开大括号
  • 总是放在其关联语句的同一行(大括号不换行)
  • 圆括号内侧不留空格;在圆括号内的逗号、冒号和分号后空一格。
  • 每个定义使用单独的语句。
    1
    2
    3
    4
    5
    // BAD: 
    var x = 1, y = 2;
    // GOOD:
    var x = 1;
    var y = 2;
  • const > let > var
  • 当单行无法容下参数声明时, 在多行显示, 且参数必须对齐. 同理, 类成员, 数组成员接口成员也需要对齐
  • 行末是否使用分号,不硬性规定
  • 对于跨越多行的数组字面量, 对象字面量/import/export/type字面量/函数形参列表/函数调用实参等, 都应该尾随逗号
    1
    2
    3
    4
    const trailingWhitespace = {
    foo: 'string',
    bar: 1,
    }

    注释

  • 使用// 作为单行注释, 注释内容前面必须有空格
  • 每个文件开头必须提供关于这个文件的注释.说明文件的内容, TODO, 更新日期, 权限等信息
  • 使用/** …*/作为多行注释, 并使用JSDoc格式对代码进行文档化

箭头函数

  • 只有一个参数时,可以省略括号
    1
    const arrowFunctiono = i => i 
  • 如果可以尽量使用简写的返回
    1
    const shorthandReturno = () => true
  • 别保存 this 的引用。优先使用箭头函数
    1
    2
    3
    4
    5
    function foo() {
    return () => {
    console.log(this)
    }
    }

    函数

  • new 操作始终加上()
  • 使用函数声明代替函数表达式
    1
    2
    3
    4
    // BAD:
    const foo = function () {}
    // GOOD:
    function foo() {}
  • 不要使用 arguments。可以选择 rest 语法 … 替代。
    1
    2
    3
    function concatenateAll(...args) {
    return args.join('');
    }

    对象

  • 只有在必要时, 才添加引号
    1
    2
    3
    4
    const objo = {
    foo: 'string',
    'foo bar': 1,
    }
  • 优先使用object spread 语法
    1
    2
    3
    4
    // BAD:
    let objectAssign = Object.assign({}, obj1)
    // GOOD:
    let objectSpread = { ...obj1 }

    接口

  • 始终显式设置成员的可见性, 这样可以提醒你慎重考虑属性的可见性, 避免将因为省略描述符而将私有成员暴露出去
    1
    2
    3
    4
    5
    class Membero {
    public foo() {
    // do somthing
    }
    }

    操作符/表达式

  • 二元操作符中的字面量应该始终在变量的右边
  • 不要比较boolean 字面量
    1
    2
    3
    4
    //BAD:
    let baz = ok === true ? 'foo' : 'baz'
    // GOOD:
    baz = ok ? 'foo' : 'baz'
  • 当使用if语句比较超过三个时, 考虑使用switch
  • 优先使用字符串模板
    1
    temp = `print${hello} ${world}`

    模块

  • 导入默认导出, 最好匹配库默认的导出名
    1
    2
    3
    4
    // BAD:
    import IReact from 'react'
    // GOOD:
    import React from 'react'
  • 为了减少打包体积, 避免引入整个模块, 而是按需引入需要的子模块. 典型的应用就是lodash
    1
    2
    3
    4
    // BAD:
    import _ from 'lodash'
    // GOOD:
    import findIndex from 'lodash/findIndex'

    其它

  • 除非类型/函数需要在多个组件中共享,否则不要导出(export)
  • 在文件中,类型定义应该放在最前面
  • 使用 undefined,不要使用 null

简介

项目中经常需要将Model和DTO互转,产生很多getXXX/setXXX的模板化代码。使用ModelMapper可以简化转换步骤。

官网

http://modelmapper.org/

添加依赖

1
2
3
4
5
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.0</version>
</dependency>

对象示例

源对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Assume getters and setters on each class
class Order {
Customer customer;
Address billingAddress;
}

class Customer {
Name name;
}

class Name {
String firstName;
String lastName;
}

class Address {
String street;
String city;
}

目标对象:

1
2
3
4
5
6
7
// Assume getters and setters
class OrderDTO {
String customerFirstName;
String customerLastName;
String billingStreet;
String billingCity;
}

自动转换

1
2
ModelMapper modelMapper = new ModelMapper();
OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);

自定义转换

1
2
3
4
5
6
modelMapper.typeMap(Order.class, OrderDTO.class).addMappings(mapper -> {
mapper.map(src -> src.getBillingAddress().getStreet(),
Destination::setBillingStreet);
mapper.map(src -> src.getBillingAddress().getCity(),
Destination::setBillingCity);
});

转换配置

ModelMapper有Standard、Loose、Strict 3种模式:

1
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE);

参考文档:
http://modelmapper.org/user-manual/configuration/#matching-strategies

转换校验

assert手工校验:

1
assertEquals(order.getCustomer().getName().getFirstName(), orderDTO.getCustomerFirstName());

ModelMapper自动校验:

1
2
3
modelMapper.createTypeMap(Order.class, OrderDTO.class);
modelMapper.addMappings(new OrderMap());
modelMapper.validate();

参考文档

单一职责

  • 一个文件定义一样东西,比如一个组件、一个服务、一个管道、一个指令
  • 每个文件最多不要超过400行
  • 定义功能单一的函数
  • 一个函数最多不要超过75行

命名规范

  • 文件名采用feature.type.**,feature表示特性,type表示类型
    • 模块用 .module.ts
    • 路由模块用 -routing.module.ts
    • 组件用 .component.ts|html|css
    • 服务用 .service.ts
    • 管道用 .pipe.ts
    • 指令用 .directive.ts
    • 类型用 .model.ts
    • 数据用 .data.ts
  • 用”-“来分割单词,比如hero-list.component.ts
  • 单元测试文件名保持和测试对象一致,并以.spec.ts结尾
  • 端到端测试文件名保持和测试对象一致,并以.e2e-spec.ts结尾
  • 类名用大写驼峰规则,并且保持跟文件名的一致
    • 模块:比如app.module.ts定义的类名为AppModule
    • 路由模块:比如app-routing.module.ts定义的类名为AppRoutingModule
    • 组件:比如hero-list.component.ts定义的类名为HeroListComponent
    • 服务:比如logger.service.ts定义的类名为LoggerService
    • 管道:比如address.pipe.ts定义的类名为AddressPipe
    • 指令:比如highlight.directive.ts定义的类名为HighlightDirective
    • 类型:按模块来划分,一个.model.ts定义多个类型
    • 数据:比如address-book.data.ts定义的变量名为addressBook
  • 脚本启动入口的文件名规定为main.ts,里面不包括任何业务逻辑,但要记得处理失败的情况
  • 指令选择器的命名采用小写驼峰规则,比如clickOutSide
  • 组件选择器的命名采用分隔符“-”连接小写字母的形式,比如hero-list

代码规范

  • 类命名采用大写驼峰规则
  • 常量定义用const,并且全部大写,如果有多个单词,用“_”连接,比如HERO_URL
  • 支持ES6的环境下,禁止使用var定义变量
  • 变量命名尽量控制在3个单词以内,有常见缩写形式的单词可采用缩写形式
  • 接口类型用大写驼峰规则,不建议以I为前缀,考虑用类代替接口
  • 属性和方法名用小写驼峰,私有属性和方法不建议以“_”为前缀
  • 建议用空一行的方式来区分第三方库的导入和项目本身文件的导入

项目结构

  • 好的项目结构的准则就是快速定位,快速识别,尽可能保持扁平,不要写冗余/重复代码
  • 把所有项目代码放入src文件夹,为每个特性创建文件夹
  • 第三方库放在src外的一个文件夹
  • 每个组件、服务、管道、指令都独成一个文件
  • 当组件有多个附属文件时(htm,css,ts,spec.ts),建议创建一个独立的文件夹

模块

  • src/app下创建根模块,建议命名为app.module.ts
  • 为每个特性创建一个模块,并且保持文件夹命名和模块命名一致
  • 共享模块建议用SharedModule命名,放在app/shared/shared.module.ts
  • 在共享模块中定义复用的组件、指令和管道,避免在共享模块中定义服务
  • 在共享模块中导入所有必需的模块,比如CommonModuleFormsModule,导出所有复用的模块、组件、指令、管道
  • 考虑将只用一次的类放在核心特性模块中,并且仅在根模块中导入,建议写guard.ts来保证
  • 核心特性模块建议用CoreModule命名,放在app/core/core.module.ts
  • 将单例服务放在核心特性模块中,比如ExceptionServiceLoggerService
  • 在核心特性模块中导入所有必需的模块,比如CommonModuleFormsModule,导出所有定义的组件,服务等
  • 将全局仅用一次的组件放在核心特性模块,然后只在AppModule中导入,比如NavComponentSpinnerComponent
  • 独立的特性模块可以做成懒加载模块,避免在任何地方导入懒加载模块,不然模块就会直接加载

组件

  • 当模板/样式超过3行时,写成单独文件
  • 删除无样式定义的样式文件
  • 模板/样式的定义与组件命名保持一致
  • 导入时使用相对路径
  • 用装饰器@Input@Output来修改输入输出数据,而不是使用元数据中的inputsoutputs属性
  • 不推荐重命名输入输出数据
  • 按照变量,构造器,生命周期函数,一般方法的顺序定义;一般方法按页面的功能模块放一起,被调用的方法写在后面;变量和方法均先公有后私有排列;
  • 组件中只写跟视图相关的逻辑,把其他的业务逻辑放在服务中
  • 将可复用的业务逻辑放在服务中
  • 不推荐事件命名加上on前缀
  • 将展示逻辑放在组件的类中,而不是在模板中

指令

  • 当有跟模板无关的展示逻辑时,使用属性指令,比如[highlight]指令
  • 推荐使用@HostListener@HostBinding,而不是元数据的host属性

服务

  • 保持单例服务的使用规则,服务用来在特性模块或者应用中共享数据和方法
  • 跟函数一样,一个服务只有一个目的
  • 把服务注入在最高层的组件/模块中,使得该单例服务能在子组件、子模块中共享
  • 使用@Injectable装饰服务类,而不是@Inject装饰参数
  • 跟数据相关的操作,比如xhr请求,本地储存等抽象成服务

生命周期

  • 需要生命周期钩子时,实现相关的接口,可有效防止错误

辅助工具

  • 用codelyzer来保持代码遵守该指南,当然你可以根据自己情况调整
  • 使用IDE的代码片段工具来快速生成具有一致性的代码片段,比如给VS Code安装snippets