blob: f23d025f93cdf35523a350a93124a4564ff389f5 [file] [log] [blame]
// 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 "media/capture/video/chromeos/video_capture_device_arc_chromeos.h"
#include <string>
#include <utility>
#include "base/bind_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "media/capture/video/chromeos/camera_device_delegate.h"
#include "media/capture/video/chromeos/camera_hal_delegate.h"
#include "ui/display/display.h"
#include "ui/display/display_observer.h"
#include "ui/display/screen.h"
namespace media {
VideoCaptureDeviceArcChromeOS::VideoCaptureDeviceArcChromeOS(
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_screen_observer,
const VideoCaptureDeviceDescriptor& device_descriptor,
scoped_refptr<CameraHalDelegate> camera_hal_delegate)
: device_descriptor_(device_descriptor),
camera_hal_delegate_(std::move(camera_hal_delegate)),
capture_task_runner_(base::ThreadTaskRunnerHandle::Get()),
camera_device_ipc_thread_(std::string("CameraDeviceIpcThread") +
device_descriptor.device_id),
screen_observer_delegate_(new ScreenObserverDelegate(
this,
std::move(task_runner_for_screen_observer))),
lens_facing_(device_descriptor.facing),
camera_orientation_(0),
// External cameras have lens_facing as MEDIA_VIDEO_FACING_NONE.
// We don't want to rotate the frame even if the device rotates.
rotates_with_device_(lens_facing_ !=
VideoFacingMode::MEDIA_VIDEO_FACING_NONE),
rotation_(0) {}
VideoCaptureDeviceArcChromeOS::~VideoCaptureDeviceArcChromeOS() {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
DCHECK(!camera_device_ipc_thread_.IsRunning());
screen_observer_delegate_->RemoveObserver();
}
// VideoCaptureDevice implementation.
void VideoCaptureDeviceArcChromeOS::AllocateAndStart(
const VideoCaptureParams& params,
std::unique_ptr<Client> client) {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
DCHECK(!camera_device_delegate_);
if (!camera_device_ipc_thread_.Start()) {
std::string error_msg = "Failed to start device thread";
LOG(ERROR) << error_msg;
client->OnError(FROM_HERE, error_msg);
return;
}
camera_device_delegate_ = base::MakeUnique<CameraDeviceDelegate>(
device_descriptor_, camera_hal_delegate_,
camera_device_ipc_thread_.task_runner());
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraDeviceDelegate::AllocateAndStart,
camera_device_delegate_->GetWeakPtr(), params,
base::Passed(&client)));
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraDeviceDelegate::SetRotation,
camera_device_delegate_->GetWeakPtr(), rotation_));
}
void VideoCaptureDeviceArcChromeOS::StopAndDeAllocate() {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
if (!camera_device_delegate_) {
return;
}
// We do our best to allow the camera HAL cleanly shut down the device. In
// general we don't trust the camera HAL so if the device does not close in
// time we simply terminate the Mojo channel by resetting
// |camera_device_delegate_|.
base::WaitableEvent device_closed(
base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraDeviceDelegate::StopAndDeAllocate,
camera_device_delegate_->GetWeakPtr(),
base::Bind(
[](base::WaitableEvent* device_closed) {
device_closed->Signal();
},
base::Unretained(&device_closed))));
base::TimeDelta kWaitTimeoutSecs = base::TimeDelta::FromSeconds(3);
device_closed.TimedWait(kWaitTimeoutSecs);
camera_device_ipc_thread_.Stop();
camera_device_delegate_.reset();
}
void VideoCaptureDeviceArcChromeOS::TakePhoto(TakePhotoCallback callback) {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
DCHECK(camera_device_delegate_);
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraDeviceDelegate::TakePhoto,
camera_device_delegate_->GetWeakPtr(),
base::Passed(&callback)));
}
void VideoCaptureDeviceArcChromeOS::GetPhotoState(
GetPhotoStateCallback callback) {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraDeviceDelegate::GetPhotoState,
camera_device_delegate_->GetWeakPtr(),
base::Passed(&callback)));
}
void VideoCaptureDeviceArcChromeOS::SetPhotoOptions(
mojom::PhotoSettingsPtr settings,
SetPhotoOptionsCallback callback) {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE, base::Bind(&CameraDeviceDelegate::SetPhotoOptions,
camera_device_delegate_->GetWeakPtr(),
base::Passed(&settings), base::Passed(&callback)));
}
void VideoCaptureDeviceArcChromeOS::SetDisplayRotation(
const display::Display& display) {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
if (display.IsInternal())
SetRotation(display.rotation() * 90);
}
void VideoCaptureDeviceArcChromeOS::SetRotation(int rotation) {
DCHECK(capture_task_runner_->BelongsToCurrentThread());
if (!rotates_with_device_) {
rotation = 0;
} else if (lens_facing_ == VideoFacingMode::MEDIA_VIDEO_FACING_ENVIRONMENT) {
// Original frame when |rotation| = 0
// -----------------------
// | * |
// | * * |
// | * * |
// | ******* |
// | * * |
// | * * |
// -----------------------
//
// |rotation| = 90, this is what back camera sees
// -----------------------
// | ******** |
// | * **** |
// | * *** |
// | * *** |
// | * **** |
// | ******** |
// -----------------------
//
// |rotation| = 90, this is what front camera sees
// -----------------------
// | ******** |
// | **** * |
// | *** * |
// | *** * |
// | **** * |
// | ******** |
// -----------------------
//
// Therefore, for back camera, we need to rotate (360 - |rotation|).
rotation = (360 - rotation) % 360;
}
// Take into account camera orientation w.r.t. the display. External cameras
// would have camera_orientation_ as 0.
rotation_ = (rotation + camera_orientation_) % 360;
if (camera_device_ipc_thread_.IsRunning()) {
camera_device_ipc_thread_.task_runner()->PostTask(
FROM_HERE,
base::Bind(&CameraDeviceDelegate::SetRotation,
camera_device_delegate_->GetWeakPtr(), rotation_));
}
}
} // namespace media