| // Copyright 2017 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 "content/browser/renderer_host/media/in_process_video_capture_device_launcher.h" |
| |
| #include "base/metrics/histogram_macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "build/build_config.h" |
| #include "content/browser/renderer_host/media/in_process_launched_video_capture_device.h" |
| #include "content/browser/renderer_host/media/video_capture_controller.h" |
| #include "content/browser/renderer_host/media/video_capture_dependencies.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/media_stream_request.h" |
| #include "media/base/bind_to_current_loop.h" |
| #include "media/capture/video/video_capture_buffer_pool_impl.h" |
| #include "media/capture/video/video_capture_buffer_tracker_factory_impl.h" |
| #include "media/capture/video/video_capture_device_client.h" |
| #include "media/capture/video/video_capture_jpeg_decoder_impl.h" |
| #include "media/capture/video/video_frame_receiver.h" |
| #include "media/capture/video/video_frame_receiver_on_task_runner.h" |
| |
| #if defined(ENABLE_SCREEN_CAPTURE) |
| #include "content/browser/media/capture/desktop_capture_device_uma_types.h" |
| #include "content/public/browser/desktop_media_id.h" |
| #if defined(OS_ANDROID) |
| #include "content/browser/media/capture/screen_capture_device_android.h" |
| #else |
| #include "content/browser/media/capture/web_contents_video_capture_device.h" |
| #if defined(USE_AURA) |
| #include "content/browser/media/capture/aura_window_video_capture_device.h" |
| #endif |
| #include "content/browser/media/capture/desktop_capture_device.h" |
| #endif // defined(OS_ANDROID) |
| #endif // defined(ENABLE_SCREEN_CAPTURE) |
| |
| namespace { |
| |
| std::unique_ptr<media::VideoCaptureJpegDecoder> CreateGpuJpegDecoder( |
| media::VideoCaptureJpegDecoder::DecodeDoneCB decode_done_cb, |
| base::Callback<void(const std::string&)> send_log_message_cb) { |
| return std::make_unique<media::VideoCaptureJpegDecoderImpl>( |
| base::BindRepeating( |
| &content::VideoCaptureDependencies::CreateJpegDecodeAccelerator), |
| content::BrowserThread::GetTaskRunnerForThread( |
| content::BrowserThread::IO), |
| std::move(decode_done_cb), std::move(send_log_message_cb)); |
| } |
| |
| // The maximum number of video frame buffers in-flight at any one time. This |
| // value should be based on the logical capacity of the capture pipeline, and |
| // not on hardware performance. |
| const int kMaxNumberOfBuffers = 3; |
| |
| } // anonymous namespace |
| |
| namespace content { |
| |
| InProcessVideoCaptureDeviceLauncher::InProcessVideoCaptureDeviceLauncher( |
| scoped_refptr<base::SingleThreadTaskRunner> device_task_runner, |
| media::VideoCaptureSystem* video_capture_system) |
| : device_task_runner_(std::move(device_task_runner)), |
| video_capture_system_(video_capture_system), |
| state_(State::READY_TO_LAUNCH) {} |
| |
| InProcessVideoCaptureDeviceLauncher::~InProcessVideoCaptureDeviceLauncher() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(state_ == State::READY_TO_LAUNCH); |
| } |
| |
| void InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync( |
| const std::string& device_id, |
| MediaStreamType stream_type, |
| const media::VideoCaptureParams& params, |
| base::WeakPtr<media::VideoFrameReceiver> receiver_on_io_thread, |
| base::OnceClosure /* connection_lost_cb */, |
| Callbacks* callbacks, |
| base::OnceClosure done_cb) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(state_ == State::READY_TO_LAUNCH); |
| |
| if (receiver_on_io_thread) { |
| std::ostringstream string_stream; |
| string_stream |
| << "InProcessVideoCaptureDeviceLauncher::LaunchDeviceAsync: Posting " |
| "start request to device thread for device_id = " |
| << device_id; |
| receiver_on_io_thread->OnLog(string_stream.str()); |
| } |
| |
| // Wrap the receiver, to trampoline all its method calls from the device |
| // to the IO thread. |
| auto receiver = std::make_unique<media::VideoFrameReceiverOnTaskRunner>( |
| receiver_on_io_thread, |
| BrowserThread::GetTaskRunnerForThread(BrowserThread::IO)); |
| |
| base::OnceClosure start_capture_closure; |
| // Use of Unretained |this| is safe, because |done_cb| guarantees that |this| |
| // stays alive. |
| ReceiveDeviceCallback after_start_capture_callback = media::BindToCurrentLoop( |
| base::BindOnce(&InProcessVideoCaptureDeviceLauncher::OnDeviceStarted, |
| base::Unretained(this), callbacks, std::move(done_cb))); |
| |
| switch (stream_type) { |
| case MEDIA_DEVICE_VIDEO_CAPTURE: { |
| if (!video_capture_system_) { |
| // Clients who create an instance of |this| without providing a |
| // VideoCaptureSystem instance are expected to know that |
| // MEDIA_DEVICE_VIDEO_CAPTURE is not supported in this case. |
| NOTREACHED(); |
| return; |
| } |
| start_capture_closure = base::BindOnce( |
| &InProcessVideoCaptureDeviceLauncher:: |
| DoStartDeviceCaptureOnDeviceThread, |
| base::Unretained(this), device_id, params, |
| CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory, |
| kMaxNumberOfBuffers, std::move(receiver), |
| std::move(receiver_on_io_thread)), |
| std::move(after_start_capture_callback)); |
| break; |
| } |
| |
| #if defined(ENABLE_SCREEN_CAPTURE) |
| #if !defined(OS_ANDROID) |
| case MEDIA_GUM_TAB_VIDEO_CAPTURE: |
| start_capture_closure = base::BindOnce( |
| &InProcessVideoCaptureDeviceLauncher::DoStartTabCaptureOnDeviceThread, |
| base::Unretained(this), device_id, params, std::move(receiver), |
| std::move(after_start_capture_callback)); |
| break; |
| #endif // !defined(OS_ANDROID) |
| |
| case MEDIA_GUM_DESKTOP_VIDEO_CAPTURE: |
| FALLTHROUGH; |
| case MEDIA_DISPLAY_VIDEO_CAPTURE: { |
| const DesktopMediaID desktop_id = DesktopMediaID::Parse(device_id); |
| if (desktop_id.is_null()) { |
| DLOG(ERROR) << "Desktop media ID is null"; |
| start_capture_closure = |
| base::BindOnce(std::move(after_start_capture_callback), nullptr); |
| break; |
| } |
| |
| #if !defined(OS_ANDROID) |
| if (desktop_id.type == DesktopMediaID::TYPE_WEB_CONTENTS) { |
| after_start_capture_callback = base::BindOnce( |
| [](bool with_audio, ReceiveDeviceCallback callback, |
| std::unique_ptr<media::VideoCaptureDevice> device) { |
| // Special case: Only call IncrementDesktopCaptureCounter() |
| // for WebContents capture if it was started from a desktop |
| // capture API. |
| if (device) { |
| IncrementDesktopCaptureCounter(TAB_VIDEO_CAPTURER_CREATED); |
| IncrementDesktopCaptureCounter( |
| with_audio ? TAB_VIDEO_CAPTURER_CREATED_WITH_AUDIO |
| : TAB_VIDEO_CAPTURER_CREATED_WITHOUT_AUDIO); |
| } |
| std::move(callback).Run(std::move(device)); |
| }, |
| desktop_id.audio_share, std::move(after_start_capture_callback)); |
| start_capture_closure = base::BindOnce( |
| &InProcessVideoCaptureDeviceLauncher:: |
| DoStartTabCaptureOnDeviceThread, |
| base::Unretained(this), device_id, params, std::move(receiver), |
| std::move(after_start_capture_callback)); |
| break; |
| } |
| #endif // !defined(OS_ANDROID) |
| |
| #if defined(USE_AURA) |
| if (desktop_id.aura_id != DesktopMediaID::kNullId) { |
| start_capture_closure = base::BindOnce( |
| &InProcessVideoCaptureDeviceLauncher:: |
| DoStartAuraWindowCaptureOnDeviceThread, |
| base::Unretained(this), desktop_id, params, std::move(receiver), |
| std::move(after_start_capture_callback)); |
| break; |
| } |
| #endif // defined(USE_AURA) |
| |
| // All cases other than tab capture or Aura desktop/window capture. |
| start_capture_closure = base::BindOnce( |
| &InProcessVideoCaptureDeviceLauncher:: |
| DoStartDesktopCaptureOnDeviceThread, |
| base::Unretained(this), desktop_id, params, |
| CreateDeviceClient(media::VideoCaptureBufferType::kSharedMemory, |
| kMaxNumberOfBuffers, std::move(receiver), |
| std::move(receiver_on_io_thread)), |
| std::move(after_start_capture_callback)); |
| break; |
| } |
| #endif // defined(ENABLE_SCREEN_CAPTURE) |
| |
| default: { |
| NOTIMPLEMENTED(); |
| std::move(after_start_capture_callback).Run(nullptr); |
| return; |
| } |
| } |
| |
| device_task_runner_->PostTask(FROM_HERE, std::move(start_capture_closure)); |
| state_ = State::DEVICE_START_IN_PROGRESS; |
| } |
| |
| void InProcessVideoCaptureDeviceLauncher::AbortLaunch() { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| if (state_ == State::DEVICE_START_IN_PROGRESS) |
| state_ = State::DEVICE_START_ABORTING; |
| } |
| |
| std::unique_ptr<media::VideoCaptureDeviceClient> |
| InProcessVideoCaptureDeviceLauncher::CreateDeviceClient( |
| media::VideoCaptureBufferType requested_buffer_type, |
| int buffer_pool_max_buffer_count, |
| std::unique_ptr<media::VideoFrameReceiver> receiver, |
| base::WeakPtr<media::VideoFrameReceiver> receiver_on_io_thread) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| scoped_refptr<media::VideoCaptureBufferPool> buffer_pool = |
| new media::VideoCaptureBufferPoolImpl( |
| std::make_unique<media::VideoCaptureBufferTrackerFactoryImpl>(), |
| buffer_pool_max_buffer_count); |
| |
| return std::make_unique<media::VideoCaptureDeviceClient>( |
| requested_buffer_type, std::move(receiver), std::move(buffer_pool), |
| base::BindRepeating( |
| &CreateGpuJpegDecoder, |
| base::BindRepeating(&media::VideoFrameReceiver::OnFrameReadyInBuffer, |
| receiver_on_io_thread), |
| base::BindRepeating(&media::VideoFrameReceiver::OnLog, |
| receiver_on_io_thread))); |
| } |
| |
| void InProcessVideoCaptureDeviceLauncher::OnDeviceStarted( |
| Callbacks* callbacks, |
| base::OnceClosure done_cb, |
| std::unique_ptr<media::VideoCaptureDevice> device) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| State state_copy = state_; |
| state_ = State::READY_TO_LAUNCH; |
| if (!device) { |
| switch (state_copy) { |
| case State::DEVICE_START_IN_PROGRESS: |
| callbacks->OnDeviceLaunchFailed(); |
| base::ResetAndReturn(&done_cb).Run(); |
| return; |
| case State::DEVICE_START_ABORTING: |
| callbacks->OnDeviceLaunchAborted(); |
| base::ResetAndReturn(&done_cb).Run(); |
| return; |
| case State::READY_TO_LAUNCH: |
| NOTREACHED(); |
| return; |
| } |
| } |
| |
| auto launched_device = std::make_unique<InProcessLaunchedVideoCaptureDevice>( |
| std::move(device), device_task_runner_); |
| |
| switch (state_copy) { |
| case State::DEVICE_START_IN_PROGRESS: |
| callbacks->OnDeviceLaunched(std::move(launched_device)); |
| base::ResetAndReturn(&done_cb).Run(); |
| return; |
| case State::DEVICE_START_ABORTING: |
| launched_device.reset(); |
| callbacks->OnDeviceLaunchAborted(); |
| base::ResetAndReturn(&done_cb).Run(); |
| return; |
| case State::READY_TO_LAUNCH: |
| NOTREACHED(); |
| return; |
| } |
| } |
| |
| void InProcessVideoCaptureDeviceLauncher::DoStartDeviceCaptureOnDeviceThread( |
| const std::string& device_id, |
| const media::VideoCaptureParams& params, |
| std::unique_ptr<media::VideoCaptureDeviceClient> device_client, |
| ReceiveDeviceCallback result_callback) { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
| DCHECK(device_task_runner_->BelongsToCurrentThread()); |
| DCHECK(video_capture_system_); |
| |
| std::unique_ptr<media::VideoCaptureDevice> video_capture_device = |
| video_capture_system_->CreateDevice(device_id); |
| |
| if (video_capture_device) |
| video_capture_device->AllocateAndStart(params, std::move(device_client)); |
| std::move(result_callback).Run(std::move(video_capture_device)); |
| } |
| |
| #if defined(ENABLE_SCREEN_CAPTURE) |
| |
| #if !defined(OS_ANDROID) |
| void InProcessVideoCaptureDeviceLauncher::DoStartTabCaptureOnDeviceThread( |
| const std::string& device_id, |
| const media::VideoCaptureParams& params, |
| std::unique_ptr<media::VideoFrameReceiver> receiver, |
| ReceiveDeviceCallback result_callback) { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
| DCHECK(device_task_runner_->BelongsToCurrentThread()); |
| |
| std::unique_ptr<WebContentsVideoCaptureDevice> video_capture_device = |
| WebContentsVideoCaptureDevice::Create(device_id); |
| if (video_capture_device) { |
| video_capture_device->AllocateAndStartWithReceiver(params, |
| std::move(receiver)); |
| } |
| std::move(result_callback).Run(std::move(video_capture_device)); |
| } |
| #endif // !defined(OS_ANDROID) |
| |
| #if defined(USE_AURA) |
| void InProcessVideoCaptureDeviceLauncher:: |
| DoStartAuraWindowCaptureOnDeviceThread( |
| const DesktopMediaID& device_id, |
| const media::VideoCaptureParams& params, |
| std::unique_ptr<media::VideoFrameReceiver> receiver, |
| ReceiveDeviceCallback result_callback) { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
| DCHECK(device_task_runner_->BelongsToCurrentThread()); |
| |
| auto video_capture_device = |
| std::make_unique<AuraWindowVideoCaptureDevice>(device_id); |
| if (video_capture_device) { |
| video_capture_device->AllocateAndStartWithReceiver(params, |
| std::move(receiver)); |
| switch (device_id.type) { |
| case DesktopMediaID::TYPE_SCREEN: |
| IncrementDesktopCaptureCounter(SCREEN_CAPTURER_CREATED); |
| IncrementDesktopCaptureCounter( |
| device_id.audio_share ? SCREEN_CAPTURER_CREATED_WITH_AUDIO |
| : SCREEN_CAPTURER_CREATED_WITHOUT_AUDIO); |
| break; |
| case DesktopMediaID::TYPE_WINDOW: |
| IncrementDesktopCaptureCounter(WINDOW_CAPTURER_CREATED); |
| break; |
| case DesktopMediaID::TYPE_NONE: |
| case DesktopMediaID::TYPE_WEB_CONTENTS: |
| NOTREACHED(); |
| break; |
| } |
| } |
| std::move(result_callback).Run(std::move(video_capture_device)); |
| } |
| #endif // defined(USE_AURA) |
| |
| void InProcessVideoCaptureDeviceLauncher::DoStartDesktopCaptureOnDeviceThread( |
| const DesktopMediaID& desktop_id, |
| const media::VideoCaptureParams& params, |
| std::unique_ptr<media::VideoCaptureDeviceClient> device_client, |
| ReceiveDeviceCallback result_callback) { |
| SCOPED_UMA_HISTOGRAM_TIMER("Media.VideoCaptureManager.StartDeviceTime"); |
| DCHECK(device_task_runner_->BelongsToCurrentThread()); |
| DCHECK(!desktop_id.is_null()); |
| |
| std::unique_ptr<media::VideoCaptureDevice> video_capture_device; |
| #if defined(OS_ANDROID) |
| video_capture_device = std::make_unique<ScreenCaptureDeviceAndroid>(); |
| #else |
| if (!video_capture_device) |
| video_capture_device = DesktopCaptureDevice::Create(desktop_id); |
| #endif // defined (OS_ANDROID) |
| |
| if (video_capture_device) |
| video_capture_device->AllocateAndStart(params, std::move(device_client)); |
| std::move(result_callback).Run(std::move(video_capture_device)); |
| } |
| |
| #endif // defined(ENABLE_SCREEN_CAPTURE) |
| |
| } // namespace content |