Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/capture/video/win/video_capture_device_win.h" | 5 #include "media/capture/video/win/video_capture_device_win.h" |
| 6 | 6 |
| 7 #include <ks.h> | 7 #include <ks.h> |
| 8 #include <ksmedia.h> | 8 #include <ksmedia.h> |
| 9 #include <objbase.h> | 9 #include <objbase.h> |
| 10 #include <vidcap.h> | |
| 10 | 11 |
| 11 #include <algorithm> | 12 #include <algorithm> |
| 12 #include <list> | 13 #include <list> |
| 13 #include <utility> | 14 #include <utility> |
| 14 | 15 |
| 15 #include "base/macros.h" | 16 #include "base/macros.h" |
| 16 #include "base/strings/sys_string_conversions.h" | 17 #include "base/strings/sys_string_conversions.h" |
| 17 #include "base/win/scoped_co_mem.h" | 18 #include "base/win/scoped_co_mem.h" |
| 18 #include "base/win/scoped_variant.h" | 19 #include "base/win/scoped_variant.h" |
| 19 #include "media/base/timestamp_constants.h" | 20 #include "media/base/timestamp_constants.h" |
| 20 #include "media/capture/video/blob_utils.h" | 21 #include "media/capture/video/blob_utils.h" |
| 21 | 22 |
| 22 using base::win::ScopedCoMem; | 23 using base::win::ScopedCoMem; |
| 23 using base::win::ScopedComPtr; | 24 using base::win::ScopedComPtr; |
| 24 using base::win::ScopedVariant; | 25 using base::win::ScopedVariant; |
| 25 | 26 |
| 26 namespace media { | 27 namespace media { |
| 27 | 28 |
| 28 #if DCHECK_IS_ON() | 29 #if DCHECK_IS_ON() |
| 29 #define DLOG_IF_FAILED_WITH_HRESULT(message, hr) \ | 30 #define DLOG_IF_FAILED_WITH_HRESULT(message, hr) \ |
| 30 { \ | 31 { \ |
| 31 DLOG_IF(ERROR, FAILED(hr)) << (message) << ": " \ | 32 DLOG_IF(ERROR, FAILED(hr)) \ |
| 32 << logging::SystemErrorCodeToString(hr); \ | 33 << (message) << ": " << logging::SystemErrorCodeToString(hr); \ |
| 33 } | 34 } |
| 34 #else | 35 #else |
| 35 #define DLOG_IF_FAILED_WITH_HRESULT(message, hr) \ | 36 #define DLOG_IF_FAILED_WITH_HRESULT(message, hr) \ |
| 36 {} | 37 {} |
| 37 #endif | 38 #endif |
| 38 | 39 |
| 39 // Check if a Pin matches a category. | 40 // Check if a Pin matches a category. |
| 40 bool PinMatchesCategory(IPin* pin, REFGUID category) { | 41 bool PinMatchesCategory(IPin* pin, REFGUID category) { |
| 41 DCHECK(pin); | 42 DCHECK(pin); |
| 42 bool found = false; | 43 bool found = false; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 55 } | 56 } |
| 56 | 57 |
| 57 // Check if a Pin's MediaType matches a given |major_type|. | 58 // Check if a Pin's MediaType matches a given |major_type|. |
| 58 bool PinMatchesMajorType(IPin* pin, REFGUID major_type) { | 59 bool PinMatchesMajorType(IPin* pin, REFGUID major_type) { |
| 59 DCHECK(pin); | 60 DCHECK(pin); |
| 60 AM_MEDIA_TYPE connection_media_type; | 61 AM_MEDIA_TYPE connection_media_type; |
| 61 const HRESULT hr = pin->ConnectionMediaType(&connection_media_type); | 62 const HRESULT hr = pin->ConnectionMediaType(&connection_media_type); |
| 62 return SUCCEEDED(hr) && connection_media_type.majortype == major_type; | 63 return SUCCEEDED(hr) && connection_media_type.majortype == major_type; |
| 63 } | 64 } |
| 64 | 65 |
| 66 // Retrieves the control range and value using the provided getters, and | |
| 67 // optionally returns the associated supported and current mode. | |
| 68 template <typename RangeGetter, typename ValueGetter> | |
| 69 mojom::RangePtr RetrieveControlRangeAndCurrent( | |
| 70 RangeGetter range_getter, | |
| 71 ValueGetter value_getter, | |
| 72 std::vector<mojom::MeteringMode>* supported_modes, | |
|
robliao
2017/05/03 21:38:26
You can use the default = nullptr argument to save
mcasas
2017/05/03 22:16:49
Done.
| |
| 73 mojom::MeteringMode* current_mode) { | |
| 74 auto control_range = mojom::Range::New(); | |
|
robliao
2017/05/03 21:38:27
Will the caller be okay with any failure below?
mcasas
2017/05/03 22:16:49
Yes, returning an empty mojom::RangePtr is fine.
| |
| 75 long min, max, step, default_value, flags, current; | |
| 76 HRESULT hr = range_getter(&min, &max, &step, &default_value, &flags); | |
| 77 DLOG_IF_FAILED_WITH_HRESULT("Control range reading failed", hr); | |
| 78 if (!FAILED(hr)) { | |
|
robliao
2017/05/03 21:38:26
Use SUCCEEDED(hr) here and below.
mcasas
2017/05/03 22:16:49
Done.
| |
| 79 control_range->min = min; | |
| 80 control_range->max = max; | |
| 81 control_range->step = step; | |
| 82 if (supported_modes != nullptr) { | |
| 83 if (flags && CameraControl_Flags_Auto) | |
| 84 supported_modes->push_back(mojom::MeteringMode::CONTINUOUS); | |
| 85 if (flags && CameraControl_Flags_Manual) | |
| 86 supported_modes->push_back(mojom::MeteringMode::MANUAL); | |
| 87 } | |
| 88 } | |
| 89 hr = value_getter(¤t, &flags); | |
| 90 DLOG_IF_FAILED_WITH_HRESULT("Control value reading failed", hr); | |
| 91 if (!FAILED(hr)) { | |
| 92 control_range->current = current; | |
| 93 if (current_mode != nullptr) { | |
| 94 if (flags && CameraControl_Flags_Auto) | |
| 95 *current_mode = mojom::MeteringMode::CONTINUOUS; | |
| 96 else if (flags && CameraControl_Flags_Manual) | |
| 97 *current_mode = mojom::MeteringMode::MANUAL; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 return control_range; | |
| 102 } | |
| 103 | |
| 65 // Finds and creates a DirectShow Video Capture filter matching the |device_id|. | 104 // Finds and creates a DirectShow Video Capture filter matching the |device_id|. |
| 66 // static | 105 // static |
| 67 HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, | 106 HRESULT VideoCaptureDeviceWin::GetDeviceFilter(const std::string& device_id, |
| 68 IBaseFilter** filter) { | 107 IBaseFilter** filter) { |
| 69 DCHECK(filter); | 108 DCHECK(filter); |
| 70 | 109 |
| 71 ScopedComPtr<ICreateDevEnum> dev_enum; | 110 ScopedComPtr<ICreateDevEnum> dev_enum; |
| 72 HRESULT hr = | 111 HRESULT hr = |
| 73 dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC); | 112 dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC); |
| 74 if (FAILED(hr)) | 113 if (FAILED(hr)) |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 86 for (ScopedComPtr<IMoniker> moniker; | 125 for (ScopedComPtr<IMoniker> moniker; |
| 87 enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK; | 126 enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK; |
| 88 moniker.Reset()) { | 127 moniker.Reset()) { |
| 89 ScopedComPtr<IPropertyBag> prop_bag; | 128 ScopedComPtr<IPropertyBag> prop_bag; |
| 90 hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag)); | 129 hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag)); |
| 91 if (FAILED(hr)) | 130 if (FAILED(hr)) |
| 92 continue; | 131 continue; |
| 93 | 132 |
| 94 // Find |device_id| via DevicePath, Description or FriendlyName, whichever | 133 // Find |device_id| via DevicePath, Description or FriendlyName, whichever |
| 95 // is available first and is a VT_BSTR (i.e. String) type. | 134 // is available first and is a VT_BSTR (i.e. String) type. |
| 96 static const wchar_t* kPropertyNames[] = { | 135 static const wchar_t* kPropertyNames[] = {L"DevicePath", L"Description", |
| 97 L"DevicePath", L"Description", L"FriendlyName"}; | 136 L"FriendlyName"}; |
| 98 | 137 |
| 99 ScopedVariant name; | 138 ScopedVariant name; |
| 100 for (const auto* property_name : kPropertyNames) { | 139 for (const auto* property_name : kPropertyNames) { |
| 101 prop_bag->Read(property_name, name.Receive(), 0); | 140 prop_bag->Read(property_name, name.Receive(), 0); |
| 102 if (name.type() == VT_BSTR) | 141 if (name.type() == VT_BSTR) |
| 103 break; | 142 break; |
| 104 } | 143 } |
| 105 | 144 |
| 106 if (name.type() == VT_BSTR) { | 145 if (name.type() == VT_BSTR) { |
| 107 const std::string device_path(base::SysWideToUTF8(V_BSTR(name.ptr()))); | 146 const std::string device_path(base::SysWideToUTF8(V_BSTR(name.ptr()))); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 148 } | 187 } |
| 149 } | 188 } |
| 150 pin.Reset(); | 189 pin.Reset(); |
| 151 } | 190 } |
| 152 | 191 |
| 153 DCHECK(!pin.Get()); | 192 DCHECK(!pin.Get()); |
| 154 return pin; | 193 return pin; |
| 155 } | 194 } |
| 156 | 195 |
| 157 // static | 196 // static |
| 158 VideoPixelFormat | 197 VideoPixelFormat VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat( |
| 159 VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat( | |
| 160 const GUID& sub_type) { | 198 const GUID& sub_type) { |
| 161 static struct { | 199 static struct { |
| 162 const GUID& sub_type; | 200 const GUID& sub_type; |
| 163 VideoPixelFormat format; | 201 VideoPixelFormat format; |
| 164 } const kMediaSubtypeToPixelFormatCorrespondence[] = { | 202 } const kMediaSubtypeToPixelFormatCorrespondence[] = { |
| 165 {kMediaSubTypeI420, PIXEL_FORMAT_I420}, | 203 {kMediaSubTypeI420, PIXEL_FORMAT_I420}, |
| 166 {MEDIASUBTYPE_IYUV, PIXEL_FORMAT_I420}, | 204 {MEDIASUBTYPE_IYUV, PIXEL_FORMAT_I420}, |
| 167 {MEDIASUBTYPE_RGB24, PIXEL_FORMAT_RGB24}, | 205 {MEDIASUBTYPE_RGB24, PIXEL_FORMAT_RGB24}, |
| 168 {MEDIASUBTYPE_YUY2, PIXEL_FORMAT_YUY2}, | 206 {MEDIASUBTYPE_YUY2, PIXEL_FORMAT_YUY2}, |
| 169 {MEDIASUBTYPE_MJPG, PIXEL_FORMAT_MJPEG}, | 207 {MEDIASUBTYPE_MJPG, PIXEL_FORMAT_MJPEG}, |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 446 | 484 |
| 447 void VideoCaptureDeviceWin::TakePhoto(TakePhotoCallback callback) { | 485 void VideoCaptureDeviceWin::TakePhoto(TakePhotoCallback callback) { |
| 448 DCHECK(thread_checker_.CalledOnValidThread()); | 486 DCHECK(thread_checker_.CalledOnValidThread()); |
| 449 // DirectShow has other means of capturing still pictures, e.g. connecting a | 487 // DirectShow has other means of capturing still pictures, e.g. connecting a |
| 450 // SampleGrabber filter to a PIN_CATEGORY_STILL of |capture_filter_|. This | 488 // SampleGrabber filter to a PIN_CATEGORY_STILL of |capture_filter_|. This |
| 451 // way, however, is not widespread and proves too cumbersome, so we just grab | 489 // way, however, is not widespread and proves too cumbersome, so we just grab |
| 452 // the next captured frame instead. | 490 // the next captured frame instead. |
| 453 take_photo_callbacks_.push(std::move(callback)); | 491 take_photo_callbacks_.push(std::move(callback)); |
| 454 } | 492 } |
| 455 | 493 |
| 494 void VideoCaptureDeviceWin::GetPhotoCapabilities( | |
| 495 GetPhotoCapabilitiesCallback callback) { | |
| 496 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 497 | |
| 498 base::win::ScopedComPtr<IKsTopologyInfo> info; | |
| 499 HRESULT hr = capture_filter_.QueryInterface(info.Receive()); | |
| 500 if (FAILED(hr)) { | |
| 501 SetErrorState(FROM_HERE, "Failed to obtain the topology info.", hr); | |
| 502 return; | |
| 503 } | |
| 504 | |
| 505 DWORD num_nodes = 0; | |
| 506 hr = info->get_NumNodes(&num_nodes); | |
| 507 if (FAILED(hr)) { | |
| 508 SetErrorState(FROM_HERE, "Failed to obtain the number of nodes.", hr); | |
| 509 return; | |
| 510 } | |
| 511 | |
| 512 GUID node_type; | |
| 513 base::win::ScopedComPtr<ICameraControl> camera_control; | |
| 514 base::win::ScopedComPtr<IVideoProcAmp> video_control; | |
| 515 for (size_t i = 0; i < num_nodes; i++) { | |
| 516 info->get_NodeType(i, &node_type); | |
| 517 if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_CAMERA_TERMINAL)) { | |
| 518 hr = info->CreateNodeInstance(i, IID_PPV_ARGS(camera_control.Receive())); | |
|
robliao
2017/05/03 21:38:26
Is it possible for the node list to have two types
mcasas
2017/05/03 22:16:49
Yes, as a matter of fact it's the usual and expect
robliao
2017/05/04 21:34:35
In that case, you might want to clear camera_contr
| |
| 519 if (FAILED(hr)) { | |
| 520 SetErrorState(FROM_HERE, "Failed to retrieve the ICameraControl.", hr); | |
| 521 return; | |
| 522 } | |
| 523 } | |
| 524 if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_PROCESSING)) { | |
| 525 hr = info->CreateNodeInstance(i, IID_PPV_ARGS(video_control.Receive())); | |
| 526 if (FAILED(hr)) { | |
| 527 SetErrorState(FROM_HERE, "Failed to retrieve the IVideoProcAmp.", hr); | |
| 528 return; | |
| 529 } | |
| 530 } | |
| 531 } | |
| 532 if (!camera_control || !video_control) | |
| 533 return; | |
| 534 | |
| 535 auto photo_capabilities = mojom::PhotoCapabilities::New(); | |
| 536 | |
| 537 photo_capabilities->exposure_compensation = RetrieveControlRangeAndCurrent( | |
| 538 [camera_control](auto... args) { | |
|
robliao
2017/05/03 21:38:26
Optional: It might be better to just do the long*,
mcasas
2017/05/03 22:16:49
Correct, but if the calls in l.76 and l.89 would
h
robliao
2017/05/04 21:34:35
Well, it would error out anyways in a failure to s
| |
| 539 return camera_control->getRange_Exposure(args...); | |
| 540 }, | |
| 541 [camera_control](auto... args) { | |
| 542 return camera_control->get_Exposure(args...); | |
| 543 }, | |
| 544 &photo_capabilities->supported_exposure_modes, | |
| 545 &photo_capabilities->current_exposure_mode); | |
| 546 | |
| 547 photo_capabilities->color_temperature = RetrieveControlRangeAndCurrent( | |
| 548 [video_control](auto... args) { | |
| 549 return video_control->getRange_WhiteBalance(args...); | |
| 550 }, | |
| 551 [video_control](auto... args) { | |
| 552 return video_control->get_WhiteBalance(args...); | |
| 553 }, | |
| 554 &photo_capabilities->supported_white_balance_modes, | |
| 555 &photo_capabilities->current_white_balance_mode); | |
| 556 | |
| 557 // Ignore the returned Focus control range and status. | |
| 558 RetrieveControlRangeAndCurrent( | |
| 559 [camera_control](auto... args) { | |
| 560 return camera_control->getRange_Focus(args...); | |
| 561 }, | |
| 562 [camera_control](auto... args) { | |
| 563 return camera_control->get_Focus(args...); | |
| 564 }, | |
| 565 &photo_capabilities->supported_focus_modes, | |
| 566 &photo_capabilities->current_focus_mode); | |
| 567 | |
| 568 photo_capabilities->iso = mojom::Range::New(); | |
| 569 | |
| 570 photo_capabilities->brightness = RetrieveControlRangeAndCurrent( | |
| 571 [video_control](auto... args) { | |
| 572 return video_control->getRange_Brightness(args...); | |
| 573 }, | |
| 574 [video_control](auto... args) { | |
| 575 return video_control->get_Brightness(args...); | |
| 576 }, | |
| 577 nullptr, nullptr); | |
| 578 photo_capabilities->contrast = RetrieveControlRangeAndCurrent( | |
| 579 [video_control](auto... args) { | |
| 580 return video_control->getRange_Contrast(args...); | |
| 581 }, | |
| 582 [video_control](auto... args) { | |
| 583 return video_control->get_Contrast(args...); | |
| 584 }, | |
| 585 nullptr, nullptr); | |
| 586 photo_capabilities->saturation = RetrieveControlRangeAndCurrent( | |
| 587 [video_control](auto... args) { | |
| 588 return video_control->getRange_Saturation(args...); | |
| 589 }, | |
| 590 [video_control](auto... args) { | |
| 591 return video_control->get_Saturation(args...); | |
| 592 }, | |
| 593 nullptr, nullptr); | |
| 594 photo_capabilities->sharpness = RetrieveControlRangeAndCurrent( | |
| 595 [video_control](auto... args) { | |
| 596 return video_control->getRange_Sharpness(args...); | |
| 597 }, | |
| 598 [video_control](auto... args) { | |
| 599 return video_control->get_Sharpness(args...); | |
| 600 }, | |
| 601 nullptr, nullptr); | |
| 602 | |
| 603 photo_capabilities->zoom = RetrieveControlRangeAndCurrent( | |
| 604 [camera_control](auto... args) { | |
| 605 return camera_control->getRange_Zoom(args...); | |
| 606 }, | |
| 607 [camera_control](auto... args) { | |
| 608 return camera_control->get_Zoom(args...); | |
| 609 }, | |
| 610 nullptr, nullptr); | |
| 611 | |
| 612 photo_capabilities->red_eye_reduction = mojom::RedEyeReduction::NEVER; | |
| 613 photo_capabilities->height = mojom::Range::New(); | |
| 614 photo_capabilities->width = mojom::Range::New(); | |
| 615 photo_capabilities->torch = false; | |
| 616 | |
| 617 callback.Run(std::move(photo_capabilities)); | |
| 618 } | |
| 619 | |
| 456 // Implements SinkFilterObserver::SinkFilterObserver. | 620 // Implements SinkFilterObserver::SinkFilterObserver. |
| 457 void VideoCaptureDeviceWin::FrameReceived(const uint8_t* buffer, | 621 void VideoCaptureDeviceWin::FrameReceived(const uint8_t* buffer, |
| 458 int length, | 622 int length, |
| 459 const VideoCaptureFormat& format, | 623 const VideoCaptureFormat& format, |
| 460 base::TimeDelta timestamp) { | 624 base::TimeDelta timestamp) { |
| 461 if (first_ref_time_.is_null()) | 625 if (first_ref_time_.is_null()) |
| 462 first_ref_time_ = base::TimeTicks::Now(); | 626 first_ref_time_ = base::TimeTicks::Now(); |
| 463 | 627 |
| 464 // There is a chance that the platform does not provide us with the timestamp, | 628 // There is a chance that the platform does not provide us with the timestamp, |
| 465 // in which case, we use reference time to calculate a timestamp. | 629 // in which case, we use reference time to calculate a timestamp. |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 590 void VideoCaptureDeviceWin::SetErrorState( | 754 void VideoCaptureDeviceWin::SetErrorState( |
| 591 const tracked_objects::Location& from_here, | 755 const tracked_objects::Location& from_here, |
| 592 const std::string& reason, | 756 const std::string& reason, |
| 593 HRESULT hr) { | 757 HRESULT hr) { |
| 594 DCHECK(thread_checker_.CalledOnValidThread()); | 758 DCHECK(thread_checker_.CalledOnValidThread()); |
| 595 DLOG_IF_FAILED_WITH_HRESULT(reason, hr); | 759 DLOG_IF_FAILED_WITH_HRESULT(reason, hr); |
| 596 state_ = kError; | 760 state_ = kError; |
| 597 client_->OnError(from_here, reason); | 761 client_->OnError(from_here, reason); |
| 598 } | 762 } |
| 599 } // namespace media | 763 } // namespace media |
| OLD | NEW |