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/video/capture/win/video_capture_device_win.h" | 5 #include "media/video/capture/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 | 9 |
| 10 #include <algorithm> | 10 #include <algorithm> |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 if (capture_filter_) | 216 if (capture_filter_) |
| 217 graph_builder_->RemoveFilter(capture_filter_); | 217 graph_builder_->RemoveFilter(capture_filter_); |
| 218 | 218 |
| 219 if (mjpg_filter_) | 219 if (mjpg_filter_) |
| 220 graph_builder_->RemoveFilter(mjpg_filter_); | 220 graph_builder_->RemoveFilter(mjpg_filter_); |
| 221 } | 221 } |
| 222 } | 222 } |
| 223 | 223 |
| 224 bool VideoCaptureDeviceWin::Init() { | 224 bool VideoCaptureDeviceWin::Init() { |
| 225 DCHECK(CalledOnValidThread()); | 225 DCHECK(CalledOnValidThread()); |
| 226 HRESULT hr = GetDeviceFilter(device_name_.id(), capture_filter_.Receive()); | 226 |
| 227 if (!capture_filter_) { | 227 if (!VideoCaptureDeviceWin::GetDeviceSupportedFormats(device_name_, |
| 228 DLOG(ERROR) << "Failed to create capture filter: " | 228 &capture_filter_, |
| 229 << logging::SystemErrorCodeToString(hr); | 229 &output_capture_pin_, |
| 230 &capabilities_)) { | |
| 230 return false; | 231 return false; |
| 231 } | 232 } |
| 232 | 233 |
| 233 output_capture_pin_ = | |
| 234 GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE); | |
| 235 if (!output_capture_pin_) { | |
| 236 DLOG(ERROR) << "Failed to get capture output pin"; | |
| 237 return false; | |
| 238 } | |
| 239 | |
| 240 // Create the sink filter used for receiving Captured frames. | 234 // Create the sink filter used for receiving Captured frames. |
| 241 sink_filter_ = new SinkFilter(this); | 235 sink_filter_ = new SinkFilter(this); |
| 242 if (sink_filter_ == NULL) { | 236 if (sink_filter_ == NULL) { |
| 243 DLOG(ERROR) << "Failed to create send filter"; | 237 DLOG(ERROR) << "Failed to create send filter"; |
| 244 return false; | 238 return false; |
| 245 } | 239 } |
| 246 | 240 |
| 247 input_sink_pin_ = sink_filter_->GetPin(0); | 241 input_sink_pin_ = sink_filter_->GetPin(0); |
| 248 | 242 |
| 249 hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL, | 243 HRESULT hr = graph_builder_.CreateInstance( |
| 250 CLSCTX_INPROC_SERVER); | 244 CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER); |
| 251 if (FAILED(hr)) { | 245 if (FAILED(hr)) { |
| 252 DLOG(ERROR) << "Failed to create graph builder: " | 246 DLOG(ERROR) << "Failed to create graph builder: " |
| 253 << logging::SystemErrorCodeToString(hr); | 247 << logging::SystemErrorCodeToString(hr); |
| 254 return false; | 248 return false; |
| 255 } | 249 } |
| 256 | 250 |
| 257 hr = graph_builder_.QueryInterface(media_control_.Receive()); | 251 hr = graph_builder_.QueryInterface(media_control_.Receive()); |
| 258 if (FAILED(hr)) { | 252 if (FAILED(hr)) { |
| 259 DLOG(ERROR) << "Failed to create media control builder: " | 253 DLOG(ERROR) << "Failed to create media control builder: " |
| 260 << logging::SystemErrorCodeToString(hr); | 254 << logging::SystemErrorCodeToString(hr); |
| 261 return false; | 255 return false; |
| 262 } | 256 } |
| 263 | 257 |
| 264 hr = graph_builder_->AddFilter(capture_filter_, NULL); | 258 hr = graph_builder_->AddFilter(capture_filter_, NULL); |
| 265 if (FAILED(hr)) { | 259 if (FAILED(hr)) { |
| 266 DLOG(ERROR) << "Failed to add the capture device to the graph: " | 260 DLOG(ERROR) << "Failed to add the capture device to the graph: " |
| 267 << logging::SystemErrorCodeToString(hr); | 261 << logging::SystemErrorCodeToString(hr); |
| 268 return false; | 262 return false; |
| 269 } | 263 } |
| 270 | 264 |
| 271 hr = graph_builder_->AddFilter(sink_filter_, NULL); | 265 hr = graph_builder_->AddFilter(sink_filter_, NULL); |
| 272 if (FAILED(hr)) { | 266 if (FAILED(hr)) { |
| 273 DLOG(ERROR) << "Failed to add the send filter to the graph: " | 267 DLOG(ERROR) << "Failed to add the send filter to the graph: " |
| 274 << logging::SystemErrorCodeToString(hr); | 268 << logging::SystemErrorCodeToString(hr); |
| 275 return false; | 269 return false; |
| 276 } | 270 } |
| 277 | 271 |
| 278 return CreateCapabilityMap(); | 272 return true; |
| 279 } | 273 } |
| 280 | 274 |
| 281 void VideoCaptureDeviceWin::AllocateAndStart( | 275 void VideoCaptureDeviceWin::AllocateAndStart( |
| 282 const VideoCaptureParams& params, | 276 const VideoCaptureParams& params, |
| 283 scoped_ptr<VideoCaptureDevice::Client> client) { | 277 scoped_ptr<VideoCaptureDevice::Client> client) { |
| 284 DCHECK(CalledOnValidThread()); | 278 DCHECK(CalledOnValidThread()); |
| 285 if (state_ != kIdle) | 279 if (state_ != kIdle) |
| 286 return; | 280 return; |
| 287 | 281 |
| 288 client_ = client.Pass(); | 282 client_ = client.Pass(); |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 432 state_ = kIdle; | 426 state_ = kIdle; |
| 433 } | 427 } |
| 434 | 428 |
| 435 // Implements SinkFilterObserver::SinkFilterObserver. | 429 // Implements SinkFilterObserver::SinkFilterObserver. |
| 436 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, | 430 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, |
| 437 int length) { | 431 int length) { |
| 438 client_->OnIncomingCapturedData( | 432 client_->OnIncomingCapturedData( |
| 439 buffer, length, capture_format_, 0, base::TimeTicks::Now()); | 433 buffer, length, capture_format_, 0, base::TimeTicks::Now()); |
| 440 } | 434 } |
| 441 | 435 |
| 442 bool VideoCaptureDeviceWin::CreateCapabilityMap() { | 436 // static |
| 443 DCHECK(CalledOnValidThread()); | 437 bool VideoCaptureDeviceWin::GetDeviceSupportedFormats( |
|
perkj_chrome
2014/09/19 10:04:55
It looks like this is doing much more that gettign
| |
| 438 const Name& device, | |
| 439 ScopedComPtr<IBaseFilter>* capture_filter, | |
| 440 ScopedComPtr<IPin>* output_capture_pin, | |
| 441 CapabilityList* capabilities) { | |
| 442 HRESULT hr = GetDeviceFilter(device.id(), capture_filter->Receive()); | |
| 443 if (!capture_filter) { | |
| 444 DLOG(ERROR) << "Failed to create capture filter: " | |
| 445 << logging::SystemErrorCodeToString(hr); | |
| 446 return false; | |
| 447 } | |
| 448 | |
| 449 *output_capture_pin = | |
| 450 GetPin(*capture_filter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE); | |
| 451 if (!*output_capture_pin) { | |
| 452 DLOG(ERROR) << "Failed to get capture output pin"; | |
| 453 return false; | |
| 454 } | |
| 455 | |
| 444 ScopedComPtr<IAMStreamConfig> stream_config; | 456 ScopedComPtr<IAMStreamConfig> stream_config; |
| 445 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); | 457 hr = output_capture_pin->QueryInterface(stream_config.Receive()); |
| 446 if (FAILED(hr)) { | 458 if (FAILED(hr)) { |
| 447 DPLOG(ERROR) << "Failed to get IAMStreamConfig interface from " | 459 DPLOG(ERROR) << "Failed to get IAMStreamConfig interface from " |
| 448 "capture device: " << logging::SystemErrorCodeToString(hr); | 460 "capture device: " << logging::SystemErrorCodeToString(hr); |
| 449 return false; | 461 return false; |
| 450 } | 462 } |
| 451 | 463 |
| 452 // Get interface used for getting the frame rate. | 464 // Get interface used for getting the frame rate. |
| 453 ScopedComPtr<IAMVideoControl> video_control; | 465 ScopedComPtr<IAMVideoControl> video_control; |
| 454 hr = capture_filter_.QueryInterface(video_control.Receive()); | 466 hr = capture_filter->QueryInterface(video_control.Receive()); |
| 455 DLOG_IF(WARNING, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED: " | 467 DLOG_IF(WARNING, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED: " |
| 456 << logging::SystemErrorCodeToString(hr); | 468 << logging::SystemErrorCodeToString(hr); |
| 457 | 469 |
| 458 int count = 0, size = 0; | 470 int count = 0, size = 0; |
| 459 hr = stream_config->GetNumberOfCapabilities(&count, &size); | 471 hr = stream_config->GetNumberOfCapabilities(&count, &size); |
| 460 if (FAILED(hr)) { | 472 if (FAILED(hr)) { |
| 461 DLOG(ERROR) << "Failed to GetNumberOfCapabilities: " | 473 DLOG(ERROR) << "Failed to GetNumberOfCapabilities: " |
| 462 << logging::SystemErrorCodeToString(hr); | 474 << logging::SystemErrorCodeToString(hr); |
| 463 return false; | 475 return false; |
| 464 } | 476 } |
| 465 | 477 |
| 466 scoped_ptr<BYTE[]> caps(new BYTE[size]); | 478 scoped_ptr<BYTE[]> caps(new BYTE[size]); |
| 467 for (int i = 0; i < count; ++i) { | 479 for (int i = 0; i < count; ++i) { |
| 468 ScopedMediaType media_type; | 480 ScopedMediaType media_type; |
| 469 hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get()); | 481 hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get()); |
| 470 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() | 482 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() |
| 471 // macros here since they'll trigger incorrectly. | 483 // macros here since they'll trigger incorrectly. |
| 472 if (hr != S_OK) { | 484 if (hr != S_OK) { |
| 473 DLOG(ERROR) << "Failed to GetStreamCaps: " | 485 DLOG(ERROR) << "Failed to GetStreamCaps: " |
| 474 << logging::SystemErrorCodeToString(hr); | 486 << logging::SystemErrorCodeToString(hr); |
| 475 return false; | 487 continue; |
| 476 } | 488 } |
| 477 | 489 |
| 478 if (media_type->majortype == MEDIATYPE_Video && | 490 if (media_type->majortype == MEDIATYPE_Video && |
| 479 media_type->formattype == FORMAT_VideoInfo) { | 491 media_type->formattype == FORMAT_VideoInfo) { |
| 480 VideoCaptureCapabilityWin capability(i); | 492 VideoCaptureCapabilityWin capability(i); |
| 481 capability.supported_format.pixel_format = | 493 capability.supported_format.pixel_format = |
| 482 TranslateMediaSubtypeToPixelFormat(media_type->subtype); | 494 TranslateMediaSubtypeToPixelFormat(media_type->subtype); |
| 483 if (capability.supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) | 495 if (capability.supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) |
| 484 continue; | 496 continue; |
| 485 | 497 |
| 486 VIDEOINFOHEADER* h = | 498 VIDEOINFOHEADER* h = |
| 487 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); | 499 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); |
| 488 capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth, | 500 capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth, |
| 489 h->bmiHeader.biHeight); | 501 h->bmiHeader.biHeight); |
| 490 | 502 |
| 491 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use | 503 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use |
| 492 // the value from VIDEOINFOHEADER. | 504 // the value from VIDEOINFOHEADER. |
| 493 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; | 505 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; |
| 494 if (video_control) { | 506 if (video_control) { |
| 495 ScopedCoMem<LONGLONG> max_fps; | 507 ScopedCoMem<LONGLONG> max_fps; |
| 496 LONG list_size = 0; | 508 LONG list_size = 0; |
| 497 SIZE size = {capability.supported_format.frame_size.width(), | 509 SIZE size = {capability.supported_format.frame_size.width(), |
| 498 capability.supported_format.frame_size.height()}; | 510 capability.supported_format.frame_size.height()}; |
| 499 | 511 |
| 500 // GetFrameRateList doesn't return max frame rate always | 512 // GetFrameRateList doesn't return max frame rate always |
| 501 // eg: Logitech Notebook. This may be due to a bug in that API | 513 // eg: Logitech Notebook. This may be due to a bug in that API |
| 502 // because GetFrameRateList array is reversed in the above camera. So | 514 // because GetFrameRateList array is reversed in the above camera. So |
| 503 // a util method written. Can't assume the first value will return | 515 // a util method written. Can't assume the first value will return |
| 504 // the max fps. | 516 // the max fps. |
| 505 hr = video_control->GetFrameRateList(output_capture_pin_, i, size, | 517 hr = video_control->GetFrameRateList(*output_capture_pin, i, size, |
| 506 &list_size, &max_fps); | 518 &list_size, &max_fps); |
| 507 // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some | 519 // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some |
| 508 // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates | 520 // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates |
| 509 // into success, so explicitly check S_OK. See http://crbug.com/306237. | 521 // into success, so explicitly check S_OK. See http://crbug.com/306237. |
| 510 if (hr == S_OK && list_size > 0 && max_fps) { | 522 if (hr == S_OK && list_size > 0 && max_fps) { |
| 511 time_per_frame = *std::min_element(max_fps.get(), | 523 time_per_frame = *std::min_element(max_fps.get(), |
| 512 max_fps.get() + list_size); | 524 max_fps.get() + list_size); |
| 513 } | 525 } |
| 514 } | 526 } |
| 515 | 527 |
| 516 capability.supported_format.frame_rate = | 528 capability.supported_format.frame_rate = |
| 517 (time_per_frame > 0) | 529 (time_per_frame > 0) |
| 518 ? (kSecondsToReferenceTime / static_cast<float>(time_per_frame)) | 530 ? (kSecondsToReferenceTime / static_cast<float>(time_per_frame)) |
| 519 : 0.0; | 531 : 0.0; |
| 520 | 532 |
| 521 // DirectShow works at the moment only on integer frame_rate but the | 533 capabilities->Add(capability); |
| 522 // best capability matching class works on rational frame rates. | |
| 523 capability.frame_rate_numerator = capability.supported_format.frame_rate; | |
| 524 capability.frame_rate_denominator = 1; | |
| 525 | |
| 526 capabilities_.Add(capability); | |
| 527 } | 534 } |
| 528 } | 535 } |
| 529 | 536 |
| 530 return !capabilities_.empty(); | 537 return !capabilities->empty(); |
| 531 } | 538 } |
| 532 | 539 |
| 533 // Set the power line frequency removal in |capture_filter_| if available. | 540 // Set the power line frequency removal in |capture_filter_| if available. |
| 534 void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() { | 541 void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() { |
| 535 const int power_line_frequency = GetPowerLineFrequencyForLocation(); | 542 const int power_line_frequency = GetPowerLineFrequencyForLocation(); |
| 536 if (power_line_frequency != kPowerLine50Hz && | 543 if (power_line_frequency != kPowerLine50Hz && |
| 537 power_line_frequency != kPowerLine60Hz) { | 544 power_line_frequency != kPowerLine60Hz) { |
| 538 return; | 545 return; |
| 539 } | 546 } |
| 540 ScopedComPtr<IKsPropertySet> ks_propset; | 547 ScopedComPtr<IKsPropertySet> ks_propset; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 560 DVLOG(2) << "Anti-flicker setting not supported."; | 567 DVLOG(2) << "Anti-flicker setting not supported."; |
| 561 } | 568 } |
| 562 } | 569 } |
| 563 | 570 |
| 564 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { | 571 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { |
| 565 DCHECK(CalledOnValidThread()); | 572 DCHECK(CalledOnValidThread()); |
| 566 state_ = kError; | 573 state_ = kError; |
| 567 client_->OnError(reason); | 574 client_->OnError(reason); |
| 568 } | 575 } |
| 569 } // namespace media | 576 } // namespace media |
| OLD | NEW |