| Message ID | 20261019224902.177382-4-robert.mader@collabora.com |
|---|---|
| State | New |
| Headers | show |
| Series |
|
| Related | show |
On Tue, Oct 20, 2026 at 12:49:02AM +0200, Robert Mader wrote: > While NV12 is the most common native format for hardware video encoders, > YUV420 is the equivalent for most software video encoders. Thus > supporting both is great for testing. > > Similarly having some RGB formats with and without alpha can be very > useful, especially as the swISP is currently limited to such formats. > > Signed-off-by: Robert Mader <robert.mader@collabora.com> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > Reviewed-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > --- > .../pipeline/virtual/frame_generator.h | 3 +- > .../virtual/image_frame_generator.cpp | 61 ++++++++++++++++--- > .../pipeline/virtual/image_frame_generator.h | 2 +- > .../virtual/test_pattern_generator.cpp | 52 +++++++++++++--- > .../pipeline/virtual/test_pattern_generator.h | 2 +- > src/libcamera/pipeline/virtual/virtual.cpp | 45 +++++++++++--- > 6 files changed, 135 insertions(+), 30 deletions(-) > > diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h > index a0658c45cd82..fd34a91a5e50 100644 > --- a/src/libcamera/pipeline/virtual/frame_generator.h > +++ b/src/libcamera/pipeline/virtual/frame_generator.h > @@ -9,6 +9,7 @@ > > #include <libcamera/framebuffer.h> > #include <libcamera/geometry.h> > +#include <libcamera/stream.h> > > namespace libcamera { > > @@ -19,7 +20,7 @@ public: > > virtual void configure(const Size &size) = 0; > > - virtual int generateFrame(const Size &size, > + virtual int generateFrame(const StreamConfiguration &cfg, > const FrameBuffer *buffer) = 0; > > protected: > diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.cpp b/src/libcamera/pipeline/virtual/image_frame_generator.cpp > index 5caad8b72763..7a9bc9ac1ebd 100644 > --- a/src/libcamera/pipeline/virtual/image_frame_generator.cpp > +++ b/src/libcamera/pipeline/virtual/image_frame_generator.cpp > @@ -126,7 +126,7 @@ void ImageFrameGenerator::configure(const Size &size) > } > } > > -int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buffer) > +int ImageFrameGenerator::generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) > { > ASSERT(!scaledFrameDatas_.empty()); > > @@ -143,17 +143,62 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff > MappedFrameBuffer::MapFlag::Write); > > const auto &planes = mappedFrameBuffer.planes(); > - const PixelFormatInfo &info = PixelFormatInfo::info(formats::NV12); > + const Size size = cfg.size; Sorry, I forgot the & in my review of v4: const Size &size = cfg.size; > + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); > > /* Loop only around the number of images available */ > frameIndex_ %= imageFrameDatas_.size(); > > + int ret; > + auto Y = scaledFrameDatas_[frameIndex_].Y.get(); > + auto UV = scaledFrameDatas_[frameIndex_].UV.get(); Lowercase for variables please. > + > /* Write the scaledY and scaledUV to the mapped frame buffer */ > - libyuv::NV12Copy(scaledFrameDatas_[frameIndex_].Y.get(), size.width, > - scaledFrameDatas_[frameIndex_].UV.get(), size.width, > - planes[0].data(), info.stride(size.width, 0), > - planes[1].data(), info.stride(size.width, 1), > - size.width, size.height); > + switch (cfg.pixelFormat) { > + case formats::NV12: > + ret = libyuv::NV12Copy(Y, > + size.width, > + UV, > + size.width, > + planes[0].data(), > + info.stride(size.width, 0), > + planes[1].data(), > + info.stride(size.width, 1), > + size.width, size.height); I think the following would be more readable as it groups related arguments without making lines too long: ret = libyuv::NV12Copy(Y, size.width, UV, size.width, planes[0].data(), info.stride(size.width, 0), planes[1].data(), info.stride(size.width, 1), size.width, size.height); Same below. > + break; > + > + case formats::YUV420: > + ret = libyuv::NV12ToI420(Y, > + size.width, > + UV, > + size.width, > + planes[0].data(), > + info.stride(size.width, 0), > + planes[1].data(), > + info.stride(size.width, 1), > + planes[2].data(), > + info.stride(size.width, 2), > + size.width, size.height); > + break; > + > + case formats::ABGR8888: > + case formats::XBGR8888: > + ret = libyuv::NV12ToABGR(Y, > + size.width, > + UV, > + size.width, > + planes[0].data(), > + info.stride(size.width, 0), > + size.width, size.height); > + break; > + > + default: > + LOG(Virtual, Error) << "Unsupported format"; > + return -1; > + } > + > + if (ret != 0) > + LOG(Virtual, Error) << "Conversation failed with " << ret; > > /* Proceed to the next image every 4 frames */ > /* \todo Consider setting the frameRepeat in the config file */ > @@ -161,7 +206,7 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff > if (parameter_ % frameRepeat == 0) > frameIndex_++; > > - return 0; > + return ret; > } > > /* > diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.h b/src/libcamera/pipeline/virtual/image_frame_generator.h > index 42a077ba803c..19b6fe38654e 100644 > --- a/src/libcamera/pipeline/virtual/image_frame_generator.h > +++ b/src/libcamera/pipeline/virtual/image_frame_generator.h > @@ -37,7 +37,7 @@ private: > }; > > void configure(const Size &size) override; > - int generateFrame(const Size &size, const FrameBuffer *buffer) override; > + int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) override; > > std::vector<ImageFrameData> imageFrameDatas_; > std::vector<ImageFrameData> scaledFrameDatas_; > diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp > index 571be4fbf65d..e214048d288a 100644 > --- a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp > +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp > @@ -44,7 +44,7 @@ LOG_DECLARE_CATEGORY(Virtual) > > static const unsigned int kARGBSize = 4; > > -int TestPatternGenerator::generateFrame(const Size &size, > +int TestPatternGenerator::generateFrame(const StreamConfiguration &cfg, > const FrameBuffer *buffer) > { > /* > @@ -60,19 +60,53 @@ int TestPatternGenerator::generateFrame(const Size &size, > MappedFrameBuffer::MapFlag::Write); > > const auto &planes = mappedFrameBuffer.planes(); > - const PixelFormatInfo &info = PixelFormatInfo::info(formats::NV12); > + const Size size = cfg.size; const Size &size = cfg.size; > + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); > > rotateLeft1Column<kARGBSize>(size, template_.get()); > > + int ret; > + > /* Convert the template_ to the frame buffer */ > - int ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize, > - planes[0].data(), > - info.stride(size.width, 0), > - planes[1].data(), > - info.stride(size.width, 1), > - size.width, size.height); > + switch (cfg.pixelFormat) { > + case formats::NV12: > + ret = libyuv::ARGBToNV12(template_.get(), > + size.width * kARGBSize, > + planes[0].data(), > + info.stride(size.width, 0), > + planes[1].data(), > + info.stride(size.width, 1), > + size.width, size.height); Similarly as above, ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize, planes[0].data(), info.stride(size.width, 0), planes[1].data(), info.stride(size.width, 1), size.width, size.height); Same below. Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> I think we can handle that when applying if you let us know if you're fine with the small changes. > + break; > + > + case formats::YUV420: > + ret = libyuv::ARGBToI420(template_.get(), > + size.width * kARGBSize, > + planes[0].data(), > + info.stride(size.width, 0), > + planes[1].data(), > + info.stride(size.width, 1), > + planes[2].data(), > + info.stride(size.width, 2), > + size.width, size.height); > + break; > + > + case formats::ABGR8888: > + case formats::XBGR8888: > + ret = libyuv::ARGBCopy(template_.get(), > + size.width * kARGBSize, > + planes[0].data(), > + info.stride(size.width, 0), > + size.width, size.height); > + break; > + > + default: > + LOG(Virtual, Error) << "Unsupported format"; > + return -1; > + } > + > if (ret != 0) > - LOG(Virtual, Error) << "ARGBToNV12() failed with " << ret; > + LOG(Virtual, Error) << "Conversation failed with " << ret; > > return ret; > } > diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.h b/src/libcamera/pipeline/virtual/test_pattern_generator.h > index 2a51bd31abdf..9f72d652ab2c 100644 > --- a/src/libcamera/pipeline/virtual/test_pattern_generator.h > +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.h > @@ -24,7 +24,7 @@ enum class TestPattern : char { > class TestPatternGenerator : public FrameGenerator > { > public: > - int generateFrame(const Size &size, const FrameBuffer *buffer) override; > + int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) override; > > protected: > /* Buffer of test pattern template */ > diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp > index e25dd85a7391..f35af97b103d 100644 > --- a/src/libcamera/pipeline/virtual/virtual.cpp > +++ b/src/libcamera/pipeline/virtual/virtual.cpp > @@ -47,6 +47,13 @@ LOG_DEFINE_CATEGORY(Virtual) > > namespace { > > +constexpr PixelFormat outputFormats[] = { > + formats::NV12, > + formats::YUV420, > + formats::ABGR8888, > + formats::XBGR8888, > +}; > + > uint64_t currentTimestamp() > { > const auto now = std::chrono::steady_clock::now(); > @@ -148,7 +155,7 @@ void VirtualCameraData::processRequest(Request *request) > found = true; > > if (streamConfig.frameGenerator->generateFrame( > - stream->configuration().size, buffer)) > + stream->configuration(), buffer)) > fmd.status = FrameMetadata::Status::FrameError; > > bufferCompleted.emit(buffer); > @@ -202,18 +209,37 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() > adjusted = true; > } > > - if (cfg.pixelFormat != formats::NV12) { > - cfg.pixelFormat = formats::NV12; > + if (std::find(std::begin(outputFormats), > + std::end(outputFormats), > + cfg.pixelFormat) == std::end(outputFormats)) { > + cfg.pixelFormat = outputFormats[0]; > status = Adjusted; > adjusted = true; > } > > - if (cfg.colorSpace != ColorSpace::Smpte170m) { > - cfg.colorSpace = ColorSpace::Smpte170m; > + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); > + std::optional<ColorSpace> colorSpace; > + > + switch (info.colourEncoding) { > + case PixelFormatInfo::ColourEncodingRGB: > + colorSpace = ColorSpace::Srgb; > + break; > + > + case PixelFormatInfo::ColourEncodingYUV: > + colorSpace = ColorSpace::Smpte170m; > + break; > + > + default: > + ASSERT(false); > + } > + > + if (cfg.colorSpace && cfg.colorSpace != colorSpace) { > status = Adjusted; > adjusted = true; > } > > + cfg.colorSpace = colorSpace; > + > if (validateColorSpaces() == Adjusted) { > status = Adjusted; > adjusted = true; > @@ -223,7 +249,6 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() > LOG(Virtual, Info) > << "Stream configuration adjusted to " << cfg.toString(); > > - const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); > cfg.stride = info.stride(cfg.size.width, 0, 1); > cfg.frameSize = info.frameSize(cfg.size, 1); > > @@ -275,12 +300,12 @@ PipelineHandlerVirtual::generateConfiguration(Camera *camera, > } > > std::map<PixelFormat, std::vector<SizeRange>> streamFormats; > - PixelFormat pixelFormat = formats::NV12; > - streamFormats[pixelFormat] = { { data->config_.minResolutionSize, > - data->config_.maxResolutionSize } }; > + for (auto pixelFormat : outputFormats) > + streamFormats[pixelFormat] = { { data->config_.minResolutionSize, > + data->config_.maxResolutionSize } }; > StreamFormats formats(streamFormats); > StreamConfiguration cfg(formats); > - cfg.pixelFormat = pixelFormat; > + cfg.pixelFormat = outputFormats[0]; > cfg.size = data->config_.maxResolutionSize; > cfg.bufferCount = VirtualCameraConfiguration::kBufferCount; > cfg.colorSpace = ColorSpace::Smpte170m;
diff --git a/src/libcamera/pipeline/virtual/frame_generator.h b/src/libcamera/pipeline/virtual/frame_generator.h index a0658c45cd82..fd34a91a5e50 100644 --- a/src/libcamera/pipeline/virtual/frame_generator.h +++ b/src/libcamera/pipeline/virtual/frame_generator.h @@ -9,6 +9,7 @@ #include <libcamera/framebuffer.h> #include <libcamera/geometry.h> +#include <libcamera/stream.h> namespace libcamera { @@ -19,7 +20,7 @@ public: virtual void configure(const Size &size) = 0; - virtual int generateFrame(const Size &size, + virtual int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) = 0; protected: diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.cpp b/src/libcamera/pipeline/virtual/image_frame_generator.cpp index 5caad8b72763..7a9bc9ac1ebd 100644 --- a/src/libcamera/pipeline/virtual/image_frame_generator.cpp +++ b/src/libcamera/pipeline/virtual/image_frame_generator.cpp @@ -126,7 +126,7 @@ void ImageFrameGenerator::configure(const Size &size) } } -int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buffer) +int ImageFrameGenerator::generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) { ASSERT(!scaledFrameDatas_.empty()); @@ -143,17 +143,62 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff MappedFrameBuffer::MapFlag::Write); const auto &planes = mappedFrameBuffer.planes(); - const PixelFormatInfo &info = PixelFormatInfo::info(formats::NV12); + const Size size = cfg.size; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); /* Loop only around the number of images available */ frameIndex_ %= imageFrameDatas_.size(); + int ret; + auto Y = scaledFrameDatas_[frameIndex_].Y.get(); + auto UV = scaledFrameDatas_[frameIndex_].UV.get(); + /* Write the scaledY and scaledUV to the mapped frame buffer */ - libyuv::NV12Copy(scaledFrameDatas_[frameIndex_].Y.get(), size.width, - scaledFrameDatas_[frameIndex_].UV.get(), size.width, - planes[0].data(), info.stride(size.width, 0), - planes[1].data(), info.stride(size.width, 1), - size.width, size.height); + switch (cfg.pixelFormat) { + case formats::NV12: + ret = libyuv::NV12Copy(Y, + size.width, + UV, + size.width, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + size.width, size.height); + break; + + case formats::YUV420: + ret = libyuv::NV12ToI420(Y, + size.width, + UV, + size.width, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + planes[2].data(), + info.stride(size.width, 2), + size.width, size.height); + break; + + case formats::ABGR8888: + case formats::XBGR8888: + ret = libyuv::NV12ToABGR(Y, + size.width, + UV, + size.width, + planes[0].data(), + info.stride(size.width, 0), + size.width, size.height); + break; + + default: + LOG(Virtual, Error) << "Unsupported format"; + return -1; + } + + if (ret != 0) + LOG(Virtual, Error) << "Conversation failed with " << ret; /* Proceed to the next image every 4 frames */ /* \todo Consider setting the frameRepeat in the config file */ @@ -161,7 +206,7 @@ int ImageFrameGenerator::generateFrame(const Size &size, const FrameBuffer *buff if (parameter_ % frameRepeat == 0) frameIndex_++; - return 0; + return ret; } /* diff --git a/src/libcamera/pipeline/virtual/image_frame_generator.h b/src/libcamera/pipeline/virtual/image_frame_generator.h index 42a077ba803c..19b6fe38654e 100644 --- a/src/libcamera/pipeline/virtual/image_frame_generator.h +++ b/src/libcamera/pipeline/virtual/image_frame_generator.h @@ -37,7 +37,7 @@ private: }; void configure(const Size &size) override; - int generateFrame(const Size &size, const FrameBuffer *buffer) override; + int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) override; std::vector<ImageFrameData> imageFrameDatas_; std::vector<ImageFrameData> scaledFrameDatas_; diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp index 571be4fbf65d..e214048d288a 100644 --- a/src/libcamera/pipeline/virtual/test_pattern_generator.cpp +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.cpp @@ -44,7 +44,7 @@ LOG_DECLARE_CATEGORY(Virtual) static const unsigned int kARGBSize = 4; -int TestPatternGenerator::generateFrame(const Size &size, +int TestPatternGenerator::generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) { /* @@ -60,19 +60,53 @@ int TestPatternGenerator::generateFrame(const Size &size, MappedFrameBuffer::MapFlag::Write); const auto &planes = mappedFrameBuffer.planes(); - const PixelFormatInfo &info = PixelFormatInfo::info(formats::NV12); + const Size size = cfg.size; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); rotateLeft1Column<kARGBSize>(size, template_.get()); + int ret; + /* Convert the template_ to the frame buffer */ - int ret = libyuv::ARGBToNV12(template_.get(), size.width * kARGBSize, - planes[0].data(), - info.stride(size.width, 0), - planes[1].data(), - info.stride(size.width, 1), - size.width, size.height); + switch (cfg.pixelFormat) { + case formats::NV12: + ret = libyuv::ARGBToNV12(template_.get(), + size.width * kARGBSize, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + size.width, size.height); + break; + + case formats::YUV420: + ret = libyuv::ARGBToI420(template_.get(), + size.width * kARGBSize, + planes[0].data(), + info.stride(size.width, 0), + planes[1].data(), + info.stride(size.width, 1), + planes[2].data(), + info.stride(size.width, 2), + size.width, size.height); + break; + + case formats::ABGR8888: + case formats::XBGR8888: + ret = libyuv::ARGBCopy(template_.get(), + size.width * kARGBSize, + planes[0].data(), + info.stride(size.width, 0), + size.width, size.height); + break; + + default: + LOG(Virtual, Error) << "Unsupported format"; + return -1; + } + if (ret != 0) - LOG(Virtual, Error) << "ARGBToNV12() failed with " << ret; + LOG(Virtual, Error) << "Conversation failed with " << ret; return ret; } diff --git a/src/libcamera/pipeline/virtual/test_pattern_generator.h b/src/libcamera/pipeline/virtual/test_pattern_generator.h index 2a51bd31abdf..9f72d652ab2c 100644 --- a/src/libcamera/pipeline/virtual/test_pattern_generator.h +++ b/src/libcamera/pipeline/virtual/test_pattern_generator.h @@ -24,7 +24,7 @@ enum class TestPattern : char { class TestPatternGenerator : public FrameGenerator { public: - int generateFrame(const Size &size, const FrameBuffer *buffer) override; + int generateFrame(const StreamConfiguration &cfg, const FrameBuffer *buffer) override; protected: /* Buffer of test pattern template */ diff --git a/src/libcamera/pipeline/virtual/virtual.cpp b/src/libcamera/pipeline/virtual/virtual.cpp index e25dd85a7391..f35af97b103d 100644 --- a/src/libcamera/pipeline/virtual/virtual.cpp +++ b/src/libcamera/pipeline/virtual/virtual.cpp @@ -47,6 +47,13 @@ LOG_DEFINE_CATEGORY(Virtual) namespace { +constexpr PixelFormat outputFormats[] = { + formats::NV12, + formats::YUV420, + formats::ABGR8888, + formats::XBGR8888, +}; + uint64_t currentTimestamp() { const auto now = std::chrono::steady_clock::now(); @@ -148,7 +155,7 @@ void VirtualCameraData::processRequest(Request *request) found = true; if (streamConfig.frameGenerator->generateFrame( - stream->configuration().size, buffer)) + stream->configuration(), buffer)) fmd.status = FrameMetadata::Status::FrameError; bufferCompleted.emit(buffer); @@ -202,18 +209,37 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() adjusted = true; } - if (cfg.pixelFormat != formats::NV12) { - cfg.pixelFormat = formats::NV12; + if (std::find(std::begin(outputFormats), + std::end(outputFormats), + cfg.pixelFormat) == std::end(outputFormats)) { + cfg.pixelFormat = outputFormats[0]; status = Adjusted; adjusted = true; } - if (cfg.colorSpace != ColorSpace::Smpte170m) { - cfg.colorSpace = ColorSpace::Smpte170m; + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); + std::optional<ColorSpace> colorSpace; + + switch (info.colourEncoding) { + case PixelFormatInfo::ColourEncodingRGB: + colorSpace = ColorSpace::Srgb; + break; + + case PixelFormatInfo::ColourEncodingYUV: + colorSpace = ColorSpace::Smpte170m; + break; + + default: + ASSERT(false); + } + + if (cfg.colorSpace && cfg.colorSpace != colorSpace) { status = Adjusted; adjusted = true; } + cfg.colorSpace = colorSpace; + if (validateColorSpaces() == Adjusted) { status = Adjusted; adjusted = true; @@ -223,7 +249,6 @@ CameraConfiguration::Status VirtualCameraConfiguration::validate() LOG(Virtual, Info) << "Stream configuration adjusted to " << cfg.toString(); - const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); cfg.stride = info.stride(cfg.size.width, 0, 1); cfg.frameSize = info.frameSize(cfg.size, 1); @@ -275,12 +300,12 @@ PipelineHandlerVirtual::generateConfiguration(Camera *camera, } std::map<PixelFormat, std::vector<SizeRange>> streamFormats; - PixelFormat pixelFormat = formats::NV12; - streamFormats[pixelFormat] = { { data->config_.minResolutionSize, - data->config_.maxResolutionSize } }; + for (auto pixelFormat : outputFormats) + streamFormats[pixelFormat] = { { data->config_.minResolutionSize, + data->config_.maxResolutionSize } }; StreamFormats formats(streamFormats); StreamConfiguration cfg(formats); - cfg.pixelFormat = pixelFormat; + cfg.pixelFormat = outputFormats[0]; cfg.size = data->config_.maxResolutionSize; cfg.bufferCount = VirtualCameraConfiguration::kBufferCount; cfg.colorSpace = ColorSpace::Smpte170m;