Skip to content

Commit

Permalink
Rotate frames correctly for back camera
Browse files Browse the repository at this point in the history
In 90 and 270 degree tablet mode, back camera used to show upside-down
frames. We need to rotate the frames correctly for back cameras.

BUG=56762
TEST=Verify that back camera shows correct preview in all rotations.

Review-Url: https://codereview.chromium.org/2508803002
Cr-Commit-Position: refs/heads/master@{#436594}
  • Loading branch information
shenghao authored and Commit bot committed Dec 6, 2016
1 parent ad24ce2 commit fd4e73e
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 6 deletions.
3 changes: 3 additions & 0 deletions media/capture/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ component("capture") {
"video/file_video_capture_device.h",
"video/file_video_capture_device_factory.cc",
"video/file_video_capture_device_factory.h",
"video/linux/camera_facing_chromeos.cc",
"video/linux/camera_facing_chromeos.h",
"video/linux/v4l2_capture_delegate.cc",
"video/linux/v4l2_capture_delegate.h",
"video/linux/video_capture_device_chromeos.cc",
Expand Down Expand Up @@ -169,6 +171,7 @@ test("capture_unittests") {
"content/smooth_event_sampler_unittest.cc",
"content/video_capture_oracle_unittest.cc",
"video/fake_video_capture_device_unittest.cc",
"video/linux/camera_facing_chromeos_unittest.cc",
"video/mac/video_capture_device_factory_mac_unittest.mm",
"video/video_capture_device_unittest.cc",
]
Expand Down
185 changes: 185 additions & 0 deletions media/capture/video/linux/camera_facing_chromeos.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright 2016 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 "camera_facing_chromeos.h"

#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_piece.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>

namespace media {

namespace {

bool GetCameraId(const base::StringPiece& sub_key, int* camera_id) {
const base::StringPiece camera_id_prefix = "camera";
if (!sub_key.starts_with(camera_id_prefix))
return false;
return base::StringToInt(sub_key.substr(camera_id_prefix.size()), camera_id);
}
}

// /etc/camera/camera_characteristics.conf contains camera information which
// driver cannot provide.
static const char kCameraCharacteristicsConfigFile[] =
"/etc/camera/camera_characteristics.conf";
static const char kLensFacing[] = "lens_facing";
static const char kUsbVidPid[] = "usb_vid_pid";
static const char kUsbPath[] = "usb_path";

CameraFacingChromeOS::CameraFacingChromeOS() {
InitializeDeviceInfo(std::string(kCameraCharacteristicsConfigFile));
}

CameraFacingChromeOS::CameraFacingChromeOS(
const std::string& config_file_path) {
InitializeDeviceInfo(config_file_path);
}

CameraFacingChromeOS::~CameraFacingChromeOS() {}

CameraFacingChromeOS::LensFacing CameraFacingChromeOS::GetCameraFacing(
const std::string& device_id,
const std::string& model_id) const {
std::string usb_id = GetUsbId(device_id);
const auto& usb_id_to_camera_id_const = usb_id_to_camera_id_;
const auto& model_id_to_camera_id_const = model_id_to_camera_id_;
const auto& camera_id_to_facing_const = camera_id_to_facing_;
auto usb_id_iter = usb_id_to_camera_id_const.find(usb_id);
int camera_id;
if (usb_id_iter == usb_id_to_camera_id_const.end()) {
// Can't find Usb ID. Fall back to use model_id.
auto model_id_iter = model_id_to_camera_id_const.find(model_id);
if (model_id_iter == model_id_to_camera_id_const.end()) {
DLOG(ERROR) << "Can't find model ID in config file: " << model_id;
return kLensFacingDefault;
}
camera_id = model_id_iter->second;
} else {
camera_id = usb_id_iter->second;
}

auto camera_id_iter = camera_id_to_facing_const.find(camera_id);
if (camera_id_iter == camera_id_to_facing_const.end()) {
DLOG(ERROR) << "Can't find lens_facing of camera ID " << camera_id
<< " in config file";
return kLensFacingDefault;
}
return camera_id_iter->second;
}

std::string CameraFacingChromeOS::GetUsbId(const std::string& device_id) const {
// |device_id| is of the form "/dev/video2". We want to retrieve "video2"
// into |file_name|.
const std::string device_dir = "/dev/";
if (!base::StartsWith(device_id, device_dir, base::CompareCase::SENSITIVE)) {
DLOG(ERROR) << "device_id is invalid: " << device_id;
return std::string();
}
const std::string file_name = device_id.substr(device_dir.length());

// Usb ID can be obtained by "readlink /sys/class/video4linux/video2/device".
const std::string symlink =
base::StringPrintf("/sys/class/video4linux/%s/device", file_name.c_str());
base::FilePath symlinkTarget;
if (!base::ReadSymbolicLink(base::FilePath(symlink), &symlinkTarget)) {
DPLOG(ERROR) << "Failed to readlink: " << symlink;
return std::string();
}

// |symlinkTarget| is of the format "../../../A-B:C.D". Remove the path
// prefix.
base::StringPiece usb_part = symlinkTarget.BaseName().value();

// |usb_part| is of the format "A-B:C.D" or "A-B.C:D". We want everything
// before ":".
std::vector<base::StringPiece> usb_id_pieces = base::SplitStringPiece(
usb_part, ":", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);

if (usb_id_pieces.empty()) {
DLOG(ERROR) << "Error after split: " << usb_part;
return std::string();
}
return usb_id_pieces[0].as_string();
}

void CameraFacingChromeOS::InitializeDeviceInfo(
const std::string& config_file_path) {
const base::FilePath path(config_file_path);
std::string content;
if (!base::ReadFileToString(path, &content)) {
DPLOG(ERROR) << "ReadFileToString fails";
return;
}
const std::vector<base::StringPiece> lines = base::SplitStringPiece(
content, "\n", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);

for (const base::StringPiece& line : lines) {
if (line.starts_with("#")) // Ignore the comments that starts with "#".
continue;
const std::vector<base::StringPiece> key_value = base::SplitStringPiece(
line, "=", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);
if (key_value.size() != 2) {
DLOG(ERROR) << "Invalid line in config file: " << line;
continue;
}
const auto& key = key_value[0];
const auto& value = key_value[1];
const std::vector<base::StringPiece> sub_keys = base::SplitStringPiece(
key, ".", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);

if (sub_keys.size() < 1) {
DLOG(ERROR) << "No valid sub key exists. Line format is invalid: "
<< line;
continue;
}
int camera_id = 0;
if (!GetCameraId(sub_keys[0], &camera_id)) {
DLOG(ERROR) << "Invalid sub key for camera id: " << sub_keys[0];
continue;
}

if (sub_keys.size() == 2 && sub_keys[1] == kLensFacing) {
int lens_facing = -1;
if (!base::StringToInt(value, &lens_facing)) {
DLOG(ERROR) << "Invalid value for lens_facing: " << value;
continue;
}
switch (lens_facing) {
case LensFacing::FRONT:
camera_id_to_facing_[camera_id] = LensFacing::FRONT;
break;
case LensFacing::BACK:
camera_id_to_facing_[camera_id] = LensFacing::BACK;
break;
default:
DLOG(ERROR) << "Invalid value for lens_facing: " << lens_facing;
continue;
}
} else if (sub_keys.size() == 3 && sub_keys[2] == kUsbVidPid) {
if (value.empty()) {
DLOG(ERROR) << "model_id is empty";
continue;
}
model_id_to_camera_id_[value.as_string()] = camera_id;
} else if (sub_keys.size() == 3 && sub_keys[2] == kUsbPath) {
if (value.empty()) {
DLOG(ERROR) << "usb_path is empty";
continue;
}
usb_id_to_camera_id_[value.as_string()] = camera_id;
}
// Ignore unknown or unutilized attributes.
}
}

} // namespace media
81 changes: 81 additions & 0 deletions media/capture/video/linux/camera_facing_chromeos.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2016 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.

#ifndef MEDIA_CAPTURE_VIDEO_LINUX_CAMERA_FACING_CHROMEOS_H_
#define MEDIA_CAPTURE_VIDEO_LINUX_CAMERA_FACING_CHROMEOS_H_

#include <stddef.h>

#include <string>
#include <unordered_map>

#include <base/strings/string_piece.h>

namespace media {

// CameraFacingChromeOS reads the file /etc/camera/camera_characteristics.conf
// and populates |camera_id_to_facing_|, |usb_id_to_camera_id_| and
// |model_id_to_camera_id_|.
//
// Each line in the config file can be:
// 1. Empty line
// 2. Line starts with '#': comments
// 3. Line follows the format:
// camera[camera_id].[root_level_attribute]=[value] OR
// camera[camera_id].module[module_id].[module_level_attribute]=[value]
//
// There are several assumptions of the config file:
// 1. One camera ID has exactly one lens_facing attribute, at root level.
// 2. usb_path is specified at module level. usb_path may not present at all,
// but if it presents, the same usb_path does not appear accross different
// camera IDs.
// 3. usb_vid_pid is specified at module level. If usb_path does not present,
// each module needs to have one unique usb_vid_pid.
//
// Example of the config file:
// camera0.lens_facing=0
// camera0.sensor_orientation=0
// camera0.module0.usb_vid_pid=0123:4567
// camera0.module0.horizontal_view_angle=68.4
// camera0.module0.lens_info_available_focal_lengths=1.64
// camera0.module0.lens_info_minimum_focus_distance=0.22
// camera0.module0.lens_info_optimal_focus_distance=0.5
// camera0.module0.vertical_view_angle=41.6
// camera0.module1.usb_vid_pid=89ab:cdef
// camera0.module1.lens_info_available_focal_lengths=1.69,2
// camera1.lens_facing=1
// camera1.sensor_orientation=180
class CameraFacingChromeOS {
public:
enum LensFacing { FRONT = 0, BACK = 1 };

CameraFacingChromeOS();
CameraFacingChromeOS(const std::string& config_file_path);
~CameraFacingChromeOS();

// Get camera facing by specifying USB vid and pid and device path. |model_id|
// should be formatted as "vid:pid". |device_id| is something like
// "/dev/video2". It first tries to match usb path, obtained from |device_id|.
// If fails, |model_id| is then used.
// Returns LensFacing::FRONT or LensFacing::BACK.
// Default is LensFacing::FRONT.
LensFacing GetCameraFacing(const std::string& device_id,
const std::string& model_id) const;

static const LensFacing kLensFacingDefault = LensFacing::FRONT;

private:
std::string GetUsbId(const std::string& device_id) const;
// Read file content and populate |camera_id_to_facing_|,
// |usb_id_to_camera_id_| and |model_id_to_camera_id_|.
void InitializeDeviceInfo(const std::string& config_file_path);

std::unordered_map<int, LensFacing> camera_id_to_facing_;
std::unordered_map<std::string, int> usb_id_to_camera_id_;
std::unordered_map<std::string, int> model_id_to_camera_id_;
};

} // namespace media

#endif // MEDIA_CAPTURE_VIDEO_LINUX_CAMERA_FACING_CHROMEOS_H_
41 changes: 41 additions & 0 deletions media/capture/video/linux/camera_facing_chromeos_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2016 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 <string>

#include "media/capture/video/linux/camera_facing_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"

#include <base/files/file_util.h>
#include <base/files/file.h>

namespace media {

namespace {

const char kConfigFileContent[] =
"camera0.lens_facing=1\ncamera0.sensor_orientation=0\ncamera0.module0.usb_"
"vid_pid=04f2:b53a\n";
}

TEST(CameraFacingChromeOSTest, ParseSuccessfully) {
const char file_name[] = "fake_camera_characteristics.conf";
base::WriteFile(base::FilePath(file_name), kConfigFileContent,
sizeof(kConfigFileContent));

std::string file_name_str(file_name);
CameraFacingChromeOS camera_facing(file_name_str);
EXPECT_EQ(CameraFacingChromeOS::LensFacing::BACK,
camera_facing.GetCameraFacing(std::string("/dev/video2"),
std::string("04f2:b53a")));
}

TEST(CameraFacingChromeOSTest, ConfigFileNotExist) {
CameraFacingChromeOS camera_facing(std::string("file_not_exist"));
EXPECT_EQ(CameraFacingChromeOS::LensFacing::FRONT,
camera_facing.GetCameraFacing(std::string("/dev/video2"),
std::string("04f2:b53a")));
}

} // namespace media
Loading

0 comments on commit fd4e73e

Please sign in to comment.