support live stream transcoding by ffmpeg.

pull/133/head
winlin 11 years ago
parent ee18b9f537
commit 525af4599f

@ -109,3 +109,12 @@ if [ $SRS_FFMPEG = YES ]; then
else else
echo "#undef SRS_FFMPEG" >> $SRS_AUTO_HEADERS_H echo "#undef SRS_FFMPEG" >> $SRS_AUTO_HEADERS_H
fi fi
#####################################################################################
# build research code
#####################################################################################
(cd research/hls && make)
ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/hls failed, ret=$ret"; exit $ret; fi
(cd research/ffempty && make)
ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/ffempty failed, ret=$ret"; exit $ret; fi

@ -18,7 +18,8 @@ vhost __defaultVhost__ {
#forward 127.0.0.1:1936; #forward 127.0.0.1:1936;
transcode { transcode {
enabled on; enabled on;
ffmpeg /home/winlin/srs/objs/ffmpeg/bin/ffmpeg; ffmpeg ./objs/ffmpeg/bin/ffmpeg;
#ffmpeg ./research/ffempty/ffempty;
engine fd{ engine fd{
enabled on; enabled on;
vcodec libx264; vcodec libx264;
@ -29,12 +30,14 @@ vhost __defaultVhost__ {
vthreads 2; vthreads 2;
vprofile baseline; vprofile baseline;
vpreset superfast; vpreset superfast;
vparams {} vparams {
}
acodec libaacplus; acodec libaacplus;
abitrate 30; abitrate 30;
asample_rate 22050; asample_rate 44100;
achannels 2; achannels 2;
aparams {} aparams {
}
output rtmp://[vhost]:[port]/[app]/[stream]_fast; output rtmp://[vhost]:[port]/[app]/[stream]_fast;
} }
} }
@ -151,7 +154,34 @@ vhost all.transcode.vhost.com {
} }
acodec libaacplus; acodec libaacplus;
abitrate 30; abitrate 30;
asample_rate 22050; asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://[vhost]:[port]/[app]/[stream]_fast;
}
}
}
# transcode all stream using the empty ffmpeg demo, donothing.
vhost ffempty.transcode.vhost.com {
transcode {
enabled on;
ffmpeg ./research/ffempty/ffempty;
engine fd{
enabled on;
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; achannels 2;
aparams { aparams {
} }

@ -31,6 +31,12 @@ int main(int argc, char** argv)
for (int i = 0; i < argc; i++) { for (int i = 0; i < argc; i++) {
fprintf(stderr, "argv[%d]=%s\n", i, argv[i]); fprintf(stderr, "argv[%d]=%s\n", i, argv[i]);
} }
fprintf(stderr, "summary:\n");
for (int i = 0; i < argc; i++) {
fprintf(stderr, "%s ", argv[i]);
}
fprintf(stderr, "\n");
return 0; return 0;
} }

@ -81,6 +81,7 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
vheight -= vheight % 2; vheight -= vheight % 2;
// input stream, from local. // input stream, from local.
// ie. rtmp://127.0.0.1:1935/live/livestream
input = "rtmp://127.0.0.1:"; input = "rtmp://127.0.0.1:";
input += port; input += port;
input += "/"; input += "/";
@ -88,6 +89,8 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
input += "/"; input += "/";
input += stream; input += stream;
// output stream, to other/self server
// ie. rtmp://127.0.0.1:1935/live/livestream_sd
if (vhost == RTMP_VHOST_DEFAULT) { if (vhost == RTMP_VHOST_DEFAULT) {
output = srs_replace(output, "[vhost]", "127.0.0.1"); output = srs_replace(output, "[vhost]", "127.0.0.1");
} else { } else {
@ -96,6 +99,22 @@ int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app,
output = srs_replace(output, "[port]", port); output = srs_replace(output, "[port]", port);
output = srs_replace(output, "[app]", app); output = srs_replace(output, "[app]", app);
output = srs_replace(output, "[stream]", stream); output = srs_replace(output, "[stream]", stream);
// important: loop check, donot transcode again.
// we think the following is loop circle:
// input: rtmp://127.0.0.1:1935/live/livestream_sd
// output: rtmp://127.0.0.1:1935/live/livestream_sd_sd
std::string tail = ""; // tail="_sd"
if (output.length() > input.length()) {
tail = output.substr(input.length());
}
// if input also endwiths the tail, loop detected.
if (!tail.empty() && input.rfind(tail) == input.length() - tail.length()) {
ret = ERROR_ENCODER_LOOP;
srs_info("detect a loop cycle, input=%s, output=%s, ignore it. ret=%d",
input.c_str(), output.c_str(), ret);
return ret;
}
if (vcodec != SRS_ENCODER_VCODEC) { if (vcodec != SRS_ENCODER_VCODEC) {
ret = ERROR_ENCODER_VCODEC; ret = ERROR_ENCODER_VCODEC;
@ -184,9 +203,32 @@ int SrsFFMPEG::start()
snprintf(vsize, sizeof(vsize), "%dx%d", vwidth, vheight); snprintf(vsize, sizeof(vsize), "%dx%d", vwidth, vheight);
char vaspect[22]; char vaspect[22];
snprintf(vaspect, sizeof(vaspect), "%d:%d", vwidth, vheight); 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);
// video params
std::string s_vpreset = vpreset;
if (!vparams.empty()) {
s_vpreset += " ";
s_vpreset += vparams;
}
// audio params
std::string s_aparams = s_achannels;
if (!aparams.empty()) {
s_aparams += " ";
s_aparams += aparams;
}
// we use vfork, for we use fored process
// to start ffmpeg, that is, exec after vfork.
if ((pid = fork()) < 0) { if ((pid = fork()) < 0) {
ret = ERROR_ENCODER_FORK; ret = ERROR_ENCODER_FORK;
srs_error("vfork process failed. ret=%d", ret); srs_error("vfork process failed. ret=%d", ret);
@ -195,25 +237,24 @@ int SrsFFMPEG::start()
// child process: ffmpeg encoder engine. // child process: ffmpeg encoder engine.
if (pid == 0) { if (pid == 0) {
// must exec immediately, or may introduce bug.
ret = execl(ffmpeg.c_str(), ret = execl(ffmpeg.c_str(),
ffmpeg.c_str(),
"-f", "flv",
"-i", input.c_str(), "-i", input.c_str(),
// video specified. // video specified.
"-vcodec", vcodec.c_str(), "-vcodec", vcodec.c_str(),
"-b:v", vbitrate * 1000, "-b:v", s_vbitrate,
"-r", vfps, "-r", s_vfps,
"-size", vsize, "-s", vsize,
"-aspect", vaspect, // TODO: add aspect if needed. "-aspect", vaspect, // TODO: add aspect if needed.
"-threads", vthreads, "-threads", s_vthreads,
"-profile", vprofile.c_str(), "-profile:v", vprofile.c_str(),
"-preset", vpreset.c_str(), "-preset", s_vpreset.c_str(),
vparams.c_str(),
// audio specified. // audio specified.
"-acodec", acodec.c_str(), "-acodec", acodec.c_str(),
"-b:a", abitrate * 1000, "-b:a", s_abitrate,
"-ar", asample_rate, "-ar", s_asample_rate,
"-ac", achannels, "-ac", s_aparams.c_str(),
aparams.c_str(),
"-f", "flv", "-f", "flv",
"-y", output.c_str(), "-y", output.c_str(),
NULL NULL
@ -253,14 +294,9 @@ SrsEncoder::~SrsEncoder()
on_unpublish(); on_unpublish();
} }
int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _app, std::string _stream) int SrsEncoder::parse_scope_engines()
{ {
int ret = ERROR_SUCCESS; int ret = ERROR_SUCCESS;
vhost = _vhost;
port = _port;
app = _app;
stream = _stream;
// parse all transcode engines. // parse all transcode engines.
SrsConfDirective* conf = NULL; SrsConfDirective* conf = NULL;
@ -293,6 +329,30 @@ int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _a
return ret; return ret;
} }
} }
return ret;
}
int SrsEncoder::on_publish(std::string _vhost, std::string _port, std::string _app, std::string _stream)
{
int ret = ERROR_SUCCESS;
vhost = _vhost;
port = _port;
app = _app;
stream = _stream;
ret = parse_scope_engines();
// ignore the loop encoder
if (ret = ERROR_ENCODER_LOOP) {
ret = ERROR_SUCCESS;
}
// return for error or no engine.
if (ret != ERROR_SUCCESS || ffmpegs.empty()) {
return ret;
}
// start thread to run all encoding engines. // start thread to run all encoding engines.
srs_assert(!tid); srs_assert(!tid);
@ -313,7 +373,12 @@ void SrsEncoder::on_unpublish()
st_thread_join(tid, NULL); st_thread_join(tid, NULL);
tid = NULL; tid = NULL;
} }
clear_engines();
}
void SrsEncoder::clear_engines()
{
std::vector<SrsFFMPEG*>::iterator it; std::vector<SrsFFMPEG*>::iterator it;
for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) { for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
SrsFFMPEG* ffmpeg = *it; SrsFFMPEG* ffmpeg = *it;
@ -371,6 +436,12 @@ int SrsEncoder::parse_transcode(SrsConfDirective* conf)
if ((ret = ffmpeg->initialize(vhost, port, app, stream, engine)) != ERROR_SUCCESS) { if ((ret = ffmpeg->initialize(vhost, port, app, stream, engine)) != ERROR_SUCCESS) {
srs_freep(ffmpeg); srs_freep(ffmpeg);
// if got a loop, donot transcode the whole stream.
if (ret == ERROR_ENCODER_LOOP) {
clear_engines();
break;
}
srs_error("invalid transcode engine: %s %s", srs_error("invalid transcode engine: %s %s",
conf->arg0().c_str(), engine->arg0().c_str()); conf->arg0().c_str(), engine->arg0().c_str());
return ret; return ret;

@ -95,6 +95,8 @@ public:
virtual int on_publish(std::string vhost, std::string port, std::string app, std::string stream); virtual int on_publish(std::string vhost, std::string port, std::string app, std::string stream);
virtual void on_unpublish(); virtual void on_unpublish();
private: private:
virtual int parse_scope_engines();
virtual void clear_engines();
virtual SrsFFMPEG* at(int index); virtual SrsFFMPEG* at(int index);
virtual int parse_transcode(SrsConfDirective* conf); virtual int parse_transcode(SrsConfDirective* conf);
virtual int cycle(); virtual int cycle();

@ -136,5 +136,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define ERROR_ENCODER_VFPS 711 #define ERROR_ENCODER_VFPS 711
#define ERROR_ENCODER_VBITRATE 712 #define ERROR_ENCODER_VBITRATE 712
#define ERROR_ENCODER_FORK 713 #define ERROR_ENCODER_FORK 713
#define ERROR_ENCODER_LOOP 714
#endif #endif
Loading…
Cancel
Save