diff --git a/misc/prerequisites.sh b/misc/prerequisites.sh index a647558d3..f4c70f4ae 100755 --- a/misc/prerequisites.sh +++ b/misc/prerequisites.sh @@ -19,6 +19,7 @@ PCRE2_VERSION=10.39 OPENH264_VERSION=2.4.0 HIREDIS_VERSION=1.0.2 NVCC_HDR_VERSION=11.1.5.2 +X264_VERSION=20191217-2245-stable INTEL_QSV_HWACCELS=false NETINT_LOGAN_HWACCELS=false @@ -26,6 +27,8 @@ NETINT_LOGAN_PATCH_PATH="" NETINT_LOGAN_XCODER_COMPILE_PATH="" NVIDIA_NV_CODEC_HWACCELS=false XILINX_XMA_CODEC_HWACCELS=false +VIDEOLAN_X264_CODEC=true + if [[ "$OSTYPE" == "darwin"* ]]; then NCPU=$(sysctl -n hw.ncpu) @@ -101,6 +104,22 @@ install_libopus() rm -rf ${DIR}) || fail_exit "opus" } +install_libx264() +{ + if [ "$VIDEOLAN_X264_CODEC" = false ] ; then + return + fi + + (DIR=${TEMP_PATH}/x264 && \ + mkdir -p ${DIR} && \ + cd ${DIR} && \ + curl -sLf https://download.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-${X264_VERSION}.tar.bz2 | tar -jx --strip-components=1 && \ + ./configure --prefix="${PREFIX}" --enable-shared --enable-pic --disable-cli && \ + make -j$(nproc) && \ + sudo make install && \ + rm -rf ${DIR}) || fail_exit "x264" +} + install_libopenh264() { (DIR=${TEMP_PATH}/openh264 && \ @@ -254,6 +273,12 @@ install_ffmpeg() ADDI_EXTRA_LIBS+="--extra-libs=-lxma2api --extra-libs=-lxrt_core --extra-libs=-lxrm --extra-libs=-lxrt_coreutil --extra-libs=-lpthread --extra-libs=-ldl " fi + if [ "$VIDEOLAN_X264_CODEC" == true ]; then + ADDI_LIBS+=" --enable-libx264 " + ADDI_ENCODER+=",libx264" + ADDI_LICENSE+=" --enable-gpl --enable-nonfree " + fi + # Options are added by external scripts. if [[ -n "${EXT_FFMPEG_LICENSE}" ]]; then ADDI_LICENSE+=${EXT_FFMPEG_LICENSE} @@ -512,7 +537,15 @@ case $i in --enable-xma) XILINX_XMA_CODEC_HWACCELS=true shift - ;; + ;; + --disable-x264) + VIDEOLAN_X264_CODEC=false + shift + ;; + --enable-x264) + VIDEOLAN_X264_CODEC=true + shift + ;; *) # unknown option ;; @@ -560,6 +593,7 @@ install_libsrtp install_libsrt install_libopus install_libopenh264 +install_libx264 install_libvpx install_fdk_aac install_nvcc_hdr diff --git a/src/projects/base/mediarouter/media_type.h b/src/projects/base/mediarouter/media_type.h index 5f7af4f71..1fc933935 100644 --- a/src/projects/base/mediarouter/media_type.h +++ b/src/projects/base/mediarouter/media_type.h @@ -102,6 +102,7 @@ namespace cmn DEFAULT, // SW OPENH264, // SW BEAMR, // SW + X264, // SW NVENC, // HW QSV, // HW XMA, // HW @@ -250,7 +251,7 @@ namespace cmn { return cmn::MediaCodecModuleId::BEAMR; } - else if (name.HasSuffix("_NVENC") || name.HasSuffix("NV") || name.HasSuffix("NVENC")) + else if (name.HasSuffix("_NVENC") || name.HasSuffix("_NV") || name.HasSuffix("NV") || name.HasSuffix("NVENC")) { return cmn::MediaCodecModuleId::NVENC; } @@ -274,6 +275,10 @@ namespace cmn { return cmn::MediaCodecModuleId::FDKAAC; } + else if (name.HasSuffix("_X264") || name.HasSuffix("X264") ) + { + return cmn::MediaCodecModuleId::X264; + } else if (name.HasSuffix("_DEFAULT") || name.HasSuffix("DEFAULT")) { return cmn::MediaCodecModuleId::DEFAULT; @@ -306,6 +311,8 @@ namespace cmn return "fdkaac"; case cmn::MediaCodecModuleId::LIBOPUS: return "libopus"; + case cmn::MediaCodecModuleId::X264: + return "x264"; case cmn::MediaCodecModuleId::None: default: break; @@ -334,6 +341,7 @@ namespace cmn { switch (id) { + // Video codecs case cmn::MediaCodecId::H264: return "H264"; case cmn::MediaCodecId::H265: @@ -342,16 +350,19 @@ namespace cmn return "VP8"; case cmn::MediaCodecId::Vp9: return "VP9"; + case cmn::MediaCodecId::Flv: + return "FLV"; + case cmn::MediaCodecId::Jpeg: + return "JPEG"; + case cmn::MediaCodecId::Png: + return "PNG"; + // Audio codecs case cmn::MediaCodecId::Aac: return "AAC"; case cmn::MediaCodecId::Mp3: return "MP3"; case cmn::MediaCodecId::Opus: return "OPUS"; - case cmn::MediaCodecId::Jpeg: - return "JPEG"; - case cmn::MediaCodecId::Png: - return "PNG"; default: break; } @@ -364,55 +375,44 @@ namespace cmn name.MakeUpper(); // Video codecs - if (name == "H264" || - name == "H264_OPENH264" || - name == "H264_BEAMR" || - name == "H264_NVENC" || - name == "H264_QSV" || - name == "H264_NILOGAN" || - name == "H264_XMA") + if (name.HasPrefix("H264")) { return cmn::MediaCodecId::H264; } - else if (name == "H265" || - name == "H265_NVENC" || - name == "H265_QSV" || - name == "H265_NILOGAN" || - name == "H265_XMA") + else if (name.HasPrefix("H265")) { return cmn::MediaCodecId::H265; } - else if (name == "VP8") + else if (name.HasPrefix("VP8")) { return cmn::MediaCodecId::Vp8; } - else if (name == "VP9") + else if (name.HasPrefix("VP9")) { return cmn::MediaCodecId::Vp9; } - else if (name == "FLV") + else if (name.HasPrefix("FLV")) { return cmn::MediaCodecId::Flv; } - else if (name == "JPEG") + else if (name.HasPrefix("JPEG")) { return cmn::MediaCodecId::Jpeg; } - else if (name == "PNG") + else if (name.HasPrefix("PNG")) { return cmn::MediaCodecId::Png; } - // Audio codecs - if (name == "AAC") + if (name.HasPrefix("AAC")) { return cmn::MediaCodecId::Aac; } - else if (name == "MP3") + else if (name.HasPrefix("MP3")) { return cmn::MediaCodecId::Mp3; } - else if (name == "OPUS") + else if (name.HasPrefix("OPUS")) { return cmn::MediaCodecId::Opus; } diff --git a/src/projects/transcoder/codec/encoder/encoder_avc_x264.cpp b/src/projects/transcoder/codec/encoder/encoder_avc_x264.cpp new file mode 100644 index 000000000..78f1254cf --- /dev/null +++ b/src/projects/transcoder/codec/encoder/encoder_avc_x264.cpp @@ -0,0 +1,162 @@ +//============================================================================== +// +// Transcoder +// +// Created by Kwon Keuk Han +// Copyright (c) 2018 AirenSoft. All rights reserved. +// +//============================================================================== +#include "encoder_avc_x264.h" + +#include + +#include "../../transcoder_private.h" + + +bool EncoderAVCx264::SetCodecParams() +{ + _codec_context->bit_rate = GetRefTrack()->GetBitrate(); + _codec_context->rc_min_rate = _codec_context->rc_max_rate = _codec_context->bit_rate; + _codec_context->rc_buffer_size = static_cast(_codec_context->bit_rate / 2); + _codec_context->framerate = ::av_d2q((GetRefTrack()->GetFrameRateByConfig() > 0) ? GetRefTrack()->GetFrameRateByConfig() : GetRefTrack()->GetEstimateFrameRate(), AV_TIME_BASE); + _codec_context->sample_aspect_ratio = ::av_make_q(1, 1); + + // From avcodec.h: + // For some codecs, the time base is closer to the field rate than the frame rate. + // Most notably, H.264 and MPEG-2 specify time_base as half of frame duration + // if no telecine is used ... + // Set to time_base ticks per frame. Default 1, e.g., H.264/MPEG-2 set it to 2. + _codec_context->ticks_per_frame = 2; + + // From avcodec.h: + // For fixed-fps content, timebase should be 1/framerate and timestamp increments should be identically 1. + // This often, but not always is the inverse of the frame rate or field rate for video. 1/time_base is not the average frame rate if the frame rate is not constant. + _codec_context->time_base = (AVRational){GetRefTrack()->GetTimeBase().GetNum(), GetRefTrack()->GetTimeBase().GetDen()}; + _codec_context->max_b_frames = GetRefTrack()->GetBFrames(); + _codec_context->pix_fmt = (AVPixelFormat)GetSupportedFormat(); + _codec_context->width = GetRefTrack()->GetWidth(); + _codec_context->height = GetRefTrack()->GetHeight(); + + // Keyframe Interval + //@see transcoder_encoder.cpp / force_keyframe_by_time_interval + auto key_frame_interval_type = GetRefTrack()->GetKeyFrameIntervalTypeByConfig(); + if (key_frame_interval_type == cmn::KeyFrameIntervalType::TIME) + { + _codec_context->gop_size = (int32_t)(GetRefTrack()->GetFrameRate() * (double)GetRefTrack()->GetKeyFrameInterval() / 1000 * 2); + } + else if (key_frame_interval_type == cmn::KeyFrameIntervalType::FRAME) + { + _codec_context->gop_size = (GetRefTrack()->GetKeyFrameInterval() == 0) ? (_codec_context->framerate.num / _codec_context->framerate.den) : GetRefTrack()->GetKeyFrameInterval(); + } + + // -1(Default) => FFMIN(FFMAX(4, av_cpu_count() / 3), 8) + // 0 => Auto + // >1 => Set + _codec_context->thread_count = GetRefTrack()->GetThreadCount() < 0 ? FFMIN(FFMAX(4, av_cpu_count() / 3), 8) : GetRefTrack()->GetThreadCount(); + + // Preset + if (GetRefTrack()->GetPreset() == "slower") + { + ::av_opt_set(_codec_context->priv_data, "preset", "slower", 0); + } + else if (GetRefTrack()->GetPreset() == "slow") + { + ::av_opt_set(_codec_context->priv_data, "preset", "slow", 0); + } + else if (GetRefTrack()->GetPreset() == "medium") + { + ::av_opt_set(_codec_context->priv_data, "preset", "medium", 0); + } + else if (GetRefTrack()->GetPreset() == "fast") + { + ::av_opt_set(_codec_context->priv_data, "preset", "fast", 0); + } + else if (GetRefTrack()->GetPreset() == "faster") + { + ::av_opt_set(_codec_context->priv_data, "preset", "faster", 0); + } + else + { + // Default + ::av_opt_set(_codec_context->priv_data, "preset", "faster", 0); + } + + // Profile + ::av_opt_set(_codec_context->priv_data, "profile", "main", 0); + + // Tune + ::av_opt_set(_codec_context->priv_data, "tune", "zerolatency", 0); + + // Remove the sliced-thread option from encoding delay. Browser compatibility in MAC environment + ::av_opt_set(_codec_context->priv_data, "x264opts", + ov::String::FormatString( + "bframes=%d:sliced-threads=0:b-adapt=1:no-scenecut:keyint=%d:min-keyint=%d", + GetRefTrack()->GetBFrames(), + _codec_context->gop_size, + _codec_context->gop_size) + .CStr(), + 0); + + _bitstream_format = cmn::BitstreamFormat::H264_ANNEXB; + + _packet_type = cmn::PacketType::NALU; + + return true; +} + +// Notes. +// +// - B-frame must be disabled. because, WEBRTC does not support B-Frame. +// +bool EncoderAVCx264::Configure(std::shared_ptr context) +{ + if (TranscodeEncoder::Configure(context) == false) + { + return false; + } + + auto codec_id = GetCodecID(); + + const AVCodec *codec = ::avcodec_find_encoder_by_name("libx264"); + if (codec == nullptr) + { + logte("Could not find encoder: %s (%d)", ::avcodec_get_name(codec_id), codec_id); + return false; + } + + _codec_context = ::avcodec_alloc_context3(codec); + if (_codec_context == nullptr) + { + logte("Could not allocate codec context for %s (%d)", ::avcodec_get_name(GetCodecID()), GetCodecID()); + return false; + } + + if (SetCodecParams() == false) + { + logte("Could not set codec parameters for %s (%d)", ::avcodec_get_name(GetCodecID()), GetCodecID()); + return false; + } + if (::avcodec_open2(_codec_context, _codec_context->codec, nullptr) < 0) + { + logte("Could not open codec: %s (%d)", ::avcodec_get_name(GetCodecID()), GetCodecID()); + return false; + } + + // Generates a thread that reads and encodes frames in the input_buffer queue and places them in the output queue. + try + { + _kill_flag = false; + + _codec_thread = std::thread(&EncoderAVCx264::CodecThread, this); + pthread_setname_np(_codec_thread.native_handle(), ov::String::FormatString("Enc%s", avcodec_get_name(GetCodecID())).CStr()); + } + catch (const std::system_error &e) + { + logte("Failed to start encoder thread."); + _kill_flag = true; + + return false; + } + + return true; +} diff --git a/src/projects/transcoder/codec/encoder/encoder_avc_x264.h b/src/projects/transcoder/codec/encoder/encoder_avc_x264.h new file mode 100644 index 000000000..19f325b68 --- /dev/null +++ b/src/projects/transcoder/codec/encoder/encoder_avc_x264.h @@ -0,0 +1,40 @@ +//============================================================================== +// +// Transcode +// +// Created by Kwon Keuk Han +// Copyright (c) 2018 AirenSoft. All rights reserved. +// +//============================================================================== +#pragma once + +#include "../../transcoder_encoder.h" + +class EncoderAVCx264 : public TranscodeEncoder +{ +public: + EncoderAVCx264(const info::Stream &stream_info) + : TranscodeEncoder(stream_info) + { + } + + AVCodecID GetCodecID() const noexcept override + { + return AV_CODEC_ID_H264; + } + + int GetSupportedFormat() const noexcept override + { + return AV_PIX_FMT_YUV420P; + } + + cmn::BitstreamFormat GetBitstreamFormat() const noexcept override + { + return cmn::BitstreamFormat::H264_ANNEXB; + } + + bool Configure(std::shared_ptr context) override; + +private: + bool SetCodecParams() override; +}; diff --git a/src/projects/transcoder/filter/filter_rescaler.cpp b/src/projects/transcoder/filter/filter_rescaler.cpp index a347eaded..8871093fb 100644 --- a/src/projects/transcoder/filter/filter_rescaler.cpp +++ b/src/projects/transcoder/filter/filter_rescaler.cpp @@ -115,6 +115,8 @@ bool FilterRescaler::InitializeFilterDescription() if (output_module_id == cmn::MediaCodecModuleId::DEFAULT || output_module_id == cmn::MediaCodecModuleId::BEAMR || + output_module_id == cmn::MediaCodecModuleId::OPENH264 || + output_module_id == cmn::MediaCodecModuleId::X264 || output_module_id == cmn::MediaCodecModuleId::QSV || output_module_id == cmn::MediaCodecModuleId::LIBVPX || // Until now, Logan VPU processes in CPU memory like SW-based modules. Performance needs to be improved in the future diff --git a/src/projects/transcoder/transcoder_encoder.cpp b/src/projects/transcoder/transcoder_encoder.cpp index deff9bba7..7b001abc7 100644 --- a/src/projects/transcoder/transcoder_encoder.cpp +++ b/src/projects/transcoder/transcoder_encoder.cpp @@ -10,6 +10,7 @@ #include +#include "codec/encoder/encoder_avc_x264.h" #include "codec/encoder/encoder_aac.h" #include "codec/encoder/encoder_avc_nv.h" #include "codec/encoder/encoder_avc_openh264.h" @@ -36,7 +37,8 @@ std::shared_ptr>> TranscodeEncoder::GetCandidates(bool hwaccels_enable, ov::String hwaccles_modules, std::shared_ptr track) { - logtd("Codec(%s), HWAccels.Enable(%s), HWAccels.Modules(%s), Video.Modules(%s)", + logtd("Track(%d) Codec(%s), HWAccels.Enable(%s), HWAccels.Modules(%s), Encode.Modules(%s)", + track->GetId(), GetCodecIdToString(track->GetCodecId()).CStr(), hwaccels_enable ? "true" : "false", hwaccles_modules.CStr(), @@ -182,8 +184,9 @@ std::shared_ptr TranscodeEncoder::Create( { switch (candidate->GetModuleId()) { - CASE_CREATE_CODEC_IFNEED(DEFAULT, EncoderAVCxOpenH264); + CASE_CREATE_CODEC_IFNEED(DEFAULT, EncoderAVCx264); CASE_CREATE_CODEC_IFNEED(OPENH264, EncoderAVCxOpenH264); + CASE_CREATE_CODEC_IFNEED(X264, EncoderAVCx264); CASE_CREATE_CODEC_IFNEED(QSV, EncoderAVCxQSV); CASE_CREATE_CODEC_IFNEED(NILOGAN, EncoderAVCxNILOGAN); CASE_CREATE_CODEC_IFNEED(XMA, EncoderAVCxXMA); @@ -191,12 +194,12 @@ std::shared_ptr TranscodeEncoder::Create( default: break; } - break; } else if (candidate->GetCodecId() == cmn::MediaCodecId::H265) { switch (candidate->GetModuleId()) { + // No default module for HEVC CASE_CREATE_CODEC_IFNEED(QSV, EncoderHEVCxQSV); CASE_CREATE_CODEC_IFNEED(NILOGAN, EncoderHEVCxNILOGAN); CASE_CREATE_CODEC_IFNEED(XMA, EncoderHEVCxXMA); @@ -237,7 +240,6 @@ std::shared_ptr TranscodeEncoder::Create( CASE_CREATE_CODEC_IFNEED(DEFAULT, EncoderFFOPUS); CASE_CREATE_CODEC_IFNEED(LIBOPUS, EncoderFFOPUS); #endif - break; } } @@ -247,7 +249,6 @@ std::shared_ptr TranscodeEncoder::Create( { default: CASE_CREATE_CODEC_IFNEED(DEFAULT, EncoderJPEG); - break; } break; diff --git a/src/projects/transcoder/transcoder_stream.cpp b/src/projects/transcoder/transcoder_stream.cpp index 3094c1cb3..1bc272322 100644 --- a/src/projects/transcoder/transcoder_stream.cpp +++ b/src/projects/transcoder/transcoder_stream.cpp @@ -801,7 +801,10 @@ bool TranscoderStream::CreateDecoder(MediaTrackId decoder_id, std::shared_ptrIsHardwareAcceleration() is deprecated. It will be deleted soon. - auto candidates = TranscodeDecoder::GetCandidates(cfg_hwaccels.GetDecoder().IsEnable() || GetOutputProfilesCfg()->IsHardwareAcceleration(), cfg_hwaccels.GetDecoder().GetModules(), input_track); + auto candidates = TranscodeDecoder::GetCandidates( + cfg_hwaccels.GetDecoder().IsEnable() || GetOutputProfilesCfg()->IsHardwareAcceleration(), + cfg_hwaccels.GetDecoder().GetModules(), + input_track); if(candidates == nullptr) { logte("%s Decoder candidates are not found. InputTrack(%u)", _log_prefix.CStr(), input_track->GetId()); @@ -902,7 +905,9 @@ bool TranscoderStream::CreateEncoder(MediaTrackId encoder_id, std::shared_ptrIsHardwareAcceleration() is deprecated. It will be deleted soon. - auto candidates = TranscodeEncoder::GetCandidates(cfg_hwaccels.GetEncoder().IsEnable() || GetOutputProfilesCfg()->IsHardwareAcceleration(), cfg_hwaccels.GetEncoder().GetModules(), output_track); + auto candidates = TranscodeEncoder::GetCandidates( + cfg_hwaccels.GetEncoder().IsEnable() || GetOutputProfilesCfg()->IsHardwareAcceleration(), + cfg_hwaccels.GetEncoder().GetModules(), output_track); if(candidates == nullptr) { logte("%s Decoder candidates are not found. InputTrack(%d)", _log_prefix.CStr(), output_track->GetId()); @@ -946,7 +951,8 @@ int32_t TranscoderStream::CreateFilters(MediaFrame *buffer) MediaTrackId encoder_id = _link_filter_to_encoder[filter_id]; if (_encoders.find(encoder_id) == _encoders.end()) { - logte("%s Filter creation failed because the encoder could not be found. EncoderId(%d), FilterId(%d)", _log_prefix.CStr(), encoder_id, filter_id); + logte("%s Filter creation failed because the encoder could not be found. EncoderId(%d), FilterId(%d)", + _log_prefix.CStr(), encoder_id, filter_id); continue; } @@ -988,7 +994,8 @@ bool TranscoderStream::CreateFilter(MediaTrackId filter_id, std::shared_ptr(); - if (filter->Configure(filter_id, input_stream, input_track, output_stream, output_track, bind(&TranscoderStream::OnFilteredFrame, this, std::placeholders::_1, std::placeholders::_2)) != true) + if (filter->Configure(filter_id, input_stream, input_track, output_stream, output_track, + bind(&TranscoderStream::OnFilteredFrame, this, std::placeholders::_1, std::placeholders::_2)) != true) { logte("%s Failed to create filter. Filter(%d)", _log_prefix.CStr(), filter_id); return false; @@ -1486,7 +1493,8 @@ void TranscoderStream::SendFrame(std::shared_ptr &stream, std::sha bool ret = _parent->SendFrame(stream, std::move(packet)); if (ret == false) { - logtw("%s Could not send frame to mediarouter. Stream(%s(%u)), OutputTrack(%u)", _log_prefix.CStr(), stream->GetName().CStr(), stream->GetId(), packet->GetTrackId()); + logtw("%s Could not send frame to mediarouter. Stream(%s(%u)), OutputTrack(%u)", + _log_prefix.CStr(), stream->GetName().CStr(), stream->GetId(), packet->GetTrackId()); } } @@ -1525,7 +1533,8 @@ void TranscoderStream::NotifyCreateStreams() if (_parent->CreateStream(output_stream) == false) { - logtw("%s Could not create stream. [%s/%s(%u)]", _log_prefix.CStr(), _application_info.GetVHostAppName().CStr(), output_stream->GetName().CStr(), output_stream->GetId()); + logtw("%s Could not create stream. [%s/%s(%u)]", + _log_prefix.CStr(), _application_info.GetVHostAppName().CStr(), output_stream->GetName().CStr(), output_stream->GetId()); } } } @@ -1538,7 +1547,8 @@ void TranscoderStream::NotifyDeleteStreams() if (_parent->DeleteStream(output_stream) == false) { - logtw("%s Could not delete stream. %s/%s(%u)", _log_prefix.CStr(), _application_info.GetVHostAppName().CStr(), output_stream->GetName().CStr(), output_stream->GetId()); + logtw("%s Could not delete stream. %s/%s(%u)", + _log_prefix.CStr(), _application_info.GetVHostAppName().CStr(), output_stream->GetName().CStr(), output_stream->GetId()); } } @@ -1553,7 +1563,8 @@ void TranscoderStream::NotifyUpdateStreams() if (_parent->UpdateStream(output_stream) == false) { - logtw("%s Could not update stream. %s/%s(%u)", _log_prefix.CStr(), _application_info.GetVHostAppName().CStr(), output_stream->GetName().CStr(), output_stream->GetId()); + logtw("%s Could not update stream. %s/%s(%u)", + _log_prefix.CStr(), _application_info.GetVHostAppName().CStr(), output_stream->GetName().CStr(), output_stream->GetId()); } } } diff --git a/src/projects/transcoder/transcoder_stream_internal.cpp b/src/projects/transcoder/transcoder_stream_internal.cpp index 7c308ea89..2ecc09603 100644 --- a/src/projects/transcoder/transcoder_stream_internal.cpp +++ b/src/projects/transcoder/transcoder_stream_internal.cpp @@ -184,6 +184,7 @@ std::shared_ptr TranscoderStreamInternal::CreateOutputTrack( if (need_bypass == true) { output_track->SetBypass(true); + output_track->SetCodecId(input_track->GetCodecId()); output_track->SetCodecModules(input_track->GetCodecModules()); output_track->SetCodecModuleId(input_track->GetCodecModuleId()); @@ -194,15 +195,34 @@ std::shared_ptr TranscoderStreamInternal::CreateOutputTrack( else { output_track->SetBypass(false); - output_track->SetCodecId(cmn::GetCodecIdByName(profile.GetCodec())); + + auto codec_id = cmn::GetCodecIdByName(profile.GetCodec()); + + output_track->SetCodecId(codec_id); output_track->SetCodecModules(profile.GetModules()); output_track->SetWidth(profile.GetWidth()); output_track->SetHeight(profile.GetHeight()); - output_track->SetTimeBase(GetDefaultTimebaseByCodecId(output_track->GetCodecId())); + output_track->SetTimeBase(GetDefaultTimebaseByCodecId(codec_id)); output_track->SetPreset(profile.GetPreset()); output_track->SetThreadCount(profile.GetThreadCount()); output_track->SetBFrames(profile.GetBFrames()); output_track->SetProfile(profile.GetProfile()); + + // Used when the encoding profile codec name includes the module name. + // ex) h264_nvenc + // ex) h264_openh264 + auto module_id = cmn::GetCodecModuleIdByName(profile.GetCodec()); + if(module_id != cmn::MediaCodecModuleId::None) + { + if(output_track->GetCodecModules().IsEmpty() == false) + { + output_track->SetCodecModules(ov::String::FormatString("%s,%s", cmn::GetStringFromCodecModuleId(module_id).CStr(), output_track->GetCodecModules().CStr())); + } + else + { + output_track->SetCodecModules(cmn::GetStringFromCodecModuleId(module_id)); + } + } } // If the framerate is not set, it is set to the same value as the input. @@ -434,7 +454,7 @@ bool TranscoderStreamInternal::IsMatchesBypassCondition(const std::shared_ptrGetCodecId())) + if ((condition == "EQ") && (cmn::GetCodecIdByName(profile.GetCodec()) != input_track->GetCodecId())) { return false; } @@ -541,9 +561,9 @@ bool TranscoderStreamInternal::IsMatchesBypassCondition(const std::shared_ptrGetCodecId())) + if ((condition == "EQ") && (cmn::GetCodecIdByName(profile.GetCodec()) != input_track->GetCodecId())) { - if (cmn::GetCodecIdByName(profile.GetCodec().CStr()) != input_track->GetCodecId()) + if (cmn::GetCodecIdByName(profile.GetCodec()) != input_track->GetCodecId()) { return false; }