winlin

support live stream transcoding by ffmpeg.

@@ -109,3 +109,12 @@ if [ $SRS_FFMPEG = YES ]; then @@ -109,3 +109,12 @@ if [ $SRS_FFMPEG = YES ]; then
109 else 109 else
110 echo "#undef SRS_FFMPEG" >> $SRS_AUTO_HEADERS_H 110 echo "#undef SRS_FFMPEG" >> $SRS_AUTO_HEADERS_H
111 fi 111 fi
  112 +
  113 +#####################################################################################
  114 +# build research code
  115 +#####################################################################################
  116 +(cd research/hls && make)
  117 +ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/hls failed, ret=$ret"; exit $ret; fi
  118 +
  119 +(cd research/ffempty && make)
  120 +ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/ffempty failed, ret=$ret"; exit $ret; fi
@@ -18,7 +18,8 @@ vhost __defaultVhost__ { @@ -18,7 +18,8 @@ vhost __defaultVhost__ {
18 #forward 127.0.0.1:1936; 18 #forward 127.0.0.1:1936;
19 transcode { 19 transcode {
20 enabled on; 20 enabled on;
21 - ffmpeg /home/winlin/srs/objs/ffmpeg/bin/ffmpeg; 21 + ffmpeg ./objs/ffmpeg/bin/ffmpeg;
  22 + #ffmpeg ./research/ffempty/ffempty;
22 engine fd{ 23 engine fd{
23 enabled on; 24 enabled on;
24 vcodec libx264; 25 vcodec libx264;
@@ -29,12 +30,14 @@ vhost __defaultVhost__ { @@ -29,12 +30,14 @@ vhost __defaultVhost__ {
29 vthreads 2; 30 vthreads 2;
30 vprofile baseline; 31 vprofile baseline;
31 vpreset superfast; 32 vpreset superfast;
32 - vparams {} 33 + vparams {
  34 + }
33 acodec libaacplus; 35 acodec libaacplus;
34 abitrate 30; 36 abitrate 30;
35 - asample_rate 22050; 37 + asample_rate 44100;
36 achannels 2; 38 achannels 2;
37 - aparams {} 39 + aparams {
  40 + }
38 output rtmp://[vhost]:[port]/[app]/[stream]_fast; 41 output rtmp://[vhost]:[port]/[app]/[stream]_fast;
39 } 42 }
40 } 43 }
@@ -151,7 +154,34 @@ vhost all.transcode.vhost.com { @@ -151,7 +154,34 @@ vhost all.transcode.vhost.com {
151 } 154 }
152 acodec libaacplus; 155 acodec libaacplus;
153 abitrate 30; 156 abitrate 30;
154 - asample_rate 22050; 157 + asample_rate 44100;
  158 + achannels 2;
  159 + aparams {
  160 + }
  161 + output rtmp://[vhost]:[port]/[app]/[stream]_fast;
  162 + }
  163 + }
  164 +}
  165 +# transcode all stream using the empty ffmpeg demo, donothing.
  166 +vhost ffempty.transcode.vhost.com {
  167 + transcode {
  168 + enabled on;
  169 + ffmpeg ./research/ffempty/ffempty;
  170 + engine fd{
  171 + enabled on;
  172 + vcodec libx264;
  173 + vbitrate 300;
  174 + vfps 20;
  175 + vwidth 480;
  176 + vheight 320;
  177 + vthreads 2;
  178 + vprofile baseline;
  179 + vpreset superfast;
  180 + vparams {
  181 + }
  182 + acodec libaacplus;
  183 + abitrate 30;
  184 + asample_rate 44100;
155 achannels 2; 185 achannels 2;
156 aparams { 186 aparams {
157 } 187 }
@@ -31,6 +31,12 @@ int main(int argc, char** argv) @@ -31,6 +31,12 @@ int main(int argc, char** argv)
31 for (int i = 0; i < argc; i++) { 31 for (int i = 0; i < argc; i++) {
32 fprintf(stderr, "argv[%d]=%s\n", i, argv[i]); 32 fprintf(stderr, "argv[%d]=%s\n", i, argv[i]);
33 } 33 }
  34 +
  35 + fprintf(stderr, "summary:\n");
  36 + for (int i = 0; i < argc; i++) {
  37 + fprintf(stderr, "%s ", argv[i]);
  38 + }
  39 + fprintf(stderr, "\n");
34 40
35 return 0; 41 return 0;
36 } 42 }
@@ -81,6 +81,7 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app, @@ -81,6 +81,7 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
81 vheight -= vheight % 2; 81 vheight -= vheight % 2;
82 82
83 // input stream, from local. 83 // input stream, from local.
  84 + // ie. rtmp://127.0.0.1:1935/live/livestream
84 input = "rtmp://127.0.0.1:"; 85 input = "rtmp://127.0.0.1:";
85 input += port; 86 input += port;
86 input += "/"; 87 input += "/";
@@ -88,6 +89,8 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app, @@ -88,6 +89,8 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
88 input += "/"; 89 input += "/";
89 input += stream; 90 input += stream;
90 91
  92 + // output stream, to other/self server
  93 + // ie. rtmp://127.0.0.1:1935/live/livestream_sd
91 if (vhost == RTMP_VHOST_DEFAULT) { 94 if (vhost == RTMP_VHOST_DEFAULT) {
92 output = srs_replace(output, "[vhost]", "127.0.0.1"); 95 output = srs_replace(output, "[vhost]", "127.0.0.1");
93 } else { 96 } else {
@@ -96,6 +99,22 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app, @@ -96,6 +99,22 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
96 output = srs_replace(output, "[port]", port); 99 output = srs_replace(output, "[port]", port);
97 output = srs_replace(output, "[app]", app); 100 output = srs_replace(output, "[app]", app);
98 output = srs_replace(output, "[stream]", stream); 101 output = srs_replace(output, "[stream]", stream);
  102 +
  103 + // important: loop check, donot transcode again.
  104 + // we think the following is loop circle:
  105 + // input: rtmp://127.0.0.1:1935/live/livestream_sd
  106 + // output: rtmp://127.0.0.1:1935/live/livestream_sd_sd
  107 + std::string tail = ""; // tail="_sd"
  108 + if (output.length() > input.length()) {
  109 + tail = output.substr(input.length());
  110 + }
  111 + // if input also endwiths the tail, loop detected.
  112 + if (!tail.empty() && input.rfind(tail) == input.length() - tail.length()) {
  113 + ret = ERROR_ENCODER_LOOP;
  114 + srs_info("detect a loop cycle, input=%s, output=%s, ignore it. ret=%d",
  115 + input.c_str(), output.c_str(), ret);
  116 + return ret;
  117 + }
99 118
100 if (vcodec != SRS_ENCODER_VCODEC) { 119 if (vcodec != SRS_ENCODER_VCODEC) {
101 ret = ERROR_ENCODER_VCODEC; 120 ret = ERROR_ENCODER_VCODEC;
@@ -184,9 +203,32 @@ int SrsFFMPEG::start() @@ -184,9 +203,32 @@ int SrsFFMPEG::start()
184 snprintf(vsize, sizeof(vsize), "%dx%d", vwidth, vheight); 203 snprintf(vsize, sizeof(vsize), "%dx%d", vwidth, vheight);
185 char vaspect[22]; 204 char vaspect[22];
186 snprintf(vaspect, sizeof(vaspect), "%d:%d", vwidth, vheight); 205 snprintf(vaspect, sizeof(vaspect), "%d:%d", vwidth, vheight);
  206 + char s_vbitrate[10];
  207 + snprintf(s_vbitrate, sizeof(s_vbitrate), "%d", vbitrate * 1000);
  208 + char s_vfps[10];
  209 + snprintf(s_vfps, sizeof(s_vfps), "%.2f", vfps);
  210 + char s_vthreads[10];
  211 + snprintf(s_vthreads, sizeof(s_vthreads), "%d", vthreads);
  212 + char s_abitrate[10];
  213 + snprintf(s_abitrate, sizeof(s_abitrate), "%d", abitrate * 1000);
  214 + char s_asample_rate[10];
  215 + snprintf(s_asample_rate, sizeof(s_asample_rate), "%d", asample_rate);
  216 + char s_achannels[10];
  217 + snprintf(s_achannels, sizeof(s_achannels), "%d", achannels);
  218 +
  219 + // video params
  220 + std::string s_vpreset = vpreset;
  221 + if (!vparams.empty()) {
  222 + s_vpreset += " ";
  223 + s_vpreset += vparams;
  224 + }
  225 + // audio params
  226 + std::string s_aparams = s_achannels;
  227 + if (!aparams.empty()) {
  228 + s_aparams += " ";
  229 + s_aparams += aparams;
  230 + }
187 231
188 - // we use vfork, for we use fored process  
189 - // to start ffmpeg, that is, exec after vfork.  
190 if ((pid = fork()) < 0) { 232 if ((pid = fork()) < 0) {
191 ret = ERROR_ENCODER_FORK; 233 ret = ERROR_ENCODER_FORK;
192 srs_error("vfork process failed. ret=%d", ret); 234 srs_error("vfork process failed. ret=%d", ret);
@@ -195,25 +237,24 @@ int SrsFFMPEG::start() @@ -195,25 +237,24 @@ int SrsFFMPEG::start()
195 237
196 // child process: ffmpeg encoder engine. 238 // child process: ffmpeg encoder engine.
197 if (pid == 0) { 239 if (pid == 0) {
198 - // must exec immediately, or may introduce bug.  
199 ret = execl(ffmpeg.c_str(), 240 ret = execl(ffmpeg.c_str(),
  241 + ffmpeg.c_str(),
  242 + "-f", "flv",
200 "-i", input.c_str(), 243 "-i", input.c_str(),
201 // video specified. 244 // video specified.
202 "-vcodec", vcodec.c_str(), 245 "-vcodec", vcodec.c_str(),
203 - "-b:v", vbitrate * 1000,  
204 - "-r", vfps,  
205 - "-size", vsize, 246 + "-b:v", s_vbitrate,
  247 + "-r", s_vfps,
  248 + "-s", vsize,
206 "-aspect", vaspect, // TODO: add aspect if needed. 249 "-aspect", vaspect, // TODO: add aspect if needed.
207 - "-threads", vthreads,  
208 - "-profile", vprofile.c_str(),  
209 - "-preset", vpreset.c_str(),  
210 - vparams.c_str(), 250 + "-threads", s_vthreads,
  251 + "-profile:v", vprofile.c_str(),
  252 + "-preset", s_vpreset.c_str(),
211 // audio specified. 253 // audio specified.
212 "-acodec", acodec.c_str(), 254 "-acodec", acodec.c_str(),
213 - "-b:a", abitrate * 1000,  
214 - "-ar", asample_rate,  
215 - "-ac", achannels,  
216 - aparams.c_str(), 255 + "-b:a", s_abitrate,
  256 + "-ar", s_asample_rate,
  257 + "-ac", s_aparams.c_str(),
217 "-f", "flv", 258 "-f", "flv",
218 "-y", output.c_str(), 259 "-y", output.c_str(),
219 NULL 260 NULL
@@ -253,14 +294,9 @@ SrsEncoder::~SrsEncoder() @@ -253,14 +294,9 @@ SrsEncoder::~SrsEncoder()
253 on_unpublish(); 294 on_unpublish();
254 } 295 }
255 296
256 -int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _app, std::string _stream) 297 +int SrsEncoder::parse_scope_engines()
257 { 298 {
258 int ret = ERROR_SUCCESS; 299 int ret = ERROR_SUCCESS;
259 -  
260 - vhost = _vhost;  
261 - port = _port;  
262 - app = _app;  
263 - stream = _stream;  
264 300
265 // parse all transcode engines. 301 // parse all transcode engines.
266 SrsConfDirective* conf = NULL; 302 SrsConfDirective* conf = NULL;
@@ -293,6 +329,30 @@ int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _a @@ -293,6 +329,30 @@ int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _a
293 return ret; 329 return ret;
294 } 330 }
295 } 331 }
  332 +
  333 + return ret;
  334 +}
  335 +
  336 +int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _app, std::string _stream)
  337 +{
  338 + int ret = ERROR_SUCCESS;
  339 +
  340 + vhost = _vhost;
  341 + port = _port;
  342 + app = _app;
  343 + stream = _stream;
  344 +
  345 + ret = parse_scope_engines();
  346 +
  347 + // ignore the loop encoder
  348 + if (ret = ERROR_ENCODER_LOOP) {
  349 + ret = ERROR_SUCCESS;
  350 + }
  351 +
  352 + // return for error or no engine.
  353 + if (ret != ERROR_SUCCESS || ffmpegs.empty()) {
  354 + return ret;
  355 + }
296 356
297 // start thread to run all encoding engines. 357 // start thread to run all encoding engines.
298 srs_assert(!tid); 358 srs_assert(!tid);
@@ -313,7 +373,12 @@ void SrsEncoder::on_unpublish() @@ -313,7 +373,12 @@ void SrsEncoder::on_unpublish()
313 st_thread_join(tid, NULL); 373 st_thread_join(tid, NULL);
314 tid = NULL; 374 tid = NULL;
315 } 375 }
316 - 376 +
  377 + clear_engines();
  378 +}
  379 +
  380 +void SrsEncoder::clear_engines()
  381 +{
317 std::vector<SrsFFMPEG*>::iterator it; 382 std::vector<SrsFFMPEG*>::iterator it;
318 for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { 383 for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
319 SrsFFMPEG* ffmpeg = *it; 384 SrsFFMPEG* ffmpeg = *it;
@@ -371,6 +436,12 @@ int SrsEncoder::parse_transcode(SrsConfDirective* conf) @@ -371,6 +436,12 @@ int SrsEncoder::parse_transcode(SrsConfDirective* conf)
371 if ((ret = ffmpeg->initialize(vhost, port, app, stream, engine)) != ERROR_SUCCESS) { 436 if ((ret = ffmpeg->initialize(vhost, port, app, stream, engine)) != ERROR_SUCCESS) {
372 srs_freep(ffmpeg); 437 srs_freep(ffmpeg);
373 438
  439 + // if got a loop, donot transcode the whole stream.
  440 + if (ret == ERROR_ENCODER_LOOP) {
  441 + clear_engines();
  442 + break;
  443 + }
  444 +
374 srs_error("invalid transcode engine: %s %s", 445 srs_error("invalid transcode engine: %s %s",
375 conf->arg0().c_str(), engine->arg0().c_str()); 446 conf->arg0().c_str(), engine->arg0().c_str());
376 return ret; 447 return ret;
@@ -95,6 +95,8 @@ public: @@ -95,6 +95,8 @@ public:
95 virtual int on_publish(std::string vhost, std::string port, std::string app, std::string stream); 95 virtual int on_publish(std::string vhost, std::string port, std::string app, std::string stream);
96 virtual void on_unpublish(); 96 virtual void on_unpublish();
97 private: 97 private:
  98 + virtual int parse_scope_engines();
  99 + virtual void clear_engines();
98 virtual SrsFFMPEG* at(int index); 100 virtual SrsFFMPEG* at(int index);
99 virtual int parse_transcode(SrsConfDirective* conf); 101 virtual int parse_transcode(SrsConfDirective* conf);
100 virtual int cycle(); 102 virtual int cycle();
@@ -136,5 +136,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -136,5 +136,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
136 #define ERROR_ENCODER_VFPS 711 136 #define ERROR_ENCODER_VFPS 711
137 #define ERROR_ENCODER_VBITRATE 712 137 #define ERROR_ENCODER_VBITRATE 712
138 #define ERROR_ENCODER_FORK 713 138 #define ERROR_ENCODER_FORK 713
  139 +#define ERROR_ENCODER_LOOP 714
139 140
140 #endif 141 #endif