[idd] network: make video chroma configurable

This commit is contained in:
Geoffrey McRae
2026-10-01 16:17:15 +10:00
parent 967b43f4a8
commit 6aea2496f0
5 changed files with 82 additions and 37 deletions

View File

@@ -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<unsigned>(m_settings.port),
static_cast<unsigned>(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<LGNetVideoChromaSubsampling>(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();

View File

@@ -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<bool> 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<LGNetVideoChromaSubsampling>(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<const FrameCaps> CNetworkVideo::GetFrameCaps() const
{
static const std::shared_ptr<const FrameCaps> caps(
new (std::nothrow) NetworkVideoFrameCaps());
return caps;
if (!m_impl)
return std::shared_ptr<const FrameCaps>();
LGNetVideoChromaSubsampling chroma;
{
CSRWSharedLock lock(m_impl->m_lock);
chroma = m_impl->m_videoChroma;
}
return std::shared_ptr<const FrameCaps>(
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;

View File

@@ -96,7 +96,7 @@ public:
bool AttachSender(INetworkVideoSender& sender);
void DetachSender();
bool Start();
bool Start(LGNetVideoChromaSubsampling videoChroma);
void Stop();
void Process(uint64_t nowMs);

View File

@@ -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<LGNetVideoChromaSubsampling>(
LG_NET_VIDEO_CHROMA_420);
else if (Equal(value, L"444"))
settings.videoChroma =
static_cast<LGNetVideoChromaSubsampling>(
LG_NET_VIDEO_CHROMA_444);
else
return Fail(error, L"invalid video chroma subsampling");
}
else
return Fail(error, L"unknown setting");

View File

@@ -25,21 +25,27 @@
#include <stdint.h>
#include <string>
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<LGNetVideoChromaSubsampling>(LG_NET_VIDEO_CHROMA_444);
struct NetworkSettings
{
std::wstring bindAddress = L"*";
uint16_t port = static_cast<uint16_t>(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<uint16_t>(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);