| 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 <algorithm> | 7 #include <algorithm> |
| 8 #include <list> | 8 #include <list> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 323 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 334 hr = graph_builder_->AddFilter(sink_filter_, NULL); | 334 hr = graph_builder_->AddFilter(sink_filter_, NULL); |
| 335 if (FAILED(hr)) { | 335 if (FAILED(hr)) { |
| 336 DVLOG(2)<< "Failed to add the send filter to the graph."; | 336 DVLOG(2)<< "Failed to add the send filter to the graph."; |
| 337 return false; | 337 return false; |
| 338 } | 338 } |
| 339 | 339 |
| 340 return CreateCapabilityMap(); | 340 return CreateCapabilityMap(); |
| 341 } | 341 } |
| 342 | 342 |
| 343 void VideoCaptureDeviceWin::AllocateAndStart( | 343 void VideoCaptureDeviceWin::AllocateAndStart( |
| 344 const VideoCaptureCapability& capture_format, | 344 const VideoCaptureParams& params, |
| 345 scoped_ptr<VideoCaptureDevice::Client> client) { | 345 scoped_ptr<VideoCaptureDevice::Client> client) { |
| 346 DCHECK(CalledOnValidThread()); | 346 DCHECK(CalledOnValidThread()); |
| 347 if (state_ != kIdle) | 347 if (state_ != kIdle) |
| 348 return; | 348 return; |
| 349 | 349 |
| 350 client_ = client.Pass(); | 350 client_ = client.Pass(); |
| 351 | 351 |
| 352 // Get the camera capability that best match the requested resolution. | 352 // Get the camera capability that best match the requested resolution. |
| 353 const VideoCaptureCapabilityWin& found_capability = | 353 const VideoCaptureCapabilityWin& found_capability = |
| 354 capabilities_.GetBestMatchedCapability(capture_format.width, | 354 capabilities_.GetBestMatchedFormat( |
| 355 capture_format.height, | 355 params.requested_format.frame_size.width(), |
| 356 capture_format.frame_rate); | 356 params.requested_format.frame_size.height(), |
| 357 VideoCaptureCapability capability = found_capability; | 357 params.requested_format.frame_rate); |
| 358 VideoCaptureFormat format = found_capability.supported_format; |
| 358 | 359 |
| 359 // Reduce the frame rate if the requested frame rate is lower | 360 // Reduce the frame rate if the requested frame rate is lower |
| 360 // than the capability. | 361 // than the capability. |
| 361 if (capability.frame_rate > capture_format.frame_rate) | 362 if (format.frame_rate > params.requested_format.frame_rate) |
| 362 capability.frame_rate = capture_format.frame_rate; | 363 format.frame_rate = params.requested_format.frame_rate; |
| 363 | 364 |
| 364 AM_MEDIA_TYPE* pmt = NULL; | 365 AM_MEDIA_TYPE* pmt = NULL; |
| 365 VIDEO_STREAM_CONFIG_CAPS caps; | 366 VIDEO_STREAM_CONFIG_CAPS caps; |
| 366 | 367 |
| 367 ScopedComPtr<IAMStreamConfig> stream_config; | 368 ScopedComPtr<IAMStreamConfig> stream_config; |
| 368 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); | 369 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
| 369 if (FAILED(hr)) { | 370 if (FAILED(hr)) { |
| 370 SetErrorState("Can't get the Capture format settings"); | 371 SetErrorState("Can't get the Capture format settings"); |
| 371 return; | 372 return; |
| 372 } | 373 } |
| 373 | 374 |
| 374 // Get the windows capability from the capture device. | 375 // Get the windows capability from the capture device. |
| 375 hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt, | 376 hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt, |
| 376 reinterpret_cast<BYTE*>(&caps)); | 377 reinterpret_cast<BYTE*>(&caps)); |
| 377 if (SUCCEEDED(hr)) { | 378 if (SUCCEEDED(hr)) { |
| 378 if (pmt->formattype == FORMAT_VideoInfo) { | 379 if (pmt->formattype == FORMAT_VideoInfo) { |
| 379 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); | 380 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); |
| 380 if (capability.frame_rate > 0) | 381 if (format.frame_rate > 0) |
| 381 h->AvgTimePerFrame = kSecondsToReferenceTime / capability.frame_rate; | 382 h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate; |
| 382 } | 383 } |
| 383 // Set the sink filter to request this capability. | 384 // Set the sink filter to request this format. |
| 384 sink_filter_->SetRequestedMediaCapability(capability); | 385 sink_filter_->SetRequestedMediaFormat(format); |
| 385 // Order the capture device to use this capability. | 386 // Order the capture device to use this format. |
| 386 hr = stream_config->SetFormat(pmt); | 387 hr = stream_config->SetFormat(pmt); |
| 387 } | 388 } |
| 388 | 389 |
| 389 if (FAILED(hr)) | 390 if (FAILED(hr)) |
| 390 SetErrorState("Failed to set capture device output format"); | 391 SetErrorState("Failed to set capture device output format"); |
| 391 | 392 |
| 392 if (capability.color == PIXEL_FORMAT_MJPEG && | 393 if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) { |
| 393 !mjpg_filter_.get()) { | |
| 394 // Create MJPG filter if we need it. | 394 // Create MJPG filter if we need it. |
| 395 hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); | 395 hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); |
| 396 | 396 |
| 397 if (SUCCEEDED(hr)) { | 397 if (SUCCEEDED(hr)) { |
| 398 GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, input_mjpg_pin_.Receive()); | 398 GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, input_mjpg_pin_.Receive()); |
| 399 GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL, | 399 GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL, |
| 400 output_mjpg_pin_.Receive()); | 400 output_mjpg_pin_.Receive()); |
| 401 hr = graph_builder_->AddFilter(mjpg_filter_, NULL); | 401 hr = graph_builder_->AddFilter(mjpg_filter_, NULL); |
| 402 } | 402 } |
| 403 | 403 |
| 404 if (FAILED(hr)) { | 404 if (FAILED(hr)) { |
| 405 mjpg_filter_.Release(); | 405 mjpg_filter_.Release(); |
| 406 input_mjpg_pin_.Release(); | 406 input_mjpg_pin_.Release(); |
| 407 output_mjpg_pin_.Release(); | 407 output_mjpg_pin_.Release(); |
| 408 } | 408 } |
| 409 } | 409 } |
| 410 | 410 |
| 411 if (capability.color == PIXEL_FORMAT_MJPEG && | 411 if (format.pixel_format == PIXEL_FORMAT_MJPEG && mjpg_filter_.get()) { |
| 412 mjpg_filter_.get()) { | |
| 413 // Connect the camera to the MJPEG decoder. | 412 // Connect the camera to the MJPEG decoder. |
| 414 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_, | 413 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_, |
| 415 NULL); | 414 NULL); |
| 416 // Connect the MJPEG filter to the Capture filter. | 415 // Connect the MJPEG filter to the Capture filter. |
| 417 hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_, | 416 hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_, |
| 418 NULL); | 417 NULL); |
| 419 } else { | 418 } else { |
| 420 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_, | 419 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_, |
| 421 NULL); | 420 NULL); |
| 422 } | 421 } |
| 423 | 422 |
| 424 if (FAILED(hr)) { | 423 if (FAILED(hr)) { |
| 425 SetErrorState("Failed to connect the Capture graph."); | 424 SetErrorState("Failed to connect the Capture graph."); |
| 426 return; | 425 return; |
| 427 } | 426 } |
| 428 | 427 |
| 429 hr = media_control_->Pause(); | 428 hr = media_control_->Pause(); |
| 430 if (FAILED(hr)) { | 429 if (FAILED(hr)) { |
| 431 SetErrorState("Failed to Pause the Capture device. " | 430 SetErrorState("Failed to Pause the Capture device. " |
| 432 "Is it already occupied?"); | 431 "Is it already occupied?"); |
| 433 return; | 432 return; |
| 434 } | 433 } |
| 435 | 434 |
| 436 // Get the capability back from the sink filter after the filter have been | 435 // Get the format back from the sink filter after the filter have been |
| 437 // connected. | 436 // connected. |
| 438 current_setting_ = sink_filter_->ResultingCapability(); | 437 capture_format_ = sink_filter_->ResultingFormat(); |
| 439 | 438 |
| 440 // Start capturing. | 439 // Start capturing. |
| 441 hr = media_control_->Run(); | 440 hr = media_control_->Run(); |
| 442 if (FAILED(hr)) { | 441 if (FAILED(hr)) { |
| 443 SetErrorState("Failed to start the Capture device."); | 442 SetErrorState("Failed to start the Capture device."); |
| 444 return; | 443 return; |
| 445 } | 444 } |
| 446 | 445 |
| 447 state_ = kCapturing; | 446 state_ = kCapturing; |
| 448 } | 447 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 472 return; | 471 return; |
| 473 } | 472 } |
| 474 client_.reset(); | 473 client_.reset(); |
| 475 state_ = kIdle; | 474 state_ = kIdle; |
| 476 } | 475 } |
| 477 | 476 |
| 478 // Implements SinkFilterObserver::SinkFilterObserver. | 477 // Implements SinkFilterObserver::SinkFilterObserver. |
| 479 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, | 478 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, |
| 480 int length) { | 479 int length) { |
| 481 client_->OnIncomingCapturedFrame( | 480 client_->OnIncomingCapturedFrame( |
| 482 buffer, length, base::Time::Now(), 0, false, false, current_setting_); | 481 buffer, length, base::Time::Now(), 0, false, false, capture_format_); |
| 483 } | 482 } |
| 484 | 483 |
| 485 bool VideoCaptureDeviceWin::CreateCapabilityMap() { | 484 bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
| 486 DCHECK(CalledOnValidThread()); | 485 DCHECK(CalledOnValidThread()); |
| 487 ScopedComPtr<IAMStreamConfig> stream_config; | 486 ScopedComPtr<IAMStreamConfig> stream_config; |
| 488 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); | 487 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
| 489 if (FAILED(hr)) { | 488 if (FAILED(hr)) { |
| 490 DVLOG(2) << "Failed to get IAMStreamConfig interface from " | 489 DVLOG(2) << "Failed to get IAMStreamConfig interface from " |
| 491 "capture device"; | 490 "capture device"; |
| 492 return false; | 491 return false; |
| (...skipping 22 matching lines...) Expand all Loading... |
| 515 if (hr != S_OK) { | 514 if (hr != S_OK) { |
| 516 DVLOG(2) << "Failed to GetStreamCaps"; | 515 DVLOG(2) << "Failed to GetStreamCaps"; |
| 517 return false; | 516 return false; |
| 518 } | 517 } |
| 519 | 518 |
| 520 if (media_type->majortype == MEDIATYPE_Video && | 519 if (media_type->majortype == MEDIATYPE_Video && |
| 521 media_type->formattype == FORMAT_VideoInfo) { | 520 media_type->formattype == FORMAT_VideoInfo) { |
| 522 VideoCaptureCapabilityWin capability(i); | 521 VideoCaptureCapabilityWin capability(i); |
| 523 VIDEOINFOHEADER* h = | 522 VIDEOINFOHEADER* h = |
| 524 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); | 523 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); |
| 525 capability.width = h->bmiHeader.biWidth; | 524 capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth, |
| 526 capability.height = h->bmiHeader.biHeight; | 525 h->bmiHeader.biHeight); |
| 527 | 526 |
| 528 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use | 527 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use |
| 529 // the value from VIDEOINFOHEADER. | 528 // the value from VIDEOINFOHEADER. |
| 530 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; | 529 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; |
| 531 if (video_control) { | 530 if (video_control) { |
| 532 ScopedCoMem<LONGLONG> max_fps; | 531 ScopedCoMem<LONGLONG> max_fps; |
| 533 LONG list_size = 0; | 532 LONG list_size = 0; |
| 534 SIZE size = { capability.width, capability.height }; | 533 SIZE size = {capability.supported_format.frame_size.width(), |
| 534 capability.supported_format.frame_size.height()}; |
| 535 | 535 |
| 536 // GetFrameRateList doesn't return max frame rate always | 536 // GetFrameRateList doesn't return max frame rate always |
| 537 // eg: Logitech Notebook. This may be due to a bug in that API | 537 // eg: Logitech Notebook. This may be due to a bug in that API |
| 538 // because GetFrameRateList array is reversed in the above camera. So | 538 // because GetFrameRateList array is reversed in the above camera. So |
| 539 // a util method written. Can't assume the first value will return | 539 // a util method written. Can't assume the first value will return |
| 540 // the max fps. | 540 // the max fps. |
| 541 hr = video_control->GetFrameRateList(output_capture_pin_, i, size, | 541 hr = video_control->GetFrameRateList(output_capture_pin_, i, size, |
| 542 &list_size, &max_fps); | 542 &list_size, &max_fps); |
| 543 // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some | 543 // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some |
| 544 // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates | 544 // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates |
| 545 // into success, so explicitly check S_OK. See http://crbug.com/306237. | 545 // into success, so explicitly check S_OK. See http://crbug.com/306237. |
| 546 if (hr == S_OK && list_size > 0 && max_fps) { | 546 if (hr == S_OK && list_size > 0 && max_fps) { |
| 547 time_per_frame = *std::min_element(max_fps.get(), | 547 time_per_frame = *std::min_element(max_fps.get(), |
| 548 max_fps.get() + list_size); | 548 max_fps.get() + list_size); |
| 549 } | 549 } |
| 550 } | 550 } |
| 551 | 551 |
| 552 capability.frame_rate = (time_per_frame > 0) ? | 552 capability.supported_format.frame_rate = |
| 553 static_cast<int>(kSecondsToReferenceTime / time_per_frame) : 0; | 553 (time_per_frame > 0) |
| 554 ? static_cast<int>(kSecondsToReferenceTime / time_per_frame) |
| 555 : 0; |
| 554 | 556 |
| 555 // DirectShow works at the moment only on integer frame_rate but the | 557 // DirectShow works at the moment only on integer frame_rate but the |
| 556 // best capability matching class works on rational frame rates. | 558 // best capability matching class works on rational frame rates. |
| 557 capability.frame_rate_numerator = capability.frame_rate; | 559 capability.frame_rate_numerator = capability.supported_format.frame_rate; |
| 558 capability.frame_rate_denominator = 1; | 560 capability.frame_rate_denominator = 1; |
| 559 | 561 |
| 560 // We can't switch MEDIATYPE :~(. | 562 // We can't switch MEDIATYPE :~(. |
| 561 if (media_type->subtype == kMediaSubTypeI420) { | 563 if (media_type->subtype == kMediaSubTypeI420) { |
| 562 capability.color = PIXEL_FORMAT_I420; | 564 capability.supported_format.pixel_format = PIXEL_FORMAT_I420; |
| 563 } else if (media_type->subtype == MEDIASUBTYPE_IYUV) { | 565 } else if (media_type->subtype == MEDIASUBTYPE_IYUV) { |
| 564 // This is identical to PIXEL_FORMAT_I420. | 566 // This is identical to PIXEL_FORMAT_I420. |
| 565 capability.color = PIXEL_FORMAT_I420; | 567 capability.supported_format.pixel_format = PIXEL_FORMAT_I420; |
| 566 } else if (media_type->subtype == MEDIASUBTYPE_RGB24) { | 568 } else if (media_type->subtype == MEDIASUBTYPE_RGB24) { |
| 567 capability.color = PIXEL_FORMAT_RGB24; | 569 capability.supported_format.pixel_format = PIXEL_FORMAT_RGB24; |
| 568 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { | 570 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { |
| 569 capability.color = PIXEL_FORMAT_YUY2; | 571 capability.supported_format.pixel_format = PIXEL_FORMAT_YUY2; |
| 570 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { | 572 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { |
| 571 capability.color = PIXEL_FORMAT_MJPEG; | 573 capability.supported_format.pixel_format = PIXEL_FORMAT_MJPEG; |
| 572 } else if (media_type->subtype == MEDIASUBTYPE_UYVY) { | 574 } else if (media_type->subtype == MEDIASUBTYPE_UYVY) { |
| 573 capability.color = PIXEL_FORMAT_UYVY; | 575 capability.supported_format.pixel_format = PIXEL_FORMAT_UYVY; |
| 574 } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) { | 576 } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) { |
| 575 capability.color = PIXEL_FORMAT_ARGB; | 577 capability.supported_format.pixel_format = PIXEL_FORMAT_ARGB; |
| 576 } else { | 578 } else { |
| 577 WCHAR guid_str[128]; | 579 WCHAR guid_str[128]; |
| 578 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); | 580 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); |
| 579 DVLOG(2) << "Device supports (also) an unknown media type " << guid_str; | 581 DVLOG(2) << "Device supports (also) an unknown media type " << guid_str; |
| 580 continue; | 582 continue; |
| 581 } | 583 } |
| 582 capabilities_.Add(capability); | 584 capabilities_.Add(capability); |
| 583 } | 585 } |
| 584 DeleteMediaType(media_type); | 586 DeleteMediaType(media_type); |
| 585 media_type = NULL; | 587 media_type = NULL; |
| 586 } | 588 } |
| 587 | 589 |
| 588 return !capabilities_.empty(); | 590 return !capabilities_.empty(); |
| 589 } | 591 } |
| 590 | 592 |
| 591 void VideoCaptureDeviceWin::SetErrorState(const char* reason) { | 593 void VideoCaptureDeviceWin::SetErrorState(const char* reason) { |
| 592 DCHECK(CalledOnValidThread()); | 594 DCHECK(CalledOnValidThread()); |
| 593 DVLOG(1) << reason; | 595 DVLOG(1) << reason; |
| 594 state_ = kError; | 596 state_ = kError; |
| 595 client_->OnError(); | 597 client_->OnError(); |
| 596 } | 598 } |
| 597 } // namespace media | 599 } // namespace media |
| OLD | NEW |