blob: 1844e6a73f9210842ebdc3d133280375e04e3345 [file] [log] [blame]
// Copyright 2017 The Chromium OS 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 "camera3_test/camera3_perf_log.h"
#include <inttypes.h>
#include <numeric>
#include <base/command_line.h>
#include <base/containers/contains.h>
#include <base/optional.h>
#include <base/files/file_util.h>
#include <base/strings/stringprintf.h>
#include "cros-camera/common.h"
namespace camera3_test {
// static
Camera3PerfLog* Camera3PerfLog::GetInstance() {
static Camera3PerfLog perf;
return &perf;
}
Camera3PerfLog::~Camera3PerfLog() {
VLOGF_ENTER();
if (!base::CommandLine::ForCurrentProcess()->HasSwitch("output_log"))
return;
VLOGF(1) << "Outputing to log file: "
<< base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
"output_log");
base::FilePath file_path(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
"output_log"));
if (base::WriteFile(file_path, NULL, 0) < 0) {
LOGF(ERROR) << "Error writing to file " << file_path.value();
return;
}
for (const auto& cam_id_name : camera_name_map_) {
const std::string s =
base::StringPrintf("Camera: %s\n", cam_id_name.second.c_str());
base::AppendToFile(file_path, s);
const std::vector<std::pair<std::string, int64_t>> perf_logs =
CollectPerfLogs(cam_id_name.first);
for (const auto& perf_log : perf_logs) {
const std::string s = base::StringPrintf(
"%s: %" PRId64 " us\n", perf_log.first.c_str(), perf_log.second);
base::AppendToFile(file_path, s);
}
}
}
void Camera3PerfLog::SetCameraNameMap(
const std::map<int, std::string>& camera_name_map) {
camera_name_map_ = camera_name_map;
}
std::string Camera3PerfLog::GetCameraNameForId(int id) {
auto it = camera_name_map_.find(id);
return it != camera_name_map_.end() ? it->second : std::to_string(id);
}
bool Camera3PerfLog::UpdateDeviceEvent(int cam_id,
DeviceEvent event,
base::TimeTicks time) {
VLOGF(1) << "Updating device event " << static_cast<int>(event)
<< " of camera " << cam_id << " at " << time << " us";
if (base::Contains(device_events_[cam_id], event)) {
LOGF(ERROR) << "Device event " << static_cast<int>(event) << " of camera "
<< cam_id << " is being updated multiple times";
return false;
}
device_events_[cam_id][event] = time;
return true;
}
bool Camera3PerfLog::UpdateFrameEvent(int cam_id,
uint32_t frame_number,
FrameEvent event,
base::TimeTicks time) {
VLOGF(1) << "Updating frame event " << static_cast<int>(event)
<< " of camera " << cam_id << " for frame number " << frame_number
<< " at " << time << " us";
if (base::Contains(frame_events_[cam_id][frame_number], event)) {
LOGF(ERROR) << "Frame event " << static_cast<int>(event) << " of camera "
<< cam_id << " frame number " << frame_number
<< " is being updated multiple times";
return false;
}
frame_events_[cam_id][frame_number][event] = time;
return true;
}
std::vector<std::pair<std::string, int64_t>> Camera3PerfLog::CollectPerfLogs(
int cam_id) const {
std::vector<std::pair<std::string, int64_t>> perf_logs;
// Collect perf logs from device events.
constexpr std::pair<DeviceEvent, const char*> kDeviceEventNameMap[] = {
{DeviceEvent::OPENED, "device_open"},
{DeviceEvent::PREVIEW_STARTED, "preview_start"},
};
if (base::Contains(device_events_, cam_id)) {
const std::map<DeviceEvent, base::TimeTicks>& events =
device_events_.at(cam_id);
if (!base::Contains(events, DeviceEvent::OPENING)) {
LOGF(ERROR) << "Failed to find device opening performance log";
return perf_logs;
}
const base::TimeTicks start_ticks = events.at(DeviceEvent::OPENING);
for (const auto& event_name : kDeviceEventNameMap) {
if (!base::Contains(events, event_name.first))
continue;
const base::TimeTicks end_ticks = events.at(event_name.first);
perf_logs.emplace_back(event_name.second,
(end_ticks - start_ticks).InMicroseconds());
}
// The first still image captured time.
if (base::Contains(frame_events_, cam_id)) {
const auto it = std::find_if(
frame_events_.at(cam_id).begin(), frame_events_.at(cam_id).end(),
[](const auto& fn_events) {
return base::Contains(fn_events.second,
FrameEvent::STILL_CAPTURE_RESULT);
});
if (it != frame_events_.at(cam_id).end()) {
const base::TimeTicks end_ticks =
it->second.at(FrameEvent::STILL_CAPTURE_RESULT);
perf_logs.emplace_back("still_image_capture",
(end_ticks - start_ticks).InMicroseconds());
}
}
}
// Collect perf logs from frame events.
constexpr std::pair<FrameEvent, const char*> kFrameEventNameMap[] = {
{FrameEvent::PREVIEW_RESULT, "preview_latency"},
{FrameEvent::STILL_CAPTURE_RESULT, "still_capture_latency"},
{FrameEvent::VIDEO_RECORD_RESULT, "video_record_latency"},
};
std::map<std::string, std::vector<int64_t>> frame_perf_logs;
if (base::Contains(frame_events_, cam_id)) {
for (const auto& it : frame_events_.at(cam_id)) {
const uint32_t frame_number = it.first;
const std::map<FrameEvent, base::TimeTicks>& events = it.second;
if (!base::Contains(events, FrameEvent::SHUTTER)) {
VLOGF(1) << "No shutter event found for frame " << frame_number
<< " of camera " << cam_id;
continue;
}
const base::TimeTicks start_ticks = events.at(FrameEvent::SHUTTER);
for (const auto& event_name : kFrameEventNameMap) {
if (!base::Contains(events, event_name.first))
continue;
const base::TimeTicks end_ticks = events.at(event_name.first);
frame_perf_logs[event_name.second].push_back(
(end_ticks - start_ticks).InMicroseconds());
}
}
for (const auto& event_name : kFrameEventNameMap) {
if (!base::Contains(frame_perf_logs, event_name.second))
continue;
const std::vector<int64_t>& logs = frame_perf_logs.at(event_name.second);
if (!logs.empty()) {
VLOGF(1) << "Calculate " << event_name.second << " from " << logs.size()
<< " frames";
perf_logs.emplace_back(
event_name.second,
std::accumulate(logs.begin(), logs.end(), 0) / logs.size());
}
}
// Still capture shot to shot times.
std::vector<int64_t> logs;
base::Optional<base::TimeTicks> start_ticks;
for (const auto& it : frame_events_.at(cam_id)) {
if (!base::Contains(it.second, FrameEvent::STILL_CAPTURE_RESULT))
continue;
const base::TimeTicks end_ticks =
it.second.at(FrameEvent::STILL_CAPTURE_RESULT);
if (start_ticks)
logs.push_back((end_ticks - *start_ticks).InMicroseconds());
start_ticks.emplace(end_ticks);
}
if (!logs.empty()) {
VLOGF(1) << "Calculate shot_to_shot time from " << logs.size()
<< " samples";
perf_logs.emplace_back(
"shot_to_shot",
std::accumulate(logs.begin(), logs.end(), 0) / logs.size());
}
// Portrait mode time.
for (const auto& it : frame_events_.at(cam_id)) {
if (!base::Contains(it.second, FrameEvent::PORTRAIT_MODE_STARTED) ||
!base::Contains(it.second, FrameEvent::PORTRAIT_MODE_ENDED))
continue;
const base::TimeTicks start_ticks =
it.second.at(FrameEvent::PORTRAIT_MODE_STARTED);
const base::TimeTicks end_ticks =
it.second.at(FrameEvent::PORTRAIT_MODE_ENDED);
perf_logs.emplace_back("portrait_mode",
(end_ticks - start_ticks).InMicroseconds());
break;
}
}
return perf_logs;
}
} // namespace camera3_test