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 #include <vidcap.h> |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 DCHECK(filter); | 109 DCHECK(filter); |
110 | 110 |
111 ScopedComPtr<ICreateDevEnum> dev_enum; | 111 ScopedComPtr<ICreateDevEnum> dev_enum; |
112 HRESULT hr = | 112 HRESULT hr = |
113 dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC); | 113 dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC); |
114 if (FAILED(hr)) | 114 if (FAILED(hr)) |
115 return hr; | 115 return hr; |
116 | 116 |
117 ScopedComPtr<IEnumMoniker> enum_moniker; | 117 ScopedComPtr<IEnumMoniker> enum_moniker; |
118 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, | 118 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
119 enum_moniker.Receive(), 0); | 119 enum_moniker.GetAddressOf(), 0); |
120 // CreateClassEnumerator returns S_FALSE on some Windows OS | 120 // CreateClassEnumerator returns S_FALSE on some Windows OS |
121 // when no camera exist. Therefore the FAILED macro can't be used. | 121 // when no camera exist. Therefore the FAILED macro can't be used. |
122 if (hr != S_OK) | 122 if (hr != S_OK) |
123 return hr; | 123 return hr; |
124 | 124 |
125 ScopedComPtr<IBaseFilter> capture_filter; | 125 ScopedComPtr<IBaseFilter> capture_filter; |
126 for (ScopedComPtr<IMoniker> moniker; | 126 for (ScopedComPtr<IMoniker> moniker; |
127 enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK; | 127 enum_moniker->Next(1, moniker.GetAddressOf(), NULL) == S_OK; |
128 moniker.Reset()) { | 128 moniker.Reset()) { |
129 ScopedComPtr<IPropertyBag> prop_bag; | 129 ScopedComPtr<IPropertyBag> prop_bag; |
130 hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag)); | 130 hr = moniker->BindToStorage(0, 0, IID_PPV_ARGS(&prop_bag)); |
131 if (FAILED(hr)) | 131 if (FAILED(hr)) |
132 continue; | 132 continue; |
133 | 133 |
134 // Find |device_id| via DevicePath, Description or FriendlyName, whichever | 134 // Find |device_id| via DevicePath, Description or FriendlyName, whichever |
135 // is available first and is a VT_BSTR (i.e. String) type. | 135 // is available first and is a VT_BSTR (i.e. String) type. |
136 static const wchar_t* kPropertyNames[] = {L"DevicePath", L"Description", | 136 static const wchar_t* kPropertyNames[] = {L"DevicePath", L"Description", |
137 L"FriendlyName"}; | 137 L"FriendlyName"}; |
(...skipping 26 matching lines...) Expand all Loading... |
164 | 164 |
165 // Finds an IPin on an IBaseFilter given the direction, Category and/or Major | 165 // Finds an IPin on an IBaseFilter given the direction, Category and/or Major |
166 // Type. If either |category| or |major_type| are GUID_NULL, they are ignored. | 166 // Type. If either |category| or |major_type| are GUID_NULL, they are ignored. |
167 // static | 167 // static |
168 ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, | 168 ScopedComPtr<IPin> VideoCaptureDeviceWin::GetPin(IBaseFilter* filter, |
169 PIN_DIRECTION pin_dir, | 169 PIN_DIRECTION pin_dir, |
170 REFGUID category, | 170 REFGUID category, |
171 REFGUID major_type) { | 171 REFGUID major_type) { |
172 ScopedComPtr<IPin> pin; | 172 ScopedComPtr<IPin> pin; |
173 ScopedComPtr<IEnumPins> pin_enum; | 173 ScopedComPtr<IEnumPins> pin_enum; |
174 HRESULT hr = filter->EnumPins(pin_enum.Receive()); | 174 HRESULT hr = filter->EnumPins(pin_enum.GetAddressOf()); |
175 if (pin_enum.Get() == NULL) | 175 if (pin_enum.Get() == NULL) |
176 return pin; | 176 return pin; |
177 | 177 |
178 // Get first unconnected pin. | 178 // Get first unconnected pin. |
179 hr = pin_enum->Reset(); // set to first pin | 179 hr = pin_enum->Reset(); // set to first pin |
180 while ((hr = pin_enum->Next(1, pin.Receive(), NULL)) == S_OK) { | 180 while ((hr = pin_enum->Next(1, pin.GetAddressOf(), NULL)) == S_OK) { |
181 PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); | 181 PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); |
182 hr = pin->QueryDirection(&this_pin_dir); | 182 hr = pin->QueryDirection(&this_pin_dir); |
183 if (pin_dir == this_pin_dir) { | 183 if (pin_dir == this_pin_dir) { |
184 if ((category == GUID_NULL || PinMatchesCategory(pin.Get(), category)) && | 184 if ((category == GUID_NULL || PinMatchesCategory(pin.Get(), category)) && |
185 (major_type == GUID_NULL || | 185 (major_type == GUID_NULL || |
186 PinMatchesMajorType(pin.Get(), major_type))) { | 186 PinMatchesMajorType(pin.Get(), major_type))) { |
187 return pin; | 187 return pin; |
188 } | 188 } |
189 } | 189 } |
190 pin.Reset(); | 190 pin.Reset(); |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
287 } | 287 } |
288 | 288 |
289 if (capture_graph_builder_.Get()) | 289 if (capture_graph_builder_.Get()) |
290 capture_graph_builder_.Reset(); | 290 capture_graph_builder_.Reset(); |
291 } | 291 } |
292 | 292 |
293 bool VideoCaptureDeviceWin::Init() { | 293 bool VideoCaptureDeviceWin::Init() { |
294 DCHECK(thread_checker_.CalledOnValidThread()); | 294 DCHECK(thread_checker_.CalledOnValidThread()); |
295 HRESULT hr; | 295 HRESULT hr; |
296 | 296 |
297 hr = GetDeviceFilter(device_descriptor_.device_id, capture_filter_.Receive()); | 297 hr = GetDeviceFilter(device_descriptor_.device_id, |
| 298 capture_filter_.GetAddressOf()); |
298 DLOG_IF_FAILED_WITH_HRESULT("Failed to create capture filter", hr); | 299 DLOG_IF_FAILED_WITH_HRESULT("Failed to create capture filter", hr); |
299 if (!capture_filter_.Get()) | 300 if (!capture_filter_.Get()) |
300 return false; | 301 return false; |
301 | 302 |
302 output_capture_pin_ = GetPin(capture_filter_.Get(), PINDIR_OUTPUT, | 303 output_capture_pin_ = GetPin(capture_filter_.Get(), PINDIR_OUTPUT, |
303 PIN_CATEGORY_CAPTURE, GUID_NULL); | 304 PIN_CATEGORY_CAPTURE, GUID_NULL); |
304 if (!output_capture_pin_.Get()) { | 305 if (!output_capture_pin_.Get()) { |
305 DLOG(ERROR) << "Failed to get capture output pin"; | 306 DLOG(ERROR) << "Failed to get capture output pin"; |
306 return false; | 307 return false; |
307 } | 308 } |
(...skipping 18 matching lines...) Expand all Loading... |
326 DLOG_IF_FAILED_WITH_HRESULT("Failed to create the Capture Graph Builder", hr); | 327 DLOG_IF_FAILED_WITH_HRESULT("Failed to create the Capture Graph Builder", hr); |
327 if (FAILED(hr)) | 328 if (FAILED(hr)) |
328 return false; | 329 return false; |
329 | 330 |
330 hr = capture_graph_builder_->SetFiltergraph(graph_builder_.Get()); | 331 hr = capture_graph_builder_->SetFiltergraph(graph_builder_.Get()); |
331 DLOG_IF_FAILED_WITH_HRESULT("Failed to give graph to capture graph builder", | 332 DLOG_IF_FAILED_WITH_HRESULT("Failed to give graph to capture graph builder", |
332 hr); | 333 hr); |
333 if (FAILED(hr)) | 334 if (FAILED(hr)) |
334 return false; | 335 return false; |
335 | 336 |
336 hr = graph_builder_.CopyTo(media_control_.Receive()); | 337 hr = graph_builder_.CopyTo(media_control_.GetAddressOf()); |
337 DLOG_IF_FAILED_WITH_HRESULT("Failed to create media control builder", hr); | 338 DLOG_IF_FAILED_WITH_HRESULT("Failed to create media control builder", hr); |
338 if (FAILED(hr)) | 339 if (FAILED(hr)) |
339 return false; | 340 return false; |
340 | 341 |
341 hr = graph_builder_->AddFilter(capture_filter_.Get(), NULL); | 342 hr = graph_builder_->AddFilter(capture_filter_.Get(), NULL); |
342 DLOG_IF_FAILED_WITH_HRESULT("Failed to add the capture device to the graph", | 343 DLOG_IF_FAILED_WITH_HRESULT("Failed to add the capture device to the graph", |
343 hr); | 344 hr); |
344 if (FAILED(hr)) | 345 if (FAILED(hr)) |
345 return false; | 346 return false; |
346 | 347 |
347 hr = graph_builder_->AddFilter(sink_filter_.get(), NULL); | 348 hr = graph_builder_->AddFilter(sink_filter_.get(), NULL); |
348 DLOG_IF_FAILED_WITH_HRESULT("Failed to add the sink filter to the graph", hr); | 349 DLOG_IF_FAILED_WITH_HRESULT("Failed to add the sink filter to the graph", hr); |
349 if (FAILED(hr)) | 350 if (FAILED(hr)) |
350 return false; | 351 return false; |
351 | 352 |
352 // The following code builds the upstream portions of the graph, for example | 353 // The following code builds the upstream portions of the graph, for example |
353 // if a capture device uses a Windows Driver Model (WDM) driver, the graph may | 354 // if a capture device uses a Windows Driver Model (WDM) driver, the graph may |
354 // require certain filters upstream from the WDM Video Capture filter, such as | 355 // require certain filters upstream from the WDM Video Capture filter, such as |
355 // a TV Tuner filter or an Analog Video Crossbar filter. We try using the more | 356 // a TV Tuner filter or an Analog Video Crossbar filter. We try using the more |
356 // prevalent MEDIATYPE_Interleaved first. | 357 // prevalent MEDIATYPE_Interleaved first. |
357 base::win::ScopedComPtr<IAMStreamConfig> stream_config; | 358 base::win::ScopedComPtr<IAMStreamConfig> stream_config; |
358 | 359 |
359 hr = capture_graph_builder_->FindInterface( | 360 hr = capture_graph_builder_->FindInterface( |
360 &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, capture_filter_.Get(), | 361 &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Interleaved, capture_filter_.Get(), |
361 IID_IAMStreamConfig, (void**)stream_config.Receive()); | 362 IID_IAMStreamConfig, (void**)stream_config.GetAddressOf()); |
362 if (FAILED(hr)) { | 363 if (FAILED(hr)) { |
363 hr = capture_graph_builder_->FindInterface( | 364 hr = capture_graph_builder_->FindInterface( |
364 &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, capture_filter_.Get(), | 365 &PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, capture_filter_.Get(), |
365 IID_IAMStreamConfig, (void**)stream_config.Receive()); | 366 IID_IAMStreamConfig, (void**)stream_config.GetAddressOf()); |
366 DLOG_IF_FAILED_WITH_HRESULT("Failed to find CapFilter:IAMStreamConfig", hr); | 367 DLOG_IF_FAILED_WITH_HRESULT("Failed to find CapFilter:IAMStreamConfig", hr); |
367 } | 368 } |
368 | 369 |
369 return CreateCapabilityMap(); | 370 return CreateCapabilityMap(); |
370 } | 371 } |
371 | 372 |
372 void VideoCaptureDeviceWin::AllocateAndStart( | 373 void VideoCaptureDeviceWin::AllocateAndStart( |
373 const VideoCaptureParams& params, | 374 const VideoCaptureParams& params, |
374 std::unique_ptr<VideoCaptureDevice::Client> client) { | 375 std::unique_ptr<VideoCaptureDevice::Client> client) { |
375 DCHECK(thread_checker_.CalledOnValidThread()); | 376 DCHECK(thread_checker_.CalledOnValidThread()); |
376 if (state_ != kIdle) | 377 if (state_ != kIdle) |
377 return; | 378 return; |
378 | 379 |
379 client_ = std::move(client); | 380 client_ = std::move(client); |
380 | 381 |
381 // Get the camera capability that best match the requested format. | 382 // Get the camera capability that best match the requested format. |
382 const CapabilityWin found_capability = | 383 const CapabilityWin found_capability = |
383 GetBestMatchedCapability(params.requested_format, capabilities_); | 384 GetBestMatchedCapability(params.requested_format, capabilities_); |
384 | 385 |
385 // Reduce the frame rate if the requested frame rate is lower | 386 // Reduce the frame rate if the requested frame rate is lower |
386 // than the capability. | 387 // than the capability. |
387 const float frame_rate = | 388 const float frame_rate = |
388 std::min(params.requested_format.frame_rate, | 389 std::min(params.requested_format.frame_rate, |
389 found_capability.supported_format.frame_rate); | 390 found_capability.supported_format.frame_rate); |
390 | 391 |
391 ScopedComPtr<IAMStreamConfig> stream_config; | 392 ScopedComPtr<IAMStreamConfig> stream_config; |
392 HRESULT hr = output_capture_pin_.CopyTo(stream_config.Receive()); | 393 HRESULT hr = output_capture_pin_.CopyTo(stream_config.GetAddressOf()); |
393 if (FAILED(hr)) { | 394 if (FAILED(hr)) { |
394 SetErrorState(FROM_HERE, "Can't get the Capture format settings", hr); | 395 SetErrorState(FROM_HERE, "Can't get the Capture format settings", hr); |
395 return; | 396 return; |
396 } | 397 } |
397 | 398 |
398 int count = 0, size = 0; | 399 int count = 0, size = 0; |
399 hr = stream_config->GetNumberOfCapabilities(&count, &size); | 400 hr = stream_config->GetNumberOfCapabilities(&count, &size); |
400 if (FAILED(hr)) { | 401 if (FAILED(hr)) { |
401 SetErrorState(FROM_HERE, "Failed to GetNumberOfCapabilities", hr); | 402 SetErrorState(FROM_HERE, "Failed to GetNumberOfCapabilities", hr); |
402 return; | 403 return; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
490 // way, however, is not widespread and proves too cumbersome, so we just grab | 491 // way, however, is not widespread and proves too cumbersome, so we just grab |
491 // the next captured frame instead. | 492 // the next captured frame instead. |
492 take_photo_callbacks_.push(std::move(callback)); | 493 take_photo_callbacks_.push(std::move(callback)); |
493 } | 494 } |
494 | 495 |
495 void VideoCaptureDeviceWin::GetPhotoCapabilities( | 496 void VideoCaptureDeviceWin::GetPhotoCapabilities( |
496 GetPhotoCapabilitiesCallback callback) { | 497 GetPhotoCapabilitiesCallback callback) { |
497 DCHECK(thread_checker_.CalledOnValidThread()); | 498 DCHECK(thread_checker_.CalledOnValidThread()); |
498 | 499 |
499 base::win::ScopedComPtr<IKsTopologyInfo> info; | 500 base::win::ScopedComPtr<IKsTopologyInfo> info; |
500 HRESULT hr = capture_filter_.CopyTo(info.Receive()); | 501 HRESULT hr = capture_filter_.CopyTo(info.GetAddressOf()); |
501 if (FAILED(hr)) { | 502 if (FAILED(hr)) { |
502 SetErrorState(FROM_HERE, "Failed to obtain the topology info.", hr); | 503 SetErrorState(FROM_HERE, "Failed to obtain the topology info.", hr); |
503 return; | 504 return; |
504 } | 505 } |
505 | 506 |
506 DWORD num_nodes = 0; | 507 DWORD num_nodes = 0; |
507 hr = info->get_NumNodes(&num_nodes); | 508 hr = info->get_NumNodes(&num_nodes); |
508 if (FAILED(hr)) { | 509 if (FAILED(hr)) { |
509 SetErrorState(FROM_HERE, "Failed to obtain the number of nodes.", hr); | 510 SetErrorState(FROM_HERE, "Failed to obtain the number of nodes.", hr); |
510 return; | 511 return; |
511 } | 512 } |
512 | 513 |
513 // Every UVC camera is expected to have a single ICameraControl and a single | 514 // Every UVC camera is expected to have a single ICameraControl and a single |
514 // IVideoProcAmp nodes, and both are needed; ignore any unlikely later ones. | 515 // IVideoProcAmp nodes, and both are needed; ignore any unlikely later ones. |
515 GUID node_type; | 516 GUID node_type; |
516 base::win::ScopedComPtr<ICameraControl> camera_control; | 517 base::win::ScopedComPtr<ICameraControl> camera_control; |
517 for (size_t i = 0; i < num_nodes; i++) { | 518 for (size_t i = 0; i < num_nodes; i++) { |
518 info->get_NodeType(i, &node_type); | 519 info->get_NodeType(i, &node_type); |
519 if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_CAMERA_TERMINAL)) { | 520 if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_CAMERA_TERMINAL)) { |
520 hr = info->CreateNodeInstance(i, IID_PPV_ARGS(camera_control.Receive())); | 521 hr = info->CreateNodeInstance(i, IID_PPV_ARGS(&camera_control)); |
521 if (SUCCEEDED(hr)) | 522 if (SUCCEEDED(hr)) |
522 break; | 523 break; |
523 SetErrorState(FROM_HERE, "Failed to retrieve the ICameraControl.", hr); | 524 SetErrorState(FROM_HERE, "Failed to retrieve the ICameraControl.", hr); |
524 return; | 525 return; |
525 } | 526 } |
526 } | 527 } |
527 if (!camera_control) | 528 if (!camera_control) |
528 return; | 529 return; |
529 base::win::ScopedComPtr<IVideoProcAmp> video_control; | 530 base::win::ScopedComPtr<IVideoProcAmp> video_control; |
530 for (size_t i = 0; i < num_nodes; i++) { | 531 for (size_t i = 0; i < num_nodes; i++) { |
531 info->get_NodeType(i, &node_type); | 532 info->get_NodeType(i, &node_type); |
532 if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_PROCESSING)) { | 533 if (IsEqualGUID(node_type, KSNODETYPE_VIDEO_PROCESSING)) { |
533 hr = info->CreateNodeInstance(i, IID_PPV_ARGS(video_control.Receive())); | 534 hr = info->CreateNodeInstance(i, IID_PPV_ARGS(&video_control)); |
534 if (SUCCEEDED(hr)) | 535 if (SUCCEEDED(hr)) |
535 break; | 536 break; |
536 SetErrorState(FROM_HERE, "Failed to retrieve the IVideoProcAmp.", hr); | 537 SetErrorState(FROM_HERE, "Failed to retrieve the IVideoProcAmp.", hr); |
537 return; | 538 return; |
538 } | 539 } |
539 } | 540 } |
540 if (!video_control) | 541 if (!video_control) |
541 return; | 542 return; |
542 | 543 |
543 auto photo_capabilities = mojom::PhotoCapabilities::New(); | 544 auto photo_capabilities = mojom::PhotoCapabilities::New(); |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
642 | 643 |
643 mojom::BlobPtr blob = Blobify(buffer, length, format); | 644 mojom::BlobPtr blob = Blobify(buffer, length, format); |
644 if (blob) | 645 if (blob) |
645 cb.Run(std::move(blob)); | 646 cb.Run(std::move(blob)); |
646 } | 647 } |
647 } | 648 } |
648 | 649 |
649 bool VideoCaptureDeviceWin::CreateCapabilityMap() { | 650 bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
650 DCHECK(thread_checker_.CalledOnValidThread()); | 651 DCHECK(thread_checker_.CalledOnValidThread()); |
651 ScopedComPtr<IAMStreamConfig> stream_config; | 652 ScopedComPtr<IAMStreamConfig> stream_config; |
652 HRESULT hr = output_capture_pin_.CopyTo(stream_config.Receive()); | 653 HRESULT hr = output_capture_pin_.CopyTo(stream_config.GetAddressOf()); |
653 DLOG_IF_FAILED_WITH_HRESULT( | 654 DLOG_IF_FAILED_WITH_HRESULT( |
654 "Failed to get IAMStreamConfig from capture device", hr); | 655 "Failed to get IAMStreamConfig from capture device", hr); |
655 if (FAILED(hr)) | 656 if (FAILED(hr)) |
656 return false; | 657 return false; |
657 | 658 |
658 // Get interface used for getting the frame rate. | 659 // Get interface used for getting the frame rate. |
659 ScopedComPtr<IAMVideoControl> video_control; | 660 ScopedComPtr<IAMVideoControl> video_control; |
660 hr = capture_filter_.CopyTo(video_control.Receive()); | 661 hr = capture_filter_.CopyTo(video_control.GetAddressOf()); |
661 | 662 |
662 int count = 0, size = 0; | 663 int count = 0, size = 0; |
663 hr = stream_config->GetNumberOfCapabilities(&count, &size); | 664 hr = stream_config->GetNumberOfCapabilities(&count, &size); |
664 DLOG_IF_FAILED_WITH_HRESULT("Failed to GetNumberOfCapabilities", hr); | 665 DLOG_IF_FAILED_WITH_HRESULT("Failed to GetNumberOfCapabilities", hr); |
665 if (FAILED(hr)) | 666 if (FAILED(hr)) |
666 return false; | 667 return false; |
667 | 668 |
668 std::unique_ptr<BYTE[]> caps(new BYTE[size]); | 669 std::unique_ptr<BYTE[]> caps(new BYTE[size]); |
669 for (int stream_index = 0; stream_index < count; ++stream_index) { | 670 for (int stream_index = 0; stream_index < count; ++stream_index) { |
670 ScopedMediaType media_type; | 671 ScopedMediaType media_type; |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
757 void VideoCaptureDeviceWin::SetErrorState( | 758 void VideoCaptureDeviceWin::SetErrorState( |
758 const tracked_objects::Location& from_here, | 759 const tracked_objects::Location& from_here, |
759 const std::string& reason, | 760 const std::string& reason, |
760 HRESULT hr) { | 761 HRESULT hr) { |
761 DCHECK(thread_checker_.CalledOnValidThread()); | 762 DCHECK(thread_checker_.CalledOnValidThread()); |
762 DLOG_IF_FAILED_WITH_HRESULT(reason, hr); | 763 DLOG_IF_FAILED_WITH_HRESULT(reason, hr); |
763 state_ = kError; | 764 state_ = kError; |
764 client_->OnError(from_here, reason); | 765 client_->OnError(from_here, reason); |
765 } | 766 } |
766 } // namespace media | 767 } // namespace media |
OLD | NEW |