mirror of
https://github.com/ossrs/srs.git
synced 2025-02-12 11:21:52 +00:00
support ffmpeg filter
This commit is contained in:
parent
f85b70966c
commit
9a0d8855d8
8 changed files with 280 additions and 85 deletions
19
README.md
19
README.md
|
@ -69,15 +69,16 @@ Supported operating systems and hardware:
|
||||||
14. support forward publish stream to build active-standby cluster.<br/>
|
14. support forward publish stream to build active-standby cluster.<br/>
|
||||||
15. support broadcast by forward the stream to other servers(origin/edge).<br/>
|
15. support broadcast by forward the stream to other servers(origin/edge).<br/>
|
||||||
16. support live stream transcoding by ffmpeg.<br/>
|
16. support live stream transcoding by ffmpeg.<br/>
|
||||||
17. [plan] support full http callback api.<br/>
|
17. support live stream transcoding by ffmpeg.<br/>
|
||||||
18. [plan] support network based cli and json result.<br/>
|
18. support ffmpeg filters(logo/overlay/crop), x264 params.<br/>
|
||||||
19. [plan] support bandwidth test api and flash client.<br/>
|
19. [plan] support network based cli and json result.<br/>
|
||||||
20. [plan] support adobe flash refer/token/swf verification.<br/>
|
20. [plan] support bandwidth test api and flash client.<br/>
|
||||||
21. [plan] support adobe amf3 codec.<br/>
|
21. [plan] support adobe flash refer/token/swf verification.<br/>
|
||||||
22. [plan] support dvr(record live to vod file)<br/>
|
22. [plan] support adobe amf3 codec.<br/>
|
||||||
23. [plan] support FMS edge protocol<br/>
|
23. [plan] support dvr(record live to vod file)<br/>
|
||||||
24. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>
|
24. [plan] support FMS edge protocol<br/>
|
||||||
25. [plan] support RTMPT, http to tranverse firewalls<br/>
|
25. [plan] support encryption: RTMPE/RTMPS, HLS DRM<br/>
|
||||||
|
26. [plan] support RTMPT, http to tranverse firewalls<br/>
|
||||||
|
|
||||||
### Performance
|
### Performance
|
||||||
1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.
|
1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB.
|
||||||
|
|
|
@ -84,6 +84,7 @@ else
|
||||||
--extra-ldflags='-L${ffmpeg_exported_release_dir}/lib -lm -ldl' \
|
--extra-ldflags='-L${ffmpeg_exported_release_dir}/lib -lm -ldl' \
|
||||||
--disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc \
|
--disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc \
|
||||||
--enable-postproc --enable-bzlib --enable-zlib --enable-parsers \
|
--enable-postproc --enable-bzlib --enable-zlib --enable-parsers \
|
||||||
|
--enable-libfreetype \
|
||||||
--enable-libx264 --enable-libmp3lame --enable-libaacplus \
|
--enable-libx264 --enable-libmp3lame --enable-libaacplus \
|
||||||
--enable-pthreads --extra-libs=-lpthread --enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers &&
|
--enable-pthreads --extra-libs=-lpthread --enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers &&
|
||||||
make && make install
|
make && make install
|
||||||
|
|
|
@ -15,13 +15,19 @@ vhost __defaultVhost__ {
|
||||||
hls_path ./objs/nginx/html;
|
hls_path ./objs/nginx/html;
|
||||||
hls_fragment 5;
|
hls_fragment 5;
|
||||||
hls_window 30;
|
hls_window 30;
|
||||||
#forward 127.0.0.1:1936;
|
forward 127.0.0.1:1936;
|
||||||
transcode {
|
transcode {
|
||||||
enabled on;
|
enabled on;
|
||||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||||
#ffmpeg ./research/ffempty/ffempty;
|
#ffmpeg ./research/ffempty/ffempty;
|
||||||
engine fast{
|
engine fast{
|
||||||
enabled on;
|
enabled on;
|
||||||
|
vfilter {
|
||||||
|
vf 'drawtext=text=SRS';
|
||||||
|
#vf 'crop=in_w-20:in_h-160:10:80';
|
||||||
|
#i ./doc/ffmpeg-logo.png;
|
||||||
|
#filter_complex 'overlay=10:10';
|
||||||
|
}
|
||||||
vcodec libx264;
|
vcodec libx264;
|
||||||
vbitrate 300;
|
vbitrate 300;
|
||||||
vfps 20;
|
vfps 20;
|
||||||
|
@ -37,11 +43,15 @@ vhost __defaultVhost__ {
|
||||||
asample_rate 44100;
|
asample_rate 44100;
|
||||||
achannels 2;
|
achannels 2;
|
||||||
aparams {
|
aparams {
|
||||||
|
profile:a aac_low;
|
||||||
}
|
}
|
||||||
output rtmp://[vhost]:[port]/[app]/[stream]_fast;
|
output rtmp://[vhost]:[port]/[app]/[stream]_fast;
|
||||||
}
|
}
|
||||||
engine sd{
|
engine sd{
|
||||||
enabled on;
|
enabled off;
|
||||||
|
vfilter {
|
||||||
|
vf 'split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2';
|
||||||
|
}
|
||||||
vcodec libx264;
|
vcodec libx264;
|
||||||
vbitrate 500;
|
vbitrate 500;
|
||||||
vfps 20;
|
vfps 20;
|
||||||
|
@ -79,6 +89,14 @@ vhost all.transcode.vhost.com {
|
||||||
# whether the engine is enabled
|
# whether the engine is enabled
|
||||||
# default: off.
|
# default: off.
|
||||||
enabled on;
|
enabled on;
|
||||||
|
# ffmpeg filters, follows the main input.
|
||||||
|
vfilter {
|
||||||
|
# the logo input file.
|
||||||
|
i ./doc/ffmpeg-logo.png;
|
||||||
|
# the ffmpeg complex filter.
|
||||||
|
# for filters, @see: http://ffmpeg.org/ffmpeg-filters.html
|
||||||
|
filter_complex 'overlay=10:10';
|
||||||
|
}
|
||||||
# video encoder name
|
# video encoder name
|
||||||
vcodec libx264;
|
vcodec libx264;
|
||||||
# video bitrate, in kbps
|
# video bitrate, in kbps
|
||||||
|
@ -100,6 +118,13 @@ vhost all.transcode.vhost.com {
|
||||||
vpreset medium;
|
vpreset medium;
|
||||||
# other x264 or ffmpeg video params
|
# other x264 or ffmpeg video params
|
||||||
vparams {
|
vparams {
|
||||||
|
# ffmpeg options, @see: http://ffmpeg.org/ffmpeg.html
|
||||||
|
t 100;
|
||||||
|
# 264 params, @see: http://ffmpeg.org/ffmpeg-codecs.html#libx264
|
||||||
|
coder 1;
|
||||||
|
b_strategy 2;
|
||||||
|
bf 3;
|
||||||
|
refs 10;
|
||||||
}
|
}
|
||||||
# audio encoder name
|
# audio encoder name
|
||||||
acodec libaacplus;
|
acodec libaacplus;
|
||||||
|
@ -182,12 +207,102 @@ vhost all.transcode.vhost.com {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
# the mirror filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction
|
||||||
|
vhost mirror.transcode.vhost.com {
|
||||||
|
transcode {
|
||||||
|
enabled on;
|
||||||
|
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||||
|
engine mirror{
|
||||||
|
enabled on;
|
||||||
|
vfilter {
|
||||||
|
vf 'split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2';
|
||||||
|
}
|
||||||
|
vcodec libx264;
|
||||||
|
vbitrate 300;
|
||||||
|
vfps 20;
|
||||||
|
vwidth 480;
|
||||||
|
vheight 320;
|
||||||
|
vthreads 2;
|
||||||
|
vprofile baseline;
|
||||||
|
vpreset superfast;
|
||||||
|
vparams {
|
||||||
|
}
|
||||||
|
acodec libaacplus;
|
||||||
|
abitrate 30;
|
||||||
|
asample_rate 44100;
|
||||||
|
achannels 2;
|
||||||
|
aparams {
|
||||||
|
}
|
||||||
|
output rtmp://[vhost]:[port]/[app]/[stream]_mirror;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# the logo filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#crop
|
||||||
|
vhost crop.transcode.vhost.com {
|
||||||
|
transcode {
|
||||||
|
enabled on;
|
||||||
|
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||||
|
engine crop{
|
||||||
|
enabled on;
|
||||||
|
vfilter {
|
||||||
|
vf 'crop=in_w-20:in_h-160:10:80';
|
||||||
|
}
|
||||||
|
vcodec libx264;
|
||||||
|
vbitrate 300;
|
||||||
|
vfps 20;
|
||||||
|
vwidth 480;
|
||||||
|
vheight 320;
|
||||||
|
vthreads 2;
|
||||||
|
vprofile baseline;
|
||||||
|
vpreset superfast;
|
||||||
|
vparams {
|
||||||
|
}
|
||||||
|
acodec libaacplus;
|
||||||
|
abitrate 30;
|
||||||
|
asample_rate 44100;
|
||||||
|
achannels 2;
|
||||||
|
aparams {
|
||||||
|
}
|
||||||
|
output rtmp://[vhost]:[port]/[app]/[stream]_crop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# the crop filter of ffmpeg, @see: http://ffmpeg.org/ffmpeg-filters.html#crop
|
||||||
|
vhost logo.transcode.vhost.com {
|
||||||
|
transcode {
|
||||||
|
enabled on;
|
||||||
|
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||||
|
engine logo{
|
||||||
|
enabled on;
|
||||||
|
vfilter {
|
||||||
|
vf 'crop=200:100:10:10';
|
||||||
|
}
|
||||||
|
vcodec libx264;
|
||||||
|
vbitrate 300;
|
||||||
|
vfps 20;
|
||||||
|
vwidth 480;
|
||||||
|
vheight 320;
|
||||||
|
vthreads 2;
|
||||||
|
vprofile baseline;
|
||||||
|
vpreset superfast;
|
||||||
|
vparams {
|
||||||
|
}
|
||||||
|
acodec libaacplus;
|
||||||
|
abitrate 30;
|
||||||
|
asample_rate 44100;
|
||||||
|
achannels 2;
|
||||||
|
aparams {
|
||||||
|
}
|
||||||
|
output rtmp://[vhost]:[port]/[app]/[stream]_logo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
# transcode all stream using the empty ffmpeg demo, donothing.
|
# transcode all stream using the empty ffmpeg demo, donothing.
|
||||||
vhost ffempty.transcode.vhost.com {
|
vhost ffempty.transcode.vhost.com {
|
||||||
transcode {
|
transcode {
|
||||||
enabled on;
|
enabled on;
|
||||||
ffmpeg ./research/ffempty/ffempty;
|
ffmpeg ./research/ffempty/ffempty;
|
||||||
engine fd{
|
engine empty{
|
||||||
enabled on;
|
enabled on;
|
||||||
vcodec libx264;
|
vcodec libx264;
|
||||||
vbitrate 300;
|
vbitrate 300;
|
||||||
|
@ -205,7 +320,7 @@ vhost ffempty.transcode.vhost.com {
|
||||||
achannels 2;
|
achannels 2;
|
||||||
aparams {
|
aparams {
|
||||||
}
|
}
|
||||||
output rtmp://[vhost]:[port]/[app]/[stream]_fast;
|
output rtmp://[vhost]:[port]/[app]/[stream]_empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
trunk/doc/ffmpeg-logo.png
Normal file
BIN
trunk/doc/ffmpeg-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.5 KiB |
|
@ -768,30 +768,48 @@ std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine)
|
||||||
return conf->arg0();
|
return conf->arg0();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SrsConfig::get_engine_vparams(SrsConfDirective* engine)
|
void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams)
|
||||||
{
|
{
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
return "";
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConfDirective* conf = engine->get("vparams");
|
SrsConfDirective* conf = engine->get("vparams");
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return "";
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string avparams;
|
|
||||||
for (int i = 0; i < (int)conf->directives.size(); i++) {
|
for (int i = 0; i < (int)conf->directives.size(); i++) {
|
||||||
SrsConfDirective* p = conf->directives[i];
|
SrsConfDirective* p = conf->directives[i];
|
||||||
if (!p) {
|
if (!p) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
avparams += p->name;
|
vparams.push_back("-" + p->name);
|
||||||
avparams += " ";
|
vparams.push_back(p->arg0());
|
||||||
avparams += p->arg0();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter)
|
||||||
|
{
|
||||||
|
if (!engine) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return avparams;
|
SrsConfDirective* conf = engine->get("vfilter");
|
||||||
|
if (!conf) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < (int)conf->directives.size(); i++) {
|
||||||
|
SrsConfDirective* p = conf->directives[i];
|
||||||
|
if (!p) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
vfilter.push_back("-" + p->name);
|
||||||
|
vfilter.push_back(p->arg0());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
|
std::string SrsConfig::get_engine_acodec(SrsConfDirective* engine)
|
||||||
|
@ -850,30 +868,26 @@ int SrsConfig::get_engine_achannels(SrsConfDirective* engine)
|
||||||
return ::atoi(conf->arg0().c_str());
|
return ::atoi(conf->arg0().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SrsConfig::get_engine_aparams(SrsConfDirective* engine)
|
void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams)
|
||||||
{
|
{
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
return "";
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SrsConfDirective* conf = engine->get("aparams");
|
SrsConfDirective* conf = engine->get("aparams");
|
||||||
if (!conf) {
|
if (!conf) {
|
||||||
return "";
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string avparams;
|
|
||||||
for (int i = 0; i < (int)conf->directives.size(); i++) {
|
for (int i = 0; i < (int)conf->directives.size(); i++) {
|
||||||
SrsConfDirective* p = conf->directives[i];
|
SrsConfDirective* p = conf->directives[i];
|
||||||
if (!p) {
|
if (!p) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
avparams += p->name;
|
aparams.push_back("-" + p->name);
|
||||||
avparams += " ";
|
aparams.push_back(p->arg0());
|
||||||
avparams += p->arg0();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return avparams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
|
std::string SrsConfig::get_engine_output(SrsConfDirective* engine)
|
||||||
|
|
|
@ -128,12 +128,13 @@ public:
|
||||||
virtual int get_engine_vthreads(SrsConfDirective* engine);
|
virtual int get_engine_vthreads(SrsConfDirective* engine);
|
||||||
virtual std::string get_engine_vprofile(SrsConfDirective* engine);
|
virtual std::string get_engine_vprofile(SrsConfDirective* engine);
|
||||||
virtual std::string get_engine_vpreset(SrsConfDirective* engine);
|
virtual std::string get_engine_vpreset(SrsConfDirective* engine);
|
||||||
virtual std::string get_engine_vparams(SrsConfDirective* engine);
|
virtual void get_engine_vparams(SrsConfDirective* engine, std::vector<std::string>& vparams);
|
||||||
|
virtual void get_engine_vfilter(SrsConfDirective* engine, std::vector<std::string>& vfilter);
|
||||||
virtual std::string get_engine_acodec(SrsConfDirective* engine);
|
virtual std::string get_engine_acodec(SrsConfDirective* engine);
|
||||||
virtual int get_engine_abitrate(SrsConfDirective* engine);
|
virtual int get_engine_abitrate(SrsConfDirective* engine);
|
||||||
virtual int get_engine_asample_rate(SrsConfDirective* engine);
|
virtual int get_engine_asample_rate(SrsConfDirective* engine);
|
||||||
virtual int get_engine_achannels(SrsConfDirective* engine);
|
virtual int get_engine_achannels(SrsConfDirective* engine);
|
||||||
virtual std::string get_engine_aparams(SrsConfDirective* engine);
|
virtual void get_engine_aparams(SrsConfDirective* engine, std::vector<std::string>& aparams);
|
||||||
virtual std::string get_engine_output(SrsConfDirective* engine);
|
virtual std::string get_engine_output(SrsConfDirective* engine);
|
||||||
virtual SrsConfDirective* get_gop_cache(std::string vhost);
|
virtual SrsConfDirective* get_gop_cache(std::string vhost);
|
||||||
virtual SrsConfDirective* get_forward(std::string vhost);
|
virtual SrsConfDirective* get_forward(std::string vhost);
|
||||||
|
|
|
@ -60,6 +60,7 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
|
||||||
{
|
{
|
||||||
int ret = ERROR_SUCCESS;
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
config->get_engine_vfilter(engine, vfilter);
|
||||||
vcodec = config->get_engine_vcodec(engine);
|
vcodec = config->get_engine_vcodec(engine);
|
||||||
vbitrate = config->get_engine_vbitrate(engine);
|
vbitrate = config->get_engine_vbitrate(engine);
|
||||||
vfps = config->get_engine_vfps(engine);
|
vfps = config->get_engine_vfps(engine);
|
||||||
|
@ -68,12 +69,12 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
|
||||||
vthreads = config->get_engine_vthreads(engine);
|
vthreads = config->get_engine_vthreads(engine);
|
||||||
vprofile = config->get_engine_vprofile(engine);
|
vprofile = config->get_engine_vprofile(engine);
|
||||||
vpreset = config->get_engine_vpreset(engine);
|
vpreset = config->get_engine_vpreset(engine);
|
||||||
vparams = config->get_engine_vparams(engine);
|
config->get_engine_vparams(engine, vparams);
|
||||||
acodec = config->get_engine_acodec(engine);
|
acodec = config->get_engine_acodec(engine);
|
||||||
abitrate = config->get_engine_abitrate(engine);
|
abitrate = config->get_engine_abitrate(engine);
|
||||||
asample_rate = config->get_engine_asample_rate(engine);
|
asample_rate = config->get_engine_asample_rate(engine);
|
||||||
achannels = config->get_engine_achannels(engine);
|
achannels = config->get_engine_achannels(engine);
|
||||||
aparams = config->get_engine_aparams(engine);
|
config->get_engine_aparams(engine, aparams);
|
||||||
output = config->get_engine_output(engine);
|
output = config->get_engine_output(engine);
|
||||||
|
|
||||||
// ensure the size is even.
|
// ensure the size is even.
|
||||||
|
@ -198,37 +199,109 @@ int SrsFFMPEG::start()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare execl params
|
// prepare exec params
|
||||||
char vsize[22];
|
char tmp[256];
|
||||||
snprintf(vsize, sizeof(vsize), "%dx%d", vwidth, vheight);
|
std::vector<std::string> params;
|
||||||
char vaspect[22];
|
|
||||||
snprintf(vaspect, sizeof(vaspect), "%d:%d", vwidth, vheight);
|
|
||||||
char s_vbitrate[10];
|
|
||||||
snprintf(s_vbitrate, sizeof(s_vbitrate), "%d", vbitrate * 1000);
|
|
||||||
char s_vfps[10];
|
|
||||||
snprintf(s_vfps, sizeof(s_vfps), "%.2f", vfps);
|
|
||||||
char s_vthreads[10];
|
|
||||||
snprintf(s_vthreads, sizeof(s_vthreads), "%d", vthreads);
|
|
||||||
char s_abitrate[10];
|
|
||||||
snprintf(s_abitrate, sizeof(s_abitrate), "%d", abitrate * 1000);
|
|
||||||
char s_asample_rate[10];
|
|
||||||
snprintf(s_asample_rate, sizeof(s_asample_rate), "%d", asample_rate);
|
|
||||||
char s_achannels[10];
|
|
||||||
snprintf(s_achannels, sizeof(s_achannels), "%d", achannels);
|
|
||||||
|
|
||||||
// TODO: execl donot support the params.
|
// argv[0], set to ffmpeg bin.
|
||||||
// video params
|
// The execv() and execvp() functions ....
|
||||||
std::string s_vpreset = vpreset;
|
// The first argument, by convention, should point to
|
||||||
|
// the filename associated with the file being executed.
|
||||||
|
params.push_back(ffmpeg);
|
||||||
|
|
||||||
|
// input.
|
||||||
|
params.push_back("-f");
|
||||||
|
params.push_back("flv");
|
||||||
|
|
||||||
|
params.push_back("-i");
|
||||||
|
params.push_back(input);
|
||||||
|
|
||||||
|
// build the filter
|
||||||
|
if (!vfilter.empty()) {
|
||||||
|
std::vector<std::string>::iterator it;
|
||||||
|
for (it = vfilter.begin(); it != vfilter.end(); ++it) {
|
||||||
|
std::string p = *it;
|
||||||
|
if (!p.empty()) {
|
||||||
|
params.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// video specified.
|
||||||
|
params.push_back("-vcodec");
|
||||||
|
params.push_back(vcodec);
|
||||||
|
|
||||||
|
params.push_back("-b:v");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
params.push_back("-r");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%.2f", vfps);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
params.push_back("-s");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
// TODO: add aspect if needed.
|
||||||
|
params.push_back("-aspect");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
params.push_back("-threads");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%d", vthreads);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
params.push_back("-profile:v");
|
||||||
|
params.push_back(vprofile);
|
||||||
|
|
||||||
|
params.push_back("-preset");
|
||||||
|
params.push_back(vpreset);
|
||||||
|
|
||||||
|
// vparams
|
||||||
if (!vparams.empty()) {
|
if (!vparams.empty()) {
|
||||||
s_vpreset += " ";
|
std::vector<std::string>::iterator it;
|
||||||
s_vpreset += vparams;
|
for (it = vparams.begin(); it != vparams.end(); ++it) {
|
||||||
|
std::string p = *it;
|
||||||
|
if (!p.empty()) {
|
||||||
|
params.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// audio params
|
|
||||||
std::string s_aparams = s_achannels;
|
// audio specified.
|
||||||
|
params.push_back("-acodec");
|
||||||
|
params.push_back(acodec);
|
||||||
|
|
||||||
|
params.push_back("-b:a");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
params.push_back("-ar");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%d", asample_rate);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
params.push_back("-ac");
|
||||||
|
snprintf(tmp, sizeof(tmp), "%d", achannels);
|
||||||
|
params.push_back(tmp);
|
||||||
|
|
||||||
|
// aparams
|
||||||
if (!aparams.empty()) {
|
if (!aparams.empty()) {
|
||||||
s_aparams += " ";
|
std::vector<std::string>::iterator it;
|
||||||
s_aparams += aparams;
|
for (it = aparams.begin(); it != aparams.end(); ++it) {
|
||||||
|
std::string p = *it;
|
||||||
|
if (!p.empty()) {
|
||||||
|
params.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// output
|
||||||
|
params.push_back("-f");
|
||||||
|
params.push_back("flv");
|
||||||
|
|
||||||
|
params.push_back("-y");
|
||||||
|
params.push_back(output);
|
||||||
|
|
||||||
// TODO: fork or vfork?
|
// TODO: fork or vfork?
|
||||||
if ((pid = fork()) < 0) {
|
if ((pid = fork()) < 0) {
|
||||||
|
@ -239,28 +312,17 @@ int SrsFFMPEG::start()
|
||||||
|
|
||||||
// child process: ffmpeg encoder engine.
|
// child process: ffmpeg encoder engine.
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
// TODO: execl or execlp
|
// memory leak in child process, it's ok.
|
||||||
ret = execl(ffmpeg.c_str(),
|
char** charpv_params = new char*[params.size() + 1];
|
||||||
"-f", "flv",
|
for (int i = 0; i < (int)params.size(); i++) {
|
||||||
"-i", input.c_str(),
|
std::string p = params[i];
|
||||||
// video specified.
|
charpv_params[i] = (char*)p.c_str();
|
||||||
"-vcodec", vcodec.c_str(),
|
}
|
||||||
"-b:v", s_vbitrate,
|
// EOF: NULL
|
||||||
"-r", s_vfps,
|
charpv_params[params.size()] = NULL;
|
||||||
"-s", vsize,
|
|
||||||
"-aspect", vaspect, // TODO: add aspect if needed.
|
// TODO: execv or execvp
|
||||||
"-threads", s_vthreads,
|
ret = execv(ffmpeg.c_str(), charpv_params);
|
||||||
"-profile:v", vprofile.c_str(),
|
|
||||||
"-preset", s_vpreset.c_str(),
|
|
||||||
// audio specified.
|
|
||||||
"-acodec", acodec.c_str(),
|
|
||||||
"-b:a", s_abitrate,
|
|
||||||
"-ar", s_asample_rate,
|
|
||||||
"-ac", s_aparams.c_str(),
|
|
||||||
"-f", "flv",
|
|
||||||
"-y", output.c_str(),
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
fprintf(stderr, "fork ffmpeg failed, errno=%d(%s)",
|
fprintf(stderr, "fork ffmpeg failed, errno=%d(%s)",
|
||||||
errno, strerror(errno));
|
errno, strerror(errno));
|
||||||
|
@ -347,7 +409,7 @@ int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _a
|
||||||
ret = parse_scope_engines();
|
ret = parse_scope_engines();
|
||||||
|
|
||||||
// ignore the loop encoder
|
// ignore the loop encoder
|
||||||
if (ret = ERROR_ENCODER_LOOP) {
|
if (ret == ERROR_ENCODER_LOOP) {
|
||||||
ret = ERROR_SUCCESS;
|
ret = ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ private:
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
private:
|
private:
|
||||||
std::string ffmpeg;
|
std::string ffmpeg;
|
||||||
|
std::vector<std::string> vfilter;
|
||||||
std::string vcodec;
|
std::string vcodec;
|
||||||
int vbitrate;
|
int vbitrate;
|
||||||
double vfps;
|
double vfps;
|
||||||
|
@ -55,12 +56,12 @@ private:
|
||||||
int vthreads;
|
int vthreads;
|
||||||
std::string vprofile;
|
std::string vprofile;
|
||||||
std::string vpreset;
|
std::string vpreset;
|
||||||
std::string vparams;
|
std::vector<std::string> vparams;
|
||||||
std::string acodec;
|
std::string acodec;
|
||||||
int abitrate;
|
int abitrate;
|
||||||
int asample_rate;
|
int asample_rate;
|
||||||
int achannels;
|
int achannels;
|
||||||
std::string aparams;
|
std::vector<std::string> aparams;
|
||||||
std::string output;
|
std::string output;
|
||||||
std::string input;
|
std::string input;
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in a new issue