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 |