Path: blob/master/modules/videoio/src/cap_winrt_video.cpp
16345 views
// Video support with XAML12// Copyright (c) Microsoft Open Technologies, Inc.3// All rights reserved.4//5// (3 - clause BSD License)6//7// Redistribution and use in source and binary forms, with or without modification, are permitted provided that8// the following conditions are met:9//10// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the11// following disclaimer.12// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the13// following disclaimer in the documentation and/or other materials provided with the distribution.14// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or15// promote products derived from this software without specific prior written permission.16//17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED18// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A19// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY20// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING23// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE24// POSSIBILITY OF SUCH DAMAGE.2526#include "cap_winrt_video.hpp"2728#include <ppl.h>29#include <ppltasks.h>30#include <concrt.h>31#include <agile.h>3233#include <atomic>34#include <future>35#include <vector>363738using namespace ::concurrency;39using namespace ::Windows::Foundation;40using namespace ::std;4142using namespace Microsoft::WRL;43using namespace Windows::Media::Devices;44using namespace Windows::Media::MediaProperties;45using namespace Windows::Media::Capture;46using namespace Windows::UI::Xaml::Media::Imaging;47using namespace Windows::Devices::Enumeration;4849#include "cap_winrt/CaptureFrameGrabber.hpp"5051// pull in Media Foundation libs52#pragma comment(lib, "mfplat")53#pragma comment(lib, "mf")54#pragma comment(lib, "mfuuid")5556#if (WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP) && !defined(_M_ARM)57#pragma comment(lib, "Shlwapi")58#endif5960#include "cap_winrt_bridge.hpp"6162Video::Video() {}6364Video &Video::getInstance() {65static Video v;66return v;67}6869bool Video::isStarted() {70return bGrabberInited.load();71}7273void Video::closeGrabber() {74// assigning nullptr causes deref of grabber and thus closes the device75m_frameGrabber = nullptr;76bGrabberInited = false;77bGrabberInitInProgress = false;78}7980// non-blocking81bool Video::initGrabber(int device, int w, int h) {82// already started?83if (bGrabberInited || bGrabberInitInProgress) return false;8485width = w;86height = h;8788bGrabberInited = false;89bGrabberInitInProgress = true;9091m_deviceID = device;9293create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture))94.then([this](task<DeviceInformationCollection^> findTask)95{96m_devices = findTask.get();9798// got selected device?99if ((unsigned)m_deviceID >= m_devices.Get()->Size)100{101OutputDebugStringA("Video::initGrabber - no video device found\n");102return false;103}104105auto devInfo = m_devices.Get()->GetAt(m_deviceID);106107auto settings = ref new MediaCaptureInitializationSettings();108settings->StreamingCaptureMode = StreamingCaptureMode::Video; // Video-only capture109settings->VideoDeviceId = devInfo->Id;110111auto location = devInfo->EnclosureLocation;112bFlipImageX = true;113if (location != nullptr && location->Panel == Windows::Devices::Enumeration::Panel::Back)114{115bFlipImageX = false;116}117118m_capture = ref new MediaCapture();119create_task(m_capture->InitializeAsync(settings)).then([this](){120121auto props = safe_cast<VideoEncodingProperties^>(m_capture->VideoDeviceController->GetMediaStreamProperties(MediaStreamType::VideoPreview));122123// for 24 bpp124props->Subtype = MediaEncodingSubtypes::Rgb24; bytesPerPixel = 3;125126// XAML & WBM use BGRA8, so it would look like127// props->Subtype = MediaEncodingSubtypes::Bgra8; bytesPerPixel = 4;128129props->Width = width;130props->Height = height;131132return ::Media::CaptureFrameGrabber::CreateAsync(m_capture.Get(), props);133134}).then([this](::Media::CaptureFrameGrabber^ frameGrabber)135{136m_frameGrabber = frameGrabber;137bGrabberInited = true;138bGrabberInitInProgress = false;139//ready = true;140_GrabFrameAsync(frameGrabber);141});142143return true;144});145146// nb. cannot block here - this will lock the UI thread:147148return true;149}150151152void Video::_GrabFrameAsync(::Media::CaptureFrameGrabber^ frameGrabber) {153// use rgb24 layout154create_task(frameGrabber->GetFrameAsync()).then([this, frameGrabber](const ComPtr<IMF2DBuffer2>& buffer)155{156// do the RGB swizzle while copying the pixels from the IMF2DBuffer2157BYTE *pbScanline;158LONG plPitch;159unsigned int colBytes = width * bytesPerPixel;160CHK(buffer->Lock2D(&pbScanline, &plPitch));161162// flip163if (bFlipImageX)164{165std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex);166167// ptr to input Mat data array168auto buf = VideoioBridge::getInstance().backInputPtr;169170for (unsigned int row = 0; row < height; row++)171{172unsigned int i = 0;173unsigned int j = colBytes - 1;174175while (i < colBytes)176{177// reverse the scan line178// as a side effect this also swizzles R and B channels179buf[j--] = pbScanline[i++];180buf[j--] = pbScanline[i++];181buf[j--] = pbScanline[i++];182}183pbScanline += plPitch;184buf += colBytes;185}186VideoioBridge::getInstance().bIsFrameNew = true;187} else188{189std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex);190191// ptr to input Mat data array192auto buf = VideoioBridge::getInstance().backInputPtr;193194for (unsigned int row = 0; row < height; row++)195{196// used for Bgr8:197//for (unsigned int i = 0; i < colBytes; i++ )198// buf[i] = pbScanline[i];199200// used for RGB24:201for (unsigned int i = 0; i < colBytes; i += bytesPerPixel)202{203// swizzle the R and B values (BGR to RGB)204buf[i] = pbScanline[i + 2];205buf[i + 1] = pbScanline[i + 1];206buf[i + 2] = pbScanline[i];207208// no swizzle209//buf[i] = pbScanline[i];210//buf[i + 1] = pbScanline[i + 1];211//buf[i + 2] = pbScanline[i + 2];212}213214pbScanline += plPitch;215buf += colBytes;216}217VideoioBridge::getInstance().bIsFrameNew = true;218}219CHK(buffer->Unlock2D());220221VideoioBridge::getInstance().frameCounter++;222223if (bGrabberInited)224{225_GrabFrameAsync(frameGrabber);226}227}, task_continuation_context::use_current());228}229230231// copy from input Mat to output WBM232// must be on UI thread233void Video::CopyOutput() {234{235std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().outputBufferMutex);236237auto inAr = VideoioBridge::getInstance().frontInputPtr;238auto outAr = GetData(VideoioBridge::getInstance().frontOutputBuffer->PixelBuffer);239240const unsigned int bytesPerPixel = 3;241auto pbScanline = inAr;242auto plPitch = width * bytesPerPixel;243244auto buf = outAr;245unsigned int colBytes = width * 4;246247// copy RGB24 to bgra8248for (unsigned int row = 0; row < height; row++)249{250// used for Bgr8:251// nb. no alpha252// for (unsigned int i = 0; i < colBytes; i++ ) buf[i] = pbScanline[i];253254// used for RGB24:255// nb. alpha is set to full opaque256for (unsigned int i = 0, j = 0; i < plPitch; i += bytesPerPixel, j += 4)257{258// swizzle the R and B values (RGB24 to Bgr8)259buf[j] = pbScanline[i + 2];260buf[j + 1] = pbScanline[i + 1];261buf[j + 2] = pbScanline[i];262buf[j + 3] = 0xff;263264// if no swizzle is desired:265//buf[i] = pbScanline[i];266//buf[i + 1] = pbScanline[i + 1];267//buf[i + 2] = pbScanline[i + 2];268//buf[i + 3] = 0xff;269}270271pbScanline += plPitch;272buf += colBytes;273}274VideoioBridge::getInstance().frontOutputBuffer->PixelBuffer->Length = width * height * 4;275}276}277278279bool Video::listDevicesTask() {280std::atomic<bool> ready(false);281282auto settings = ref new MediaCaptureInitializationSettings();283284create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture))285.then([this, &ready](task<DeviceInformationCollection^> findTask)286{287m_devices = findTask.get();288289// TODO: collect device data290// for (size_t i = 0; i < m_devices->Size; i++)291// {292// .. deviceInfo;293// auto d = m_devices->GetAt(i);294// deviceInfo.bAvailable = true;295// deviceInfo.deviceName = PlatformStringToString(d->Name);296// deviceInfo.hardwareName = deviceInfo.deviceName;297// }298299ready = true;300});301302// wait for async task to complete303int count = 0;304while (!ready)305{306count++;307}308309return true;310}311312313bool Video::listDevices() {314// synchronous version of listing video devices on WinRT315std::future<bool> result = std::async(std::launch::async, &Video::listDevicesTask, this);316return result.get();317}318319// end320321