Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/videoio/src/cap_winrt_video.cpp
16345 views
1
// Video support with XAML
2
3
// Copyright (c) Microsoft Open Technologies, Inc.
4
// All rights reserved.
5
//
6
// (3 - clause BSD License)
7
//
8
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
9
// the following conditions are met:
10
//
11
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
12
// following disclaimer.
13
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
14
// following disclaimer in the documentation and/or other materials provided with the distribution.
15
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or
16
// promote products derived from this software without specific prior written permission.
17
//
18
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
19
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20
// PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
21
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
22
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
24
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
// POSSIBILITY OF SUCH DAMAGE.
26
27
#include "cap_winrt_video.hpp"
28
29
#include <ppl.h>
30
#include <ppltasks.h>
31
#include <concrt.h>
32
#include <agile.h>
33
34
#include <atomic>
35
#include <future>
36
#include <vector>
37
38
39
using namespace ::concurrency;
40
using namespace ::Windows::Foundation;
41
using namespace ::std;
42
43
using namespace Microsoft::WRL;
44
using namespace Windows::Media::Devices;
45
using namespace Windows::Media::MediaProperties;
46
using namespace Windows::Media::Capture;
47
using namespace Windows::UI::Xaml::Media::Imaging;
48
using namespace Windows::Devices::Enumeration;
49
50
#include "cap_winrt/CaptureFrameGrabber.hpp"
51
52
// pull in Media Foundation libs
53
#pragma comment(lib, "mfplat")
54
#pragma comment(lib, "mf")
55
#pragma comment(lib, "mfuuid")
56
57
#if (WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP) && !defined(_M_ARM)
58
#pragma comment(lib, "Shlwapi")
59
#endif
60
61
#include "cap_winrt_bridge.hpp"
62
63
Video::Video() {}
64
65
Video &Video::getInstance() {
66
static Video v;
67
return v;
68
}
69
70
bool Video::isStarted() {
71
return bGrabberInited.load();
72
}
73
74
void Video::closeGrabber() {
75
// assigning nullptr causes deref of grabber and thus closes the device
76
m_frameGrabber = nullptr;
77
bGrabberInited = false;
78
bGrabberInitInProgress = false;
79
}
80
81
// non-blocking
82
bool Video::initGrabber(int device, int w, int h) {
83
// already started?
84
if (bGrabberInited || bGrabberInitInProgress) return false;
85
86
width = w;
87
height = h;
88
89
bGrabberInited = false;
90
bGrabberInitInProgress = true;
91
92
m_deviceID = device;
93
94
create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture))
95
.then([this](task<DeviceInformationCollection^> findTask)
96
{
97
m_devices = findTask.get();
98
99
// got selected device?
100
if ((unsigned)m_deviceID >= m_devices.Get()->Size)
101
{
102
OutputDebugStringA("Video::initGrabber - no video device found\n");
103
return false;
104
}
105
106
auto devInfo = m_devices.Get()->GetAt(m_deviceID);
107
108
auto settings = ref new MediaCaptureInitializationSettings();
109
settings->StreamingCaptureMode = StreamingCaptureMode::Video; // Video-only capture
110
settings->VideoDeviceId = devInfo->Id;
111
112
auto location = devInfo->EnclosureLocation;
113
bFlipImageX = true;
114
if (location != nullptr && location->Panel == Windows::Devices::Enumeration::Panel::Back)
115
{
116
bFlipImageX = false;
117
}
118
119
m_capture = ref new MediaCapture();
120
create_task(m_capture->InitializeAsync(settings)).then([this](){
121
122
auto props = safe_cast<VideoEncodingProperties^>(m_capture->VideoDeviceController->GetMediaStreamProperties(MediaStreamType::VideoPreview));
123
124
// for 24 bpp
125
props->Subtype = MediaEncodingSubtypes::Rgb24; bytesPerPixel = 3;
126
127
// XAML & WBM use BGRA8, so it would look like
128
// props->Subtype = MediaEncodingSubtypes::Bgra8; bytesPerPixel = 4;
129
130
props->Width = width;
131
props->Height = height;
132
133
return ::Media::CaptureFrameGrabber::CreateAsync(m_capture.Get(), props);
134
135
}).then([this](::Media::CaptureFrameGrabber^ frameGrabber)
136
{
137
m_frameGrabber = frameGrabber;
138
bGrabberInited = true;
139
bGrabberInitInProgress = false;
140
//ready = true;
141
_GrabFrameAsync(frameGrabber);
142
});
143
144
return true;
145
});
146
147
// nb. cannot block here - this will lock the UI thread:
148
149
return true;
150
}
151
152
153
void Video::_GrabFrameAsync(::Media::CaptureFrameGrabber^ frameGrabber) {
154
// use rgb24 layout
155
create_task(frameGrabber->GetFrameAsync()).then([this, frameGrabber](const ComPtr<IMF2DBuffer2>& buffer)
156
{
157
// do the RGB swizzle while copying the pixels from the IMF2DBuffer2
158
BYTE *pbScanline;
159
LONG plPitch;
160
unsigned int colBytes = width * bytesPerPixel;
161
CHK(buffer->Lock2D(&pbScanline, &plPitch));
162
163
// flip
164
if (bFlipImageX)
165
{
166
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex);
167
168
// ptr to input Mat data array
169
auto buf = VideoioBridge::getInstance().backInputPtr;
170
171
for (unsigned int row = 0; row < height; row++)
172
{
173
unsigned int i = 0;
174
unsigned int j = colBytes - 1;
175
176
while (i < colBytes)
177
{
178
// reverse the scan line
179
// as a side effect this also swizzles R and B channels
180
buf[j--] = pbScanline[i++];
181
buf[j--] = pbScanline[i++];
182
buf[j--] = pbScanline[i++];
183
}
184
pbScanline += plPitch;
185
buf += colBytes;
186
}
187
VideoioBridge::getInstance().bIsFrameNew = true;
188
} else
189
{
190
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().inputBufferMutex);
191
192
// ptr to input Mat data array
193
auto buf = VideoioBridge::getInstance().backInputPtr;
194
195
for (unsigned int row = 0; row < height; row++)
196
{
197
// used for Bgr8:
198
//for (unsigned int i = 0; i < colBytes; i++ )
199
// buf[i] = pbScanline[i];
200
201
// used for RGB24:
202
for (unsigned int i = 0; i < colBytes; i += bytesPerPixel)
203
{
204
// swizzle the R and B values (BGR to RGB)
205
buf[i] = pbScanline[i + 2];
206
buf[i + 1] = pbScanline[i + 1];
207
buf[i + 2] = pbScanline[i];
208
209
// no swizzle
210
//buf[i] = pbScanline[i];
211
//buf[i + 1] = pbScanline[i + 1];
212
//buf[i + 2] = pbScanline[i + 2];
213
}
214
215
pbScanline += plPitch;
216
buf += colBytes;
217
}
218
VideoioBridge::getInstance().bIsFrameNew = true;
219
}
220
CHK(buffer->Unlock2D());
221
222
VideoioBridge::getInstance().frameCounter++;
223
224
if (bGrabberInited)
225
{
226
_GrabFrameAsync(frameGrabber);
227
}
228
}, task_continuation_context::use_current());
229
}
230
231
232
// copy from input Mat to output WBM
233
// must be on UI thread
234
void Video::CopyOutput() {
235
{
236
std::lock_guard<std::mutex> lock(VideoioBridge::getInstance().outputBufferMutex);
237
238
auto inAr = VideoioBridge::getInstance().frontInputPtr;
239
auto outAr = GetData(VideoioBridge::getInstance().frontOutputBuffer->PixelBuffer);
240
241
const unsigned int bytesPerPixel = 3;
242
auto pbScanline = inAr;
243
auto plPitch = width * bytesPerPixel;
244
245
auto buf = outAr;
246
unsigned int colBytes = width * 4;
247
248
// copy RGB24 to bgra8
249
for (unsigned int row = 0; row < height; row++)
250
{
251
// used for Bgr8:
252
// nb. no alpha
253
// for (unsigned int i = 0; i < colBytes; i++ ) buf[i] = pbScanline[i];
254
255
// used for RGB24:
256
// nb. alpha is set to full opaque
257
for (unsigned int i = 0, j = 0; i < plPitch; i += bytesPerPixel, j += 4)
258
{
259
// swizzle the R and B values (RGB24 to Bgr8)
260
buf[j] = pbScanline[i + 2];
261
buf[j + 1] = pbScanline[i + 1];
262
buf[j + 2] = pbScanline[i];
263
buf[j + 3] = 0xff;
264
265
// if no swizzle is desired:
266
//buf[i] = pbScanline[i];
267
//buf[i + 1] = pbScanline[i + 1];
268
//buf[i + 2] = pbScanline[i + 2];
269
//buf[i + 3] = 0xff;
270
}
271
272
pbScanline += plPitch;
273
buf += colBytes;
274
}
275
VideoioBridge::getInstance().frontOutputBuffer->PixelBuffer->Length = width * height * 4;
276
}
277
}
278
279
280
bool Video::listDevicesTask() {
281
std::atomic<bool> ready(false);
282
283
auto settings = ref new MediaCaptureInitializationSettings();
284
285
create_task(DeviceInformation::FindAllAsync(DeviceClass::VideoCapture))
286
.then([this, &ready](task<DeviceInformationCollection^> findTask)
287
{
288
m_devices = findTask.get();
289
290
// TODO: collect device data
291
// for (size_t i = 0; i < m_devices->Size; i++)
292
// {
293
// .. deviceInfo;
294
// auto d = m_devices->GetAt(i);
295
// deviceInfo.bAvailable = true;
296
// deviceInfo.deviceName = PlatformStringToString(d->Name);
297
// deviceInfo.hardwareName = deviceInfo.deviceName;
298
// }
299
300
ready = true;
301
});
302
303
// wait for async task to complete
304
int count = 0;
305
while (!ready)
306
{
307
count++;
308
}
309
310
return true;
311
}
312
313
314
bool Video::listDevices() {
315
// synchronous version of listing video devices on WinRT
316
std::future<bool> result = std::async(std::launch::async, &Video::listDevicesTask, this);
317
return result.get();
318
}
319
320
// end
321