diff --git a/CMakeLists.txt b/CMakeLists.txt index af3b949d..ff3ab744 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,9 +112,11 @@ target_sources(sora src/sora_signaling.cpp src/sora_video_decoder_factory.cpp src/sora_video_encoder_factory.cpp + src/sora_video_codec.cpp src/ssl_verifier.cpp src/url_parts.cpp src/version.cpp + src/vpl_session_impl.cpp src/websocket.cpp src/zlib_helper.cpp ) @@ -258,6 +260,7 @@ if (SORA_TARGET_OS STREQUAL "windows") if (USE_NVCODEC_ENCODER) target_sources(sora PRIVATE + src/hwenc_nvcodec/nvcodec_video_codec.cpp src/hwenc_nvcodec/nvcodec_video_encoder.cpp third_party/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp third_party/NvCodec/NvCodec/NvEncoder/NvEncoderD3D11.cpp @@ -281,6 +284,7 @@ if (SORA_TARGET_OS STREQUAL "windows") set_source_files_properties( src/cuda_context_cuda.cpp + src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp third_party/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp src/hwenc_nvcodec/nvcodec_decoder_cuda.cpp PROPERTIES @@ -288,6 +292,7 @@ if (SORA_TARGET_OS STREQUAL "windows") ) cuda_compile(CUDA_FILES src/cuda_context_cuda.cpp + src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp third_party/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp src/hwenc_nvcodec/nvcodec_decoder_cuda.cpp OPTIONS @@ -329,7 +334,7 @@ if (SORA_TARGET_OS STREQUAL "windows") find_package(VPL REQUIRED) target_sources(sora PRIVATE - src/hwenc_vpl/vpl_session_impl.cpp + src/hwenc_vpl/vpl_video_codec.cpp src/hwenc_vpl/vpl_video_decoder.cpp src/hwenc_vpl/vpl_video_encoder.cpp ) @@ -472,6 +477,8 @@ elseif (SORA_TARGET_OS STREQUAL "ubuntu") target_sources(sora PRIVATE src/cuda_context_cuda.cpp + src/hwenc_nvcodec/nvcodec_video_codec.cpp + src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp src/hwenc_nvcodec/nvcodec_video_encoder.cpp src/hwenc_nvcodec/nvcodec_v4l2_capturer.cpp src/hwenc_nvcodec/nvcodec_video_encoder_cuda.cpp @@ -489,6 +496,7 @@ elseif (SORA_TARGET_OS STREQUAL "ubuntu") # これらのソースは CUDA としてコンパイルする set_source_files_properties( src/cuda_context_cuda.cpp + src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp src/hwenc_nvcodec/nvcodec_video_encoder_cuda.cpp src/hwenc_nvcodec/nvcodec_decoder_cuda.cpp third_party/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp @@ -506,7 +514,7 @@ elseif (SORA_TARGET_OS STREQUAL "ubuntu") target_include_directories(sora PRIVATE /usr/local/cuda/include) target_sources(sora PRIVATE - src/hwenc_vpl/vpl_session_impl.cpp + src/hwenc_vpl/vpl_video_codec.cpp src/hwenc_vpl/vpl_video_decoder.cpp src/hwenc_vpl/vpl_video_encoder.cpp ) diff --git a/VERSION b/VERSION index 655bb30d..0e45e4d8 100644 --- a/VERSION +++ b/VERSION @@ -1,5 +1,5 @@ SORA_CPP_SDK_VERSION=2025.2.0-canary.0 -WEBRTC_BUILD_VERSION=m132.6834.5.2 +WEBRTC_BUILD_VERSION=m132.6834.5.4 BOOST_VERSION=1.87.0 CMAKE_VERSION=3.31.4 CUDA_VERSION=11.8.0-1 diff --git a/examples/VERSION b/examples/VERSION index bf72b8d2..2eaca53c 100644 --- a/examples/VERSION +++ b/examples/VERSION @@ -1,5 +1,5 @@ SORA_CPP_SDK_VERSION=2025.2.0-canary.0 -WEBRTC_BUILD_VERSION=m132.6834.5.2 +WEBRTC_BUILD_VERSION=m132.6834.5.4 BOOST_VERSION=1.87.0 CMAKE_VERSION=3.31.4 SDL2_VERSION=2.30.11 diff --git a/include/sora/cuda_context.h b/include/sora/cuda_context.h index 0fe455ab..09ffc67e 100644 --- a/include/sora/cuda_context.h +++ b/include/sora/cuda_context.h @@ -11,10 +11,6 @@ class CudaContext { // CUDA コンテキスト生成する。 // CUDA に対応していないプラットフォームでは nullptr を返す。 static std::shared_ptr Create(); - void* Context() const; - - private: - std::shared_ptr impl_; }; enum class CudaVideoCodec { diff --git a/include/sora/hwenc_nvcodec/nvcodec_video_codec.h b/include/sora/hwenc_nvcodec/nvcodec_video_codec.h new file mode 100644 index 00000000..c2522aea --- /dev/null +++ b/include/sora/hwenc_nvcodec/nvcodec_video_codec.h @@ -0,0 +1,16 @@ +#ifndef SORA_HWENC_NVCODEC_NVCODEC_VIDEO_CODEC_H_ +#define SORA_HWENC_NVCODEC_NVCODEC_VIDEO_CODEC_H_ + +#include + +#include "sora/cuda_context.h" +#include "sora/sora_video_codec.h" + +namespace sora { + +VideoCodecCapability::Engine GetNvCodecVideoCodecCapability( + std::shared_ptr context); + +} // namespace sora + +#endif diff --git a/include/sora/hwenc_vpl/vpl_session.h b/include/sora/hwenc_vpl/vpl_session.h deleted file mode 100644 index b026c464..00000000 --- a/include/sora/hwenc_vpl/vpl_session.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef SORA_HWENC_VPL_VPL_SESSION_H_ -#define SORA_HWENC_VPL_VPL_SESSION_H_ - -#include - -namespace sora { - -struct VplSession { - static std::shared_ptr Create(); -}; - -} // namespace sora - -#endif diff --git a/include/sora/hwenc_vpl/vpl_video_codec.h b/include/sora/hwenc_vpl/vpl_video_codec.h new file mode 100644 index 00000000..31164d9f --- /dev/null +++ b/include/sora/hwenc_vpl/vpl_video_codec.h @@ -0,0 +1,14 @@ +#ifndef SORA_HWENC_VPL_VPL_VIDEO_CODEC_H_ +#define SORA_HWENC_VPL_VPL_VIDEO_CODEC_H_ + +#include "sora/sora_video_codec.h" +#include "sora/vpl_session.h" + +namespace sora { + +VideoCodecCapability::Engine GetVplVideoCodecCapability( + std::shared_ptr session); + +} // namespace sora + +#endif diff --git a/include/sora/hwenc_vpl/vpl_video_decoder.h b/include/sora/hwenc_vpl/vpl_video_decoder.h index 3d34e87b..7e3fb864 100644 --- a/include/sora/hwenc_vpl/vpl_video_decoder.h +++ b/include/sora/hwenc_vpl/vpl_video_decoder.h @@ -8,7 +8,7 @@ #include #include -#include "vpl_session.h" +#include "sora/vpl_session.h" namespace sora { diff --git a/include/sora/hwenc_vpl/vpl_video_encoder.h b/include/sora/hwenc_vpl/vpl_video_encoder.h index 354e0d0b..184862de 100644 --- a/include/sora/hwenc_vpl/vpl_video_encoder.h +++ b/include/sora/hwenc_vpl/vpl_video_encoder.h @@ -7,7 +7,7 @@ #include #include -#include "vpl_session.h" +#include "sora/vpl_session.h" namespace sora { diff --git a/include/sora/open_h264_video_encoder.h b/include/sora/open_h264_video_encoder.h index c30b8b4a..18aa2993 100644 --- a/include/sora/open_h264_video_encoder.h +++ b/include/sora/open_h264_video_encoder.h @@ -8,12 +8,17 @@ #include #include +#include "sora_video_codec.h" + namespace sora { std::unique_ptr CreateOpenH264VideoEncoder( const webrtc::SdpVideoFormat& format, std::string openh264); -} +VideoCodecCapability::Engine GetOpenH264VideoCodecCapability( + std::optional openh264_path); + +} // namespace sora #endif diff --git a/include/sora/sora_video_codec.h b/include/sora/sora_video_codec.h new file mode 100644 index 00000000..b6fd1e10 --- /dev/null +++ b/include/sora/sora_video_codec.h @@ -0,0 +1,110 @@ +#ifndef SORA_SORA_VIDEO_CODEC_H_ +#define SORA_SORA_VIDEO_CODEC_H_ + +// WebRTC +#include + +// Boost +#include + +#include +#include +#include +#include + +#include "cuda_context.h" +#include "vpl_session.h" + +namespace webrtc { + +// VideoCodecType +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecType& v); +VideoCodecType tag_invoke(const boost::json::value_to_tag&, + boost::json::value const& jv); + +} // namespace webrtc + +namespace sora { + +enum class VideoCodecImplementation { + kInternal, + kCiscoOpenH264, + kIntelVpl, + kNvidiaVideoCodecSdk, +}; + +struct VideoCodecCapability { + struct Parameters { + std::optional version; + std::optional openh264_path; + std::optional vpl_impl; + std::optional vpl_impl_value; + std::optional nvcodec_gpu_device_name; + }; + struct Codec { + Codec(webrtc::VideoCodecType type, bool decoder, bool encoder) + : type(type), decoder(decoder), encoder(encoder) {} + webrtc::VideoCodecType type; + bool decoder; + bool encoder; + Parameters parameters; + }; + struct Engine { + Engine(VideoCodecImplementation name) : name(name) {} + VideoCodecImplementation name; + std::vector codecs; + Parameters parameters; + }; + std::vector engines; +}; + +// VideoCodecImplementation +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecImplementation& v); +VideoCodecImplementation tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv); +// VideoCodecCapability::Parameters +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability::Parameters& v); +VideoCodecCapability::Parameters tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv); +// VideoCodecCapability::Codec +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability::Codec& v); +VideoCodecCapability::Codec tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv); +// VideoCodecCapability::Engine +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability::Engine& v); +VideoCodecCapability::Engine tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv); +// VideoCodecCapability +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability& v); +VideoCodecCapability tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv); + +struct VideoCodecCapabilityConfig { + std::shared_ptr cuda_context; + std::shared_ptr vpl_session; + std::optional openh264_path; + void* jni_env = nullptr; +}; + +VideoCodecCapability GetVideoCodecCapability(VideoCodecCapabilityConfig config); + +} // namespace sora + +#endif \ No newline at end of file diff --git a/include/sora/vpl_session.h b/include/sora/vpl_session.h new file mode 100644 index 00000000..bf458b79 --- /dev/null +++ b/include/sora/vpl_session.h @@ -0,0 +1,16 @@ +#ifndef SORA_VPL_SESSION_H_ +#define SORA_VPL_SESSION_H_ + +#include + +namespace sora { + +struct VplSession { + // Intel VPL のセッションを作成する + // 対応してない場合やエラーが発生した場合は nullptr を返す + static std::shared_ptr Create(); +}; + +} // namespace sora + +#endif diff --git a/src/cuda_context_cuda.cpp b/src/cuda_context_cuda.cpp index 6a20f56e..12e1f35f 100644 --- a/src/cuda_context_cuda.cpp +++ b/src/cuda_context_cuda.cpp @@ -31,11 +31,7 @@ simplelogger::Logger* logger = namespace sora { -CUcontext GetCudaContext(std::shared_ptr ctx) { - return static_cast(ctx->Context()); -} - -struct CudaContextImpl { +struct CudaContextImpl : CudaContext { CUdevice device; CUcontext context; ~CudaContextImpl() { dyn::cuCtxDestroy(context); } @@ -46,7 +42,6 @@ struct CudaContextImpl { throw std::exception() std::shared_ptr CudaContext::Create() { - std::shared_ptr ctx; try { CUdevice device; CUcontext context; @@ -62,21 +57,21 @@ std::shared_ptr CudaContext::Create() { //RTC_LOG(LS_INFO) << "GPU in use: " << device_name; ckerror(dyn::cuCtxCreate(&context, 0, device)); - std::shared_ptr impl(new CudaContextImpl()); + auto impl = std::make_shared(); impl->device = device; impl->context = context; - - ctx.reset(new CudaContext()); - ctx->impl_ = impl; + return impl; } catch (std::exception&) { return nullptr; } +} - return ctx; +CUdevice GetCudaDevice(std::shared_ptr ctx) { + return std::static_pointer_cast(ctx)->device; } -void* CudaContext::Context() const { - return std::static_pointer_cast(impl_)->context; +CUcontext GetCudaContext(std::shared_ptr ctx) { + return std::static_pointer_cast(ctx)->context; } } // namespace sora diff --git a/src/cuda_context_cuda.h b/src/cuda_context_cuda.h index 25ffd2b8..0f8043dd 100644 --- a/src/cuda_context_cuda.h +++ b/src/cuda_context_cuda.h @@ -7,8 +7,9 @@ namespace sora { +CUdevice GetCudaDevice(std::shared_ptr ctx); CUcontext GetCudaContext(std::shared_ptr ctx); -} +} // namespace sora #endif \ No newline at end of file diff --git a/src/hwenc_nvcodec/nvcodec_video_codec.cpp b/src/hwenc_nvcodec/nvcodec_video_codec.cpp new file mode 100644 index 00000000..c35ecf11 --- /dev/null +++ b/src/hwenc_nvcodec/nvcodec_video_codec.cpp @@ -0,0 +1,82 @@ +#include "sora/hwenc_nvcodec/nvcodec_video_codec.h" + +#include "sora/hwenc_nvcodec/nvcodec_video_decoder.h" +#include "sora/hwenc_nvcodec/nvcodec_video_encoder.h" + +#include "../cuda_context_cuda.h" +#include "nvcodec_video_codec_cuda.h" + +namespace sora { + +namespace { + +sora::VideoCodecCapability::Parameters GetParameters( + std::shared_ptr context) { + sora::VideoCodecCapability::Parameters p; +#if defined(_WIN32) + ComPtr idxgi_factory; + if (FAILED(CreateDXGIFactory1(__uuidof(IDXGIFactory1), + (void**)idxgi_factory.GetAddressOf()))) { + return p; + } + ComPtr idxgi_adapter; + if (!FAILED(idxgi_factory->EnumAdapters(0, idxgi_adapter.GetAddressOf()))) { + return p; + } + if (!FAILED(D3D11CreateDevice(idxgi_adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, + NULL, 0, NULL, 0, D3D11_SDK_VERSION, + id3d11_device_.GetAddressOf(), NULL, + id3d11_context_.GetAddressOf()))) { + return p; + } + + // 以下デバイス名を取得するだけの処理 + DXGI_ADAPTER_DESC adapter_desc; + idxgi_adapter->GetDesc(&adapter_desc); + char szDesc[80]; + size_t result = 0; + wcstombs_s(&result, szDesc, adapter_desc.Description, sizeof(szDesc)); + p.nvcodec_gpu_device_name = std::string(szDesc); +#endif +#if defined(__linux__) + char name[80] = {}; + GetNvCodecGpuDeviceName(context, name, sizeof(name)); + if (name[0] != '\0') { + p.nvcodec_gpu_device_name = std::string(name); + } +#endif + + return p; +} + +} // namespace + +VideoCodecCapability::Engine GetNvCodecVideoCodecCapability( + std::shared_ptr context) { + VideoCodecCapability::Engine engine( + VideoCodecImplementation::kNvidiaVideoCodecSdk); + if (context == nullptr) { + return engine; + } + + // engine.parameters.version = + // std::to_string(ver.Major) + "." + std::to_string(ver.Minor); + // engine.parameters.vpl_impl = + // impl == MFX_IMPL_SOFTWARE ? "SOFTWARE" : "HARDWARE"; + // engine.parameters.vpl_impl_value = (int)impl; + + auto add = [&engine, &context](CudaVideoCodec type, + webrtc::VideoCodecType webrtc_type) { + engine.codecs.emplace_back(webrtc_type, + NvCodecVideoEncoder::IsSupported(context, type), + NvCodecVideoDecoder::IsSupported(context, type)); + }; + add(CudaVideoCodec::VP8, webrtc::kVideoCodecVP8); + add(CudaVideoCodec::VP9, webrtc::kVideoCodecVP9); + add(CudaVideoCodec::H264, webrtc::kVideoCodecH264); + add(CudaVideoCodec::H265, webrtc::kVideoCodecH265); + add(CudaVideoCodec::AV1, webrtc::kVideoCodecAV1); + return engine; +} + +} // namespace sora diff --git a/src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp b/src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp new file mode 100644 index 00000000..3432d4c3 --- /dev/null +++ b/src/hwenc_nvcodec/nvcodec_video_codec_cuda.cpp @@ -0,0 +1,23 @@ +#include "nvcodec_video_codec_cuda.h" + +#include "../cuda_context_cuda.h" +#include "sora/dyn/cuda.h" + +namespace sora { + +void GetNvCodecGpuDeviceName(std::shared_ptr context, + char* name, + size_t size) { + if (context == nullptr) { + return; + } + + CUresult r = dyn::cuDeviceGetName(name, size, GetCudaDevice(context)); + if (r != CUDA_SUCCESS) { + // const char* error = NULL; + // dyn::cuGetErrorName(r, &error); + // std::cerr << "Failed to cuDeviceGetName: error=" << error << std::endl; + } +} + +} // namespace sora diff --git a/src/hwenc_nvcodec/nvcodec_video_codec_cuda.h b/src/hwenc_nvcodec/nvcodec_video_codec_cuda.h new file mode 100644 index 00000000..5069437f --- /dev/null +++ b/src/hwenc_nvcodec/nvcodec_video_codec_cuda.h @@ -0,0 +1,14 @@ +#ifndef SORA_HWENC_NVCODEC_NVCODEC_VIDEO_CODEC_CUDA_H_ +#define SORA_HWENC_NVCODEC_NVCODEC_VIDEO_CODEC_CUDA_H_ + +#include "sora/cuda_context.h" + +namespace sora { + +void GetNvCodecGpuDeviceName(std::shared_ptr context, + char* name, + size_t size); + +} // namespace sora + +#endif diff --git a/src/hwenc_nvcodec/nvcodec_video_encoder_cuda.cpp b/src/hwenc_nvcodec/nvcodec_video_encoder_cuda.cpp index a9914d77..634ce043 100644 --- a/src/hwenc_nvcodec/nvcodec_video_encoder_cuda.cpp +++ b/src/hwenc_nvcodec/nvcodec_video_encoder_cuda.cpp @@ -142,7 +142,7 @@ void ShowEncoderCapability() { NvCodecVideoEncoderCudaImpl::NvCodecVideoEncoderCudaImpl( std::shared_ptr ctx) { - ShowEncoderCapability(); + //ShowEncoderCapability(); cuda_context_ = ctx; } NvCodecVideoEncoderCudaImpl::~NvCodecVideoEncoderCudaImpl() {} diff --git a/src/hwenc_vpl/vpl_video_codec.cpp b/src/hwenc_vpl/vpl_video_codec.cpp new file mode 100644 index 00000000..b8d1509a --- /dev/null +++ b/src/hwenc_vpl/vpl_video_codec.cpp @@ -0,0 +1,49 @@ +#include "sora/hwenc_vpl/vpl_video_codec.h" + +#include "sora/hwenc_vpl/vpl_video_decoder.h" +#include "sora/hwenc_vpl/vpl_video_encoder.h" + +#include "../vpl_session_impl.h" + +namespace sora { + +VideoCodecCapability::Engine GetVplVideoCodecCapability( + std::shared_ptr session) { + VideoCodecCapability::Engine engine(VideoCodecImplementation::kIntelVpl); + if (session == nullptr) { + return engine; + } + + mfxStatus sts = MFX_ERR_NONE; + mfxIMPL impl; + sts = MFXQueryIMPL(GetVplSession(session), &impl); + if (sts != MFX_ERR_NONE) { + return engine; + } + + mfxVersion ver; + sts = MFXQueryVersion(GetVplSession(session), &ver); + if (sts != MFX_ERR_NONE) { + return engine; + } + + engine.parameters.version = + std::to_string(ver.Major) + "." + std::to_string(ver.Minor); + engine.parameters.vpl_impl = + impl == MFX_IMPL_SOFTWARE ? "SOFTWARE" : "HARDWARE"; + engine.parameters.vpl_impl_value = (int)impl; + + auto add = [&engine, &session](webrtc::VideoCodecType type) { + engine.codecs.emplace_back(type, + VplVideoEncoder::IsSupported(session, type), + VplVideoDecoder::IsSupported(session, type)); + }; + add(webrtc::kVideoCodecVP8); + add(webrtc::kVideoCodecVP9); + add(webrtc::kVideoCodecH264); + add(webrtc::kVideoCodecH265); + add(webrtc::kVideoCodecAV1); + return engine; +} + +} // namespace sora diff --git a/src/hwenc_vpl/vpl_video_decoder.cpp b/src/hwenc_vpl/vpl_video_decoder.cpp index 606f6ccc..5f137023 100644 --- a/src/hwenc_vpl/vpl_video_decoder.cpp +++ b/src/hwenc_vpl/vpl_video_decoder.cpp @@ -19,7 +19,7 @@ #include #include -#include "vpl_session_impl.h" +#include "../vpl_session_impl.h" #include "vpl_utils.h" namespace sora { diff --git a/src/hwenc_vpl/vpl_video_encoder.cpp b/src/hwenc_vpl/vpl_video_encoder.cpp index 80b08d85..1ca0d8d6 100644 --- a/src/hwenc_vpl/vpl_video_encoder.cpp +++ b/src/hwenc_vpl/vpl_video_encoder.cpp @@ -20,7 +20,7 @@ // libyuv #include -#include "vpl_session_impl.h" +#include "../vpl_session_impl.h" #include "vpl_utils.h" namespace sora { diff --git a/src/open_h264_video_encoder.cpp b/src/open_h264_video_encoder.cpp index e24b73cb..7f278fda 100644 --- a/src/open_h264_video_encoder.cpp +++ b/src/open_h264_video_encoder.cpp @@ -31,8 +31,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -936,4 +936,51 @@ std::unique_ptr CreateOpenH264VideoEncoder( webrtc::CreateEnvironment(), settings, std::move(openh264)); } +VideoCodecCapability::Engine GetOpenH264VideoCodecCapability( + std::optional openh264_path) { + VideoCodecCapability::Engine engine(VideoCodecImplementation::kCiscoOpenH264); + engine.codecs.emplace_back(webrtc::kVideoCodecVP8, false, false); + engine.codecs.emplace_back(webrtc::kVideoCodecVP9, false, false); + engine.codecs.emplace_back(webrtc::kVideoCodecH265, false, false); + engine.codecs.emplace_back(webrtc::kVideoCodecAV1, false, false); + auto& h264 = + engine.codecs.emplace_back(webrtc::kVideoCodecH264, false, false); + if (!openh264_path) { + return engine; + } +#if defined(_WIN32) + HMODULE handle = LoadLibraryA(openh264_path->c_str()); +#else + void* handle = ::dlopen(openh264_path->c_str(), RTLD_LAZY); +#endif + + if (handle == nullptr) { + return engine; + } + typedef void (*WelsGetCodecVersionExFunc)(OpenH264Version* pVersion); +#if defined(_WIN32) + auto f = (WelsGetCodecVersionExFunc)::GetProcAddress(handle, + "WelsGetCodecVersionEx"); + if (f == nullptr) { + FreeLibrary(handle); + return engine; + } +#else + auto f = (WelsGetCodecVersionExFunc)::dlsym(handle, "WelsGetCodecVersionEx"); + if (f == nullptr) { + ::dlclose(handle); + return engine; + } +#endif + + OpenH264Version version; + f(&version); + h264.encoder = true; + h264.parameters.openh264_path = openh264_path; + h264.parameters.version = std::to_string(version.uMajor) + "." + + std::to_string(version.uMinor) + "." + + std::to_string(version.uRevision); + return engine; +} + } // namespace sora diff --git a/src/sora_video_codec.cpp b/src/sora_video_codec.cpp new file mode 100644 index 00000000..7e7a37e1 --- /dev/null +++ b/src/sora_video_codec.cpp @@ -0,0 +1,283 @@ +#include "sora/sora_video_codec.h" + +// WebRTC +#include +#include + +#include "sora/open_h264_video_encoder.h" + +#if defined(SORA_CPP_SDK_IOS) || defined(SORA_CPP_SDK_MACOS) +#include "sora/mac/mac_video_factory.h" +#elif defined(SORA_CPP_SDK_ANDROID) +#include "sora/android/android_video_factory.h" +#endif + +#if defined(USE_VPL_ENCODER) +#include "sora/hwenc_vpl/vpl_video_codec.h" +#endif + +#if defined(USE_NVCODEC_ENCODER) +#include "sora/hwenc_nvcodec/nvcodec_video_codec.h" +#endif + +namespace webrtc { + +// VideoCodecType +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecType& v) { + jv = CodecTypeToPayloadString(v); +} +VideoCodecType tag_invoke(const boost::json::value_to_tag&, + boost::json::value const& jv) { + return PayloadStringToCodecType(jv.as_string().c_str()); +} + +} // namespace webrtc + +namespace sora { + +// VideoCodecImplementation +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecImplementation& v) { + switch (v) { + case VideoCodecImplementation::kInternal: + jv = "internal"; + break; + case VideoCodecImplementation::kCiscoOpenH264: + jv = "cisco_openh264"; + break; + case VideoCodecImplementation::kIntelVpl: + jv = "intel_vpl"; + break; + case VideoCodecImplementation::kNvidiaVideoCodecSdk: + jv = "nvidia_video_codec_sdk"; + break; + } +} +VideoCodecImplementation tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv) { + if (jv.is_string()) { + std::string s = jv.as_string().c_str(); + if (s == "internal") { + return VideoCodecImplementation::kInternal; + } + if (s == "cisco_openh264") { + return VideoCodecImplementation::kCiscoOpenH264; + } + if (s == "intel_vpl") { + return VideoCodecImplementation::kIntelVpl; + } + if (s == "nvidia_video_codec_sdk") { + return VideoCodecImplementation::kNvidiaVideoCodecSdk; + } + } + throw std::invalid_argument("invalid VideoCodecImplementation"); +} + +// VideoCodecCapability::Parameters +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability::Parameters& v) { + auto& jo = jv.emplace_object(); + if (v.version) { + jo["version"] = *v.version; + } + if (v.openh264_path) { + jo["openh264_path"] = *v.openh264_path; + } +} + +VideoCodecCapability::Parameters tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv) { + VideoCodecCapability::Parameters r; + if (jv.is_object()) { + if (jv.at("version").is_string()) { + r.version = jv.at("version").as_string().c_str(); + } + if (jv.at("openh264_path").is_string()) { + r.openh264_path = jv.at("openh264_path").as_string().c_str(); + } + } + return r; +} +// VideoCodecCapability::Codec +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability::Codec& v) { + auto& jo = jv.emplace_object(); + jo["type"] = boost::json::value_from(v.type); + jo["decoder"] = v.decoder; + jo["encoder"] = v.encoder; + jo["parameters"] = boost::json::value_from(v.parameters); +} +VideoCodecCapability::Codec tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv) { + VideoCodecCapability::Codec r(webrtc::kVideoCodecGeneric, false, false); + if (!jv.is_object()) { + throw std::invalid_argument("invalid VideoCodecCapability::Codec"); + } + if (jv.at("type").is_string()) { + r.type = boost::json::value_to(jv.at("type")); + } + if (jv.at("decoder").is_bool()) { + r.decoder = jv.at("decoder").as_bool(); + } + if (jv.at("encoder").is_bool()) { + r.encoder = jv.at("encoder").as_bool(); + } + if (jv.at("params").is_object()) { + r.parameters = boost::json::value_to( + jv.at("parameters")); + } + return r; +} +// VideoCodecCapability::Engine +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability::Engine& v) { + auto& jo = jv.emplace_object(); + jo["name"] = boost::json::value_from(v.name); + auto& ja = jo["codecs"].emplace_array(); + for (const auto& codec : v.codecs) { + ja.push_back(boost::json::value_from(codec)); + } + jo["parameters"] = boost::json::value_from(v.parameters); +} +VideoCodecCapability::Engine tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv) { + VideoCodecCapability::Engine r(VideoCodecImplementation::kInternal); + if (!jv.is_object()) { + throw std::invalid_argument("invalid VideoCodecCapability::Engine"); + } + if (jv.at("name").is_string()) { + r.name = boost::json::value_to(jv.at("name")); + } + if (jv.at("codecs").is_array()) { + for (const auto& codec : jv.at("codecs").as_array()) { + r.codecs.push_back( + boost::json::value_to(codec)); + } + } + if (jv.at("parameters").is_object()) { + r.parameters = boost::json::value_to( + jv.at("parameters")); + } + return r; +} +// VideoCodecCapability +void tag_invoke(const boost::json::value_from_tag&, + boost::json::value& jv, + const VideoCodecCapability& v) { + auto& jo = jv.emplace_object(); + auto& ja = jo["engines"].emplace_array(); + for (const auto& engine : v.engines) { + ja.push_back(boost::json::value_from(engine)); + } +} +VideoCodecCapability tag_invoke( + const boost::json::value_to_tag&, + boost::json::value const& jv) { + VideoCodecCapability r; + if (!jv.is_object()) { + throw std::invalid_argument("invalid VideoCodecCapability"); + } + if (!jv.at("engines").is_array()) { + throw std::invalid_argument("invalid VideoCodecCapability"); + } + for (const auto& engine : jv.at("engines").as_array()) { + r.engines.push_back( + boost::json::value_to(engine)); + } + return r; +} + +VideoCodecCapability GetVideoCodecCapability( + VideoCodecCapabilityConfig config) { + VideoCodecCapability cap; + // kInternal + { + auto& engine = + cap.engines.emplace_back(VideoCodecImplementation::kInternal); +#if defined(SORA_CPP_SDK_IOS) || defined(SORA_CPP_SDK_MACOS) + auto internal_encoder_factory = CreateMacVideoEncoderFactory(); + auto internal_decoder_factory = CreateMacVideoDecoderFactory(); +#elif defined(SORA_CPP_SDK_ANDROID) + auto internal_encoder_factory = + config.jni_env == nullptr ? nullptr + : CreateAndroidVideoEncoderFactory( + static_cast(config.jni_env)); + auto internal_decoder_factory = + config.jni_env == nullptr ? nullptr + : CreateAndroidVideoDecoderFactory( + static_cast(config.jni_env)); +#else + auto internal_encoder_factory = webrtc::CreateBuiltinVideoEncoderFactory(); + auto internal_decoder_factory = webrtc::CreateBuiltinVideoDecoderFactory(); +#endif + if (internal_encoder_factory && internal_decoder_factory) { + auto encoder_formats = internal_encoder_factory->GetSupportedFormats(); + auto decoder_formats = internal_decoder_factory->GetSupportedFormats(); + auto has_format = [](const std::vector& formats, + webrtc::VideoCodecType type) { + for (const auto& format : formats) { + if (webrtc::PayloadStringToCodecType(format.name) == type) { + return true; + } + } + return false; + }; + engine.codecs.emplace_back( + webrtc::kVideoCodecVP8, + has_format(encoder_formats, webrtc::kVideoCodecVP8), + has_format(decoder_formats, webrtc::kVideoCodecVP8)); + engine.codecs.emplace_back( + webrtc::kVideoCodecVP9, + has_format(encoder_formats, webrtc::kVideoCodecVP9), + has_format(decoder_formats, webrtc::kVideoCodecVP9)); + engine.codecs.emplace_back( + webrtc::kVideoCodecH264, + has_format(encoder_formats, webrtc::kVideoCodecH264), + has_format(decoder_formats, webrtc::kVideoCodecH264)); + engine.codecs.emplace_back( + webrtc::kVideoCodecH265, + has_format(encoder_formats, webrtc::kVideoCodecH265), + has_format(decoder_formats, webrtc::kVideoCodecH265)); + engine.codecs.emplace_back( + webrtc::kVideoCodecAV1, + has_format(encoder_formats, webrtc::kVideoCodecAV1), + has_format(decoder_formats, webrtc::kVideoCodecAV1)); + } + } + + // kCiscoOpenH264 + cap.engines.push_back(GetOpenH264VideoCodecCapability(config.openh264_path)); + +#if defined(USE_VPL_ENCODER) + cap.engines.push_back(GetVplVideoCodecCapability(config.vpl_session)); +#endif + +#if defined(USE_NVCODEC_ENCODER) + cap.engines.push_back(GetNvCodecVideoCodecCapability(config.cuda_context)); +#endif + + // 全て false のエンジンを削除 + cap.engines.erase( + std::remove_if(cap.engines.begin(), cap.engines.end(), + [](const VideoCodecCapability::Engine& engine) { + return std::all_of( + engine.codecs.begin(), engine.codecs.end(), + [](const VideoCodecCapability::Codec& codec) { + return !codec.decoder && !codec.encoder; + }); + }), + cap.engines.end()); + return cap; +} + +} // namespace sora diff --git a/src/hwenc_vpl/vpl_session_impl.cpp b/src/vpl_session_impl.cpp similarity index 92% rename from src/hwenc_vpl/vpl_session_impl.cpp rename to src/vpl_session_impl.cpp index fbb8c6b7..25197bbf 100644 --- a/src/hwenc_vpl/vpl_session_impl.cpp +++ b/src/vpl_session_impl.cpp @@ -1,4 +1,12 @@ -#include "sora/hwenc_vpl/vpl_session.h" +#include "sora/vpl_session.h" + +#if !defined(USE_VPL_ENCODER) + +std::shared_ptr VplSession::Create() { + return nullptr; +} + +#else #include @@ -65,4 +73,6 @@ mfxSession GetVplSession(std::shared_ptr session) { return std::static_pointer_cast(session)->session; } +#endif + } // namespace sora diff --git a/src/hwenc_vpl/vpl_session_impl.h b/src/vpl_session_impl.h similarity index 56% rename from src/hwenc_vpl/vpl_session_impl.h rename to src/vpl_session_impl.h index 579359ae..84014293 100644 --- a/src/hwenc_vpl/vpl_session_impl.h +++ b/src/vpl_session_impl.h @@ -1,12 +1,14 @@ -#ifndef SORA_HWENC_VPL_VPL_SESSION_IMPL_H_ -#define SORA_HWENC_VPL_VPL_SESSION_IMPL_H_ +#ifndef SORA_VPL_SESSION_IMPL_H_ +#define SORA_VPL_SESSION_IMPL_H_ + +#if defined(USE_VPL_ENCODER) #include // Intel VPL #include -#include "sora/hwenc_vpl/vpl_session.h" +#include "sora/vpl_session.h" namespace sora { @@ -14,4 +16,6 @@ mfxSession GetVplSession(std::shared_ptr session); } // namespace sora +#endif + #endif \ No newline at end of file diff --git a/test/hello.cpp b/test/hello.cpp index 5fc5289d..7cd82638 100644 --- a/test/hello.cpp +++ b/test/hello.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -151,145 +152,154 @@ int main(int argc, char* argv[]) { opt.allow_trailing_commas = true; v = boost::json::parse(js, {}, opt); } - HelloSoraConfig config; - for (auto&& x : v.as_object().at("signaling_urls").as_array()) { - config.signaling_urls.push_back(x.as_string().c_str()); - } - config.channel_id = v.as_object().at("channel_id").as_string().c_str(); - boost::json::value x; - auto get = [](const boost::json::value& v, const char* key, - boost::json::value& x) -> bool { - if (auto it = v.as_object().find(key); - it != v.as_object().end() && !it->value().is_null()) { - x = it->value(); - return true; - } - return false; - }; - if (get(v, "role", x)) { - config.role = x.as_string(); - } - if (get(v, "video", x)) { - config.video = x.as_bool(); - } - if (get(v, "audio", x)) { - config.audio = x.as_bool(); - } - if (get(v, "capture_width", x)) { - config.capture_width = x.to_number(); - } - if (get(v, "capture_height", x)) { - config.capture_height = x.to_number(); - } - if (get(v, "video_bit_rate", x)) { - config.video_bit_rate = x.to_number(); - } - if (get(v, "video_codec_type", x)) { - config.video_codec_type = x.as_string(); - } - if (get(v, "simulcast", x)) { - config.simulcast = x.as_bool(); - } - if (get(v, "client_id", x)) { - config.client_id = x.as_string(); - } - if (get(v, "data_channel_signaling", x)) { - config.data_channel_signaling = x.as_bool(); - } - if (get(v, "ignore_disconnect_websocket", x)) { - config.ignore_disconnect_websocket = x.as_bool(); - } - if (get(v, "data_channels", x)) { - for (auto&& dc : x.as_array()) { - sora::SoraSignalingConfig::DataChannel data_channel; - data_channel.label = dc.as_object().at("label").as_string(); - data_channel.direction = dc.as_object().at("direction").as_string(); - boost::json::value y; - if (get(dc, "ordered", y)) { - data_channel.ordered = y.as_bool(); - } - if (get(dc, "max_packet_life_time", y)) { - data_channel.max_packet_life_time = y.to_number(); - } - if (get(dc, "max_retransmits", y)) { - data_channel.max_retransmits = y.to_number(); - } - if (get(dc, "protocol", y)) { - data_channel.protocol = y.as_string().c_str(); - } - if (get(dc, "compress", y)) { - data_channel.compress = y.as_bool(); - } - if (get(dc, "header", y)) { - data_channel.header.emplace(y.as_array().begin(), y.as_array().end()); - } - config.data_channels.push_back(data_channel); - } - } - if (get(v, "forwarding_filters", x)) { - for (auto&& ff : x.as_array()) { - sora::SoraSignalingConfig::ForwardingFilter forwarding_filter; - boost::json::value y; - if (get(ff, "name", y)) { - forwarding_filter.name.emplace(y.as_string()); - } - if (get(ff, "priority", y)) { - forwarding_filter.priority.emplace(y.to_number()); - } - if (get(ff, "action", y)) { - forwarding_filter.action.emplace(y.as_string()); - } - for (auto&& rs : ff.as_object().at("rules").as_array()) { - std::vector rules; - for (auto&& r : rs.as_array()) { - sora::SoraSignalingConfig::ForwardingFilter::Rule rule; - rule.field = r.as_object().at("field").as_string(); - rule.op = r.as_object().at("operator").as_string(); - for (auto&& v : r.as_object().at("values").as_array()) { - rule.values.push_back(v.as_string().c_str()); - } - rules.push_back(rule); - } - forwarding_filter.rules.push_back(rules); - } - if (get(ff, "version", y)) { - forwarding_filter.version.emplace(y.as_string()); - } - if (get(ff, "metadata", y)) { - forwarding_filter.metadata = y; - } - config.forwarding_filters.push_back(forwarding_filter); - } - } - if (get(v, "log_level", x)) { - rtc::LogMessage::LogToDebug((rtc::LoggingSeverity)x.to_number()); - } - if (get(v, "degradation_preference", x)) { - if (x.as_string() == "disabled") { - config.degradation_preference = webrtc::DegradationPreference::DISABLED; - } else if (x.as_string() == "maintain_framerate") { - config.degradation_preference = - webrtc::DegradationPreference::MAINTAIN_FRAMERATE; - } else if (x.as_string() == "maintain_resolution") { - config.degradation_preference = - webrtc::DegradationPreference::MAINTAIN_RESOLUTION; - } else if (x.as_string() == "balanced") { - config.degradation_preference = webrtc::DegradationPreference::BALANCED; - } - } - sora::SoraClientContextConfig context_config; - context_config.get_android_application_context = GetAndroidApplicationContext; - if (get(v, "use_hardware_encoder", x)) { - context_config.use_hardware_encoder = x.as_bool(); - } - if (get(v, "openh264", x)) { - context_config.openh264 = x.as_string(); + sora::VideoCodecCapabilityConfig config; + if (auto it = v.as_object().find("openh264"); + it != v.as_object().end() && !it->value().is_null()) { + config.openh264_path.emplace(it->value().as_string().c_str()); } - auto context = sora::SoraClientContext::Create(context_config); + auto cap = sora::GetVideoCodecCapability(config); + RTC_LOG(LS_ERROR) << boost::json::serialize(boost::json::value_from(cap)); + + //HelloSoraConfig config; + //for (auto&& x : v.as_object().at("signaling_urls").as_array()) { + // config.signaling_urls.push_back(x.as_string().c_str()); + //} + //config.channel_id = v.as_object().at("channel_id").as_string().c_str(); + //boost::json::value x; + //auto get = [](const boost::json::value& v, const char* key, + // boost::json::value& x) -> bool { + // if (auto it = v.as_object().find(key); + // it != v.as_object().end() && !it->value().is_null()) { + // x = it->value(); + // return true; + // } + // return false; + //}; + //if (get(v, "role", x)) { + // config.role = x.as_string(); + //} + //if (get(v, "video", x)) { + // config.video = x.as_bool(); + //} + //if (get(v, "audio", x)) { + // config.audio = x.as_bool(); + //} + //if (get(v, "capture_width", x)) { + // config.capture_width = x.to_number(); + //} + //if (get(v, "capture_height", x)) { + // config.capture_height = x.to_number(); + //} + //if (get(v, "video_bit_rate", x)) { + // config.video_bit_rate = x.to_number(); + //} + //if (get(v, "video_codec_type", x)) { + // config.video_codec_type = x.as_string(); + //} + //if (get(v, "simulcast", x)) { + // config.simulcast = x.as_bool(); + //} + //if (get(v, "client_id", x)) { + // config.client_id = x.as_string(); + //} + //if (get(v, "data_channel_signaling", x)) { + // config.data_channel_signaling = x.as_bool(); + //} + //if (get(v, "ignore_disconnect_websocket", x)) { + // config.ignore_disconnect_websocket = x.as_bool(); + //} + //if (get(v, "data_channels", x)) { + // for (auto&& dc : x.as_array()) { + // sora::SoraSignalingConfig::DataChannel data_channel; + // data_channel.label = dc.as_object().at("label").as_string(); + // data_channel.direction = dc.as_object().at("direction").as_string(); + // boost::json::value y; + // if (get(dc, "ordered", y)) { + // data_channel.ordered = y.as_bool(); + // } + // if (get(dc, "max_packet_life_time", y)) { + // data_channel.max_packet_life_time = y.to_number(); + // } + // if (get(dc, "max_retransmits", y)) { + // data_channel.max_retransmits = y.to_number(); + // } + // if (get(dc, "protocol", y)) { + // data_channel.protocol = y.as_string().c_str(); + // } + // if (get(dc, "compress", y)) { + // data_channel.compress = y.as_bool(); + // } + // if (get(dc, "header", y)) { + // data_channel.header.emplace(y.as_array().begin(), y.as_array().end()); + // } + // config.data_channels.push_back(data_channel); + // } + //} + //if (get(v, "forwarding_filters", x)) { + // for (auto&& ff : x.as_array()) { + // sora::SoraSignalingConfig::ForwardingFilter forwarding_filter; + // boost::json::value y; + // if (get(ff, "name", y)) { + // forwarding_filter.name.emplace(y.as_string()); + // } + // if (get(ff, "priority", y)) { + // forwarding_filter.priority.emplace(y.to_number()); + // } + // if (get(ff, "action", y)) { + // forwarding_filter.action.emplace(y.as_string()); + // } + // for (auto&& rs : ff.as_object().at("rules").as_array()) { + // std::vector rules; + // for (auto&& r : rs.as_array()) { + // sora::SoraSignalingConfig::ForwardingFilter::Rule rule; + // rule.field = r.as_object().at("field").as_string(); + // rule.op = r.as_object().at("operator").as_string(); + // for (auto&& v : r.as_object().at("values").as_array()) { + // rule.values.push_back(v.as_string().c_str()); + // } + // rules.push_back(rule); + // } + // forwarding_filter.rules.push_back(rules); + // } + // if (get(ff, "version", y)) { + // forwarding_filter.version.emplace(y.as_string()); + // } + // if (get(ff, "metadata", y)) { + // forwarding_filter.metadata = y; + // } + // config.forwarding_filters.push_back(forwarding_filter); + // } + //} + //if (get(v, "log_level", x)) { + // rtc::LogMessage::LogToDebug((rtc::LoggingSeverity)x.to_number()); + //} + //if (get(v, "degradation_preference", x)) { + // if (x.as_string() == "disabled") { + // config.degradation_preference = webrtc::DegradationPreference::DISABLED; + // } else if (x.as_string() == "maintain_framerate") { + // config.degradation_preference = + // webrtc::DegradationPreference::MAINTAIN_FRAMERATE; + // } else if (x.as_string() == "maintain_resolution") { + // config.degradation_preference = + // webrtc::DegradationPreference::MAINTAIN_RESOLUTION; + // } else if (x.as_string() == "balanced") { + // config.degradation_preference = webrtc::DegradationPreference::BALANCED; + // } + //} + + //sora::SoraClientContextConfig context_config; + //context_config.get_android_application_context = GetAndroidApplicationContext; + //if (get(v, "use_hardware_encoder", x)) { + // context_config.use_hardware_encoder = x.as_bool(); + //} + //if (get(v, "openh264", x)) { + // context_config.openh264 = x.as_string(); + //} + //auto context = sora::SoraClientContext::Create(context_config); - auto hello = std::make_shared(context, config); - hello->Run(); + //auto hello = std::make_shared(context, config); + //hello->Run(); } #endif