| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/mirroring/browser/single_client_video_capture_host.h" |
| |
| #include "base/memory/weak_ptr.h" |
| #include "content/public/browser/web_contents_media_capture_id.h" |
| #include "media/capture/video/video_capture_buffer_pool.h" |
| |
| using media::VideoFrameConsumerFeedbackObserver; |
| |
| namespace mirroring { |
| |
| namespace { |
| |
| class DeviceLauncherCallbacks final |
| : public content::VideoCaptureDeviceLauncher::Callbacks { |
| public: |
| explicit DeviceLauncherCallbacks( |
| base::WeakPtr<SingleClientVideoCaptureHost> host) |
| : video_capture_host_(host) {} |
| |
| ~DeviceLauncherCallbacks() override {} |
| |
| // content::VideoCaptureDeviceLauncher::Callbacks implementations |
| void OnDeviceLaunched( |
| std::unique_ptr<content::LaunchedVideoCaptureDevice> device) override { |
| if (video_capture_host_) |
| video_capture_host_->OnDeviceLaunched(std::move(device)); |
| } |
| |
| void OnDeviceLaunchFailed(media::VideoCaptureError error) override { |
| if (video_capture_host_) |
| video_capture_host_->OnDeviceLaunchFailed(error); |
| } |
| |
| void OnDeviceLaunchAborted() override { |
| if (video_capture_host_) |
| video_capture_host_->OnDeviceLaunchAborted(); |
| } |
| |
| private: |
| base::WeakPtr<SingleClientVideoCaptureHost> video_capture_host_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DeviceLauncherCallbacks); |
| }; |
| |
| } // namespace |
| |
| SingleClientVideoCaptureHost::SingleClientVideoCaptureHost( |
| const std::string& device_id, |
| blink::MediaStreamType type, |
| DeviceLauncherCreateCallback callback) |
| : device_id_(device_id), |
| type_(type), |
| device_launcher_callback_(std::move(callback)), |
| weak_factory_(this) { |
| DCHECK(!device_launcher_callback_.is_null()); |
| } |
| |
| SingleClientVideoCaptureHost::~SingleClientVideoCaptureHost() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| Stop(0); |
| } |
| |
| void SingleClientVideoCaptureHost::Start( |
| int32_t device_id, |
| int32_t session_id, |
| const VideoCaptureParams& params, |
| media::mojom::VideoCaptureObserverPtr observer) { |
| DVLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(!observer_); |
| observer_ = std::move(observer); |
| DCHECK(observer_); |
| DCHECK(!launched_device_); |
| |
| // Start a video capture device. |
| auto device_launcher_callbacks = |
| std::make_unique<DeviceLauncherCallbacks>(weak_factory_.GetWeakPtr()); |
| DeviceLauncherCallbacks* callbacks = device_launcher_callbacks.get(); |
| std::unique_ptr<content::VideoCaptureDeviceLauncher> device_launcher = |
| device_launcher_callback_.Run(); |
| content::VideoCaptureDeviceLauncher* launcher = device_launcher.get(); |
| launcher->LaunchDeviceAsync( |
| device_id_, type_, params, weak_factory_.GetWeakPtr(), |
| base::BindOnce(&SingleClientVideoCaptureHost::OnError, |
| weak_factory_.GetWeakPtr(), |
| media::VideoCaptureError:: |
| kSingleClientVideoCaptureHostLostConnectionToDevice), |
| callbacks, |
| // The |device_launcher| and |device_launcher_callbacks| must be kept |
| // alive until the device launching completes. |
| base::BindOnce([](std::unique_ptr<content::VideoCaptureDeviceLauncher>, |
| std::unique_ptr<DeviceLauncherCallbacks>) {}, |
| std::move(device_launcher), |
| std::move(device_launcher_callbacks))); |
| } |
| |
| void SingleClientVideoCaptureHost::Stop(int32_t device_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(1) << __func__; |
| |
| if (!observer_) |
| return; |
| |
| // Returns all the buffers. |
| std::vector<int> buffers_in_use; |
| buffers_in_use.reserve(buffer_context_map_.size()); |
| for (const auto& entry : buffer_context_map_) |
| buffers_in_use.push_back(entry.first); |
| for (int buffer_id : buffers_in_use) { |
| OnFinishedConsumingBuffer( |
| buffer_id, |
| media::VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded); |
| } |
| DCHECK(buffer_context_map_.empty()); |
| observer_->OnStateChanged(media::mojom::VideoCaptureState::ENDED); |
| observer_ = nullptr; |
| weak_factory_.InvalidateWeakPtrs(); |
| launched_device_ = nullptr; |
| id_map_.clear(); |
| retired_buffers_.clear(); |
| } |
| |
| void SingleClientVideoCaptureHost::Pause(int32_t device_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (launched_device_) |
| launched_device_->MaybeSuspendDevice(); |
| } |
| |
| void SingleClientVideoCaptureHost::Resume(int32_t device_id, |
| int32_t session_id, |
| const VideoCaptureParams& params) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (launched_device_) |
| launched_device_->ResumeDevice(); |
| } |
| |
| void SingleClientVideoCaptureHost::RequestRefreshFrame(int32_t device_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (launched_device_) |
| launched_device_->RequestRefreshFrame(); |
| } |
| |
| void SingleClientVideoCaptureHost::ReleaseBuffer( |
| int32_t device_id, |
| int32_t buffer_id, |
| double consumer_resource_utilization) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(3) << __func__ << ": buffer_id=" << buffer_id; |
| |
| OnFinishedConsumingBuffer(buffer_id, consumer_resource_utilization); |
| } |
| |
| void SingleClientVideoCaptureHost::GetDeviceSupportedFormats( |
| int32_t device_id, |
| int32_t session_id, |
| GetDeviceSupportedFormatsCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| NOTIMPLEMENTED(); |
| std::move(callback).Run(media::VideoCaptureFormats()); |
| } |
| |
| void SingleClientVideoCaptureHost::GetDeviceFormatsInUse( |
| int32_t device_id, |
| int32_t session_id, |
| GetDeviceFormatsInUseCallback callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| NOTIMPLEMENTED(); |
| std::move(callback).Run(media::VideoCaptureFormats()); |
| } |
| |
| void SingleClientVideoCaptureHost::OnNewBuffer( |
| int buffer_id, |
| media::mojom::VideoBufferHandlePtr buffer_handle) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(3) << __func__ << ": buffer_id=" << buffer_id; |
| DCHECK(observer_); |
| DCHECK_NE(buffer_id, media::VideoCaptureBufferPool::kInvalidId); |
| const auto insert_result = |
| id_map_.emplace(std::make_pair(buffer_id, next_buffer_context_id_)); |
| DCHECK(insert_result.second); |
| observer_->OnNewBuffer(next_buffer_context_id_++, std::move(buffer_handle)); |
| } |
| |
| void SingleClientVideoCaptureHost::OnFrameReadyInBuffer( |
| int buffer_id, |
| int frame_feedback_id, |
| std::unique_ptr<Buffer::ScopedAccessPermission> buffer_read_permission, |
| media::mojom::VideoFrameInfoPtr frame_info) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(3) << __func__ << ": buffer_id=" << buffer_id; |
| DCHECK(observer_); |
| const auto id_iter = id_map_.find(buffer_id); |
| DCHECK(id_iter != id_map_.end()); |
| const int buffer_context_id = id_iter->second; |
| const auto insert_result = buffer_context_map_.emplace(std::make_pair( |
| buffer_context_id, |
| std::make_pair(frame_feedback_id, std::move(buffer_read_permission)))); |
| DCHECK(insert_result.second); |
| observer_->OnBufferReady(buffer_context_id, std::move(frame_info)); |
| } |
| |
| void SingleClientVideoCaptureHost::OnBufferRetired(int buffer_id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(3) << __func__ << ": buffer_id=" << buffer_id; |
| |
| const auto id_iter = id_map_.find(buffer_id); |
| DCHECK(id_iter != id_map_.end()); |
| const int buffer_context_id = id_iter->second; |
| id_map_.erase(id_iter); |
| if (buffer_context_map_.find(buffer_context_id) == |
| buffer_context_map_.end()) { |
| DCHECK(observer_); |
| observer_->OnBufferDestroyed(buffer_context_id); |
| } else { |
| // The consumer is still using the buffer. The BufferContext needs to be |
| // held until the consumer finishes. |
| const auto insert_result = retired_buffers_.insert(buffer_context_id); |
| DCHECK(insert_result.second); |
| } |
| } |
| |
| void SingleClientVideoCaptureHost::OnError(media::VideoCaptureError) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(1) << __func__; |
| if (observer_) |
| observer_->OnStateChanged(media::mojom::VideoCaptureState::FAILED); |
| } |
| |
| void SingleClientVideoCaptureHost::OnFrameDropped( |
| media::VideoCaptureFrameDropReason reason) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| } |
| |
| void SingleClientVideoCaptureHost::OnLog(const std::string& message) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| VLOG(3) << message; |
| } |
| |
| void SingleClientVideoCaptureHost::OnStarted() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DVLOG(2) << __func__; |
| DCHECK(observer_); |
| observer_->OnStateChanged(media::mojom::VideoCaptureState::STARTED); |
| } |
| |
| void SingleClientVideoCaptureHost::OnStartedUsingGpuDecode() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| NOTIMPLEMENTED(); |
| } |
| |
| void SingleClientVideoCaptureHost::OnDeviceLaunched( |
| std::unique_ptr<content::LaunchedVideoCaptureDevice> device) { |
| DVLOG(1) << __func__; |
| launched_device_ = std::move(device); |
| } |
| |
| void SingleClientVideoCaptureHost::OnDeviceLaunchFailed( |
| media::VideoCaptureError error) { |
| DVLOG(1) << __func__; |
| launched_device_ = nullptr; |
| OnError(error); |
| } |
| |
| void SingleClientVideoCaptureHost::OnDeviceLaunchAborted() { |
| DVLOG(1) << __func__; |
| launched_device_ = nullptr; |
| OnError( |
| media::VideoCaptureError::kSingleClientVideoCaptureDeviceLaunchAborted); |
| } |
| |
| void SingleClientVideoCaptureHost::OnFinishedConsumingBuffer( |
| int buffer_context_id, |
| double consumer_resource_utilization) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(observer_); |
| const auto buffer_context_iter = buffer_context_map_.find(buffer_context_id); |
| if (buffer_context_iter == buffer_context_map_.end()) { |
| // Stop() must have been called before. |
| DCHECK(!launched_device_); |
| return; |
| } |
| VideoFrameConsumerFeedbackObserver* feedback_observer = |
| launched_device_.get(); |
| if (feedback_observer && |
| consumer_resource_utilization != |
| VideoFrameConsumerFeedbackObserver::kNoUtilizationRecorded) { |
| feedback_observer->OnUtilizationReport(buffer_context_iter->second.first, |
| consumer_resource_utilization); |
| } |
| buffer_context_map_.erase(buffer_context_iter); |
| const auto retired_iter = retired_buffers_.find(buffer_context_id); |
| if (retired_iter != retired_buffers_.end()) { |
| retired_buffers_.erase(retired_iter); |
| observer_->OnBufferDestroyed(buffer_context_id); |
| } |
| } |
| |
| } // namespace mirroring |