From 331ef9ffaedf4926abd556ea8b69ce24b6fff5ca Mon Sep 17 00:00:00 2001 From: jb-alvarado <2212056+jb-alvarado@users.noreply.github.com> Date: Sat, 27 Jul 2024 04:29:54 +0200 Subject: [PATCH] Transcode: Support video codec such as h264_qsv and libx265. v6.0.145 (#4127) Currently only libx264 ffmpeg encoder is supported. This pull request add also h264_qsv. But maybe a more generic solution with oder encoders would be useful to. --------- Co-authored-by: winlin --- trunk/doc/CHANGELOG.md | 1 + trunk/src/app/srs_app_ffmpeg.cpp | 98 +++++++++++++++------------- trunk/src/core/srs_core_version6.hpp | 2 +- 3 files changed, 53 insertions(+), 48 deletions(-) diff --git a/trunk/doc/CHANGELOG.md b/trunk/doc/CHANGELOG.md index ce56b82aa..fc576ec7c 100644 --- a/trunk/doc/CHANGELOG.md +++ b/trunk/doc/CHANGELOG.md @@ -7,6 +7,7 @@ The changelog for SRS. ## SRS 6.0 Changelog +* v6.0, 2024-07-27, Merge [#4127](https://github.com/ossrs/srs/pull/4127): Transcode: Support video codec such as h264_qsv and libx265. v6.0.145 (#4127) * v6.0, 2024-07-27, Merge [#4101](https://github.com/ossrs/srs/pull/4101): GB28181: Support external SIP server. v6.0.144 (#4101) * v6.0, 2024-07-24, Merge [#4115](https://github.com/ossrs/srs/pull/4115): HLS: Add missing newline to end of session manifest. v6.0.143 (#4115) * v6.0, 2024-07-24, Merge [#4029](https://github.com/ossrs/srs/pull/4029): Player: Fix empty img tag occupy 20px size in safari. v6.0.142 (#4029) diff --git a/trunk/src/app/srs_app_ffmpeg.cpp b/trunk/src/app/srs_app_ffmpeg.cpp index f3ee57a30..d4cb94857 100644 --- a/trunk/src/app/srs_app_ffmpeg.cpp +++ b/trunk/src/app/srs_app_ffmpeg.cpp @@ -33,8 +33,10 @@ using namespace std; #define SRS_RTMP_ENCODER_COPY "copy" #define SRS_RTMP_ENCODER_NO_VIDEO "vn" #define SRS_RTMP_ENCODER_NO_AUDIO "an" -// only support libx264 encoder. +// only support libx264, libx265 and h264_qsv encoder. #define SRS_RTMP_ENCODER_VCODEC_LIBX264 "libx264" +#define SRS_RTMP_ENCODER_VCODEC_LIBX265 "libx265" +#define SRS_RTMP_ENCODER_VCODEC_H264QSV "h264_qsv" #define SRS_RTMP_ENCODER_VCODEC_PNG "png" // any aac encoder is ok which contains the aac, // for example, libaacplus, aac, fdkaac @@ -45,7 +47,7 @@ using namespace std; SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin) { ffmpeg = ffmpeg_bin; - + vbitrate = 0; vfps = 0; vwidth = 0; @@ -54,14 +56,14 @@ SrsFFMPEG::SrsFFMPEG(std::string ffmpeg_bin) abitrate = 0; asample_rate = 0; achannels = 0; - + process = new SrsProcess(); } SrsFFMPEG::~SrsFFMPEG() { stop(); - + srs_freep(process); } @@ -83,18 +85,18 @@ string SrsFFMPEG::output() srs_error_t SrsFFMPEG::initialize(string in, string out, string log) { srs_error_t err = srs_success; - + input = in; _output = out; log_file = log; - + return err; } srs_error_t SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) { srs_error_t err = srs_success; - + perfile = _srs_config->get_engine_perfile(engine); iformat = _srs_config->get_engine_iformat(engine); vfilter = _srs_config->get_engine_vfilter(engine); @@ -113,18 +115,20 @@ srs_error_t SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) achannels = _srs_config->get_engine_achannels(engine); aparams = _srs_config->get_engine_aparams(engine); oformat = _srs_config->get_engine_oformat(engine); - + // ensure the size is even. vwidth -= vwidth % 2; vheight -= vheight % 2; - + if (vcodec == SRS_RTMP_ENCODER_NO_VIDEO && acodec == SRS_RTMP_ENCODER_NO_AUDIO) { return srs_error_new(ERROR_ENCODER_VCODEC, "video and audio disabled"); } - + if (vcodec != SRS_RTMP_ENCODER_COPY && vcodec != SRS_RTMP_ENCODER_NO_VIDEO && vcodec != SRS_RTMP_ENCODER_VCODEC_PNG) { - if (vcodec != SRS_RTMP_ENCODER_VCODEC_LIBX264) { - return srs_error_new(ERROR_ENCODER_VCODEC, "invalid vcodec, must be %s, actual %s", SRS_RTMP_ENCODER_VCODEC_LIBX264, vcodec.c_str()); + if (vcodec != SRS_RTMP_ENCODER_VCODEC_LIBX264 && vcodec != SRS_RTMP_ENCODER_VCODEC_LIBX265 && vcodec != SRS_RTMP_ENCODER_VCODEC_H264QSV) { + return srs_error_new( + ERROR_ENCODER_VCODEC, "invalid vcodec, must be %s, %s or %s, actual %s", + SRS_RTMP_ENCODER_VCODEC_LIBX264, SRS_RTMP_ENCODER_VCODEC_LIBX265, SRS_RTMP_ENCODER_VCODEC_H264QSV, vcodec.c_str()); } if (vbitrate < 0) { return srs_error_new(ERROR_ENCODER_VBITRATE, "invalid vbitrate: %d", vbitrate); @@ -148,14 +152,14 @@ srs_error_t SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) return srs_error_new(ERROR_ENCODER_VPRESET, "invalid vpreset: %s", vpreset.c_str()); } } - + // @see, https://github.com/ossrs/srs/issues/145 if (acodec == SRS_RTMP_ENCODER_LIBAACPLUS && acodec != SRS_RTMP_ENCODER_LIBFDKAAC) { if (abitrate != 0 && (abitrate < 16 || abitrate > 72)) { return srs_error_new(ERROR_ENCODER_ABITRATE, "invalid abitrate for aac: %d, must in [16, 72]", abitrate); } } - + if (acodec != SRS_RTMP_ENCODER_COPY && acodec != SRS_RTMP_ENCODER_NO_AUDIO) { if (abitrate < 0) { return srs_error_new(ERROR_ENCODER_ABITRATE, "invalid abitrate: %d", abitrate); @@ -170,47 +174,47 @@ srs_error_t SrsFFMPEG::initialize_transcode(SrsConfDirective* engine) if (_output.empty()) { return srs_error_new(ERROR_ENCODER_OUTPUT, "invalid empty output"); } - + // for not rtmp input, donot append the iformat, // for example, "-f flv" before "-i udp://192.168.1.252:2222" if (!srs_string_starts_with(input, "rtmp://")) { iformat = ""; } - + return err; } srs_error_t SrsFFMPEG::initialize_copy() { srs_error_t err = srs_success; - + vcodec = SRS_RTMP_ENCODER_COPY; acodec = SRS_RTMP_ENCODER_COPY; - + if (_output.empty()) { return srs_error_new(ERROR_ENCODER_OUTPUT, "invalid empty output"); } - + return err; } srs_error_t SrsFFMPEG::start() { srs_error_t err = srs_success; - + if (process->started()) { return err; } - + // the argv for process. params.clear(); - + // 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 for (int i = 0; i < (int)iparams.size(); i++) { string iparam = iparams.at(i); @@ -218,7 +222,7 @@ srs_error_t SrsFFMPEG::start() params.push_back(iparam); } } - + // build the perfile if (!perfile.empty()) { std::vector::iterator it; @@ -229,16 +233,16 @@ srs_error_t SrsFFMPEG::start() } } } - + // input. if (iformat != "off" && !iformat.empty()) { params.push_back("-f"); params.push_back(iformat); } - + params.push_back("-i"); params.push_back(input); - + // build the filter if (!vfilter.empty()) { std::vector::iterator it; @@ -249,7 +253,7 @@ srs_error_t SrsFFMPEG::start() } } } - + // video specified. if (vcodec != SRS_RTMP_ENCODER_NO_VIDEO) { params.push_back("-vcodec"); @@ -257,45 +261,45 @@ srs_error_t SrsFFMPEG::start() } else { params.push_back("-vn"); } - + // the codec params is disabled when copy if (vcodec != SRS_RTMP_ENCODER_COPY && vcodec != SRS_RTMP_ENCODER_NO_VIDEO) { if (vbitrate > 0) { params.push_back("-b:v"); params.push_back(srs_int2str(vbitrate * 1000)); } - + if (vfps > 0) { params.push_back("-r"); params.push_back(srs_float2str(vfps)); } - + if (vwidth > 0 && vheight > 0) { params.push_back("-s"); params.push_back(srs_int2str(vwidth) + "x" + srs_int2str(vheight)); } - + // TODO: add aspect if needed. if (vwidth > 0 && vheight > 0) { params.push_back("-aspect"); params.push_back(srs_int2str(vwidth) + ":" + srs_int2str(vheight)); } - + if (vthreads > 0) { params.push_back("-threads"); params.push_back(srs_int2str(vthreads)); } - + if (!vprofile.empty()) { params.push_back("-profile:v"); params.push_back(vprofile); } - + if (!vpreset.empty()) { params.push_back("-preset"); params.push_back(vpreset); } - + // vparams if (!vparams.empty()) { std::vector::iterator it; @@ -307,7 +311,7 @@ srs_error_t SrsFFMPEG::start() } } } - + // audio specified. if (acodec != SRS_RTMP_ENCODER_NO_AUDIO) { params.push_back("-acodec"); @@ -315,7 +319,7 @@ srs_error_t SrsFFMPEG::start() } else { params.push_back("-an"); } - + // the codec params is disabled when copy if (acodec != SRS_RTMP_ENCODER_NO_AUDIO) { if (acodec != SRS_RTMP_ENCODER_COPY) { @@ -323,17 +327,17 @@ srs_error_t SrsFFMPEG::start() params.push_back("-b:a"); params.push_back(srs_int2str(abitrate * 1000)); } - + if (asample_rate > 0) { params.push_back("-ar"); params.push_back(srs_int2str(asample_rate)); } - + if (achannels > 0) { params.push_back("-ac"); params.push_back(srs_int2str(achannels)); } - + // aparams std::vector::iterator it; for (it = aparams.begin(); it != aparams.end(); ++it) { @@ -346,7 +350,7 @@ srs_error_t SrsFFMPEG::start() // for audio copy. for (int i = 0; i < (int)aparams.size();) { std::string pn = aparams[i++]; - + // aparams, the adts to asc filter "-bsf:a aac_adtstoasc" if (pn == "-bsf:a" && i < (int)aparams.size()) { std::string pv = aparams[i++]; @@ -358,16 +362,16 @@ srs_error_t SrsFFMPEG::start() } } } - + // output if (oformat != "off" && !oformat.empty()) { params.push_back("-f"); params.push_back(oformat); } - + params.push_back("-y"); params.push_back(_output); - + // when specified the log file. if (!log_file.empty()) { // stdout @@ -379,12 +383,12 @@ srs_error_t SrsFFMPEG::start() params.push_back(">"); params.push_back(log_file); } - + // initialize the process. if ((err = process->initialize(ffmpeg, params)) != srs_success) { return srs_error_wrap(err, "init process"); } - + return process->start(); } diff --git a/trunk/src/core/srs_core_version6.hpp b/trunk/src/core/srs_core_version6.hpp index c6259016b..e421ca1d3 100644 --- a/trunk/src/core/srs_core_version6.hpp +++ b/trunk/src/core/srs_core_version6.hpp @@ -9,6 +9,6 @@ #define VERSION_MAJOR 6 #define VERSION_MINOR 0 -#define VERSION_REVISION 144 +#define VERSION_REVISION 145 #endif