50 #undef DXCAMSERVER_VERBOSE
60 fprintf(stderr,
"directx_camera_server: Can't create %s\n", objName);
61 throw ConstructionError(objName);
71 bool directx_camera_server::read_one_frame(
unsigned minX,
unsigned maxX,
72 unsigned minY,
unsigned maxY,
73 unsigned exposure_millisecs) {
81 open_and_find_parameters();
82 _started_graph =
false;
89 if (!_started_graph) {
93 hr = _pMediaControl->Run();
96 hr = _pMediaControl->Pause();
101 "directx_camera_server::read_one_frame(): Unknown mode (%d)\n",
106 if ((hr != S_OK) && (hr != S_FALSE)) {
107 fprintf(stderr,
"directx_camera_server::read_one_frame(): Can't "
108 "run filter graph, got %#010x\n",
109 static_cast<uint32_t>(hr));
114 _started_graph =
true;
118 const int TIMEOUT_MSECS = 500;
125 auto imageReady = sampleExchange_->waitForSample(
126 std::chrono::milliseconds(TIMEOUT_MSECS));
130 fprintf(stderr,
"directx_camera_server::read_one_frame(): Timeout "
131 "when reading image\n");
138 _pMediaControl->Pause();
142 auto sampleWrapper = sampleExchange_->get();
143 if (FAILED(sampleWrapper.get().GetPointer(&imageLocation))) {
145 "directx_camera_server::read_one_frame(): Can't get buffer\n");
151 ts_ = sampleWrapper.getTimestamp();
156 for (DWORD iRow = 0; iRow < _num_rows; iRow++) {
157 memcpy(_buffer.data() + _num_columns * 3 * iRow,
158 imageLocation + _stride * iRow, _num_columns * 3);
161 #ifdef HACK_TO_REOPEN
168 return osvr::util::wideToUTF8String(prop.
read(L
"DevicePath"));
172 auto desc = prop.
read(L
"Description");
174 desc = prop.
read(L
"FriendlyName");
176 return desc.empty() ? std::string() :
osvr::util::wideToUTF8String(desc);
179 bool directx_camera_server::start_com_and_graphbuilder() {
186 printf(
"directx_camera_server::open_and_find_parameters(): Before "
190 _com = comutils::ComInit::init();
193 printf(
"directx_camera_server::open_and_find_parameters(): Before "
194 "CoCreateInstance FilterGraph\n");
196 CoCreateInstance(CLSID_FilterGraph,
nullptr, CLSCTX_INPROC_SERVER,
197 IID_IGraphBuilder, AttachPtr(_pGraph));
200 _pGraph->QueryInterface(IID_IMediaControl, AttachPtr(_pMediaControl));
204 printf(
"directx_camera_server::open_and_find_parameters(): Before "
205 "CoCreateInstance CaptureGraphBuilder2\n");
207 CoCreateInstance(CLSID_CaptureGraphBuilder2,
nullptr, CLSCTX_INPROC,
208 IID_ICaptureGraphBuilder2, AttachPtr(_pBuilder));
213 printf(
"directx_camera_server::open_and_find_parameters(): Before "
216 _pBuilder->SetFiltergraph(_pGraph.get());
222 template <
typename T>
225 fprintf(stderr,
"directx_camera_server: Can't create %s\n", objName);
233 template <
typename F>
234 inline comutils::Ptr<IMoniker> find_first_capture_device_where(F &&f) {
235 auto ret = comutils::Ptr<IMoniker>{};
238 printf(
"find_first_capture_device_where(): Before "
239 "CoCreateInstance SystemDeviceEnum\n");
241 auto pDevEnum = comutils::Ptr<ICreateDevEnum>{};
242 CoCreateInstance(CLSID_SystemDeviceEnum,
nullptr, CLSCTX_INPROC,
243 IID_ICreateDevEnum, AttachPtr(pDevEnum));
251 printf(
"find_first_capture_device_where(): Before "
252 "CreateClassEnumerator\n");
254 auto pClassEnum = comutils::Ptr<IEnumMoniker>{};
255 pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
256 AttachPtr(pClassEnum), 0);
262 printf(
"find_first_capture_device_where(): Before Loop "
263 "over enumerators\n");
269 printf(
"\ndirectx_camera_server find_first_capture_device_where(): "
270 "Beginning enumeration of video capture devices.\n\n");
272 auto pMoniker = comutils::Ptr<IMoniker>{};
273 while (pClassEnum->Next(1, AttachPtr(pMoniker),
nullptr) == S_OK) {
276 printf(
"- '%s' at path:\n '%s'\n\n",
277 getDeviceHumanDesc(*pMoniker).c_str(),
278 getDevicePath(*pMoniker).c_str());
279 #endif // VERBOSE_ENUM
281 if (!ret && f(*pMoniker)) {
284 printf(
"^^ Accepted that device! (Would have exited "
285 "enumeration here if VERBOSE_ENUM were not defined)\n\n");
286 #else // !VERBOSE_ENUM
294 printf(
"\ndirectx_camera_server find_first_capture_device_where(): End "
298 #ifdef DXCAMSERVER_VERBOSE
301 "directx_camera_server find_first_capture_device_where(): "
302 "No device satisfied the predicate.\n");
312 bool directx_camera_server::open_and_find_parameters(
const int which,
315 auto graphbuilderRet = start_com_and_graphbuilder();
316 if (!graphbuilderRet) {
321 auto pMoniker = find_first_capture_device_where([&i, which](IMoniker &) {
330 fprintf(stderr,
"directx_camera_server::open_and_find_parameters(): "
331 "Could not get the device requested - not enough "
336 std::cout <<
"directx_camera_server::open_and_find_parameters(): Accepted!"
339 return open_moniker_and_finish_setup(pMoniker, FilterOperation{}, width,
343 bool directx_camera_server::open_and_find_parameters(
344 std::string
const &pathPrefix, FilterOperation
const &sourceConfig,
345 unsigned width,
unsigned height) {
346 auto graphbuilderRet = start_com_and_graphbuilder();
347 if (!graphbuilderRet) {
352 find_first_capture_device_where([&pathPrefix](IMoniker &mon) {
357 auto path = getDevicePath(props);
358 if (path.length() < pathPrefix.length()) {
361 return (path.substr(0, pathPrefix.length()) == pathPrefix);
365 #ifdef DXCAMSERVER_VERBOSE
366 fprintf(stderr,
"directx_camera_server::open_and_find_parameters(): "
367 "Could not get the device requested - not enough "
376 devicePath_ = getDevicePath(props);
381 std::cout <<
"directx_camera_server::open_and_find_parameters(): Accepted!"
384 return open_moniker_and_finish_setup(pMoniker, sourceConfig, width, height);
387 inline bool setBufferLatency(ICaptureGraphBuilder2 &builder,
388 IBaseFilter &filter, std::size_t numBuffers) {
391 GetVideoCapturePinInterface<IAMBufferNegotiation>(builder, filter);
395 std::cerr <<
"Could not get IAMBufferNegotiation" << std::endl;
399 ALLOCATOR_PROPERTIES props;
401 props.cBuffers = numBuffers;
404 auto hr = negotiation->SuggestAllocatorProperties(&props);
411 bool directx_camera_server::open_moniker_and_finish_setup(
412 comutils::Ptr<IMoniker> pMoniker, FilterOperation
const &sourceConfig,
413 unsigned width,
unsigned height) {
417 "directx_camera_server::open_moniker_and_finish_setup(): "
418 "Null device moniker passed: no device found?\n");
422 printf(
"directx_camera_server: Using capture device '%s' at path '%s'\n",
423 getDeviceHumanDesc(prop).c_str(), getDevicePath(prop).c_str());
426 auto pSrc = comutils::Ptr<IBaseFilter>{};
427 pMoniker->BindToObject(
nullptr,
nullptr, IID_IBaseFilter, AttachPtr(pSrc));
430 if (setBufferLatency(*_pBuilder, *pSrc, 1)) {
433 std::cout <<
"directx_camera_server: Call to set capture buffer "
446 sampleExchange_ = _pSampleGrabberWrapper->getExchange();
456 if ((width != 0) && (height != 0)) {
457 auto pStreamConfig = comutils::Ptr<IAMStreamConfig>{};
458 _pBuilder->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,
459 pSrc.get(), IID_IAMStreamConfig,
460 AttachPtr(pStreamConfig));
463 AM_MEDIA_TYPE mt = {0};
464 mt.majortype = MEDIATYPE_Video;
465 mt.subtype = MEDIASUBTYPE_RGB24;
466 VIDEOINFOHEADER vih = {0};
467 mt.pbFormat =
reinterpret_cast<BYTE *
>(&vih);
468 auto pVideoHeader = &vih;
469 pVideoHeader->bmiHeader.biBitCount = 24;
470 pVideoHeader->bmiHeader.biWidth = width;
471 pVideoHeader->bmiHeader.biHeight = height;
472 pVideoHeader->bmiHeader.biPlanes = 1;
473 pVideoHeader->bmiHeader.biSize =
sizeof(BITMAPINFOHEADER);
474 pVideoHeader->bmiHeader.biSizeImage =
dibsize(pVideoHeader->bmiHeader);
477 mt.formattype = FORMAT_VideoInfo;
478 mt.cbFormat =
sizeof(VIDEOINFOHEADER);
481 mt.bFixedSizeSamples = TRUE;
482 mt.lSampleSize =
dibsize(pVideoHeader->bmiHeader);
485 if (pStreamConfig->SetFormat(&mt) != S_OK) {
486 fprintf(stderr,
"directx_camera_server::open_and_find_parameters():"
487 " Can't set resolution to %dx%d using uncompressed "
489 pVideoHeader->bmiHeader.biWidth,
490 pVideoHeader->bmiHeader.biHeight);
500 printf(
"directx_camera_server::open_and_find_parameters(): Before "
501 "createNullRenderFilter\n");
503 auto pNullRender = createNullRenderFilter();
504 auto sampleGrabberFilter = _pSampleGrabberWrapper->getFilter();
509 auto hr = _pGraph->AddFilter(pSrc.get(), L
"Video Capture");
510 BOOST_ASSERT_MSG(SUCCEEDED(hr),
"Adding Video Capture filter to graph");
513 hr = _pGraph->AddFilter(sampleGrabberFilter.get(), L
"SampleGrabber");
514 BOOST_ASSERT_MSG(SUCCEEDED(hr),
"Adding SampleGrabber filter to graph");
517 hr = _pGraph->AddFilter(pNullRender.get(), L
"NullRenderer");
518 BOOST_ASSERT_MSG(SUCCEEDED(hr),
"Adding NullRenderer filter to graph");
522 auto capturePin = GetVideoCapturePinInterface<IPin>(*_pBuilder, *pSrc);
524 auto pIn =
GetPin(*sampleGrabberFilter, PINDIR_INPUT);
525 _pGraph->Connect(capturePin.get(), pIn.get());
538 auto pTuner = GetPinInterface<IAMTVTuner>(*_pBuilder, *pSrc);
541 printf(
"directx_camera_server::open_and_find_parameters(): Found a TV "
547 hr = pTuner->put_InputType(0, TunerInputCable);
549 fprintf(stderr,
"directx_camera_server::open_and_find_parameters():"
550 " Can't set input to cable\n");
554 hr = pTuner->put_Channel(0, -1, -1);
556 fprintf(stderr,
"directx_camera_server::open_and_find_parameters():"
557 " Can't set channel\n");
564 AM_MEDIA_TYPE mt = {0};
565 _pSampleGrabberWrapper->getConnectedMediaType(mt);
566 VIDEOINFOHEADER *pVih;
567 if (mt.formattype == FORMAT_VideoInfo ||
568 mt.formattype == FORMAT_VideoInfo2) {
569 pVih =
reinterpret_cast<VIDEOINFOHEADER *
>(mt.pbFormat);
572 "directx_camera_server::open_and_find_parameters(): "
573 "Can't get video header type\n");
574 fprintf(stderr,
" (Expected %x or %x, got %x)\n", FORMAT_VideoInfo,
575 FORMAT_VideoInfo2, mt.formattype);
576 fprintf(stderr,
" (GetConnectedMediaType is not valid for DirectX "
577 "headers later than version 7)\n");
578 fprintf(stderr,
" (We need to re-implement reading video in some "
579 "other interface)\n");
586 if (IsRectEmpty(&pVih->rcTarget)) {
587 _num_columns = pVih->bmiHeader.biWidth;
588 _num_rows = pVih->bmiHeader.biHeight;
590 _num_columns = pVih->rcTarget.right;
591 _num_rows = pVih->bmiHeader.biHeight;
592 printf(
"XXX directx_camera_server::open_and_find_parameters(): "
593 "Warning: may not work correctly with target rectangle\n");
596 printf(
"Got %dx%d video\n", _num_columns, _num_rows);
601 if (pVih->bmiHeader.biCompression != BI_RGB) {
603 "directx_camera_server::open_and_find_parameters(): "
604 "Compression not RGB\n");
605 switch (pVih->bmiHeader.biCompression) {
607 fprintf(stderr,
" (It is BI_RLE8)\n");
610 fprintf(stderr,
" (It is BI_RLE4)\n");
612 fprintf(stderr,
" (It is BI_BITFIELDS)\n");
615 fprintf(stderr,
" (Unknown compression type)\n");
619 int BytesPerPixel = pVih->bmiHeader.biBitCount / 8;
620 if (BytesPerPixel != 3) {
622 "directx_camera_server::open_and_find_parameters(): "
623 "Not 3 bytes per pixel (%d)\n",
624 pVih->bmiHeader.biBitCount);
634 "directx_camera_server::open_and_find_parameters(): "
635 "Num Rows is negative (internal error)\n");
641 _stride = (_num_columns * BytesPerPixel + 3) & ~3;
648 directx_camera_server::directx_camera_server() {
650 _minX = _maxX = _minY = _maxY = 0;
654 directx_camera_server::directx_camera_server(
int which,
unsigned width,
657 if (!open_and_find_parameters(which, width, height)) {
658 #ifdef DXCAMSERVER_VERBOSE
659 fprintf(stderr,
"directx_camera_server::directx_camera_server(): "
660 "Cannot open camera\n");
668 #ifdef HACK_TO_REOPEN
675 directx_camera_server::directx_camera_server(std::string
const &pathPrefix,
676 unsigned width,
unsigned height) {
678 if (!open_and_find_parameters(pathPrefix, FilterOperation{}, width,
681 #ifdef DXCAMSERVER_VERBOSE
682 fprintf(stderr,
"directx_camera_server::directx_camera_server(): "
683 "Cannot open camera\n");
690 #ifdef HACK_TO_REOPEN
697 directx_camera_server::directx_camera_server(
698 std::string
const &pathPrefix, FilterOperation
const &sourceConfig) {
699 if (!open_and_find_parameters(pathPrefix, sourceConfig)) {
701 #ifdef DXCAMSERVER_VERBOSE
702 fprintf(stderr,
"directx_camera_server::directx_camera_server(): "
703 "Cannot open camera\n");
710 #ifdef HACK_TO_REOPEN
719 void directx_camera_server::close_device() {
722 _pMediaControl.reset();
726 directx_camera_server::~directx_camera_server() {
729 if (_pSampleGrabberWrapper) {
730 _pSampleGrabberWrapper->shutdown();
737 _pSampleGrabberWrapper.reset();
740 bool directx_camera_server::read_image_to_memory(
unsigned minX,
unsigned maxX,
741 unsigned minY,
unsigned maxY,
742 double exposure_millisecs) {
745 "directx_camera_server::read_image_to_memory(): broken\n");
751 _minX = _minY = _maxX = _maxY = 0;
758 maxX = _num_columns - 1;
762 maxY = _num_rows - 1;
773 if (maxX >= _num_columns) {
774 maxX = _num_columns - 1;
776 if (maxY >= _num_rows) {
777 maxY = _num_rows - 1;
789 if (!read_one_frame(_minX, _maxX, _minY, _maxY, (
int)exposure_millisecs)) {
790 fprintf(stderr,
"directx_camera_server::read_image_to_memory(): "
791 "read_one_frame() failed\n");
798 bool directx_camera_server::get_pixel_from_memory(
unsigned X,
unsigned Y,
810 if ((_maxX <= _minX) || (_maxY <= _minY)) {
811 fprintf(stderr,
"directx_camera_server::get_pixel_from_memory(): No "
812 "image in memory\n");
815 if ((X < _minX) || (X > _maxX) || (Y < _minY) || (Y > _maxY)) {
818 unsigned cols = _num_columns;
819 val = _buffer[(Y * cols + X) * 3 + RGB];
823 bool directx_camera_server::get_pixel_from_memory(
unsigned X,
unsigned Y,
835 if ((_maxX <= _minX) || (_maxY <= _minY)) {
836 fprintf(stderr,
"directx_camera_server::get_pixel_from_memory(): No "
837 "image in memory\n");
840 if ((X < _minX) || (X > _maxX) || (Y < _minY) || (Y > _maxY)) {
843 unsigned cols = _num_columns;
844 val = _buffer[(Y * cols + X) * 3 + RGB];
848 void directx_camera_server::allocate_buffer() {
853 auto buflen = (_num_rows * _num_columns * 3);
854 _buffer.resize(buflen);
857 _minX = _maxX = _minY = _maxY = 0;
HRESULT ConnectTwoFilters(IGraphBuilder &pGraph, IBaseFilter &pFirst, IBaseFilter &pSecond)
bool didConstructionFail(T const &ptr, const char objName[])
Checks something to see if it's false-ish, printing a message and throwing an exception if it is...
void checkForConstructionError(T const &ptr, const char objName[])
Checks something to see if it's false-ish, printing a message and throwing an exception if it is...
comutils::Ptr< IPin > GetPin(IBaseFilter &pFilter, PIN_DIRECTION const PinDir)
Helper function to get a pin of a particular direction.
DWORD dibsize(BITMAPINFOHEADER const &bi)
Computes bytes required for an UNCOMPRESSED RGB DIB.
Configured header for internal UTF16 or Windows TCHAR to UTF8 conversion. Using this header requires ...
std::wstring read(const wchar_t propName[]) const
Reads a (wide-string) property, returning an empty string if failed.