-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplaycap.cpp
613 lines (497 loc) · 16.2 KB
/
playcap.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------
// File: PlayCap.cpp
//
// Desc: DirectShow sample code - a very basic application using video capture
// devices. It creates a window and uses the first available capture
// device to render and preview video capture data.
//------------------------------------------------------------------------------
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <dshow.h>
#include <stdio.h>
#include <strsafe.h>
#include "PlayCap.h"
#if 1 // Leav
#include "uv_inverter.h"
#endif
// An application can advertise the existence of its filter graph
// by registering the graph with a global Running Object Table (ROT).
// The GraphEdit application can detect and remotely view the running
// filter graph, allowing you to 'spy' on the graph with GraphEdit.
//
// To enable registration in this sample, define REGISTER_FILTERGRAPH.
//
#define REGISTER_FILTERGRAPH
//
// Global data
//
HWND ghApp=0;
DWORD g_dwGraphRegister=0;
IVideoWindow * g_pVW = NULL;
IMediaControl * g_pMC = NULL;
IMediaEventEx * g_pME = NULL;
IGraphBuilder * g_pGraph = NULL;
ICaptureGraphBuilder2 * g_pCapture = NULL;
PLAYSTATE g_psCurrent = Stopped;
#if 1 // Leav
UVInverter *g_pUVInv;
#endif
HRESULT CaptureVideo()
{
HRESULT hr;
IBaseFilter *pSrcFilter=NULL;
// Get DirectShow interfaces
hr = GetInterfaces();
if (FAILED(hr))
{
Msg(TEXT("Failed to get video interfaces! hr=0x%x"), hr);
return hr;
}
// Attach the filter graph to the capture graph
hr = g_pCapture->SetFiltergraph(g_pGraph);
if (FAILED(hr))
{
Msg(TEXT("Failed to set capture filter graph! hr=0x%x"), hr);
return hr;
}
// Use the system device enumerator and class enumerator to find
// a video capture/preview device, such as a desktop USB video camera.
hr = FindCaptureDevice(&pSrcFilter);
if (FAILED(hr))
{
// Don't display a message because FindCaptureDevice will handle it
return hr;
}
// Add Capture filter to our graph.
hr = g_pGraph->AddFilter(pSrcFilter, L"Video Capture");
if (FAILED(hr))
{
Msg(TEXT("Couldn't add the capture filter to the graph! hr=0x%x\r\n\r\n")
TEXT("If you have a working video capture device, please make sure\r\n")
TEXT("that it is connected and is not being used by another application.\r\n\r\n")
TEXT("The sample will now close."), hr);
pSrcFilter->Release();
return hr;
}
#if 1 // Leav: add UV inverter filter
g_pUVInv->AddRef();
hr = g_pGraph->AddFilter (g_pUVInv, L"UV inverter");
if (FAILED(hr))
{
Msg(TEXT("Add UV inverter failed, hr=0x%x"), hr);
return hr;
}
#endif
// Render the preview pin on the video capture filter
// Use this instead of g_pGraph->RenderFile
#if 1 // Leav
hr = g_pCapture->RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
pSrcFilter, g_pUVInv, NULL);
#else
hr = g_pCapture->RenderStream (&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
pSrcFilter, NULL, NULL);
#endif
if (FAILED(hr))
{
Msg(TEXT("Couldn't render the video capture stream. hr=0x%x\r\n")
TEXT("The capture device may already be in use by another application.\r\n\r\n")
TEXT("The sample will now close."), hr);
pSrcFilter->Release();
return hr;
}
#if 1 // Leav
g_pUVInv->Release();
#endif
// Now that the filter has been added to the graph and we have
// rendered its stream, we can release this reference to the filter.
pSrcFilter->Release();
// Set video window style and position
hr = SetupVideoWindow();
if (FAILED(hr))
{
Msg(TEXT("Couldn't initialize video window! hr=0x%x"), hr);
return hr;
}
#ifdef REGISTER_FILTERGRAPH
// Add our graph to the running object table, which will allow
// the GraphEdit application to "spy" on our graph
hr = AddGraphToRot(g_pGraph, &g_dwGraphRegister);
if (FAILED(hr))
{
Msg(TEXT("Failed to register filter graph with ROT! hr=0x%x"), hr);
g_dwGraphRegister = 0;
}
#endif
// Start previewing video data
hr = g_pMC->Run();
if (FAILED(hr))
{
Msg(TEXT("Couldn't run the graph! hr=0x%x"), hr);
return hr;
}
// Remember current state
g_psCurrent = Running;
return S_OK;
}
HRESULT FindCaptureDevice(IBaseFilter ** ppSrcFilter)
{
HRESULT hr = S_OK;
IBaseFilter * pSrc = NULL;
IMoniker* pMoniker =NULL;
ICreateDevEnum *pDevEnum =NULL;
IEnumMoniker *pClassEnum = NULL;
if (!ppSrcFilter)
{
return E_POINTER;
}
// Create the system device enumerator
hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
IID_ICreateDevEnum, (void **) &pDevEnum);
if (FAILED(hr))
{
Msg(TEXT("Couldn't create system enumerator! hr=0x%x"), hr);
}
// Create an enumerator for the video capture devices
if (SUCCEEDED(hr))
{
hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
if (FAILED(hr))
{
Msg(TEXT("Couldn't create class enumerator! hr=0x%x"), hr);
}
}
if (SUCCEEDED(hr))
{
// If there are no enumerators for the requested type, then
// CreateClassEnumerator will succeed, but pClassEnum will be NULL.
if (pClassEnum == NULL)
{
MessageBox(ghApp,TEXT("No video capture device was detected.\r\n\r\n")
TEXT("This sample requires a video capture device, such as a USB WebCam,\r\n")
TEXT("to be installed and working properly. The sample will now close."),
TEXT("No Video Capture Hardware"), MB_OK | MB_ICONINFORMATION);
hr = E_FAIL;
}
}
// Use the first video capture device on the device list.
// Note that if the Next() call succeeds but there are no monikers,
// it will return S_FALSE (which is not a failure). Therefore, we
// check that the return code is S_OK instead of using SUCCEEDED() macro.
if (SUCCEEDED(hr))
{
hr = pClassEnum->Next (1, &pMoniker, NULL);
if (hr == S_FALSE)
{
Msg(TEXT("Unable to access video capture device!"));
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
// Bind Moniker to a filter object
hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&pSrc);
if (FAILED(hr))
{
Msg(TEXT("Couldn't bind moniker to filter object! hr=0x%x"), hr);
}
}
// Copy the found filter pointer to the output parameter.
if (SUCCEEDED(hr))
{
*ppSrcFilter = pSrc;
(*ppSrcFilter)->AddRef();
}
SAFE_RELEASE(pSrc);
SAFE_RELEASE(pMoniker);
SAFE_RELEASE(pDevEnum);
SAFE_RELEASE(pClassEnum);
return hr;
}
HRESULT GetInterfaces(void)
{
HRESULT hr;
// Create the filter graph
hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,
IID_IGraphBuilder, (void **) &g_pGraph);
if (FAILED(hr))
return hr;
// Create the capture graph builder
hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
IID_ICaptureGraphBuilder2, (void **) &g_pCapture);
if (FAILED(hr))
return hr;
// Obtain interfaces for media control and Video Window
hr = g_pGraph->QueryInterface(IID_IMediaControl,(LPVOID *) &g_pMC);
if (FAILED(hr))
return hr;
hr = g_pGraph->QueryInterface(IID_IVideoWindow, (LPVOID *) &g_pVW);
if (FAILED(hr))
return hr;
hr = g_pGraph->QueryInterface(IID_IMediaEventEx, (LPVOID *) &g_pME);
if (FAILED(hr))
return hr;
// Set the window handle used to process graph events
hr = g_pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0);
#if 1 // Leav
g_pUVInv = new UVInverter;
#endif
return hr;
}
void CloseInterfaces(void)
{
// Stop previewing data
if (g_pMC)
g_pMC->StopWhenReady();
g_psCurrent = Stopped;
// Stop receiving events
if (g_pME)
g_pME->SetNotifyWindow(NULL, WM_GRAPHNOTIFY, 0);
// Relinquish ownership (IMPORTANT!) of the video window.
// Failing to call put_Owner can lead to assert failures within
// the video renderer, as it still assumes that it has a valid
// parent window.
if(g_pVW)
{
g_pVW->put_Visible(OAFALSE);
g_pVW->put_Owner(NULL);
}
#ifdef REGISTER_FILTERGRAPH
// Remove filter graph from the running object table
if (g_dwGraphRegister)
RemoveGraphFromRot(g_dwGraphRegister);
#endif
// Release DirectShow interfaces
SAFE_RELEASE(g_pMC);
SAFE_RELEASE(g_pME);
SAFE_RELEASE(g_pVW);
SAFE_RELEASE(g_pGraph);
SAFE_RELEASE(g_pCapture);
#if 1 // Leav
SAFE_RELEASE(g_pUVInv);
#endif
}
HRESULT SetupVideoWindow(void)
{
HRESULT hr;
// Set the video window to be a child of the main window
hr = g_pVW->put_Owner((OAHWND)ghApp);
if (FAILED(hr))
return hr;
// Set video window style
hr = g_pVW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
if (FAILED(hr))
return hr;
// Use helper function to position video window in client rect
// of main application window
ResizeVideoWindow();
// Make the video window visible, now that it is properly positioned
hr = g_pVW->put_Visible(OATRUE);
if (FAILED(hr))
return hr;
return hr;
}
void ResizeVideoWindow(void)
{
// Resize the video preview window to match owner window size
if (g_pVW)
{
RECT rc;
// Make the preview video fill our window
GetClientRect(ghApp, &rc);
g_pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
}
}
HRESULT ChangePreviewState(int nShow)
{
HRESULT hr=S_OK;
// If the media control interface isn't ready, don't call it
if (!g_pMC)
return S_OK;
if (nShow)
{
if (g_psCurrent != Running)
{
// Start previewing video data
hr = g_pMC->Run();
g_psCurrent = Running;
}
}
else
{
// Stop previewing video data
hr = g_pMC->StopWhenReady();
g_psCurrent = Stopped;
}
return hr;
}
#ifdef REGISTER_FILTERGRAPH
HRESULT AddGraphToRot(IUnknown *pUnkGraph, DWORD *pdwRegister)
{
IMoniker * pMoniker;
IRunningObjectTable *pROT;
WCHAR wsz[128];
HRESULT hr;
if (!pUnkGraph || !pdwRegister)
return E_POINTER;
if (FAILED(GetRunningObjectTable(0, &pROT)))
return E_FAIL;
hr = StringCchPrintfW(wsz, NUMELMS(wsz), L"FilterGraph %08x pid %08x\0", (DWORD_PTR)pUnkGraph,
GetCurrentProcessId());
hr = CreateItemMoniker(L"!", wsz, &pMoniker);
if (SUCCEEDED(hr))
{
// Use the ROTFLAGS_REGISTRATIONKEEPSALIVE to ensure a strong reference
// to the object. Using this flag will cause the object to remain
// registered until it is explicitly revoked with the Revoke() method.
//
// Not using this flag means that if GraphEdit remotely connects
// to this graph and then GraphEdit exits, this object registration
// will be deleted, causing future attempts by GraphEdit to fail until
// this application is restarted or until the graph is registered again.
hr = pROT->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, pUnkGraph,
pMoniker, pdwRegister);
pMoniker->Release();
}
pROT->Release();
return hr;
}
// Removes a filter graph from the Running Object Table
void RemoveGraphFromRot(DWORD pdwRegister)
{
IRunningObjectTable *pROT;
if (SUCCEEDED(GetRunningObjectTable(0, &pROT)))
{
pROT->Revoke(pdwRegister);
pROT->Release();
}
}
#endif
void Msg(TCHAR *szFormat, ...)
{
TCHAR szBuffer[1024]; // Large buffer for long filenames or URLs
const size_t NUMCHARS = sizeof(szBuffer) / sizeof(szBuffer[0]);
const int LASTCHAR = NUMCHARS - 1;
// Format the input string
va_list pArgs;
va_start(pArgs, szFormat);
// Use a bounded buffer size to prevent buffer overruns. Limit count to
// character size minus one to allow for a NULL terminating character.
(void)StringCchVPrintf(szBuffer, NUMCHARS - 1, szFormat, pArgs);
va_end(pArgs);
// Ensure that the formatted string is NULL-terminated
szBuffer[LASTCHAR] = TEXT('\0');
MessageBox(NULL, szBuffer, TEXT("PlayCap Message"), MB_OK | MB_ICONERROR);
}
HRESULT HandleGraphEvent(void)
{
LONG evCode;
LONG_PTR evParam1, evParam2;
HRESULT hr=S_OK;
if (!g_pME)
return E_POINTER;
while(SUCCEEDED(g_pME->GetEvent(&evCode, &evParam1, &evParam2, 0)))
{
//
// Free event parameters to prevent memory leaks associated with
// event parameter data. While this application is not interested
// in the received events, applications should always process them.
//
hr = g_pME->FreeEventParams(evCode, evParam1, evParam2);
// Insert event processing code here, if desired
}
return hr;
}
LRESULT CALLBACK WndMainProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_GRAPHNOTIFY:
HandleGraphEvent();
break;
case WM_SIZE:
ResizeVideoWindow();
break;
case WM_WINDOWPOSCHANGED:
ChangePreviewState(! (IsIconic(hwnd)));
break;
case WM_CLOSE:
// Hide the main window while the graph is destroyed
ShowWindow(ghApp, SW_HIDE);
CloseInterfaces(); // Stop capturing and release interfaces
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// Pass this message to the video window for notification of system changes
if (g_pVW)
g_pVW->NotifyOwnerMessage((LONG_PTR) hwnd, message, wParam, lParam);
return DefWindowProc (hwnd , message, wParam, lParam);
}
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hInstP, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg={0};
WNDCLASS wc;
// Initialize COM
if(FAILED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)))
{
Msg(TEXT("CoInitialize Failed!\r\n"));
exit(1);
}
// Register the window class
ZeroMemory(&wc, sizeof wc);
wc.lpfnWndProc = WndMainProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASSNAME;
wc.lpszMenuName = NULL;
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = NULL;
if(!RegisterClass(&wc))
{
Msg(TEXT("RegisterClass Failed! Error=0x%x\r\n"), GetLastError());
CoUninitialize();
exit(1);
}
// Create the main window. The WS_CLIPCHILDREN style is required.
ghApp = CreateWindow(CLASSNAME, APPLICATIONNAME,
WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT,
0, 0, hInstance, 0);
if(ghApp)
{
HRESULT hr;
// Create DirectShow graph and start capturing video
hr = CaptureVideo();
if (FAILED (hr))
{
CloseInterfaces();
DestroyWindow(ghApp);
}
else
{
// Don't display the main window until the DirectShow
// preview graph has been created. Once video data is
// being received and processed, the window will appear
// and immediately have useful video data to display.
// Otherwise, it will be black until video data arrives.
ShowWindow(ghApp, nCmdShow);
}
// Main message loop
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Release COM
CoUninitialize();
return (int) msg.wParam;
}