mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
refine ffmepg and encoder, extract ffmpeg.
This commit is contained in:
parent
095364a72b
commit
72c330d47c
6 changed files with 593 additions and 509 deletions
3
trunk/configure
vendored
3
trunk/configure
vendored
|
@ -435,7 +435,8 @@ MODULE_FILES=("srs_app_server" "srs_app_conn" "srs_app_rtmp_conn" "srs_app_socke
|
||||||
"srs_app_codec" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder"
|
"srs_app_codec" "srs_app_refer" "srs_app_hls" "srs_app_forward" "srs_app_encoder"
|
||||||
"srs_app_http" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log"
|
"srs_app_http" "srs_app_thread" "srs_app_bandwidth" "srs_app_st" "srs_app_log"
|
||||||
"srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api"
|
"srs_app_config" "srs_app_pithy_print" "srs_app_reload" "srs_app_http_api"
|
||||||
"srs_app_http_conn" "srs_app_http_hooks" "srs_app_json" "srs_app_ingest")
|
"srs_app_http_conn" "srs_app_http_hooks" "srs_app_json" "srs_app_ingest"
|
||||||
|
"srs_app_ffmpeg")
|
||||||
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
|
APP_INCS="src/app"; MODULE_DIR=${APP_INCS} . auto/modules.sh
|
||||||
APP_OBJS="${MODULE_OBJS[@]}"
|
APP_OBJS="${MODULE_OBJS[@]}"
|
||||||
#
|
#
|
||||||
|
|
|
@ -23,485 +23,19 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include <srs_app_encoder.hpp>
|
#include <srs_app_encoder.hpp>
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <srs_kernel_error.hpp>
|
#include <srs_kernel_error.hpp>
|
||||||
#include <srs_kernel_log.hpp>
|
#include <srs_kernel_log.hpp>
|
||||||
#include <srs_app_config.hpp>
|
#include <srs_app_config.hpp>
|
||||||
#include <srs_protocol_rtmp.hpp>
|
#include <srs_protocol_rtmp.hpp>
|
||||||
#include <srs_app_pithy_print.hpp>
|
#include <srs_app_pithy_print.hpp>
|
||||||
#include <srs_protocol_rtmp_stack.hpp>
|
#include <srs_protocol_rtmp_stack.hpp>
|
||||||
|
#include <srs_app_ffmpeg.hpp>
|
||||||
|
|
||||||
#ifdef SRS_TRANSCODE
|
#ifdef SRS_TRANSCODE
|
||||||
|
|
||||||
#define SRS_ENCODER_COPY "copy"
|
|
||||||
#define SRS_ENCODER_NO_VIDEO "vn"
|
|
||||||
#define SRS_ENCODER_NO_AUDIO "an"
|
|
||||||
// only support libx264 encoder.
|
|
||||||
#define SRS_ENCODER_VCODEC "libx264"
|
|
||||||
// any aac encoder is ok which contains the aac,
|
|
||||||
// for example, libaacplus, aac, fdkaac
|
|
||||||
#define SRS_ENCODER_ACODEC "aac"
|
|
||||||
|
|
||||||
// when error, encoder sleep for a while and retry.
|
// when error, encoder sleep for a while and retry.
|
||||||
#define SRS_ENCODER_SLEEP_US (int64_t)(3*1000*1000LL)
|
#define SRS_ENCODER_SLEEP_US (int64_t)(3*1000*1000LL)
|
||||||
|
|
||||||
// for encoder to detect the dead loop
|
|
||||||
static std::vector<std::string> _transcoded_url;
|
|
||||||
|
|
||||||
SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin)
|
|
||||||
{
|
|
||||||
started = false;
|
|
||||||
pid = -1;
|
|
||||||
ffmpeg = ffmpeg_bin;
|
|
||||||
|
|
||||||
vbitrate = 0;
|
|
||||||
vfps = 0;
|
|
||||||
vwidth = 0;
|
|
||||||
vheight = 0;
|
|
||||||
vthreads = 0;
|
|
||||||
abitrate = 0;
|
|
||||||
asample_rate = 0;
|
|
||||||
achannels = 0;
|
|
||||||
|
|
||||||
log_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsFFMPEG::~SrsFFMPEG()
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsFFMPEG::initialize(SrsRequest* req, SrsConfDirective* engine)
|
|
||||||
{
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
_srs_config->get_engine_vfilter(engine, vfilter);
|
|
||||||
vcodec = _srs_config->get_engine_vcodec(engine);
|
|
||||||
vbitrate = _srs_config->get_engine_vbitrate(engine);
|
|
||||||
vfps = _srs_config->get_engine_vfps(engine);
|
|
||||||
vwidth = _srs_config->get_engine_vwidth(engine);
|
|
||||||
vheight = _srs_config->get_engine_vheight(engine);
|
|
||||||
vthreads = _srs_config->get_engine_vthreads(engine);
|
|
||||||
vprofile = _srs_config->get_engine_vprofile(engine);
|
|
||||||
vpreset = _srs_config->get_engine_vpreset(engine);
|
|
||||||
_srs_config->get_engine_vparams(engine, vparams);
|
|
||||||
acodec = _srs_config->get_engine_acodec(engine);
|
|
||||||
abitrate = _srs_config->get_engine_abitrate(engine);
|
|
||||||
asample_rate = _srs_config->get_engine_asample_rate(engine);
|
|
||||||
achannels = _srs_config->get_engine_achannels(engine);
|
|
||||||
_srs_config->get_engine_aparams(engine, aparams);
|
|
||||||
output = _srs_config->get_engine_output(engine);
|
|
||||||
|
|
||||||
// ensure the size is even.
|
|
||||||
vwidth -= vwidth % 2;
|
|
||||||
vheight -= vheight % 2;
|
|
||||||
|
|
||||||
// input stream, from local.
|
|
||||||
// ie. rtmp://127.0.0.1:1935/live/livestream
|
|
||||||
input = "rtmp://127.0.0.1:";
|
|
||||||
input += req->port;
|
|
||||||
input += "/";
|
|
||||||
input += req->app;
|
|
||||||
input += "?vhost=";
|
|
||||||
input += req->vhost;
|
|
||||||
input += "/";
|
|
||||||
input += req->stream;
|
|
||||||
|
|
||||||
// output stream, to other/self server
|
|
||||||
// ie. rtmp://127.0.0.1:1935/live/livestream_sd
|
|
||||||
output = srs_string_replace(output, "[vhost]", req->vhost);
|
|
||||||
output = srs_string_replace(output, "[port]", req->port);
|
|
||||||
output = srs_string_replace(output, "[app]", req->app);
|
|
||||||
output = srs_string_replace(output, "[stream]", req->stream);
|
|
||||||
output = srs_string_replace(output, "[engine]", engine->arg0());
|
|
||||||
|
|
||||||
// write ffmpeg info to log file.
|
|
||||||
log_file = _srs_config->get_ffmpeg_log_dir();
|
|
||||||
log_file += "/";
|
|
||||||
log_file += "encoder";
|
|
||||||
log_file += "-";
|
|
||||||
log_file += req->vhost;
|
|
||||||
log_file += "-";
|
|
||||||
log_file += req->app;
|
|
||||||
log_file += "-";
|
|
||||||
log_file += req->stream;
|
|
||||||
log_file += ".log";
|
|
||||||
|
|
||||||
// important: loop check, donot transcode again.
|
|
||||||
std::vector<std::string>::iterator it;
|
|
||||||
it = std::find(_transcoded_url.begin(), _transcoded_url.end(), input);
|
|
||||||
if (it != _transcoded_url.end()) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
_transcoded_url.push_back(output);
|
|
||||||
|
|
||||||
if (vcodec == SRS_ENCODER_NO_VIDEO && acodec == SRS_ENCODER_NO_AUDIO) {
|
|
||||||
ret = ERROR_ENCODER_VCODEC;
|
|
||||||
srs_warn("video and audio disabled. ret=%d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {
|
|
||||||
if (vcodec != SRS_ENCODER_VCODEC) {
|
|
||||||
ret = ERROR_ENCODER_VCODEC;
|
|
||||||
srs_error("invalid vcodec, must be %s, actual %s, ret=%d",
|
|
||||||
SRS_ENCODER_VCODEC, vcodec.c_str(), ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vbitrate <= 0) {
|
|
||||||
ret = ERROR_ENCODER_VBITRATE;
|
|
||||||
srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vfps <= 0) {
|
|
||||||
ret = ERROR_ENCODER_VFPS;
|
|
||||||
srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vwidth <= 0) {
|
|
||||||
ret = ERROR_ENCODER_VWIDTH;
|
|
||||||
srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vheight <= 0) {
|
|
||||||
ret = ERROR_ENCODER_VHEIGHT;
|
|
||||||
srs_error("invalid vheight: %d, ret=%d", vheight, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vthreads < 0) {
|
|
||||||
ret = ERROR_ENCODER_VTHREADS;
|
|
||||||
srs_error("invalid vthreads: %d, ret=%d", vthreads, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vprofile.empty()) {
|
|
||||||
ret = ERROR_ENCODER_VPROFILE;
|
|
||||||
srs_error("invalid vprofile: %s, ret=%d", vprofile.c_str(), ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (vpreset.empty()) {
|
|
||||||
ret = ERROR_ENCODER_VPRESET;
|
|
||||||
srs_error("invalid vpreset: %s, ret=%d", vpreset.c_str(), ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {
|
|
||||||
if (acodec.find(SRS_ENCODER_ACODEC) == std::string::npos) {
|
|
||||||
ret = ERROR_ENCODER_ACODEC;
|
|
||||||
srs_error("invalid acodec, must be %s, actual %s, ret=%d",
|
|
||||||
SRS_ENCODER_ACODEC, acodec.c_str(), ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (abitrate <= 0) {
|
|
||||||
ret = ERROR_ENCODER_ABITRATE;
|
|
||||||
srs_error("invalid abitrate: %d, ret=%d",
|
|
||||||
abitrate, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (asample_rate <= 0) {
|
|
||||||
ret = ERROR_ENCODER_ASAMPLE_RATE;
|
|
||||||
srs_error("invalid sample rate: %d, ret=%d",
|
|
||||||
asample_rate, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (achannels != 1 && achannels != 2) {
|
|
||||||
ret = ERROR_ENCODER_ACHANNELS;
|
|
||||||
srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d",
|
|
||||||
achannels, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (output.empty()) {
|
|
||||||
ret = ERROR_ENCODER_OUTPUT;
|
|
||||||
srs_error("invalid empty output, ret=%d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsFFMPEG::start()
|
|
||||||
{
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
if (started) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prepare exec params
|
|
||||||
char tmp[256];
|
|
||||||
std::vector<std::string> params;
|
|
||||||
|
|
||||||
// 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<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.
|
|
||||||
if (vcodec != SRS_ENCODER_NO_VIDEO) {
|
|
||||||
params.push_back("-vcodec");
|
|
||||||
params.push_back(vcodec);
|
|
||||||
} else {
|
|
||||||
params.push_back("-vn");
|
|
||||||
}
|
|
||||||
|
|
||||||
// the codec params is disabled when copy
|
|
||||||
if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {
|
|
||||||
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()) {
|
|
||||||
std::vector<std::string>::iterator it;
|
|
||||||
for (it = vparams.begin(); it != vparams.end(); ++it) {
|
|
||||||
std::string p = *it;
|
|
||||||
if (!p.empty()) {
|
|
||||||
params.push_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// audio specified.
|
|
||||||
if (acodec != SRS_ENCODER_NO_AUDIO) {
|
|
||||||
params.push_back("-acodec");
|
|
||||||
params.push_back(acodec);
|
|
||||||
} else {
|
|
||||||
params.push_back("-an");
|
|
||||||
}
|
|
||||||
|
|
||||||
// the codec params is disabled when copy
|
|
||||||
if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {
|
|
||||||
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()) {
|
|
||||||
std::vector<std::string>::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);
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
int pparam_size = 8 * 1024;
|
|
||||||
char* pparam = new char[pparam_size];
|
|
||||||
char* p = pparam;
|
|
||||||
char* last = pparam + pparam_size;
|
|
||||||
for (int i = 0; i < (int)params.size(); i++) {
|
|
||||||
std::string ffp = params[i];
|
|
||||||
snprintf(p, last - p, "%s ", ffp.c_str());
|
|
||||||
p += ffp.length() + 1;
|
|
||||||
}
|
|
||||||
srs_trace("start transcoder, log: %s, params: %s",
|
|
||||||
log_file.c_str(), pparam);
|
|
||||||
srs_freepa(pparam);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: fork or vfork?
|
|
||||||
if ((pid = fork()) < 0) {
|
|
||||||
ret = ERROR_ENCODER_FORK;
|
|
||||||
srs_error("vfork process failed. ret=%d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// child process: ffmpeg encoder engine.
|
|
||||||
if (pid == 0) {
|
|
||||||
// redirect logs to file.
|
|
||||||
int flags = O_CREAT|O_WRONLY|O_APPEND;
|
|
||||||
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
|
|
||||||
if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {
|
|
||||||
ret = ERROR_ENCODER_OPEN;
|
|
||||||
srs_error("open encoder file %s failed. ret=%d", log_file.c_str(), ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (dup2(log_fd, STDOUT_FILENO) < 0) {
|
|
||||||
ret = ERROR_ENCODER_DUP2;
|
|
||||||
srs_error("dup2 encoder file failed. ret=%d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
if (dup2(log_fd, STDERR_FILENO) < 0) {
|
|
||||||
ret = ERROR_ENCODER_DUP2;
|
|
||||||
srs_error("dup2 encoder file failed. ret=%d", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
// close other fds
|
|
||||||
// TODO: do in right way.
|
|
||||||
for (int i = 3; i < 1024; i++) {
|
|
||||||
::close(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
exit(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
// parent.
|
|
||||||
if (pid > 0) {
|
|
||||||
started = true;
|
|
||||||
srs_trace("vfored ffmpeg encoder engine, pid=%d", pid);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int SrsFFMPEG::cycle()
|
|
||||||
{
|
|
||||||
int ret = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
if (!started) {
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int status = 0;
|
|
||||||
pid_t p = waitpid(pid, &status, WNOHANG);
|
|
||||||
|
|
||||||
if (p < 0) {
|
|
||||||
ret = ERROR_SYSTEM_WAITPID;
|
|
||||||
srs_error("transcode waitpid failed, pid=%d, ret=%d", pid, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p == 0) {
|
|
||||||
srs_info("transcode process pid=%d is running.", pid);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_trace("transcode process pid=%d terminate, restart it.", pid);
|
|
||||||
started = false;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SrsFFMPEG::stop()
|
|
||||||
{
|
|
||||||
if (log_fd > 0) {
|
|
||||||
::close(log_fd);
|
|
||||||
log_fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!started) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// kill the ffmpeg,
|
|
||||||
// when rewind, upstream will stop publish(unpublish),
|
|
||||||
// unpublish event will stop all ffmpeg encoders,
|
|
||||||
// then publish will start all ffmpeg encoders.
|
|
||||||
if (pid > 0) {
|
|
||||||
if (kill(pid, SIGKILL) < 0) {
|
|
||||||
srs_warn("kill the encoder failed, ignored. pid=%d", pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for the ffmpeg to quit.
|
|
||||||
// ffmpeg will gracefully quit if signal is:
|
|
||||||
// 1) SIGHUP 2) SIGINT 3) SIGQUIT
|
|
||||||
// other signals, directly exit(123), for example:
|
|
||||||
// 9) SIGKILL 15) SIGTERM
|
|
||||||
int status = 0;
|
|
||||||
if (waitpid(pid, &status, 0) < 0) {
|
|
||||||
srs_warn("wait the encoder quit failed, ignored. pid=%d", pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
srs_trace("stop the encoder success. pid=%d", pid);
|
|
||||||
pid = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string>::iterator it;
|
|
||||||
it = std::find(_transcoded_url.begin(), _transcoded_url.end(), output);
|
|
||||||
if (it != _transcoded_url.end()) {
|
|
||||||
_transcoded_url.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SrsEncoder::SrsEncoder()
|
SrsEncoder::SrsEncoder()
|
||||||
{
|
{
|
||||||
pthread = new SrsThread(this, SRS_ENCODER_SLEEP_US);
|
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.
|
||||||
class SrsConfDirective;
|
class SrsConfDirective;
|
||||||
class SrsRequest;
|
class SrsRequest;
|
||||||
class SrsPithyPrint;
|
class SrsPithyPrint;
|
||||||
|
class SrsFFMPEG;
|
||||||
/**
|
|
||||||
* a transcode engine: ffmepg,
|
|
||||||
* used to transcode a stream to another.
|
|
||||||
*/
|
|
||||||
class SrsFFMPEG
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
bool started;
|
|
||||||
pid_t pid;
|
|
||||||
private:
|
|
||||||
std::string log_file;
|
|
||||||
int log_fd;
|
|
||||||
private:
|
|
||||||
std::string ffmpeg;
|
|
||||||
std::vector<std::string> vfilter;
|
|
||||||
std::string vcodec;
|
|
||||||
int vbitrate;
|
|
||||||
double vfps;
|
|
||||||
int vwidth;
|
|
||||||
int vheight;
|
|
||||||
int vthreads;
|
|
||||||
std::string vprofile;
|
|
||||||
std::string vpreset;
|
|
||||||
std::vector<std::string> vparams;
|
|
||||||
std::string acodec;
|
|
||||||
int abitrate;
|
|
||||||
int asample_rate;
|
|
||||||
int achannels;
|
|
||||||
std::vector<std::string> aparams;
|
|
||||||
std::string output;
|
|
||||||
std::string input;
|
|
||||||
public:
|
|
||||||
SrsFFMPEG(std::string ffmpeg_bin);
|
|
||||||
virtual ~SrsFFMPEG();
|
|
||||||
public:
|
|
||||||
virtual int initialize(SrsRequest* req, SrsConfDirective* engine);
|
|
||||||
virtual int start();
|
|
||||||
virtual int cycle();
|
|
||||||
virtual void stop();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the encoder for a stream,
|
* the encoder for a stream,
|
||||||
|
|
503
trunk/src/app/srs_app_ffmpeg.cpp
Normal file
503
trunk/src/app/srs_app_ffmpeg.cpp
Normal file
|
@ -0,0 +1,503 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2014 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <srs_app_ffmpeg.hpp>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <srs_kernel_error.hpp>
|
||||||
|
#include <srs_kernel_log.hpp>
|
||||||
|
#include <srs_app_config.hpp>
|
||||||
|
#include <srs_protocol_rtmp.hpp>
|
||||||
|
#include <srs_app_pithy_print.hpp>
|
||||||
|
#include <srs_protocol_rtmp_stack.hpp>
|
||||||
|
|
||||||
|
#ifdef SRS_FFMPEG
|
||||||
|
|
||||||
|
#define SRS_ENCODER_COPY "copy"
|
||||||
|
#define SRS_ENCODER_NO_VIDEO "vn"
|
||||||
|
#define SRS_ENCODER_NO_AUDIO "an"
|
||||||
|
// only support libx264 encoder.
|
||||||
|
#define SRS_ENCODER_VCODEC "libx264"
|
||||||
|
// any aac encoder is ok which contains the aac,
|
||||||
|
// for example, libaacplus, aac, fdkaac
|
||||||
|
#define SRS_ENCODER_ACODEC "aac"
|
||||||
|
|
||||||
|
// for encoder to detect the dead loop
|
||||||
|
static std::vector<std::string> _transcoded_url;
|
||||||
|
|
||||||
|
SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin)
|
||||||
|
{
|
||||||
|
started = false;
|
||||||
|
pid = -1;
|
||||||
|
ffmpeg = ffmpeg_bin;
|
||||||
|
|
||||||
|
vbitrate = 0;
|
||||||
|
vfps = 0;
|
||||||
|
vwidth = 0;
|
||||||
|
vheight = 0;
|
||||||
|
vthreads = 0;
|
||||||
|
abitrate = 0;
|
||||||
|
asample_rate = 0;
|
||||||
|
achannels = 0;
|
||||||
|
|
||||||
|
log_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SrsFFMPEG::~SrsFFMPEG()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsFFMPEG::initialize(SrsRequest* req, SrsConfDirective* engine)
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
_srs_config->get_engine_vfilter(engine, vfilter);
|
||||||
|
vcodec = _srs_config->get_engine_vcodec(engine);
|
||||||
|
vbitrate = _srs_config->get_engine_vbitrate(engine);
|
||||||
|
vfps = _srs_config->get_engine_vfps(engine);
|
||||||
|
vwidth = _srs_config->get_engine_vwidth(engine);
|
||||||
|
vheight = _srs_config->get_engine_vheight(engine);
|
||||||
|
vthreads = _srs_config->get_engine_vthreads(engine);
|
||||||
|
vprofile = _srs_config->get_engine_vprofile(engine);
|
||||||
|
vpreset = _srs_config->get_engine_vpreset(engine);
|
||||||
|
_srs_config->get_engine_vparams(engine, vparams);
|
||||||
|
acodec = _srs_config->get_engine_acodec(engine);
|
||||||
|
abitrate = _srs_config->get_engine_abitrate(engine);
|
||||||
|
asample_rate = _srs_config->get_engine_asample_rate(engine);
|
||||||
|
achannels = _srs_config->get_engine_achannels(engine);
|
||||||
|
_srs_config->get_engine_aparams(engine, aparams);
|
||||||
|
output = _srs_config->get_engine_output(engine);
|
||||||
|
|
||||||
|
// ensure the size is even.
|
||||||
|
vwidth -= vwidth % 2;
|
||||||
|
vheight -= vheight % 2;
|
||||||
|
|
||||||
|
// input stream, from local.
|
||||||
|
// ie. rtmp://127.0.0.1:1935/live/livestream
|
||||||
|
input = "rtmp://127.0.0.1:";
|
||||||
|
input += req->port;
|
||||||
|
input += "/";
|
||||||
|
input += req->app;
|
||||||
|
input += "?vhost=";
|
||||||
|
input += req->vhost;
|
||||||
|
input += "/";
|
||||||
|
input += req->stream;
|
||||||
|
|
||||||
|
// output stream, to other/self server
|
||||||
|
// ie. rtmp://127.0.0.1:1935/live/livestream_sd
|
||||||
|
output = srs_string_replace(output, "[vhost]", req->vhost);
|
||||||
|
output = srs_string_replace(output, "[port]", req->port);
|
||||||
|
output = srs_string_replace(output, "[app]", req->app);
|
||||||
|
output = srs_string_replace(output, "[stream]", req->stream);
|
||||||
|
output = srs_string_replace(output, "[engine]", engine->arg0());
|
||||||
|
|
||||||
|
// write ffmpeg info to log file.
|
||||||
|
log_file = _srs_config->get_ffmpeg_log_dir();
|
||||||
|
log_file += "/";
|
||||||
|
log_file += "encoder";
|
||||||
|
log_file += "-";
|
||||||
|
log_file += req->vhost;
|
||||||
|
log_file += "-";
|
||||||
|
log_file += req->app;
|
||||||
|
log_file += "-";
|
||||||
|
log_file += req->stream;
|
||||||
|
log_file += ".log";
|
||||||
|
|
||||||
|
// important: loop check, donot transcode again.
|
||||||
|
std::vector<std::string>::iterator it;
|
||||||
|
it = std::find(_transcoded_url.begin(), _transcoded_url.end(), input);
|
||||||
|
if (it != _transcoded_url.end()) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
_transcoded_url.push_back(output);
|
||||||
|
|
||||||
|
if (vcodec == SRS_ENCODER_NO_VIDEO && acodec == SRS_ENCODER_NO_AUDIO) {
|
||||||
|
ret = ERROR_ENCODER_VCODEC;
|
||||||
|
srs_warn("video and audio disabled. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {
|
||||||
|
if (vcodec != SRS_ENCODER_VCODEC) {
|
||||||
|
ret = ERROR_ENCODER_VCODEC;
|
||||||
|
srs_error("invalid vcodec, must be %s, actual %s, ret=%d",
|
||||||
|
SRS_ENCODER_VCODEC, vcodec.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vbitrate <= 0) {
|
||||||
|
ret = ERROR_ENCODER_VBITRATE;
|
||||||
|
srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vfps <= 0) {
|
||||||
|
ret = ERROR_ENCODER_VFPS;
|
||||||
|
srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vwidth <= 0) {
|
||||||
|
ret = ERROR_ENCODER_VWIDTH;
|
||||||
|
srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vheight <= 0) {
|
||||||
|
ret = ERROR_ENCODER_VHEIGHT;
|
||||||
|
srs_error("invalid vheight: %d, ret=%d", vheight, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vthreads < 0) {
|
||||||
|
ret = ERROR_ENCODER_VTHREADS;
|
||||||
|
srs_error("invalid vthreads: %d, ret=%d", vthreads, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vprofile.empty()) {
|
||||||
|
ret = ERROR_ENCODER_VPROFILE;
|
||||||
|
srs_error("invalid vprofile: %s, ret=%d", vprofile.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (vpreset.empty()) {
|
||||||
|
ret = ERROR_ENCODER_VPRESET;
|
||||||
|
srs_error("invalid vpreset: %s, ret=%d", vpreset.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {
|
||||||
|
if (acodec.find(SRS_ENCODER_ACODEC) == std::string::npos) {
|
||||||
|
ret = ERROR_ENCODER_ACODEC;
|
||||||
|
srs_error("invalid acodec, must be %s, actual %s, ret=%d",
|
||||||
|
SRS_ENCODER_ACODEC, acodec.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (abitrate <= 0) {
|
||||||
|
ret = ERROR_ENCODER_ABITRATE;
|
||||||
|
srs_error("invalid abitrate: %d, ret=%d",
|
||||||
|
abitrate, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (asample_rate <= 0) {
|
||||||
|
ret = ERROR_ENCODER_ASAMPLE_RATE;
|
||||||
|
srs_error("invalid sample rate: %d, ret=%d",
|
||||||
|
asample_rate, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (achannels != 1 && achannels != 2) {
|
||||||
|
ret = ERROR_ENCODER_ACHANNELS;
|
||||||
|
srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d",
|
||||||
|
achannels, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (output.empty()) {
|
||||||
|
ret = ERROR_ENCODER_OUTPUT;
|
||||||
|
srs_error("invalid empty output, ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsFFMPEG::start()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepare exec params
|
||||||
|
char tmp[256];
|
||||||
|
std::vector<std::string> params;
|
||||||
|
|
||||||
|
// 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<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.
|
||||||
|
if (vcodec != SRS_ENCODER_NO_VIDEO) {
|
||||||
|
params.push_back("-vcodec");
|
||||||
|
params.push_back(vcodec);
|
||||||
|
} else {
|
||||||
|
params.push_back("-vn");
|
||||||
|
}
|
||||||
|
|
||||||
|
// the codec params is disabled when copy
|
||||||
|
if (vcodec != SRS_ENCODER_COPY && vcodec != SRS_ENCODER_NO_VIDEO) {
|
||||||
|
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()) {
|
||||||
|
std::vector<std::string>::iterator it;
|
||||||
|
for (it = vparams.begin(); it != vparams.end(); ++it) {
|
||||||
|
std::string p = *it;
|
||||||
|
if (!p.empty()) {
|
||||||
|
params.push_back(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// audio specified.
|
||||||
|
if (acodec != SRS_ENCODER_NO_AUDIO) {
|
||||||
|
params.push_back("-acodec");
|
||||||
|
params.push_back(acodec);
|
||||||
|
} else {
|
||||||
|
params.push_back("-an");
|
||||||
|
}
|
||||||
|
|
||||||
|
// the codec params is disabled when copy
|
||||||
|
if (acodec != SRS_ENCODER_COPY && acodec != SRS_ENCODER_NO_AUDIO) {
|
||||||
|
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()) {
|
||||||
|
std::vector<std::string>::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);
|
||||||
|
|
||||||
|
if (true) {
|
||||||
|
int pparam_size = 8 * 1024;
|
||||||
|
char* pparam = new char[pparam_size];
|
||||||
|
char* p = pparam;
|
||||||
|
char* last = pparam + pparam_size;
|
||||||
|
for (int i = 0; i < (int)params.size(); i++) {
|
||||||
|
std::string ffp = params[i];
|
||||||
|
snprintf(p, last - p, "%s ", ffp.c_str());
|
||||||
|
p += ffp.length() + 1;
|
||||||
|
}
|
||||||
|
srs_trace("start transcoder, log: %s, params: %s",
|
||||||
|
log_file.c_str(), pparam);
|
||||||
|
srs_freepa(pparam);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: fork or vfork?
|
||||||
|
if ((pid = fork()) < 0) {
|
||||||
|
ret = ERROR_ENCODER_FORK;
|
||||||
|
srs_error("vfork process failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// child process: ffmpeg encoder engine.
|
||||||
|
if (pid == 0) {
|
||||||
|
// redirect logs to file.
|
||||||
|
int flags = O_CREAT|O_WRONLY|O_APPEND;
|
||||||
|
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
|
||||||
|
if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) {
|
||||||
|
ret = ERROR_ENCODER_OPEN;
|
||||||
|
srs_error("open encoder file %s failed. ret=%d", log_file.c_str(), ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (dup2(log_fd, STDOUT_FILENO) < 0) {
|
||||||
|
ret = ERROR_ENCODER_DUP2;
|
||||||
|
srs_error("dup2 encoder file failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (dup2(log_fd, STDERR_FILENO) < 0) {
|
||||||
|
ret = ERROR_ENCODER_DUP2;
|
||||||
|
srs_error("dup2 encoder file failed. ret=%d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
// close other fds
|
||||||
|
// TODO: do in right way.
|
||||||
|
for (int i = 3; i < 1024; i++) {
|
||||||
|
::close(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// parent.
|
||||||
|
if (pid > 0) {
|
||||||
|
started = true;
|
||||||
|
srs_trace("vfored ffmpeg encoder engine, pid=%d", pid);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SrsFFMPEG::cycle()
|
||||||
|
{
|
||||||
|
int ret = ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
pid_t p = waitpid(pid, &status, WNOHANG);
|
||||||
|
|
||||||
|
if (p < 0) {
|
||||||
|
ret = ERROR_SYSTEM_WAITPID;
|
||||||
|
srs_error("transcode waitpid failed, pid=%d, ret=%d", pid, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == 0) {
|
||||||
|
srs_info("transcode process pid=%d is running.", pid);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("transcode process pid=%d terminate, restart it.", pid);
|
||||||
|
started = false;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SrsFFMPEG::stop()
|
||||||
|
{
|
||||||
|
if (log_fd > 0) {
|
||||||
|
::close(log_fd);
|
||||||
|
log_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// kill the ffmpeg,
|
||||||
|
// when rewind, upstream will stop publish(unpublish),
|
||||||
|
// unpublish event will stop all ffmpeg encoders,
|
||||||
|
// then publish will start all ffmpeg encoders.
|
||||||
|
if (pid > 0) {
|
||||||
|
if (kill(pid, SIGKILL) < 0) {
|
||||||
|
srs_warn("kill the encoder failed, ignored. pid=%d", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the ffmpeg to quit.
|
||||||
|
// ffmpeg will gracefully quit if signal is:
|
||||||
|
// 1) SIGHUP 2) SIGINT 3) SIGQUIT
|
||||||
|
// other signals, directly exit(123), for example:
|
||||||
|
// 9) SIGKILL 15) SIGTERM
|
||||||
|
int status = 0;
|
||||||
|
if (waitpid(pid, &status, 0) < 0) {
|
||||||
|
srs_warn("wait the encoder quit failed, ignored. pid=%d", pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_trace("stop the encoder success. pid=%d", pid);
|
||||||
|
pid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string>::iterator it;
|
||||||
|
it = std::find(_transcoded_url.begin(), _transcoded_url.end(), output);
|
||||||
|
if (it != _transcoded_url.end()) {
|
||||||
|
_transcoded_url.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
84
trunk/src/app/srs_app_ffmpeg.hpp
Normal file
84
trunk/src/app/srs_app_ffmpeg.hpp
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2013-2014 winlin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SRS_APP_FFMPEG_HPP
|
||||||
|
#define SRS_APP_FFMPEG_HPP
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <srs_app_ffmpeg.hpp>
|
||||||
|
*/
|
||||||
|
#include <srs_core.hpp>
|
||||||
|
|
||||||
|
#ifdef SRS_FFMPEG
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class SrsConfDirective;
|
||||||
|
class SrsRequest;
|
||||||
|
class SrsPithyPrint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a transcode engine: ffmepg,
|
||||||
|
* used to transcode a stream to another.
|
||||||
|
*/
|
||||||
|
class SrsFFMPEG
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool started;
|
||||||
|
pid_t pid;
|
||||||
|
private:
|
||||||
|
std::string log_file;
|
||||||
|
int log_fd;
|
||||||
|
private:
|
||||||
|
std::string ffmpeg;
|
||||||
|
std::vector<std::string> vfilter;
|
||||||
|
std::string vcodec;
|
||||||
|
int vbitrate;
|
||||||
|
double vfps;
|
||||||
|
int vwidth;
|
||||||
|
int vheight;
|
||||||
|
int vthreads;
|
||||||
|
std::string vprofile;
|
||||||
|
std::string vpreset;
|
||||||
|
std::vector<std::string> vparams;
|
||||||
|
std::string acodec;
|
||||||
|
int abitrate;
|
||||||
|
int asample_rate;
|
||||||
|
int achannels;
|
||||||
|
std::vector<std::string> aparams;
|
||||||
|
std::string output;
|
||||||
|
std::string input;
|
||||||
|
public:
|
||||||
|
SrsFFMPEG(std::string ffmpeg_bin);
|
||||||
|
virtual ~SrsFFMPEG();
|
||||||
|
public:
|
||||||
|
virtual int initialize(SrsRequest* req, SrsConfDirective* engine);
|
||||||
|
virtual int start();
|
||||||
|
virtual int cycle();
|
||||||
|
virtual void stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -49,6 +49,8 @@ file
|
||||||
..\app\srs_app_config.cpp,
|
..\app\srs_app_config.cpp,
|
||||||
..\app\srs_app_encoder.hpp,
|
..\app\srs_app_encoder.hpp,
|
||||||
..\app\srs_app_encoder.cpp,
|
..\app\srs_app_encoder.cpp,
|
||||||
|
..\app\srs_app_ffmpeg.hpp,
|
||||||
|
..\app\srs_app_ffmpeg.cpp,
|
||||||
..\app\srs_app_forward.hpp,
|
..\app\srs_app_forward.hpp,
|
||||||
..\app\srs_app_forward.cpp,
|
..\app\srs_app_forward.cpp,
|
||||||
..\app\srs_app_hls.hpp,
|
..\app\srs_app_hls.hpp,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue