diff --git a/README.md b/README.md
index c2af1518b..374ee7a72 100755
--- a/README.md
+++ b/README.md
@@ -126,7 +126,7 @@ Supported operating systems and hardware:
16. support live stream transcoding by ffmpeg.
17. support live stream transcoding by ffmpeg.
18. support ffmpeg filters(logo/overlay/crop), x264 params.
-19. [dev] support audio transcode only, speex/mp3 to aac
+19. support audio transcode only, speex/mp3 to aac
20. [plan] support network based cli and json result.
21. [plan] support http callback api hooks(for authentication).
22. [plan] support bandwidth test api and flash client.
diff --git a/trunk/conf/srs.conf b/trunk/conf/srs.conf
index 94c385c67..5d6864bc9 100755
--- a/trunk/conf/srs.conf
+++ b/trunk/conf/srs.conf
@@ -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 {
diff --git a/trunk/src/core/srs_core_encoder.cpp b/trunk/src/core/srs_core_encoder.cpp
index d0be700dd..3400948a5 100644
--- a/trunk/src/core/srs_core_encoder.cpp
+++ b/trunk/src/core/srs_core_encoder.cpp
@@ -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 _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 (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 (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 (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::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::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::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::iterator it;
+ for (it = aparams.begin(); it != aparams.end(); ++it) {
+ std::string p = *it;
+ if (!p.empty()) {
+ params.push_back(p);
+ }
}
}
}