winlin

support audio transcode only, speex/mp3 to aac

... ... @@ -126,7 +126,7 @@ Supported operating systems and hardware:
16. support live stream transcoding by ffmpeg.<br/>
17. support live stream transcoding by ffmpeg.<br/>
18. support ffmpeg filters(logo/overlay/crop), x264 params.<br/>
19. [dev] support audio transcode only, speex/mp3 to aac<br/>
19. support audio transcode only, speex/mp3 to aac<br/>
20. [plan] support network based cli and json result.<br/>
21. [plan] support http callback api hooks(for authentication).<br/>
22. [plan] support bandwidth test api and flash client.<br/>
... ...
... ... @@ -15,6 +15,7 @@ log_dir ./objs/logs;
# default: 2000
max_connections 2000;
# vhost list, the __defaultVhost__ is the default vhost
# for example, user use ip to access the stream: rtmp://192.168.1.2/live/livestream.
# for which cannot identify the required vhost.
# for default demo.
vhost __defaultVhost__ {
... ... @@ -44,7 +45,7 @@ vhost __defaultVhost__ {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ... @@ -90,7 +91,7 @@ vhost dev {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine dev {
enabled on;
enabled off;
vfilter {
}
vcodec libx264;
... ... @@ -104,7 +105,18 @@ vhost dev {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
engine dev_acodec {
enabled on;
vcodec copy;
acodec libaacplus;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ... @@ -134,7 +146,7 @@ vhost mirror.transcode.vhost.com {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ... @@ -164,7 +176,7 @@ vhost drawtext.transcode.vhost.com {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ... @@ -194,7 +206,7 @@ vhost crop.transcode.vhost.com {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ... @@ -224,7 +236,7 @@ vhost logo.transcode.vhost.com {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ... @@ -233,6 +245,40 @@ vhost logo.transcode.vhost.com {
}
}
}
# audio transcode only.
# for example, FMLE publish audio codec in mp3, and donot support HLS output,
# we can transcode the audio to aac and copy video to the new stream with HLS.
vhost audio.transcode.vhost.com {
transcode {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine acodec {
enabled on;
vcodec copy;
acodec libaacplus;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
}
}
# ffmpeg-copy(forward implements by ffmpeg).
# copy the video and audio to a new stream.
vhost copy.transcode.vhost.com {
transcode {
enabled on;
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
engine copy {
enabled on;
vcodec copy;
acodec copy;
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
}
}
# transcode all app and stream of vhost
vhost all.transcode.vhost.com {
# the streaming transcode configs.
... ... @@ -258,7 +304,9 @@ vhost all.transcode.vhost.com {
# for filters, @see: http://ffmpeg.org/ffmpeg-filters.html
filter_complex 'overlay=10:10';
}
# video encoder name
# video encoder name. can be:
# libx264: use h.264(libx264) video encoder.
# copy: donot encoder the video stream, copy it.
vcodec libx264;
# video bitrate, in kbps
vbitrate 1500;
... ... @@ -287,7 +335,9 @@ vhost all.transcode.vhost.com {
bf 3;
refs 10;
}
# audio encoder name
# audio encoder name. can be:
# libaacplus: use aac(libaacplus) audio encoder.
# copy: donot encoder the audio stream, copy it.
acodec libaacplus;
# audio bitrate, in kbps. [16, 72] for libaacplus.
abitrate 70;
... ... @@ -362,13 +412,45 @@ vhost all.transcode.vhost.com {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
engine vcopy {
enabled on;
vcodec copy;
acodec libaacplus;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
}
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
engine acopy {
enabled on;
vcodec libx264;
vbitrate 300;
vfps 20;
vwidth 768;
vheight 320;
vthreads 2;
vprofile baseline;
vpreset superfast;
vparams {
}
acodec copy;
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
engine copy {
enabled on;
vcodec copy;
acodec copy;
output rtmp://127.0.0.1:[port]/[app]?vhost=[vhost]/[stream]_[engine];
}
}
}
# transcode all stream using the empty ffmpeg demo, donothing.
... ... @@ -389,7 +471,7 @@ vhost ffempty.transcode.vhost.com {
vparams {
}
acodec libaacplus;
abitrate 30;
abitrate 45;
asample_rate 44100;
achannels 2;
aparams {
... ...
... ... @@ -42,8 +42,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#define SRS_ENCODER_SLEEP_MS 2000
#define SRS_ENCODER_VCODEC "libx264"
#define SRS_ENCODER_ACODEC "libaacplus"
#define SRS_ENCODER_COPY "copy"
#define SRS_ENCODER_VCODEC "libx264"
#define SRS_ENCODER_ACODEC "libaacplus"
// for encoder to detect the dead loop
static std::vector<std::string> _transcoded_url;
... ... @@ -138,70 +139,75 @@ int SrsFFMPEG::initialize(SrsRequest* req, SrsConfDirective* engine)
}
_transcoded_url.push_back(output);
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_ACODEC) {
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 (vcodec != SRS_ENCODER_COPY) {
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 (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 (acodec != SRS_ENCODER_COPY) {
if (acodec != SRS_ENCODER_ACODEC) {
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;
... ... @@ -252,40 +258,43 @@ int SrsFFMPEG::start()
params.push_back("-vcodec");
params.push_back(vcodec);
params.push_back("-b:v");
snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000);
params.push_back(tmp);
params.push_back("-r");
snprintf(tmp, sizeof(tmp), "%.2f", vfps);
params.push_back(tmp);
params.push_back("-s");
snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight);
params.push_back(tmp);
// TODO: add aspect if needed.
params.push_back("-aspect");
snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight);
params.push_back(tmp);
params.push_back("-threads");
snprintf(tmp, sizeof(tmp), "%d", vthreads);
params.push_back(tmp);
params.push_back("-profile:v");
params.push_back(vprofile);
params.push_back("-preset");
params.push_back(vpreset);
// vparams
if (!vparams.empty()) {
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);
// the codec params is disabled when copy
if (vcodec != SRS_ENCODER_COPY) {
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);
}
}
}
}
... ... @@ -294,25 +303,28 @@ int SrsFFMPEG::start()
params.push_back("-acodec");
params.push_back(acodec);
params.push_back("-b:a");
snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000);
params.push_back(tmp);
params.push_back("-ar");
snprintf(tmp, sizeof(tmp), "%d", asample_rate);
params.push_back(tmp);
params.push_back("-ac");
snprintf(tmp, sizeof(tmp), "%d", achannels);
params.push_back(tmp);
// aparams
if (!aparams.empty()) {
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);
// the codec params is disabled when copy
if (acodec != SRS_ENCODER_COPY) {
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);
}
}
}
}
... ...