srs中ffmpeg的按需转码实现方法


概述

使用srs来进行直播流转码时,如果直接配置了转码,则无论有无客户端在拉取转码流,转码都会进行。 需要注意到,转码是一件非常消耗CPU资源的事情,所以实现按需转码(只有在转码流被拉取时,才进行转码)是一件非常值得的事情。


srs的FFmpeg转码时的推拉流逻辑

在srs的vhost中,可以配置transcode来使用FFmpeg对流进行转码。

例如下面配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
listen 1935;
vhost www.transcode.com {
transcode {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine ff {
enabled on;
vcodec copy;
acodec copy;
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
}
}

假设一路流推送到rtmp://www.transcode.com/testapp/teststream,上面所配置的ffmpeg立即就会对这路流进行转码(即使没有人拉流), 因为它转码的逻辑是拉流到FFmpeg->转码->推流到output所定义的地方

那么上面配置中的写法就表示:

  1. rtmp://127.0.0.1:1935/testapp?vhost=www.transcode.com/teststream 拉流。
  2. 转码。
  3. 推流到 rtmp://127.0.0.1:1935/testapp?vhost=www.transcode.com/teststream_ff

也就是转码后反推了一路流到 www.transcode.com这个vhost下面(由于output可以自定义,所以实际上想推到哪里都可以)。

所以转码一旦开始,就会不断拉流,转码,推流,与有没有人在拉流没有关系,它会持续占用CPU资源。


如何实现srs可控的FFmpeg转码

首先,如果在一个vhost下同时配置 remote 和 transcode,则会出来不先拉原始流就无法拉取转码流的问题:

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
vhost lrm.test.com {
mode remote;
origin 192.168.90.229:2019;
transcode {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine ff {
enabled on;
vfilter {
}
vcodec libx264;
vbitrate 3500;
vfps 60;
vwidth 1920;
vheight 1080;
vthreads 2;
vprofile baseline;
vpreset superfast;
vparams {
}
acodec libfdk_aac;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
}
}

如上面的配置,如果不先拉取rtmp://lrm.test.com/app/streamname的未转码流,则无法拉取rtmp://lrm.test.com/app/streamname_ff的转码流, 并且只要转码一旦开始,就不会停止

所以,如果想要实现可控转码,则只能通过需要转码时,再下发转码配置来实现,只通过srs本身来控制貌似不行。

按需转码实现思路:

  1. 如何知道有用户拉流:用户拉流触发srs的HTTPCallBack -> 开启转码
  2. 如何开启转码:在用于转码的srs节点上下发ingest配置从origin拉取原始流 -> 进行转码后将转码流推回origin

所以这里的关键在于两点:使用HTTPCallBack来获知拉流信息;使用ingest来拉取原流进行转码,转码操作不会影响srs集群的任何配置。

ingest配置一:

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
vhost lrm.test.com {
ingest livestream {
enabled on;
input {
type stream;
url rtmp://192.168.90.229:2019/persist?vhost=lrm.test.com/long;
}
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine 720_60 {
enabled on;
vfilter {
}
vcodec libx264;
vbitrate 3500;
vfps 60;
vwidth 0;
vheight 0;
vthreads 12;
vprofile main;
vpreset fast;
vparams {
maxrate:v 3500k;
minrate:v 3500k;
bufsize:v 3500k;
}
acodec libfdk_aac;
abitrate 50;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://192.168.90.229:2019/persist?vhost=lrm.test.com/long_720_60;
}
}
}

ingest配置二:

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
vhost lrm.test.com {
ingest livestream {
enabled on;
input {
type stream;
url rtmp://192.168.90.229:2019/persist?vhost=lrm.test.com/long;
}
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine 1 {
enabled off;
output rtmp://127.0.0.1:2019/persist?vhost=www.transcode.com/long;
}
engine 2 {
enabled off;
output rtmp://127.0.0.1:2019/persist?vhost=www.transcode2.com/long;
}
}
}
vhost www.transcode.com {
transcode {
enabled off;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine 720_60 {
enabled on;
vfilter {
}
vcodec libx264;
vbitrate 3500;
vfps 60;
vwidth 0;
vheight 0;
vthreads 12;
vprofile main;
vpreset fast;
vparams {
maxrate:v 3500k;
minrate:v 3500k;
bufsize:v 3500k;
}
acodec libfdk_aac;
abitrate 50;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://192.168.90.229:2019/persist?vhost=lrm.test.com/long_720_60;
}
}
}
vhost www.transcode2.com {
transcode {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine 720_60_1 {
enabled on;
vfilter {
filter_complex 'scale=1280:-1';
}
vcodec libx264;
vbitrate 3500;
vfps 60;
vwidth 0;
vheight 0;
vthreads 12;
vprofile main;
vpreset fast;
vparams {
maxrate:v 3500k;
minrate:v 3500k;
bufsize:v 3500k;
}
acodec libfdk_aac;
abitrate 50;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://192.168.90.229:2019/persist?vhost=lrm.test.com/long_720_60_1;
}
}
}

虽然两种方式其实可以达到一样的效果(方式一中也可以添加多个engine),哪种方便用哪一种。


---------------------------------END---------------------------------