winlin

refine ffmepg and encoder, extract ffmpeg.

@@ -435,7 +435,8 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_socke @@ -435,7 +435,8 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_socke
435 "srs_app_codec" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder" 435 "srs_app_codec" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder"
436 "srs_app_http" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log" 436 "srs_app_http" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log"
437 "srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api" 437 "srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api"
438 - "srs_app_http_conn" "srs_app_http_hooks" "srs_app_json" "srs_app_ingest") 438 + "srs_app_http_conn" "srs_app_http_hooks" "srs_app_json" "srs_app_ingest"
  439 + "srs_app_ffmpeg")
439 APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh 440 APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
440 APP_OBJS="${MODULE_OBJS[@]}" 441 APP_OBJS="${MODULE_OBJS[@]}"
441 # 442 #
@@ -23,485 +23,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -23,485 +23,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 23
24 #include <srs_app_encoder.hpp> 24 #include <srs_app_encoder.hpp>
25 25
26 -#include <stdlib.h>  
27 -#include <unistd.h>  
28 -#include <sys/wait.h>  
29 -#include <fcntl.h>  
30 -#include <signal.h>  
31 -#include <sys/types.h>  
32 -  
33 -#include <algorithm>  
34 -  
35 #include <srs_kernel_error.hpp> 26 #include <srs_kernel_error.hpp>
36 #include <srs_kernel_log.hpp> 27 #include <srs_kernel_log.hpp>
37 #include <srs_app_config.hpp> 28 #include <srs_app_config.hpp>
38 #include <srs_protocol_rtmp.hpp> 29 #include <srs_protocol_rtmp.hpp>
39 #include <srs_app_pithy_print.hpp> 30 #include <srs_app_pithy_print.hpp>
40 #include <srs_protocol_rtmp_stack.hpp> 31 #include <srs_protocol_rtmp_stack.hpp>
  32 +#include <srs_app_ffmpeg.hpp>
41 33
42 #ifdef SRS_TRANSCODE 34 #ifdef SRS_TRANSCODE
43 35
44 -#define SRS_ENCODER_COPY "copy"  
45 -#define SRS_ENCODER_NO_VIDEO "vn"  
46 -#define SRS_ENCODER_NO_AUDIO "an"  
47 -// only support libx264 encoder.  
48 -#define SRS_ENCODER_VCODEC "libx264"  
49 -// any aac encoder is ok which contains the aac,  
50 -// for example, libaacplus, aac, fdkaac  
51 -#define SRS_ENCODER_ACODEC "aac"  
52 -  
53 // when error, encoder sleep for a while and retry. 36 // when error, encoder sleep for a while and retry.
54 #define SRS_ENCODER_SLEEP_US (int64_t)(3*1000*1000LL) 37 #define SRS_ENCODER_SLEEP_US (int64_t)(3*1000*1000LL)
55 38
56 -// for encoder to detect the dead loop  
57 -static std::vector<std::string> _transcoded_url;  
58 -  
59 -SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin)  
60 -{  
61 - started = false;  
62 - pid = -1;  
63 - ffmpeg = ffmpeg_bin;  
64 -  
65 - vbitrate = 0;  
66 - vfps = 0;  
67 - vwidth = 0;  
68 - vheight = 0;  
69 - vthreads = 0;  
70 - abitrate = 0;  
71 - asample_rate = 0;  
72 - achannels = 0;  
73 -  
74 - log_fd = -1;  
75 -}  
76 -  
77 -SrsFFMPEG::~SrsFFMPEG()  
78 -{  
79 - stop();  
80 -}  
81 -  
82 -int SrsFFMPEG::initialize(SrsRequest* req, SrsConfDirective* engine)  
83 -{  
84 - int ret = ERROR_SUCCESS;  
85 -  
86 - _srs_config->get_engine_vfilter(engine, vfilter);  
87 - vcodec = _srs_config->get_engine_vcodec(engine);  
88 - vbitrate = _srs_config->get_engine_vbitrate(engine);  
89 - vfps = _srs_config->get_engine_vfps(engine);  
90 - vwidth = _srs_config->get_engine_vwidth(engine);  
91 - vheight = _srs_config->get_engine_vheight(engine);  
92 - vthreads = _srs_config->get_engine_vthreads(engine);  
93 - vprofile = _srs_config->get_engine_vprofile(engine);  
94 - vpreset = _srs_config->get_engine_vpreset(engine);  
95 - _srs_config->get_engine_vparams(engine, vparams);  
96 - acodec = _srs_config->get_engine_acodec(engine);  
97 - abitrate = _srs_config->get_engine_abitrate(engine);  
98 - asample_rate = _srs_config->get_engine_asample_rate(engine);  
99 - achannels = _srs_config->get_engine_achannels(engine);  
100 - _srs_config->get_engine_aparams(engine, aparams);  
101 - output = _srs_config->get_engine_output(engine);  
102 -  
103 - // ensure the size is even.  
104 - vwidth -= vwidth % 2;  
105 - vheight -= vheight % 2;  
106 -  
107 - // input stream, from local.  
108 - // ie. rtmp://127.0.0.1:1935/live/livestream  
109 - input = "rtmp://127.0.0.1:";  
110 - input += req->port;  
111 - input += "/";  
112 - input += req->app;  
113 - input += "?vhost=";  
114 - input += req->vhost;  
115 - input += "/";  
116 - input += req->stream;  
117 -  
118 - // output stream, to other/self server  
119 - // ie. rtmp://127.0.0.1:1935/live/livestream_sd  
120 - output = srs_string_replace(output, "[vhost]", req->vhost);  
121 - output = srs_string_replace(output, "[port]", req->port);  
122 - output = srs_string_replace(output, "[app]", req->app);  
123 - output = srs_string_replace(output, "[stream]", req->stream);  
124 - output = srs_string_replace(output, "[engine]", engine->arg0());  
125 -  
126 - // write ffmpeg info to log file.  
127 - log_file = _srs_config->get_ffmpeg_log_dir();  
128 - log_file += "/";  
129 - log_file += "encoder";  
130 - log_file += "-";  
131 - log_file += req->vhost;  
132 - log_file += "-";  
133 - log_file += req->app;  
134 - log_file += "-";  
135 - log_file += req->stream;  
136 - log_file += ".log";  
137 -  
138 - // important: loop check, donot transcode again.  
139 - std::vector<std::string>::iterator it;  
140 - it = std::find(_transcoded_url.begin(), _transcoded_url.end(), input);  
141 - if (it != _transcoded_url.end()) {  
142 - ret = ERROR_ENCODER_LOOP;  
143 - srs_info("detect a loop cycle, input=%s, output=%s, ignore it. ret=%d",  
144 - input.c_str(), output.c_str(), ret);  
145 - return ret;  
146 - }  
147 - _transcoded_url.push_back(output);  
148 -  
149 - if (vcodec == SRS_ENCODER_NO_VIDEO && acodec == SRS_ENCODER_NO_AUDIO) {  
150 - ret = ERROR_ENCODER_VCODEC;  
151 - srs_warn("video and audio disabled. ret=%d", ret);  
152 - return ret;  
153 - }  
154 -  
155 - if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {  
156 - if (vcodec != SRS_ENCODER_VCODEC) {  
157 - ret = ERROR_ENCODER_VCODEC;  
158 - srs_error("invalid vcodec, must be %s, actual %s, ret=%d",  
159 - SRS_ENCODER_VCODEC, vcodec.c_str(), ret);  
160 - return ret;  
161 - }  
162 - if (vbitrate <= 0) {  
163 - ret = ERROR_ENCODER_VBITRATE;  
164 - srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);  
165 - return ret;  
166 - }  
167 - if (vfps <= 0) {  
168 - ret = ERROR_ENCODER_VFPS;  
169 - srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);  
170 - return ret;  
171 - }  
172 - if (vwidth <= 0) {  
173 - ret = ERROR_ENCODER_VWIDTH;  
174 - srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);  
175 - return ret;  
176 - }  
177 - if (vheight <= 0) {  
178 - ret = ERROR_ENCODER_VHEIGHT;  
179 - srs_error("invalid vheight: %d, ret=%d", vheight, ret);  
180 - return ret;  
181 - }  
182 - if (vthreads < 0) {  
183 - ret = ERROR_ENCODER_VTHREADS;  
184 - srs_error("invalid vthreads: %d, ret=%d", vthreads, ret);  
185 - return ret;  
186 - }  
187 - if (vprofile.empty()) {  
188 - ret = ERROR_ENCODER_VPROFILE;  
189 - srs_error("invalid vprofile: %s, ret=%d", vprofile.c_str(), ret);  
190 - return ret;  
191 - }  
192 - if (vpreset.empty()) {  
193 - ret = ERROR_ENCODER_VPRESET;  
194 - srs_error("invalid vpreset: %s, ret=%d", vpreset.c_str(), ret);  
195 - return ret;  
196 - }  
197 - }  
198 -  
199 - if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {  
200 - if (acodec.find(SRS_ENCODER_ACODEC) == std::string::npos) {  
201 - ret = ERROR_ENCODER_ACODEC;  
202 - srs_error("invalid acodec, must be %s, actual %s, ret=%d",  
203 - SRS_ENCODER_ACODEC, acodec.c_str(), ret);  
204 - return ret;  
205 - }  
206 - if (abitrate <= 0) {  
207 - ret = ERROR_ENCODER_ABITRATE;  
208 - srs_error("invalid abitrate: %d, ret=%d",  
209 - abitrate, ret);  
210 - return ret;  
211 - }  
212 - if (asample_rate <= 0) {  
213 - ret = ERROR_ENCODER_ASAMPLE_RATE;  
214 - srs_error("invalid sample rate: %d, ret=%d",  
215 - asample_rate, ret);  
216 - return ret;  
217 - }  
218 - if (achannels != 1 && achannels != 2) {  
219 - ret = ERROR_ENCODER_ACHANNELS;  
220 - srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d",  
221 - achannels, ret);  
222 - return ret;  
223 - }  
224 - }  
225 - if (output.empty()) {  
226 - ret = ERROR_ENCODER_OUTPUT;  
227 - srs_error("invalid empty output, ret=%d", ret);  
228 - return ret;  
229 - }  
230 -  
231 - return ret;  
232 -}  
233 -  
234 -int SrsFFMPEG::start()  
235 -{  
236 - int ret = ERROR_SUCCESS;  
237 -  
238 - if (started) {  
239 - return ret;  
240 - }  
241 -  
242 - // prepare exec params  
243 - char tmp[256];  
244 - std::vector<std::string> params;  
245 -  
246 - // argv[0], set to ffmpeg bin.  
247 - // The execv() and execvp() functions ....  
248 - // The first argument, by convention, should point to  
249 - // the filename associated with the file being executed.  
250 - params.push_back(ffmpeg);  
251 -  
252 - // input.  
253 - params.push_back("-f");  
254 - params.push_back("flv");  
255 -  
256 - params.push_back("-i");  
257 - params.push_back(input);  
258 -  
259 - // build the filter  
260 - if (!vfilter.empty()) {  
261 - std::vector<std::string>::iterator it;  
262 - for (it = vfilter.begin(); it != vfilter.end(); ++it) {  
263 - std::string p = *it;  
264 - if (!p.empty()) {  
265 - params.push_back(p);  
266 - }  
267 - }  
268 - }  
269 -  
270 - // video specified.  
271 - if (vcodec != SRS_ENCODER_NO_VIDEO) {  
272 - params.push_back("-vcodec");  
273 - params.push_back(vcodec);  
274 - } else {  
275 - params.push_back("-vn");  
276 - }  
277 -  
278 - // the codec params is disabled when copy  
279 - if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {  
280 - params.push_back("-b:v");  
281 - snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000);  
282 - params.push_back(tmp);  
283 -  
284 - params.push_back("-r");  
285 - snprintf(tmp, sizeof(tmp), "%.2f", vfps);  
286 - params.push_back(tmp);  
287 -  
288 - params.push_back("-s");  
289 - snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight);  
290 - params.push_back(tmp);  
291 -  
292 - // TODO: add aspect if needed.  
293 - params.push_back("-aspect");  
294 - snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight);  
295 - params.push_back(tmp);  
296 -  
297 - params.push_back("-threads");  
298 - snprintf(tmp, sizeof(tmp), "%d", vthreads);  
299 - params.push_back(tmp);  
300 -  
301 - params.push_back("-profile:v");  
302 - params.push_back(vprofile);  
303 -  
304 - params.push_back("-preset");  
305 - params.push_back(vpreset);  
306 -  
307 - // vparams  
308 - if (!vparams.empty()) {  
309 - std::vector<std::string>::iterator it;  
310 - for (it = vparams.begin(); it != vparams.end(); ++it) {  
311 - std::string p = *it;  
312 - if (!p.empty()) {  
313 - params.push_back(p);  
314 - }  
315 - }  
316 - }  
317 - }  
318 -  
319 - // audio specified.  
320 - if (acodec != SRS_ENCODER_NO_AUDIO) {  
321 - params.push_back("-acodec");  
322 - params.push_back(acodec);  
323 - } else {  
324 - params.push_back("-an");  
325 - }  
326 -  
327 - // the codec params is disabled when copy  
328 - if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {  
329 - params.push_back("-b:a");  
330 - snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000);  
331 - params.push_back(tmp);  
332 -  
333 - params.push_back("-ar");  
334 - snprintf(tmp, sizeof(tmp), "%d", asample_rate);  
335 - params.push_back(tmp);  
336 -  
337 - params.push_back("-ac");  
338 - snprintf(tmp, sizeof(tmp), "%d", achannels);  
339 - params.push_back(tmp);  
340 -  
341 - // aparams  
342 - if (!aparams.empty()) {  
343 - std::vector<std::string>::iterator it;  
344 - for (it = aparams.begin(); it != aparams.end(); ++it) {  
345 - std::string p = *it;  
346 - if (!p.empty()) {  
347 - params.push_back(p);  
348 - }  
349 - }  
350 - }  
351 - }  
352 -  
353 - // output  
354 - params.push_back("-f");  
355 - params.push_back("flv");  
356 -  
357 - params.push_back("-y");  
358 - params.push_back(output);  
359 -  
360 - if (true) {  
361 - int pparam_size = 8 * 1024;  
362 - char* pparam = new char[pparam_size];  
363 - char* p = pparam;  
364 - char* last = pparam + pparam_size;  
365 - for (int i = 0; i < (int)params.size(); i++) {  
366 - std::string ffp = params[i];  
367 - snprintf(p, last - p, "%s ", ffp.c_str());  
368 - p += ffp.length() + 1;  
369 - }  
370 - srs_trace("start transcoder, log: %s, params: %s",  
371 - log_file.c_str(), pparam);  
372 - srs_freepa(pparam);  
373 - }  
374 -  
375 - // TODO: fork or vfork?  
376 - if ((pid = fork()) < 0) {  
377 - ret = ERROR_ENCODER_FORK;  
378 - srs_error("vfork process failed. ret=%d", ret);  
379 - return ret;  
380 - }  
381 -  
382 - // child process: ffmpeg encoder engine.  
383 - if (pid == 0) {  
384 - // redirect logs to file.  
385 - int flags = O_CREAT|O_WRONLY|O_APPEND;  
386 - mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;  
387 - if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {  
388 - ret = ERROR_ENCODER_OPEN;  
389 - srs_error("open encoder file %s failed. ret=%d", log_file.c_str(), ret);  
390 - return ret;  
391 - }  
392 - if (dup2(log_fd, STDOUT_FILENO) < 0) {  
393 - ret = ERROR_ENCODER_DUP2;  
394 - srs_error("dup2 encoder file failed. ret=%d", ret);  
395 - return ret;  
396 - }  
397 - if (dup2(log_fd, STDERR_FILENO) < 0) {  
398 - ret = ERROR_ENCODER_DUP2;  
399 - srs_error("dup2 encoder file failed. ret=%d", ret);  
400 - return ret;  
401 - }  
402 - // close other fds  
403 - // TODO: do in right way.  
404 - for (int i = 3; i < 1024; i++) {  
405 - ::close(i);  
406 - }  
407 -  
408 - // memory leak in child process, it's ok.  
409 - char** charpv_params = new char*[params.size() + 1];  
410 - for (int i = 0; i < (int)params.size(); i++) {  
411 - std::string p = params[i];  
412 - charpv_params[i] = (char*)p.c_str();  
413 - }  
414 - // EOF: NULL  
415 - charpv_params[params.size()] = NULL;  
416 -  
417 - // TODO: execv or execvp  
418 - ret = execv(ffmpeg.c_str(), charpv_params);  
419 - if (ret < 0) {  
420 - fprintf(stderr, "fork ffmpeg failed, errno=%d(%s)",  
421 - errno, strerror(errno));  
422 - }  
423 - exit(ret);  
424 - }  
425 -  
426 - // parent.  
427 - if (pid > 0) {  
428 - started = true;  
429 - srs_trace("vfored ffmpeg encoder engine, pid=%d", pid);  
430 - return ret;  
431 - }  
432 -  
433 - return ret;  
434 -}  
435 -  
436 -int SrsFFMPEG::cycle()  
437 -{  
438 - int ret = ERROR_SUCCESS;  
439 -  
440 - if (!started) {  
441 - return ret;  
442 - }  
443 -  
444 - int status = 0;  
445 - pid_t p = waitpid(pid, &status, WNOHANG);  
446 -  
447 - if (p < 0) {  
448 - ret = ERROR_SYSTEM_WAITPID;  
449 - srs_error("transcode waitpid failed, pid=%d, ret=%d", pid, ret);  
450 - return ret;  
451 - }  
452 -  
453 - if (p == 0) {  
454 - srs_info("transcode process pid=%d is running.", pid);  
455 - return ret;  
456 - }  
457 -  
458 - srs_trace("transcode process pid=%d terminate, restart it.", pid);  
459 - started = false;  
460 -  
461 - return ret;  
462 -}  
463 -  
464 -void SrsFFMPEG::stop()  
465 -{  
466 - if (log_fd > 0) {  
467 - ::close(log_fd);  
468 - log_fd = -1;  
469 - }  
470 -  
471 - if (!started) {  
472 - return;  
473 - }  
474 -  
475 - // kill the ffmpeg,  
476 - // when rewind, upstream will stop publish(unpublish),  
477 - // unpublish event will stop all ffmpeg encoders,  
478 - // then publish will start all ffmpeg encoders.  
479 - if (pid > 0) {  
480 - if (kill(pid, SIGKILL) < 0) {  
481 - srs_warn("kill the encoder failed, ignored. pid=%d", pid);  
482 - }  
483 -  
484 - // wait for the ffmpeg to quit.  
485 - // ffmpeg will gracefully quit if signal is:  
486 - // 1) SIGHUP 2) SIGINT 3) SIGQUIT  
487 - // other signals, directly exit(123), for example:  
488 - // 9) SIGKILL 15) SIGTERM  
489 - int status = 0;  
490 - if (waitpid(pid, &status, 0) < 0) {  
491 - srs_warn("wait the encoder quit failed, ignored. pid=%d", pid);  
492 - }  
493 -  
494 - srs_trace("stop the encoder success. pid=%d", pid);  
495 - pid = -1;  
496 - }  
497 -  
498 - std::vector<std::string>::iterator it;  
499 - it = std::find(_transcoded_url.begin(), _transcoded_url.end(), output);  
500 - if (it != _transcoded_url.end()) {  
501 - _transcoded_url.erase(it);  
502 - }  
503 -}  
504 -  
505 SrsEncoder::SrsEncoder() 39 SrsEncoder::SrsEncoder()
506 { 40 {
507 pthread = new SrsThread(this, SRS_ENCODER_SLEEP_US); 41 pthread = new SrsThread(this, SRS_ENCODER_SLEEP_US);
@@ -39,47 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -39,47 +39,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 class SrsConfDirective; 39 class SrsConfDirective;
40 class SrsRequest; 40 class SrsRequest;
41 class SrsPithyPrint; 41 class SrsPithyPrint;
42 -  
43 -/**  
44 -* a transcode engine: ffmepg,  
45 -* used to transcode a stream to another.  
46 -*/  
47 -class SrsFFMPEG  
48 -{  
49 -private:  
50 - bool started;  
51 - pid_t pid;  
52 -private:  
53 - std::string log_file;  
54 - int log_fd;  
55 -private:  
56 - std::string ffmpeg;  
57 - std::vector<std::string> vfilter;  
58 - std::string vcodec;  
59 - int vbitrate;  
60 - double vfps;  
61 - int vwidth;  
62 - int vheight;  
63 - int vthreads;  
64 - std::string vprofile;  
65 - std::string vpreset;  
66 - std::vector<std::string> vparams;  
67 - std::string acodec;  
68 - int abitrate;  
69 - int asample_rate;  
70 - int achannels;  
71 - std::vector<std::string> aparams;  
72 - std::string output;  
73 - std::string input;  
74 -public:  
75 - SrsFFMPEG(std::string ffmpeg_bin);  
76 - virtual ~SrsFFMPEG();  
77 -public:  
78 - virtual int initialize(SrsRequest* req, SrsConfDirective* engine);  
79 - virtual int start();  
80 - virtual int cycle();  
81 - virtual void stop();  
82 -}; 42 +class SrsFFMPEG;
83 43
84 /** 44 /**
85 * the encoder for a stream, 45 * the encoder for a stream,
  1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013-2014 winlin
  5 +
  6 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 +this software and associated documentation files (the "Software"), to deal in
  8 +the Software without restriction, including without limitation the rights to
  9 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 +the Software, and to permit persons to whom the Software is furnished to do so,
  11 +subject to the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be included in all
  14 +copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +
  24 +#include <srs_app_ffmpeg.hpp>
  25 +
  26 +#include <stdlib.h>
  27 +#include <unistd.h>
  28 +#include <sys/wait.h>
  29 +#include <fcntl.h>
  30 +#include <signal.h>
  31 +#include <sys/types.h>
  32 +
  33 +#include <algorithm>
  34 +
  35 +#include <srs_kernel_error.hpp>
  36 +#include <srs_kernel_log.hpp>
  37 +#include <srs_app_config.hpp>
  38 +#include <srs_protocol_rtmp.hpp>
  39 +#include <srs_app_pithy_print.hpp>
  40 +#include <srs_protocol_rtmp_stack.hpp>
  41 +
  42 +#ifdef SRS_FFMPEG
  43 +
  44 +#define SRS_ENCODER_COPY "copy"
  45 +#define SRS_ENCODER_NO_VIDEO "vn"
  46 +#define SRS_ENCODER_NO_AUDIO "an"
  47 +// only support libx264 encoder.
  48 +#define SRS_ENCODER_VCODEC "libx264"
  49 +// any aac encoder is ok which contains the aac,
  50 +// for example, libaacplus, aac, fdkaac
  51 +#define SRS_ENCODER_ACODEC "aac"
  52 +
  53 +// for encoder to detect the dead loop
  54 +static std::vector<std::string> _transcoded_url;
  55 +
  56 +SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin)
  57 +{
  58 + started = false;
  59 + pid = -1;
  60 + ffmpeg = ffmpeg_bin;
  61 +
  62 + vbitrate = 0;
  63 + vfps = 0;
  64 + vwidth = 0;
  65 + vheight = 0;
  66 + vthreads = 0;
  67 + abitrate = 0;
  68 + asample_rate = 0;
  69 + achannels = 0;
  70 +
  71 + log_fd = -1;
  72 +}
  73 +
  74 +SrsFFMPEG::~SrsFFMPEG()
  75 +{
  76 + stop();
  77 +}
  78 +
  79 +int SrsFFMPEG::initialize(SrsRequest* req, SrsConfDirective* engine)
  80 +{
  81 + int ret = ERROR_SUCCESS;
  82 +
  83 + _srs_config->get_engine_vfilter(engine, vfilter);
  84 + vcodec = _srs_config->get_engine_vcodec(engine);
  85 + vbitrate = _srs_config->get_engine_vbitrate(engine);
  86 + vfps = _srs_config->get_engine_vfps(engine);
  87 + vwidth = _srs_config->get_engine_vwidth(engine);
  88 + vheight = _srs_config->get_engine_vheight(engine);
  89 + vthreads = _srs_config->get_engine_vthreads(engine);
  90 + vprofile = _srs_config->get_engine_vprofile(engine);
  91 + vpreset = _srs_config->get_engine_vpreset(engine);
  92 + _srs_config->get_engine_vparams(engine, vparams);
  93 + acodec = _srs_config->get_engine_acodec(engine);
  94 + abitrate = _srs_config->get_engine_abitrate(engine);
  95 + asample_rate = _srs_config->get_engine_asample_rate(engine);
  96 + achannels = _srs_config->get_engine_achannels(engine);
  97 + _srs_config->get_engine_aparams(engine, aparams);
  98 + output = _srs_config->get_engine_output(engine);
  99 +
  100 + // ensure the size is even.
  101 + vwidth -= vwidth % 2;
  102 + vheight -= vheight % 2;
  103 +
  104 + // input stream, from local.
  105 + // ie. rtmp://127.0.0.1:1935/live/livestream
  106 + input = "rtmp://127.0.0.1:";
  107 + input += req->port;
  108 + input += "/";
  109 + input += req->app;
  110 + input += "?vhost=";
  111 + input += req->vhost;
  112 + input += "/";
  113 + input += req->stream;
  114 +
  115 + // output stream, to other/self server
  116 + // ie. rtmp://127.0.0.1:1935/live/livestream_sd
  117 + output = srs_string_replace(output, "[vhost]", req->vhost);
  118 + output = srs_string_replace(output, "[port]", req->port);
  119 + output = srs_string_replace(output, "[app]", req->app);
  120 + output = srs_string_replace(output, "[stream]", req->stream);
  121 + output = srs_string_replace(output, "[engine]", engine->arg0());
  122 +
  123 + // write ffmpeg info to log file.
  124 + log_file = _srs_config->get_ffmpeg_log_dir();
  125 + log_file += "/";
  126 + log_file += "encoder";
  127 + log_file += "-";
  128 + log_file += req->vhost;
  129 + log_file += "-";
  130 + log_file += req->app;
  131 + log_file += "-";
  132 + log_file += req->stream;
  133 + log_file += ".log";
  134 +
  135 + // important: loop check, donot transcode again.
  136 + std::vector<std::string>::iterator it;
  137 + it = std::find(_transcoded_url.begin(), _transcoded_url.end(), input);
  138 + if (it != _transcoded_url.end()) {
  139 + ret = ERROR_ENCODER_LOOP;
  140 + srs_info("detect a loop cycle, input=%s, output=%s, ignore it. ret=%d",
  141 + input.c_str(), output.c_str(), ret);
  142 + return ret;
  143 + }
  144 + _transcoded_url.push_back(output);
  145 +
  146 + if (vcodec == SRS_ENCODER_NO_VIDEO && acodec == SRS_ENCODER_NO_AUDIO) {
  147 + ret = ERROR_ENCODER_VCODEC;
  148 + srs_warn("video and audio disabled. ret=%d", ret);
  149 + return ret;
  150 + }
  151 +
  152 + if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {
  153 + if (vcodec != SRS_ENCODER_VCODEC) {
  154 + ret = ERROR_ENCODER_VCODEC;
  155 + srs_error("invalid vcodec, must be %s, actual %s, ret=%d",
  156 + SRS_ENCODER_VCODEC, vcodec.c_str(), ret);
  157 + return ret;
  158 + }
  159 + if (vbitrate <= 0) {
  160 + ret = ERROR_ENCODER_VBITRATE;
  161 + srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);
  162 + return ret;
  163 + }
  164 + if (vfps <= 0) {
  165 + ret = ERROR_ENCODER_VFPS;
  166 + srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);
  167 + return ret;
  168 + }
  169 + if (vwidth <= 0) {
  170 + ret = ERROR_ENCODER_VWIDTH;
  171 + srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);
  172 + return ret;
  173 + }
  174 + if (vheight <= 0) {
  175 + ret = ERROR_ENCODER_VHEIGHT;
  176 + srs_error("invalid vheight: %d, ret=%d", vheight, ret);
  177 + return ret;
  178 + }
  179 + if (vthreads < 0) {
  180 + ret = ERROR_ENCODER_VTHREADS;
  181 + srs_error("invalid vthreads: %d, ret=%d", vthreads, ret);
  182 + return ret;
  183 + }
  184 + if (vprofile.empty()) {
  185 + ret = ERROR_ENCODER_VPROFILE;
  186 + srs_error("invalid vprofile: %s, ret=%d", vprofile.c_str(), ret);
  187 + return ret;
  188 + }
  189 + if (vpreset.empty()) {
  190 + ret = ERROR_ENCODER_VPRESET;
  191 + srs_error("invalid vpreset: %s, ret=%d", vpreset.c_str(), ret);
  192 + return ret;
  193 + }
  194 + }
  195 +
  196 + if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {
  197 + if (acodec.find(SRS_ENCODER_ACODEC) == std::string::npos) {
  198 + ret = ERROR_ENCODER_ACODEC;
  199 + srs_error("invalid acodec, must be %s, actual %s, ret=%d",
  200 + SRS_ENCODER_ACODEC, acodec.c_str(), ret);
  201 + return ret;
  202 + }
  203 + if (abitrate <= 0) {
  204 + ret = ERROR_ENCODER_ABITRATE;
  205 + srs_error("invalid abitrate: %d, ret=%d",
  206 + abitrate, ret);
  207 + return ret;
  208 + }
  209 + if (asample_rate <= 0) {
  210 + ret = ERROR_ENCODER_ASAMPLE_RATE;
  211 + srs_error("invalid sample rate: %d, ret=%d",
  212 + asample_rate, ret);
  213 + return ret;
  214 + }
  215 + if (achannels != 1 && achannels != 2) {
  216 + ret = ERROR_ENCODER_ACHANNELS;
  217 + srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d",
  218 + achannels, ret);
  219 + return ret;
  220 + }
  221 + }
  222 + if (output.empty()) {
  223 + ret = ERROR_ENCODER_OUTPUT;
  224 + srs_error("invalid empty output, ret=%d", ret);
  225 + return ret;
  226 + }
  227 +
  228 + return ret;
  229 +}
  230 +
  231 +int SrsFFMPEG::start()
  232 +{
  233 + int ret = ERROR_SUCCESS;
  234 +
  235 + if (started) {
  236 + return ret;
  237 + }
  238 +
  239 + // prepare exec params
  240 + char tmp[256];
  241 + std::vector<std::string> params;
  242 +
  243 + // argv[0], set to ffmpeg bin.
  244 + // The execv() and execvp() functions ....
  245 + // The first argument, by convention, should point to
  246 + // the filename associated with the file being executed.
  247 + params.push_back(ffmpeg);
  248 +
  249 + // input.
  250 + params.push_back("-f");
  251 + params.push_back("flv");
  252 +
  253 + params.push_back("-i");
  254 + params.push_back(input);
  255 +
  256 + // build the filter
  257 + if (!vfilter.empty()) {
  258 + std::vector<std::string>::iterator it;
  259 + for (it = vfilter.begin(); it != vfilter.end(); ++it) {
  260 + std::string p = *it;
  261 + if (!p.empty()) {
  262 + params.push_back(p);
  263 + }
  264 + }
  265 + }
  266 +
  267 + // video specified.
  268 + if (vcodec != SRS_ENCODER_NO_VIDEO) {
  269 + params.push_back("-vcodec");
  270 + params.push_back(vcodec);
  271 + } else {
  272 + params.push_back("-vn");
  273 + }
  274 +
  275 + // the codec params is disabled when copy
  276 + if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {
  277 + params.push_back("-b:v");
  278 + snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000);
  279 + params.push_back(tmp);
  280 +
  281 + params.push_back("-r");
  282 + snprintf(tmp, sizeof(tmp), "%.2f", vfps);
  283 + params.push_back(tmp);
  284 +
  285 + params.push_back("-s");
  286 + snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight);
  287 + params.push_back(tmp);
  288 +
  289 + // TODO: add aspect if needed.
  290 + params.push_back("-aspect");
  291 + snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight);
  292 + params.push_back(tmp);
  293 +
  294 + params.push_back("-threads");
  295 + snprintf(tmp, sizeof(tmp), "%d", vthreads);
  296 + params.push_back(tmp);
  297 +
  298 + params.push_back("-profile:v");
  299 + params.push_back(vprofile);
  300 +
  301 + params.push_back("-preset");
  302 + params.push_back(vpreset);
  303 +
  304 + // vparams
  305 + if (!vparams.empty()) {
  306 + std::vector<std::string>::iterator it;
  307 + for (it = vparams.begin(); it != vparams.end(); ++it) {
  308 + std::string p = *it;
  309 + if (!p.empty()) {
  310 + params.push_back(p);
  311 + }
  312 + }
  313 + }
  314 + }
  315 +
  316 + // audio specified.
  317 + if (acodec != SRS_ENCODER_NO_AUDIO) {
  318 + params.push_back("-acodec");
  319 + params.push_back(acodec);
  320 + } else {
  321 + params.push_back("-an");
  322 + }
  323 +
  324 + // the codec params is disabled when copy
  325 + if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {
  326 + params.push_back("-b:a");
  327 + snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000);
  328 + params.push_back(tmp);
  329 +
  330 + params.push_back("-ar");
  331 + snprintf(tmp, sizeof(tmp), "%d", asample_rate);
  332 + params.push_back(tmp);
  333 +
  334 + params.push_back("-ac");
  335 + snprintf(tmp, sizeof(tmp), "%d", achannels);
  336 + params.push_back(tmp);
  337 +
  338 + // aparams
  339 + if (!aparams.empty()) {
  340 + std::vector<std::string>::iterator it;
  341 + for (it = aparams.begin(); it != aparams.end(); ++it) {
  342 + std::string p = *it;
  343 + if (!p.empty()) {
  344 + params.push_back(p);
  345 + }
  346 + }
  347 + }
  348 + }
  349 +
  350 + // output
  351 + params.push_back("-f");
  352 + params.push_back("flv");
  353 +
  354 + params.push_back("-y");
  355 + params.push_back(output);
  356 +
  357 + if (true) {
  358 + int pparam_size = 8 * 1024;
  359 + char* pparam = new char[pparam_size];
  360 + char* p = pparam;
  361 + char* last = pparam + pparam_size;
  362 + for (int i = 0; i < (int)params.size(); i++) {
  363 + std::string ffp = params[i];
  364 + snprintf(p, last - p, "%s ", ffp.c_str());
  365 + p += ffp.length() + 1;
  366 + }
  367 + srs_trace("start transcoder, log: %s, params: %s",
  368 + log_file.c_str(), pparam);
  369 + srs_freepa(pparam);
  370 + }
  371 +
  372 + // TODO: fork or vfork?
  373 + if ((pid = fork()) < 0) {
  374 + ret = ERROR_ENCODER_FORK;
  375 + srs_error("vfork process failed. ret=%d", ret);
  376 + return ret;
  377 + }
  378 +
  379 + // child process: ffmpeg encoder engine.
  380 + if (pid == 0) {
  381 + // redirect logs to file.
  382 + int flags = O_CREAT|O_WRONLY|O_APPEND;
  383 + mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
  384 + if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {
  385 + ret = ERROR_ENCODER_OPEN;
  386 + srs_error("open encoder file %s failed. ret=%d", log_file.c_str(), ret);
  387 + return ret;
  388 + }
  389 + if (dup2(log_fd, STDOUT_FILENO) < 0) {
  390 + ret = ERROR_ENCODER_DUP2;
  391 + srs_error("dup2 encoder file failed. ret=%d", ret);
  392 + return ret;
  393 + }
  394 + if (dup2(log_fd, STDERR_FILENO) < 0) {
  395 + ret = ERROR_ENCODER_DUP2;
  396 + srs_error("dup2 encoder file failed. ret=%d", ret);
  397 + return ret;
  398 + }
  399 + // close other fds
  400 + // TODO: do in right way.
  401 + for (int i = 3; i < 1024; i++) {
  402 + ::close(i);
  403 + }
  404 +
  405 + // memory leak in child process, it's ok.
  406 + char** charpv_params = new char*[params.size() + 1];
  407 + for (int i = 0; i < (int)params.size(); i++) {
  408 + std::string p = params[i];
  409 + charpv_params[i] = (char*)p.c_str();
  410 + }
  411 + // EOF: NULL
  412 + charpv_params[params.size()] = NULL;
  413 +
  414 + // TODO: execv or execvp
  415 + ret = execv(ffmpeg.c_str(), charpv_params);
  416 + if (ret < 0) {
  417 + fprintf(stderr, "fork ffmpeg failed, errno=%d(%s)",
  418 + errno, strerror(errno));
  419 + }
  420 + exit(ret);
  421 + }
  422 +
  423 + // parent.
  424 + if (pid > 0) {
  425 + started = true;
  426 + srs_trace("vfored ffmpeg encoder engine, pid=%d", pid);
  427 + return ret;
  428 + }
  429 +
  430 + return ret;
  431 +}
  432 +
  433 +int SrsFFMPEG::cycle()
  434 +{
  435 + int ret = ERROR_SUCCESS;
  436 +
  437 + if (!started) {
  438 + return ret;
  439 + }
  440 +
  441 + int status = 0;
  442 + pid_t p = waitpid(pid, &status, WNOHANG);
  443 +
  444 + if (p < 0) {
  445 + ret = ERROR_SYSTEM_WAITPID;
  446 + srs_error("transcode waitpid failed, pid=%d, ret=%d", pid, ret);
  447 + return ret;
  448 + }
  449 +
  450 + if (p == 0) {
  451 + srs_info("transcode process pid=%d is running.", pid);
  452 + return ret;
  453 + }
  454 +
  455 + srs_trace("transcode process pid=%d terminate, restart it.", pid);
  456 + started = false;
  457 +
  458 + return ret;
  459 +}
  460 +
  461 +void SrsFFMPEG::stop()
  462 +{
  463 + if (log_fd > 0) {
  464 + ::close(log_fd);
  465 + log_fd = -1;
  466 + }
  467 +
  468 + if (!started) {
  469 + return;
  470 + }
  471 +
  472 + // kill the ffmpeg,
  473 + // when rewind, upstream will stop publish(unpublish),
  474 + // unpublish event will stop all ffmpeg encoders,
  475 + // then publish will start all ffmpeg encoders.
  476 + if (pid > 0) {
  477 + if (kill(pid, SIGKILL) < 0) {
  478 + srs_warn("kill the encoder failed, ignored. pid=%d", pid);
  479 + }
  480 +
  481 + // wait for the ffmpeg to quit.
  482 + // ffmpeg will gracefully quit if signal is:
  483 + // 1) SIGHUP 2) SIGINT 3) SIGQUIT
  484 + // other signals, directly exit(123), for example:
  485 + // 9) SIGKILL 15) SIGTERM
  486 + int status = 0;
  487 + if (waitpid(pid, &status, 0) < 0) {
  488 + srs_warn("wait the encoder quit failed, ignored. pid=%d", pid);
  489 + }
  490 +
  491 + srs_trace("stop the encoder success. pid=%d", pid);
  492 + pid = -1;
  493 + }
  494 +
  495 + std::vector<std::string>::iterator it;
  496 + it = std::find(_transcoded_url.begin(), _transcoded_url.end(), output);
  497 + if (it != _transcoded_url.end()) {
  498 + _transcoded_url.erase(it);
  499 + }
  500 +}
  501 +
  502 +#endif
  503 +
  1 +/*
  2 +The MIT License (MIT)
  3 +
  4 +Copyright (c) 2013-2014 winlin
  5 +
  6 +Permission is hereby granted, free of charge, to any person obtaining a copy of
  7 +this software and associated documentation files (the "Software"), to deal in
  8 +the Software without restriction, including without limitation the rights to
  9 +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  10 +the Software, and to permit persons to whom the Software is furnished to do so,
  11 +subject to the following conditions:
  12 +
  13 +The above copyright notice and this permission notice shall be included in all
  14 +copies or substantial portions of the Software.
  15 +
  16 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  18 +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  19 +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  20 +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  21 +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22 +*/
  23 +
  24 +#ifndef SRS_APP_FFMPEG_HPP
  25 +#define SRS_APP_FFMPEG_HPP
  26 +
  27 +/*
  28 +#include <srs_app_ffmpeg.hpp>
  29 +*/
  30 +#include <srs_core.hpp>
  31 +
  32 +#ifdef SRS_FFMPEG
  33 +
  34 +#include <string>
  35 +#include <vector>
  36 +
  37 +class SrsConfDirective;
  38 +class SrsRequest;
  39 +class SrsPithyPrint;
  40 +
  41 +/**
  42 +* a transcode engine: ffmepg,
  43 +* used to transcode a stream to another.
  44 +*/
  45 +class SrsFFMPEG
  46 +{
  47 +private:
  48 + bool started;
  49 + pid_t pid;
  50 +private:
  51 + std::string log_file;
  52 + int log_fd;
  53 +private:
  54 + std::string ffmpeg;
  55 + std::vector<std::string> vfilter;
  56 + std::string vcodec;
  57 + int vbitrate;
  58 + double vfps;
  59 + int vwidth;
  60 + int vheight;
  61 + int vthreads;
  62 + std::string vprofile;
  63 + std::string vpreset;
  64 + std::vector<std::string> vparams;
  65 + std::string acodec;
  66 + int abitrate;
  67 + int asample_rate;
  68 + int achannels;
  69 + std::vector<std::string> aparams;
  70 + std::string output;
  71 + std::string input;
  72 +public:
  73 + SrsFFMPEG(std::string ffmpeg_bin);
  74 + virtual ~SrsFFMPEG();
  75 +public:
  76 + virtual int initialize(SrsRequest* req, SrsConfDirective* engine);
  77 + virtual int start();
  78 + virtual int cycle();
  79 + virtual void stop();
  80 +};
  81 +
  82 +#endif
  83 +
  84 +#endif
@@ -49,6 +49,8 @@ file @@ -49,6 +49,8 @@ file
49 ..\app\srs_app_config.cpp, 49 ..\app\srs_app_config.cpp,
50 ..\app\srs_app_encoder.hpp, 50 ..\app\srs_app_encoder.hpp,
51 ..\app\srs_app_encoder.cpp, 51 ..\app\srs_app_encoder.cpp,
  52 + ..\app\srs_app_ffmpeg.hpp,
  53 + ..\app\srs_app_ffmpeg.cpp,
52 ..\app\srs_app_forward.hpp, 54 ..\app\srs_app_forward.hpp,
53 ..\app\srs_app_forward.cpp, 55 ..\app\srs_app_forward.cpp,
54 ..\app\srs_app_hls.hpp, 56 ..\app\srs_app_hls.hpp,