| // 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/optional.h> |
| #include <base/stl_util.h> |
| #include <base/command_line.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.c_str(), s.length()); |
| 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.c_str(), s.length()); |
| } |
| } |
| } |
| |
| 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 |