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 VideoCaptureParams& params, | 344 const VideoCaptureCapability& capture_format, |
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_.GetBestMatchedFormat( | 354 capabilities_.GetBestMatchedCapability(capture_format.width, |
355 params.requested_format.frame_size.width(), | 355 capture_format.height, |
356 params.requested_format.frame_size.height(), | 356 capture_format.frame_rate); |
357 params.requested_format.frame_rate); | 357 VideoCaptureCapability capability = found_capability; |
358 VideoCaptureFormat format = found_capability.supported_format; | |
359 | 358 |
360 // Reduce the frame rate if the requested frame rate is lower | 359 // Reduce the frame rate if the requested frame rate is lower |
361 // than the capability. | 360 // than the capability. |
362 if (format.frame_rate > params.requested_format.frame_rate) | 361 if (capability.frame_rate > capture_format.frame_rate) |
363 format.frame_rate = params.requested_format.frame_rate; | 362 capability.frame_rate = capture_format.frame_rate; |
364 | 363 |
365 AM_MEDIA_TYPE* pmt = NULL; | 364 AM_MEDIA_TYPE* pmt = NULL; |
366 VIDEO_STREAM_CONFIG_CAPS caps; | 365 VIDEO_STREAM_CONFIG_CAPS caps; |
367 | 366 |
368 ScopedComPtr<IAMStreamConfig> stream_config; | 367 ScopedComPtr<IAMStreamConfig> stream_config; |
369 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); | 368 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
370 if (FAILED(hr)) { | 369 if (FAILED(hr)) { |
371 SetErrorState("Can't get the Capture format settings"); | 370 SetErrorState("Can't get the Capture format settings"); |
372 return; | 371 return; |
373 } | 372 } |
374 | 373 |
375 // Get the windows capability from the capture device. | 374 // Get the windows capability from the capture device. |
376 hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt, | 375 hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt, |
377 reinterpret_cast<BYTE*>(&caps)); | 376 reinterpret_cast<BYTE*>(&caps)); |
378 if (SUCCEEDED(hr)) { | 377 if (SUCCEEDED(hr)) { |
379 if (pmt->formattype == FORMAT_VideoInfo) { | 378 if (pmt->formattype == FORMAT_VideoInfo) { |
380 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); | 379 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); |
381 if (format.frame_rate > 0) | 380 if (capability.frame_rate > 0) |
382 h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate; | 381 h->AvgTimePerFrame = kSecondsToReferenceTime / capability.frame_rate; |
383 } | 382 } |
384 // Set the sink filter to request this format. | 383 // Set the sink filter to request this capability. |
385 sink_filter_->SetRequestedMediaFormat(format); | 384 sink_filter_->SetRequestedMediaCapability(capability); |
386 // Order the capture device to use this format. | 385 // Order the capture device to use this capability. |
387 hr = stream_config->SetFormat(pmt); | 386 hr = stream_config->SetFormat(pmt); |
388 } | 387 } |
389 | 388 |
390 if (FAILED(hr)) | 389 if (FAILED(hr)) |
391 SetErrorState("Failed to set capture device output format"); | 390 SetErrorState("Failed to set capture device output format"); |
392 | 391 |
393 if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) { | 392 if (capability.color == PIXEL_FORMAT_MJPEG && |
| 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 (format.pixel_format == PIXEL_FORMAT_MJPEG && mjpg_filter_.get()) { | 411 if (capability.color == PIXEL_FORMAT_MJPEG && |
| 412 mjpg_filter_.get()) { |
412 // Connect the camera to the MJPEG decoder. | 413 // Connect the camera to the MJPEG decoder. |
413 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_, | 414 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_, |
414 NULL); | 415 NULL); |
415 // Connect the MJPEG filter to the Capture filter. | 416 // Connect the MJPEG filter to the Capture filter. |
416 hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_, | 417 hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_, |
417 NULL); | 418 NULL); |
418 } else { | 419 } else { |
419 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_, | 420 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_, |
420 NULL); | 421 NULL); |
421 } | 422 } |
422 | 423 |
423 if (FAILED(hr)) { | 424 if (FAILED(hr)) { |
424 SetErrorState("Failed to connect the Capture graph."); | 425 SetErrorState("Failed to connect the Capture graph."); |
425 return; | 426 return; |
426 } | 427 } |
427 | 428 |
428 hr = media_control_->Pause(); | 429 hr = media_control_->Pause(); |
429 if (FAILED(hr)) { | 430 if (FAILED(hr)) { |
430 SetErrorState("Failed to Pause the Capture device. " | 431 SetErrorState("Failed to Pause the Capture device. " |
431 "Is it already occupied?"); | 432 "Is it already occupied?"); |
432 return; | 433 return; |
433 } | 434 } |
434 | 435 |
435 // Get the format back from the sink filter after the filter have been | 436 // Get the capability back from the sink filter after the filter have been |
436 // connected. | 437 // connected. |
437 capture_format_ = sink_filter_->ResultingFormat(); | 438 current_setting_ = sink_filter_->ResultingCapability(); |
438 | 439 |
439 // Start capturing. | 440 // Start capturing. |
440 hr = media_control_->Run(); | 441 hr = media_control_->Run(); |
441 if (FAILED(hr)) { | 442 if (FAILED(hr)) { |
442 SetErrorState("Failed to start the Capture device."); | 443 SetErrorState("Failed to start the Capture device."); |
443 return; | 444 return; |
444 } | 445 } |
445 | 446 |
446 state_ = kCapturing; | 447 state_ = kCapturing; |
447 } | 448 } |
(...skipping 23 matching lines...) Expand all Loading... |
471 return; | 472 return; |
472 } | 473 } |
473 client_.reset(); | 474 client_.reset(); |
474 state_ = kIdle; | 475 state_ = kIdle; |
475 } | 476 } |
476 | 477 |
477 // Implements SinkFilterObserver::SinkFilterObserver. | 478 // Implements SinkFilterObserver::SinkFilterObserver. |
478 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, | 479 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, |
479 int length) { | 480 int length) { |
480 client_->OnIncomingCapturedFrame( | 481 client_->OnIncomingCapturedFrame( |
481 buffer, length, base::Time::Now(), 0, false, false, capture_format_); | 482 buffer, length, base::Time::Now(), 0, false, false, current_setting_); |
482 } | 483 } |
483 | 484 |
484 bool VideoCaptureDeviceWin::CreateCapabilityMap() { | 485 bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
485 DCHECK(CalledOnValidThread()); | 486 DCHECK(CalledOnValidThread()); |
486 ScopedComPtr<IAMStreamConfig> stream_config; | 487 ScopedComPtr<IAMStreamConfig> stream_config; |
487 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); | 488 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
488 if (FAILED(hr)) { | 489 if (FAILED(hr)) { |
489 DVLOG(2) << "Failed to get IAMStreamConfig interface from " | 490 DVLOG(2) << "Failed to get IAMStreamConfig interface from " |
490 "capture device"; | 491 "capture device"; |
491 return false; | 492 return false; |
(...skipping 22 matching lines...) Expand all Loading... |
514 if (hr != S_OK) { | 515 if (hr != S_OK) { |
515 DVLOG(2) << "Failed to GetStreamCaps"; | 516 DVLOG(2) << "Failed to GetStreamCaps"; |
516 return false; | 517 return false; |
517 } | 518 } |
518 | 519 |
519 if (media_type->majortype == MEDIATYPE_Video && | 520 if (media_type->majortype == MEDIATYPE_Video && |
520 media_type->formattype == FORMAT_VideoInfo) { | 521 media_type->formattype == FORMAT_VideoInfo) { |
521 VideoCaptureCapabilityWin capability(i); | 522 VideoCaptureCapabilityWin capability(i); |
522 VIDEOINFOHEADER* h = | 523 VIDEOINFOHEADER* h = |
523 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); | 524 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); |
524 capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth, | 525 capability.width = h->bmiHeader.biWidth; |
525 h->bmiHeader.biHeight); | 526 capability.height = h->bmiHeader.biHeight; |
526 | 527 |
527 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use | 528 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use |
528 // the value from VIDEOINFOHEADER. | 529 // the value from VIDEOINFOHEADER. |
529 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; | 530 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; |
530 if (video_control) { | 531 if (video_control) { |
531 ScopedCoMem<LONGLONG> max_fps; | 532 ScopedCoMem<LONGLONG> max_fps; |
532 LONG list_size = 0; | 533 LONG list_size = 0; |
533 SIZE size = {capability.supported_format.frame_size.width(), | 534 SIZE size = { capability.width, capability.height }; |
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.supported_format.frame_rate = | 552 capability.frame_rate = (time_per_frame > 0) ? |
553 (time_per_frame > 0) | 553 static_cast<int>(kSecondsToReferenceTime / time_per_frame) : 0; |
554 ? static_cast<int>(kSecondsToReferenceTime / time_per_frame) | |
555 : 0; | |
556 | 554 |
557 // DirectShow works at the moment only on integer frame_rate but the | 555 // DirectShow works at the moment only on integer frame_rate but the |
558 // best capability matching class works on rational frame rates. | 556 // best capability matching class works on rational frame rates. |
559 capability.frame_rate_numerator = capability.supported_format.frame_rate; | 557 capability.frame_rate_numerator = capability.frame_rate; |
560 capability.frame_rate_denominator = 1; | 558 capability.frame_rate_denominator = 1; |
561 | 559 |
562 // We can't switch MEDIATYPE :~(. | 560 // We can't switch MEDIATYPE :~(. |
563 if (media_type->subtype == kMediaSubTypeI420) { | 561 if (media_type->subtype == kMediaSubTypeI420) { |
564 capability.supported_format.pixel_format = PIXEL_FORMAT_I420; | 562 capability.color = PIXEL_FORMAT_I420; |
565 } else if (media_type->subtype == MEDIASUBTYPE_IYUV) { | 563 } else if (media_type->subtype == MEDIASUBTYPE_IYUV) { |
566 // This is identical to PIXEL_FORMAT_I420. | 564 // This is identical to PIXEL_FORMAT_I420. |
567 capability.supported_format.pixel_format = PIXEL_FORMAT_I420; | 565 capability.color = PIXEL_FORMAT_I420; |
568 } else if (media_type->subtype == MEDIASUBTYPE_RGB24) { | 566 } else if (media_type->subtype == MEDIASUBTYPE_RGB24) { |
569 capability.supported_format.pixel_format = PIXEL_FORMAT_RGB24; | 567 capability.color = PIXEL_FORMAT_RGB24; |
570 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { | 568 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { |
571 capability.supported_format.pixel_format = PIXEL_FORMAT_YUY2; | 569 capability.color = PIXEL_FORMAT_YUY2; |
572 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { | 570 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { |
573 capability.supported_format.pixel_format = PIXEL_FORMAT_MJPEG; | 571 capability.color = PIXEL_FORMAT_MJPEG; |
574 } else if (media_type->subtype == MEDIASUBTYPE_UYVY) { | 572 } else if (media_type->subtype == MEDIASUBTYPE_UYVY) { |
575 capability.supported_format.pixel_format = PIXEL_FORMAT_UYVY; | 573 capability.color = PIXEL_FORMAT_UYVY; |
576 } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) { | 574 } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) { |
577 capability.supported_format.pixel_format = PIXEL_FORMAT_ARGB; | 575 capability.color = PIXEL_FORMAT_ARGB; |
578 } else { | 576 } else { |
579 WCHAR guid_str[128]; | 577 WCHAR guid_str[128]; |
580 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); | 578 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); |
581 DVLOG(2) << "Device supports (also) an unknown media type " << guid_str; | 579 DVLOG(2) << "Device supports (also) an unknown media type " << guid_str; |
582 continue; | 580 continue; |
583 } | 581 } |
584 capabilities_.Add(capability); | 582 capabilities_.Add(capability); |
585 } | 583 } |
586 DeleteMediaType(media_type); | 584 DeleteMediaType(media_type); |
587 media_type = NULL; | 585 media_type = NULL; |
588 } | 586 } |
589 | 587 |
590 return !capabilities_.empty(); | 588 return !capabilities_.empty(); |
591 } | 589 } |
592 | 590 |
593 void VideoCaptureDeviceWin::SetErrorState(const char* reason) { | 591 void VideoCaptureDeviceWin::SetErrorState(const char* reason) { |
594 DCHECK(CalledOnValidThread()); | 592 DCHECK(CalledOnValidThread()); |
595 DVLOG(1) << reason; | 593 DVLOG(1) << reason; |
596 state_ = kError; | 594 state_ = kError; |
597 client_->OnError(); | 595 client_->OnError(); |
598 } | 596 } |
599 } // namespace media | 597 } // namespace media |
OLD | NEW |