diff --git a/README.md b/README.md index 8daf0cf9d..c82901ab8 100755 --- a/README.md +++ b/README.md @@ -69,15 +69,16 @@ Supported operating systems and hardware: 14. support forward publish stream to build active-standby cluster.
15. support broadcast by forward the stream to other servers(origin/edge).
16. support live stream transcoding by ffmpeg.
-17. [plan] support full http callback api.
-18. [plan] support network based cli and json result.
-19. [plan] support bandwidth test api and flash client.
-20. [plan] support adobe flash refer/token/swf verification.
-21. [plan] support adobe amf3 codec.
-22. [plan] support dvr(record live to vod file)
-23. [plan] support FMS edge protocol
-24. [plan] support encryption: RTMPE/RTMPS, HLS DRM
-25. [plan] support RTMPT, http to tranverse firewalls
+17. support live stream transcoding by ffmpeg.
+18. support ffmpeg filters(logo/overlay/crop), x264 params.
+19. [plan] support network based cli and json result.
+20. [plan] support bandwidth test api and flash client.
+21. [plan] support adobe flash refer/token/swf verification.
+22. [plan] support adobe amf3 codec.
+23. [plan] support dvr(record live to vod file)
+24. [plan] support FMS edge protocol
+25. [plan] support encryption: RTMPE/RTMPS, HLS DRM
+26. [plan] support RTMPT, http to tranverse firewalls
### Performance 1. 300 connections, 150Mbps, 500kbps, CPU 18.8%, 5956KB. diff --git a/trunk/auto/build_ffmpeg.sh b/trunk/auto/build_ffmpeg.sh index 1c1350393..d2ad359d0 100755 --- a/trunk/auto/build_ffmpeg.sh +++ b/trunk/auto/build_ffmpeg.sh @@ -84,6 +84,7 @@ else --extra-ldflags='-L${ffmpeg_exported_release_dir}/lib -lm -ldl' \ --disable-ffplay --disable-ffprobe --disable-ffserver --disable-doc \ --enable-postproc --enable-bzlib --enable-zlib --enable-parsers \ + --enable-libfreetype \ --enable-libx264 --enable-libmp3lame --enable-libaacplus \ --enable-pthreads --extra-libs=-lpthread --enable-encoders --enable-decoders --enable-avfilter --enable-muxers --enable-demuxers && make && make install diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf index 464341e88..3212bc449 100755 --- a/trunk/conf/srs.conf +++ b/trunk/conf/srs.conf @@ -15,13 +15,19 @@ vhost __defaultVhost__ { hls_path ./objs/nginx/html; hls_fragment 5; hls_window 30; - #forward 127.0.0.1:1936; + forward 127.0.0.1:1936; transcode { enabled on; ffmpeg ./objs/ffmpeg/bin/ffmpeg; #ffmpeg ./research/ffempty/ffempty; engine fast{ 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; vbitrate 300; vfps 20; @@ -37,11 +43,15 @@ vhost __defaultVhost__ { asample_rate 44100; achannels 2; aparams { + profile:a aac_low; } output rtmp://[vhost]:[port]/[app]/[stream]_fast; } 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; vbitrate 500; vfps 20; @@ -79,6 +89,14 @@ vhost all.transcode.vhost.com { # whether the engine is enabled # default: off. 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 vcodec libx264; # video bitrate, in kbps @@ -100,6 +118,13 @@ vhost all.transcode.vhost.com { vpreset medium; # other x264 or ffmpeg video params 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 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. vhost ffempty.transcode.vhost.com { transcode { enabled on; ffmpeg ./research/ffempty/ffempty; - engine fd{ + engine empty{ enabled on; vcodec libx264; vbitrate 300; @@ -205,7 +320,7 @@ vhost ffempty.transcode.vhost.com { achannels 2; aparams { } - output rtmp://[vhost]:[port]/[app]/[stream]_fast; + output rtmp://[vhost]:[port]/[app]/[stream]_empty; } } } diff --git a/trunk/doc/ffmpeg-logo.png b/trunk/doc/ffmpeg-logo.png new file mode 100644 index 000000000..d93339690 Binary files /dev/null and b/trunk/doc/ffmpeg-logo.png differ diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp index a517449c0..60c64c572 100644 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -768,30 +768,48 @@ std::string SrsConfig::get_engine_vpreset(SrsConfDirective* engine) return conf->arg0(); } -std::string SrsConfig::get_engine_vparams(SrsConfDirective* engine) +void SrsConfig::get_engine_vparams(SrsConfDirective* engine, std::vector& vparams) { if (!engine) { - return ""; + return; } SrsConfDirective* conf = engine->get("vparams"); if (!conf) { - return ""; + return; } - std::string avparams; for (int i = 0; i < (int)conf->directives.size(); i++) { SrsConfDirective* p = conf->directives[i]; if (!p) { continue; } - avparams += p->name; - avparams += " "; - avparams += p->arg0(); + vparams.push_back("-" + p->name); + vparams.push_back(p->arg0()); + } +} + +void SrsConfig::get_engine_vfilter(SrsConfDirective* engine, std::vector& 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) @@ -850,30 +868,26 @@ int SrsConfig::get_engine_achannels(SrsConfDirective* engine) return ::atoi(conf->arg0().c_str()); } -std::string SrsConfig::get_engine_aparams(SrsConfDirective* engine) +void SrsConfig::get_engine_aparams(SrsConfDirective* engine, std::vector& aparams) { if (!engine) { - return ""; + return; } SrsConfDirective* conf = engine->get("aparams"); if (!conf) { - return ""; + return; } - std::string avparams; for (int i = 0; i < (int)conf->directives.size(); i++) { SrsConfDirective* p = conf->directives[i]; if (!p) { continue; } - avparams += p->name; - avparams += " "; - avparams += p->arg0(); + aparams.push_back("-" + p->name); + aparams.push_back(p->arg0()); } - - return avparams; } std::string SrsConfig::get_engine_output(SrsConfDirective* engine) diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp index 7935262b0..a43c79899 100644 --- a/trunk/src/core/srs_core_config.hpp +++ b/trunk/src/core/srs_core_config.hpp @@ -128,12 +128,13 @@ public: virtual int get_engine_vthreads(SrsConfDirective* engine); virtual std::string get_engine_vprofile(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& vparams); + virtual void get_engine_vfilter(SrsConfDirective* engine, std::vector& vfilter); virtual std::string get_engine_acodec(SrsConfDirective* engine); virtual int get_engine_abitrate(SrsConfDirective* engine); virtual int get_engine_asample_rate(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& aparams); virtual std::string get_engine_output(SrsConfDirective* engine); virtual SrsConfDirective* get_gop_cache(std::string vhost); virtual SrsConfDirective* get_forward(std::string vhost); diff --git a/trunk/src/core/srs_core_encoder.cpp b/trunk/src/core/srs_core_encoder.cpp index 1ee75172d..10f4acf31 100644 --- a/trunk/src/core/srs_core_encoder.cpp +++ b/trunk/src/core/srs_core_encoder.cpp @@ -60,6 +60,7 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app, { int ret = ERROR_SUCCESS; + config->get_engine_vfilter(engine, vfilter); vcodec = config->get_engine_vcodec(engine); vbitrate = config->get_engine_vbitrate(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); vprofile = config->get_engine_vprofile(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); abitrate = config->get_engine_abitrate(engine); asample_rate = config->get_engine_asample_rate(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); // ensure the size is even. @@ -198,37 +199,109 @@ int SrsFFMPEG::start() return ret; } - // prepare execl params - char vsize[22]; - snprintf(vsize, sizeof(vsize), "%dx%d", vwidth, vheight); - 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); + // prepare exec params + char tmp[256]; + std::vector params; - // TODO: execl donot support the params. - // video params - std::string s_vpreset = vpreset; + // argv[0], set to ffmpeg bin. + // The execv() and execvp() functions .... + // 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::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()) { - s_vpreset += " "; - s_vpreset += vparams; + std::vector::iterator it; + 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()) { - s_aparams += " "; - s_aparams += aparams; + std::vector::iterator it; + 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? if ((pid = fork()) < 0) { @@ -239,28 +312,17 @@ int SrsFFMPEG::start() // child process: ffmpeg encoder engine. if (pid == 0) { - // TODO: execl or execlp - ret = execl(ffmpeg.c_str(), - "-f", "flv", - "-i", input.c_str(), - // video specified. - "-vcodec", vcodec.c_str(), - "-b:v", s_vbitrate, - "-r", s_vfps, - "-s", vsize, - "-aspect", vaspect, // TODO: add aspect if needed. - "-threads", s_vthreads, - "-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 - ); + // memory leak in child process, it's ok. + char** charpv_params = new char*[params.size() + 1]; + for (int i = 0; i < (int)params.size(); i++) { + std::string p = params[i]; + charpv_params[i] = (char*)p.c_str(); + } + // EOF: NULL + charpv_params[params.size()] = NULL; + + // TODO: execv or execvp + ret = execv(ffmpeg.c_str(), charpv_params); if (ret < 0) { fprintf(stderr, "fork ffmpeg failed, errno=%d(%s)", errno, strerror(errno)); @@ -347,7 +409,7 @@ int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _a ret = parse_scope_engines(); // ignore the loop encoder - if (ret = ERROR_ENCODER_LOOP) { + if (ret == ERROR_ENCODER_LOOP) { ret = ERROR_SUCCESS; } diff --git a/trunk/src/core/srs_core_encoder.hpp b/trunk/src/core/srs_core_encoder.hpp index f13e8dfa9..fe3bb362c 100644 --- a/trunk/src/core/srs_core_encoder.hpp +++ b/trunk/src/core/srs_core_encoder.hpp @@ -47,6 +47,7 @@ private: pid_t pid; private: std::string ffmpeg; + std::vector vfilter; std::string vcodec; int vbitrate; double vfps; @@ -55,12 +56,12 @@ private: int vthreads; std::string vprofile; std::string vpreset; - std::string vparams; + std::vector vparams; std::string acodec; int abitrate; int asample_rate; int achannels; - std::string aparams; + std::vector aparams; std::string output; std::string input; public: