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
Video()63 Video::Video() {}
64
getInstance()65 Video &Video::getInstance() {
66 static Video v;
67 return v;
68 }
69
isStarted()70 bool Video::isStarted() {
71 return bGrabberInited.load();
72 }
73
closeGrabber()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
initGrabber(int device,int w,int h)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)
__anon35e7b6d50402(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
CopyOutput()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
listDevicesTask()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
listDevices()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