diff --git a/idd/LGIdd/transport/network/CNetworkTransport.cpp b/idd/LGIdd/transport/network/CNetworkTransport.cpp index 8612f4d7..f0abf0af 100644 --- a/idd/LGIdd/transport/network/CNetworkTransport.cpp +++ b/idd/LGIdd/transport/network/CNetworkTransport.cpp @@ -58,11 +58,14 @@ ITransport::OpenResult CNetworkTransport::Open() m_settings = std::move(settings); m_state = State::OPEN; DEBUG_INFO("Network transport configured on [%ls]:%u for up to %u " - "clients (%ls authentication)", + "clients (%ls authentication, %ls video)", m_settings.bindAddress.c_str(), static_cast(m_settings.port), static_cast(m_settings.maxClients), - m_settings.auth == LG_NET_AUTH_PASSWORD ? L"password" : L"none"); + m_settings.auth == LG_NET_AUTH_PASSWORD ? L"password" : L"none", + m_settings.videoChroma == + static_cast(LG_NET_VIDEO_CHROMA_420) ? + L"4:2:0" : L"4:4:4"); return OpenResult::SUCCESS; } @@ -106,7 +109,8 @@ bool CNetworkTransport::Initialize() m_server.reset(); return false; } - if ((m_config.services & TRANSPORT_SERVICE_FRAME) && !m_video.Start()) + if ((m_config.services & TRANSPORT_SERVICE_FRAME) && + !m_video.Start(m_settings.videoChroma)) { DEBUG_ERROR("Failed to start the network video endpoint"); m_video.DetachSender(); diff --git a/idd/LGIdd/transport/network/CNetworkVideo.cpp b/idd/LGIdd/transport/network/CNetworkVideo.cpp index b3949186..6faaa683 100644 --- a/idd/LGIdd/transport/network/CNetworkVideo.cpp +++ b/idd/LGIdd/transport/network/CNetworkVideo.cpp @@ -141,6 +141,8 @@ namespace { if (!width || !height || width > MAX_WIDTH || height > MAX_HEIGHT) return false; + if (chroma == CHROMA_420 && ((width | height) & 1U)) + return false; const uint32_t alignedWidth = (std::max)( Align(width, PYROWAVE_ALIGNMENT), PYROWAVE_MIN_DIMENSION); const uint32_t alignedHeight = (std::max)( @@ -157,15 +159,22 @@ namespace class NetworkVideoFrameCaps final : public FrameCaps { + private: + const LGNetVideoChromaSubsampling m_chroma; + public: + explicit NetworkVideoFrameCaps(LGNetVideoChromaSubsampling chroma) : + m_chroma(chroma) + { + } + bool CanUseMode(const FrameMode& mode, uint32_t * requiredSizeMiB = nullptr) const override { if (requiredSizeMiB) *requiredSizeMiB = 0; return mode.width && mode.height && mode.refresh100uHz && - PlaneElementsFit(mode.width, mode.height, - CHROMA_444); + PlaneElementsFit(mode.width, mode.height, m_chroma); } }; @@ -2675,11 +2684,12 @@ struct CNetworkVideo::Impl HANDLE m_workerThread = nullptr; std::atomic m_workerStop = false; - INetworkVideoSender * m_sender = nullptr; - bool m_started = false; - bool m_stopped = false; - bool m_failed = false; - bool m_changing = false; + INetworkVideoSender * m_sender = nullptr; + LGNetVideoChromaSubsampling m_videoChroma = CHROMA_444; + bool m_started = false; + bool m_stopped = false; + bool m_failed = false; + bool m_changing = false; FrameCfg m_activeCfg = {}; FrameCfg m_pendingCfg = {}; @@ -2806,7 +2816,7 @@ struct CNetworkVideo::Impl { if (!m_active) return static_cast(0); - return CHROMA_444; + return m_videoChroma; } LGNetVideoStreamConfig StreamConfigNL() const @@ -3540,8 +3550,9 @@ struct CNetworkVideo::Impl bool SubmitPending() { - PendingFrame pending; - uint32_t frameLimit = MAX_FRAME_LENGTH; + PendingFrame pending; + LGNetVideoChromaSubsampling chroma = CHROMA_444; + uint32_t frameLimit = MAX_FRAME_LENGTH; { CSRWExclusiveLock lock(m_lock); if (m_encoder.Busy() || m_frames.empty() || !m_started || @@ -3562,11 +3573,12 @@ struct CNetworkVideo::Impl } if (!configured) return true; + chroma = m_videoChroma; } if (m_encoder.Submit(std::move(pending.frame), std::move(pending.lease), pending.cfg, - CHROMA_444, + chroma, frameLimit)) return true; @@ -3791,15 +3803,17 @@ void CNetworkVideo::DetachSender() m_impl->m_peers.clear(); } -bool CNetworkVideo::Start() +bool CNetworkVideo::Start(LGNetVideoChromaSubsampling videoChroma) { - if (!m_impl || !m_impl->WorkerReady()) + if (!m_impl || !m_impl->WorkerReady() || + (videoChroma != CHROMA_420 && videoChroma != CHROMA_444)) return false; { CSRWExclusiveLock lock(m_impl->m_lock); if (m_impl->m_stopped || m_impl->m_failed) return false; - m_impl->m_started = true; + m_impl->m_videoChroma = videoChroma; + m_impl->m_started = true; } m_impl->Wake(); return true; @@ -4084,9 +4098,15 @@ const FrameProfile * CNetworkVideo::Profiles(unsigned& count) const std::shared_ptr CNetworkVideo::GetFrameCaps() const { - static const std::shared_ptr caps( - new (std::nothrow) NetworkVideoFrameCaps()); - return caps; + if (!m_impl) + return std::shared_ptr(); + LGNetVideoChromaSubsampling chroma; + { + CSRWSharedLock lock(m_impl->m_lock); + chroma = m_impl->m_videoChroma; + } + return std::shared_ptr( + new (std::nothrow) NetworkVideoFrameCaps(chroma)); } CfgResult CNetworkVideo::Probe(const FrameCfg& cfg) const @@ -4100,7 +4120,7 @@ CfgResult CNetworkVideo::Probe(const FrameCfg& cfg) const cfg.width > MAX_WIDTH || cfg.height > MAX_HEIGHT || !SupportedProfile(cfg.profile)) return CfgResult::NEXT; - const LGNetVideoChromaSubsampling chroma = CHROMA_444; + const LGNetVideoChromaSubsampling chroma = m_impl->m_videoChroma; if (!PlaneElementsFit(cfg.width, cfg.height, chroma)) return CfgResult::NEXT; return CfgResult::ACCEPTED; diff --git a/idd/LGIdd/transport/network/CNetworkVideo.h b/idd/LGIdd/transport/network/CNetworkVideo.h index 001921d9..9890a036 100644 --- a/idd/LGIdd/transport/network/CNetworkVideo.h +++ b/idd/LGIdd/transport/network/CNetworkVideo.h @@ -96,7 +96,7 @@ public: bool AttachSender(INetworkVideoSender& sender); void DetachSender(); - bool Start(); + bool Start(LGNetVideoChromaSubsampling videoChroma); void Stop(); void Process(uint64_t nowMs); diff --git a/idd/LGIdd/transport/network/NetworkSettings.cpp b/idd/LGIdd/transport/network/NetworkSettings.cpp index 217e6959..7113a1c8 100644 --- a/idd/LGIdd/transport/network/NetworkSettings.cpp +++ b/idd/LGIdd/transport/network/NetworkSettings.cpp @@ -29,11 +29,12 @@ namespace { enum Field : unsigned { - FIELD_BIND = 1U << 0, - FIELD_PORT = 1U << 1, - FIELD_MAX_CLIENTS = 1U << 2, - FIELD_AUTH = 1U << 3, - FIELD_PASSWORD = 1U << 4, + FIELD_BIND = 1U << 0, + FIELD_PORT = 1U << 1, + FIELD_MAX_CLIENTS = 1U << 2, + FIELD_AUTH = 1U << 3, + FIELD_PASSWORD = 1U << 4, + FIELD_VIDEO_CHROMA = 1U << 5, }; std::wstring Trim(const std::wstring& value) @@ -137,6 +138,20 @@ namespace return Fail(error, L"invalid password"); settings.password = value; } + else if (Equal(key, L"videoChroma")) + { + field = FIELD_VIDEO_CHROMA; + if (Equal(value, L"420")) + settings.videoChroma = + static_cast( + LG_NET_VIDEO_CHROMA_420); + else if (Equal(value, L"444")) + settings.videoChroma = + static_cast( + LG_NET_VIDEO_CHROMA_444); + else + return Fail(error, L"invalid video chroma subsampling"); + } else return Fail(error, L"unknown setting"); diff --git a/idd/LGIdd/transport/network/NetworkSettings.h b/idd/LGIdd/transport/network/NetworkSettings.h index fed25efc..becdfed4 100644 --- a/idd/LGIdd/transport/network/NetworkSettings.h +++ b/idd/LGIdd/transport/network/NetworkSettings.h @@ -25,21 +25,27 @@ #include #include -static constexpr uint16_t NETWORK_DEFAULT_MAX_CLIENTS = 4; -static constexpr uint16_t NETWORK_MAX_CLIENTS = 32; +static constexpr uint16_t NETWORK_DEFAULT_MAX_CLIENTS = 4; +static constexpr uint16_t NETWORK_MAX_CLIENTS = 32; +static constexpr LGNetVideoChromaSubsampling NETWORK_DEFAULT_VIDEO_CHROMA = + static_cast(LG_NET_VIDEO_CHROMA_444); struct NetworkSettings { - std::wstring bindAddress = L"*"; - uint16_t port = static_cast(LG_NET_DEFAULT_PORT); - uint16_t maxClients = NETWORK_DEFAULT_MAX_CLIENTS; - LGNetAuthMode auth = LG_NET_AUTH_MODE_DEFAULT; - std::wstring password; + std::wstring bindAddress = L"*"; + uint16_t port = + static_cast(LG_NET_DEFAULT_PORT); + uint16_t maxClients = NETWORK_DEFAULT_MAX_CLIENTS; + LGNetAuthMode auth = LG_NET_AUTH_MODE_DEFAULT; + std::wstring password; + LGNetVideoChromaSubsampling videoChroma = + NETWORK_DEFAULT_VIDEO_CHROMA; }; // Settings use a semicolon-separated key=value list. Supported keys are -// bind, port, maxClients, auth, and password. Password authentication requires -// a non-empty password. The default address requests a dual-stack wildcard -// listener from the eventual QUIC provider. +// bind, port, maxClients, auth, password, and videoChroma. videoChroma accepts +// 420 or 444 and defaults to 444. Password authentication requires a non-empty +// password. The default address requests a dual-stack wildcard listener from +// the eventual QUIC provider. bool ParseNetworkSettings(const std::wstring& source, NetworkSettings& settings, std::wstring * error = nullptr);